comparison browser_side/panels.py @ 195:dd27072d8ae0

browser side: widgets refactoring: - moved base widgets in a base_widget module - widgets class now register themselves their Drag/Drop type
author Goffi <goffi@goffi.org>
date Mon, 04 Mar 2013 23:01:57 +0100
parents 6198be95a39c
children 39311c7dad77
comparison
equal deleted inserted replaced
194:6198be95a39c 195:dd27072d8ae0
22 import pyjd # this is dummy in pyjs 22 import pyjd # this is dummy in pyjs
23 from pyjamas.ui.SimplePanel import SimplePanel 23 from pyjamas.ui.SimplePanel import SimplePanel
24 from pyjamas.ui.AbsolutePanel import AbsolutePanel 24 from pyjamas.ui.AbsolutePanel import AbsolutePanel
25 from pyjamas.ui.VerticalPanel import VerticalPanel 25 from pyjamas.ui.VerticalPanel import VerticalPanel
26 from pyjamas.ui.HorizontalPanel import HorizontalPanel 26 from pyjamas.ui.HorizontalPanel import HorizontalPanel
27 from pyjamas.ui.ScrollPanel import ScrollPanel
28 from pyjamas.ui.TabPanel import TabPanel
29 from pyjamas.ui.HTMLPanel import HTMLPanel 27 from pyjamas.ui.HTMLPanel import HTMLPanel
30 from pyjamas.ui.FlexTable import FlexTable
31 from pyjamas.ui.Frame import Frame 28 from pyjamas.ui.Frame import Frame
32 from pyjamas.ui.TextArea import TextArea 29 from pyjamas.ui.TextArea import TextArea
33 from pyjamas.ui.TextBox import TextBox
34 from pyjamas.ui.Label import Label 30 from pyjamas.ui.Label import Label
35 from pyjamas.ui.Button import Button 31 from pyjamas.ui.Button import Button
36 from pyjamas.ui.HTML import HTML 32 from pyjamas.ui.HTML import HTML
37 from pyjamas.ui.Image import Image 33 from pyjamas.ui.Image import Image
38 from pyjamas.ui.DropWidget import DropWidget
39 from pyjamas.ui.ClickListener import ClickHandler 34 from pyjamas.ui.ClickListener import ClickHandler
40 from pyjamas.ui.KeyboardListener import KEY_ENTER 35 from pyjamas.ui.KeyboardListener import KEY_ENTER
41 from pyjamas.ui.MouseListener import MouseHandler 36 from pyjamas.ui.MouseListener import MouseHandler
42 from pyjamas.ui import HasAlignment
43 from pyjamas.Timer import Timer 37 from pyjamas.Timer import Timer
44 from pyjamas import Window
45 from pyjamas import DOM 38 from pyjamas import DOM
46 from card_game import CardPanel 39 from card_game import CardPanel
47 from radiocol import RadioColPanel 40 from radiocol import RadioColPanel
48 from menu import Menu 41 from menu import Menu
49 from jid import JID 42 from jid import JID
50 from tools import html_sanitize 43 from tools import html_sanitize
51 from datetime import datetime 44 from datetime import datetime
52 from time import time 45 from time import time
53 import dialog 46 import dialog
47 import base_widget
48 from pyjamas import Window
54 from __pyjamas__ import doc 49 from __pyjamas__ import doc
55
56 class DropCell(DropWidget):
57 """Cell in the middle grid which replace itself with the dropped widget on DnD"""
58
59 def __init__(self, host):
60 DropWidget.__init__(self)
61 self.host = host
62 self.setStyleName('dropCell')
63
64 def onDragEnter(self, event):
65 self.addStyleName('dragover')
66 DOM.eventPreventDefault(event)
67
68 def onDragLeave(self, event):
69 if event.clientX <= self.getAbsoluteLeft() or event.clientY <= self.getAbsoluteTop() or\
70 event.clientX >= self.getAbsoluteLeft() + self.getOffsetWidth()-1 or event.clientY >= self.getAbsoluteTop() + self.getOffsetHeight()-1:
71 #We check that we are inside widget's box, and we don't remove the style in this case because
72 #if the mouse is over a widget inside the DropWidget, if will leave the DropWidget, and we
73 #don't want that
74 self.removeStyleName('dragover')
75
76 def onDragOver(self, event):
77 DOM.eventPreventDefault(event)
78
79 def _getCellAndRow(self, grid, event):
80 """Return cell and row index where the event is occuring"""
81 cell = grid.getEventTargetCell(event)
82 row = DOM.getParent(cell)
83 return (row.rowIndex, cell.cellIndex)
84
85
86 def onDrop(self, event):
87 dt = event.dataTransfer
88 #'text', 'text/plain', and 'Text' are equivalent.
89 try:
90 item, item_type = dt.getData("text/plain").split('\n') #Workaround for webkit, only text/plain seems to be managed
91 if item_type and item_type[-1] == '\0': #Workaround for what looks like a pyjamas bug: the \0 should not be there, and
92 item_type = item_type[:-1] # .strip('\0') and .replace('\0','') don't work. TODO: check this and fill a bug report
93 #item_type = dt.getData("type")
94 print "message: %s" % item
95 print "type: %s" % item_type
96 except:
97 print "no message found"
98 item='&nbsp;'
99 item_type = None
100 DOM.eventPreventDefault(event)
101 if item_type=="GROUP":
102 _new_panel = MicroblogPanel(self.host, [item])
103 _new_panel.setAcceptedGroup(item)
104 self.host.FillMicroblogPanel(_new_panel)
105 self.host.bridge.call('getMassiveLastMblogs', _new_panel.massiveInsert, 'GROUP', [item], 10)
106 elif item_type=="CONTACT":
107 _contact = JID(item)
108 self.host.contact_panel.setContactMessageWaiting(_contact.bare, False)
109 _new_panel = ChatPanel(self.host, _contact)
110 _new_panel.historyPrint()
111 elif item_type=="CONTACT_TITLE":
112 _new_panel = MicroblogPanel(self.host, [])
113 self.host.FillMicroblogPanel(_new_panel)
114 self.host.bridge.call('getMassiveLastMblogs', _new_panel.massiveInsert, 'ALL', [], 10)
115 else:
116 return False
117 if isinstance(self, LiberviaWidget):
118 self.host.unregisterWidget(self)
119 self.onQuit()
120 if not isinstance(_new_panel, LiberviaWidget):
121 print ('WARNING: droping an object which is not a class of LiberviaWidget')
122 _flextable = self.getParent()
123 _widgetspanel = _flextable.getParent().getParent()
124 row_idx, cell_idx = self._getCellAndRow(_flextable, event)
125 if self.host.getSelected == self:
126 self.host.setSelected(None)
127 _widgetspanel.changeWidget(row_idx, cell_idx, _new_panel)
128 """_unempty_panels = filter(lambda wid:not isinstance(wid,EmptyWidget),list(_flextable))
129 _width = 90/float(len(_unempty_panels) or 1)
130 #now we resize all the cell of the column
131 for panel in _unempty_panels:
132 td_elt = panel.getElement().parentNode
133 DOM.setStyleAttribute(td_elt, "width", "%s%%" % _width)"""
134 #FIXME: delete object ? Check the right way with pyjamas
135
136 class LiberviaWidget(DropCell, VerticalPanel, ClickHandler):
137 """Libervia's widget which can replace itself with a dropped widget on DnD"""
138
139 def __init__(self, host, title='', selectable=False):
140 """Init the widget
141 @param host: SatWebFrontend object
142 @param title: title show in the header of the widget
143 @param selectable: True is widget can be selected by user"""
144 VerticalPanel.__init__(self)
145 DropCell.__init__(self, host)
146 ClickHandler.__init__(self)
147 self.__selectable = selectable
148 self.__title_id = HTMLPanel.createUniqueId()
149 self.__setting_button_id = HTMLPanel.createUniqueId()
150 self.__close_button_id = HTMLPanel.createUniqueId()
151 header = AbsolutePanel()
152 self.__title = Label(title)
153 self.__title.setStyleName('widgetHeader_title')
154 header.add(self.__title)
155 button_group_wrapper = SimplePanel()
156 button_group_wrapper.setStyleName('widgetHeader_buttonsWrapper')
157 button_group = HorizontalPanel()
158 button_group.setStyleName('widgetHeader_buttonGroup')
159 setting_button = Image("media/icons/misc/settings.png")
160 setting_button.setStyleName('widgetHeader_settingButton')
161 setting_button.addClickListener(self.onSetting)
162 close_button = Image("media/icons/misc/close.png")
163 close_button.setStyleName('widgetHeader_closeButton')
164 close_button.addClickListener(self.onClose)
165 button_group.add(setting_button)
166 button_group.add(close_button)
167 button_group_wrapper.setWidget(button_group)
168 header.add(button_group_wrapper)
169 self.add(header)
170 header.addStyleName('widgetHeader')
171 self.setSize('100%', '100%')
172 self.addStyleName('widget')
173 if self.__selectable:
174 self.addClickListener(self)
175 self.host.registerWidget(self)
176
177 def _getWidgetsPanel(self):
178 current = self
179 while current is not None and current.__class__ != WidgetsPanel:
180 current = current.getParent()
181 if current is None:
182 print "Error: can't find WidgetsPanel"
183 return current
184
185 def onClick(self, sender):
186 self.host.setSelected(self)
187
188 def onClose(self, sender):
189 """ Called when the close button is pushed """
190 _widgetspanel = self._getWidgetsPanel()
191 _widgetspanel.removeWidget(self)
192 self.onQuit()
193
194 def onQuit(self):
195 """ Called when the widget is actually ending """
196 pass
197
198 def onSetting(self, sender):
199 widpanel = self._getWidgetsPanel()
200 row, col = widpanel.getIndex(self)
201 body = VerticalPanel()
202
203 #colspan & rowspan
204 colspan = widpanel.getColSpan(row, col)
205 rowspan = widpanel.getRowSpan(row, col)
206 def onColSpanChange(value):
207 widpanel.setColSpan(row, col, value)
208 def onRowSpanChange(value):
209 widpanel.setRowSpan(row, col, value)
210 colspan_setter = dialog.IntSetter("Columns span", colspan)
211 colspan_setter.addValueChangeListener(onColSpanChange)
212 colspan_setter.setWidth('100%')
213 rowspan_setter = dialog.IntSetter("Rows span", rowspan)
214 rowspan_setter.addValueChangeListener(onRowSpanChange)
215 rowspan_setter.setWidth('100%')
216 body.add(colspan_setter)
217 body.add(rowspan_setter)
218
219 #size
220 width_str = self.getWidth()
221 if width_str.endswith('px'):
222 width=int(width_str[:-2])
223 else:
224 width = 0
225 height_str = self.getHeight()
226 if height_str.endswith('px'):
227 height=int(height_str[:-2])
228 else:
229 height = 0
230 def onWidthChange(value):
231 if not value:
232 self.setWidth('100%')
233 else:
234 self.setWidth('%dpx' % value)
235 def onHeightChange(value):
236 if not value:
237 self.setHeight('100%')
238 else:
239 self.setHeight('%dpx' % value)
240 width_setter = dialog.IntSetter("width (0=auto)", width)
241 width_setter.addValueChangeListener(onWidthChange)
242 width_setter.setWidth('100%')
243 height_setter = dialog.IntSetter("height (0=auto)", height)
244 height_setter.addValueChangeListener(onHeightChange)
245 height_setter.setHeight('100%')
246 body.add(width_setter)
247 body.add(height_setter)
248
249 #reset
250 def onReset(sender):
251 colspan_setter.setValue(1)
252 rowspan_setter.setValue(1)
253 width_setter.setValue(0)
254 height_setter.setValue(0)
255
256 reset_bt = Button("Reset", onReset)
257 body.add(reset_bt)
258 body.setCellHorizontalAlignment(reset_bt, HasAlignment.ALIGN_CENTER)
259
260 _dialog = dialog.GenericDialog("Widget setting", body)
261 _dialog.show()
262
263 def setTitle(self, text):
264 """change the title in the header of the widget
265 @param text: text of the new title"""
266 self.__title.setText(text)
267
268 def isSelectable(self):
269 return self.__selectable
270
271 def setSelectable(self, selectable):
272 if not self.__selectable:
273 try:
274 self.removeClickListener(self)
275 except ValueError:
276 pass
277 if self.selectable and not self in self._clickListeners:
278 self.addClickListener(self)
279 self.__selectable = selectable
280
281 def setWidget(self, widget, scrollable=True):
282 """Set the widget that will be in the body of the LiberviaWidget
283 @param widget: widget to put in the body
284 @param scrollable: if true, the widget will be in a ScrollPanelWrapper"""
285 if scrollable:
286 _scrollpanelwrapper = ScrollPanelWrapper()
287 _scrollpanelwrapper.setStyleName('widgetBody')
288 _scrollpanelwrapper.setWidget(widget)
289 body_wid = _scrollpanelwrapper
290 else:
291 body_wid = widget
292 self.add(body_wid)
293 self.setCellHeight(body_wid, '100%')
294
295 def doDetachChildren(self):
296 #We need to force the use of a panel subclass method here,
297 #for the same reason as doAttachChildren
298 VerticalPanel.doDetachChildren(self)
299
300 def doAttachChildren(self):
301 #We need to force the use of a panel subclass method here, else
302 #the event will not propagate to children
303 VerticalPanel.doAttachChildren(self)
304
305 class ScrollPanelWrapper(SimplePanel):
306 """Scroll Panel like component, wich use the full available space
307 to work around percent size issue, it use some of the ideas found
308 here: http://code.google.com/p/google-web-toolkit/issues/detail?id=316
309 specially in code given at comment #46, thanks to Stefan Bachert"""
310
311 def __init__(self, *args, **kwargs):
312 SimplePanel.__init__(self)
313 self.spanel = ScrollPanel(*args, **kwargs)
314 SimplePanel.setWidget(self, self.spanel)
315 DOM.setStyleAttribute(self.getElement(), "position", "relative")
316 DOM.setStyleAttribute(self.getElement(), "top", "0px")
317 DOM.setStyleAttribute(self.getElement(), "left", "0px")
318 DOM.setStyleAttribute(self.getElement(), "width", "100%")
319 DOM.setStyleAttribute(self.getElement(), "height", "100%")
320 DOM.setStyleAttribute(self.spanel.getElement(), "position", "absolute")
321 DOM.setStyleAttribute(self.spanel.getElement(), "width", "100%")
322 DOM.setStyleAttribute(self.spanel.getElement(), "height", "100%")
323
324 def setWidget(self, widget):
325 self.spanel.setWidget(widget)
326
327 def setScrollPosition(self, position):
328 self.spanel.setScrollPosition(position)
329
330 def scrollToBottom(self):
331 self.setScrollPosition(self.spanel.getElement().scrollHeight)
332
333 class EmptyWidget(DropCell, SimplePanel):
334 """Empty dropable panel"""
335
336 def __init__(self, host):
337 SimplePanel.__init__(self)
338 DropCell.__init__(self, host)
339 #self.setWidget(HTML(''))
340 self.setSize('100%','100%')
341
342 class BorderWidget(EmptyWidget):
343 def __init__(self, host):
344 EmptyWidget.__init__(self, host)
345 self.addStyleName('borderPanel')
346
347 class LeftBorderWidget(BorderWidget):
348 def __init__(self, host):
349 BorderWidget.__init__(self, host)
350 self.addStyleName('leftBorderWidget')
351
352 class RightBorderWidget(BorderWidget):
353 def __init__(self, host):
354 BorderWidget.__init__(self, host)
355 self.addStyleName('rightBorderWidget')
356
357 class BottomBorderWidget(BorderWidget):
358 def __init__(self, host):
359 BorderWidget.__init__(self, host)
360 self.addStyleName('bottomBorderWidget')
361 50
362 class UniBoxPanel(SimplePanel): 51 class UniBoxPanel(SimplePanel):
363 """Panel containing the UniBox""" 52 """Panel containing the UniBox"""
364 53
365 def __init__(self, host): 54 def __init__(self, host):
547 """Change the avatar of the entry 236 """Change the avatar of the entry
548 @param new_avatar: path to the new image""" 237 @param new_avatar: path to the new image"""
549 self.avatar.setUrl(new_avatar) 238 self.avatar.setUrl(new_avatar)
550 239
551 240
552 class MicroblogPanel(LiberviaWidget): 241 class MicroblogPanel(base_widget.LiberviaWidget):
553 242
554 def __init__(self, host, accepted_groups): 243 def __init__(self, host, accepted_groups):
555 """Panel used to show microblog 244 """Panel used to show microblog
556 @param accepted_groups: groups displayed in this panel, if empty, show all microblogs from all contacts 245 @param accepted_groups: groups displayed in this panel, if empty, show all microblogs from all contacts
557 """ 246 """
558 LiberviaWidget.__init__(self, host, ", ".join(accepted_groups)) 247 base_widget.LiberviaWidget.__init__(self, host, ", ".join(accepted_groups))
559 #ScrollPanelWrapper.__init__(self) 248 #base_widget.ScrollPanelWrapper.__init__(self)
560 #DropCell.__init__(self) 249 #DropCell.__init__(self)
561 self.accepted_groups = accepted_groups 250 self.accepted_groups = accepted_groups
562 self.entries = {} 251 self.entries = {}
563 self.vpanel = VerticalPanel() 252 self.vpanel = VerticalPanel()
564 self.vpanel.setStyleName('microblogPanel') 253 self.vpanel.setStyleName('microblogPanel')
565 self.setWidget(self.vpanel) 254 self.setWidget(self.vpanel)
255
256 @classmethod
257 def registerClass(cls):
258 base_widget.LiberviaWidget.addDropKey("GROUP", cls.onDropCreateGroup)
259 base_widget.LiberviaWidget.addDropKey("CONTACT_TITLE", cls.onDropCreateMeta)
260
261 @classmethod
262 def onDropCreateGroup(cls, host, item):
263 _new_panel = MicroblogPanel(host, [item]) #XXX: pyjamas doesn't support use of cls directly
264 _new_panel.setAcceptedGroup(item)
265 host.FillMicroblogPanel(_new_panel)
266 host.bridge.call('getMassiveLastMblogs', _new_panel.massiveInsert, 'GROUP', [item], 10)
267 return _new_panel
268
269 @classmethod
270 def onDropCreateMeta(cls, host, item):
271 _new_panel = MicroblogPanel(host, []) #XXX: pyjamas doesn't support use of cls directly
272 host.FillMicroblogPanel(_new_panel)
273 host.bridge.call('getMassiveLastMblogs', _new_panel.massiveInsert, 'ALL', [], 10)
274 return _new_panel
566 275
567 def accept_all(self): 276 def accept_all(self):
568 return not self.accepted_groups #we accept every microblog only if we are not filtering by groups 277 return not self.accepted_groups #we accept every microblog only if we are not filtering by groups
569 278
570 def getEntries(self): 279 def getEntries(self):
702 411
703 def clear(self): 412 def clear(self):
704 self.occupants_list.clear() 413 self.occupants_list.clear()
705 AbsolutePanel.clear(self) 414 AbsolutePanel.clear(self)
706 415
707 class ChatPanel(LiberviaWidget): 416 class ChatPanel(base_widget.LiberviaWidget):
708 417
709 def __init__(self, host, target, type_='one2one'): 418 def __init__(self, host, target, type_='one2one'):
710 """Panel used for conversation (one 2 one or group chat) 419 """Panel used for conversation (one 2 one or group chat)
711 @param host: SatWebFrontend instance 420 @param host: SatWebFrontend instance
712 @param target: entity (JID) with who we have a conversation (contact's jid for one 2 one chat, or MUC room) 421 @param target: entity (JID) with who we have a conversation (contact's jid for one 2 one chat, or MUC room)
713 @param type: one2one for simple conversation, group for MUC""" 422 @param type: one2one for simple conversation, group for MUC"""
714 LiberviaWidget.__init__(self, host, target.bare, selectable = True) 423 base_widget.LiberviaWidget.__init__(self, host, target.bare, selectable = True)
715 self.vpanel = VerticalPanel() 424 self.vpanel = VerticalPanel()
716 self.vpanel.setSize('100%','100%') 425 self.vpanel.setSize('100%','100%')
717 self.type = type_ 426 self.type = type_
718 self.nick = None 427 self.nick = None
719 if not target: 428 if not target:
728 self.occupants_list = OccupantsList() 437 self.occupants_list = OccupantsList()
729 chat_area.add(self.occupants_list) 438 chat_area.add(self.occupants_list)
730 self.__body.add(chat_area) 439 self.__body.add(chat_area)
731 self.content = AbsolutePanel() 440 self.content = AbsolutePanel()
732 self.content.setStyleName('chatContent') 441 self.content.setStyleName('chatContent')
733 self.content_scroll = ScrollPanelWrapper(self.content) 442 self.content_scroll = base_widget.ScrollPanelWrapper(self.content)
734 chat_area.add(self.content_scroll) 443 chat_area.add(self.content_scroll)
735 chat_area.setCellWidth(self.content_scroll, '100%') 444 chat_area.setCellWidth(self.content_scroll, '100%')
736 self.vpanel.add(self.__body) 445 self.vpanel.add(self.__body)
737 self.addStyleName('chatPanel') 446 self.addStyleName('chatPanel')
738 self.setWidget(self.vpanel) 447 self.setWidget(self.vpanel)
739 448
740 """def doDetachChildren(self): 449 """def doDetachChildren(self):
741 #We need to force the use of a panel subclass method here, 450 #We need to force the use of a panel subclass method here,
742 #for the same reason as doAttachChildren 451 #for the same reason as doAttachChildren
743 ScrollPanelWrapper.doDetachChildren(self) 452 base_widget.ScrollPanelWrapper.doDetachChildren(self)
744 453
745 def doAttachChildren(self): 454 def doAttachChildren(self):
746 #We need to force the use of a panel subclass method here, else 455 #We need to force the use of a panel subclass method here, else
747 #the event will not propagate to children 456 #the event will not propagate to children
748 ScrollPanelWrapper.doAttachChildren(self)""" 457 base_widget.ScrollPanelWrapper.doAttachChildren(self)"""
458
459 @classmethod
460 def registerClass(cls):
461 base_widget.LiberviaWidget.addDropKey("CONTACT", cls.onDropCreate)
462
463 @classmethod
464 def onDropCreate(cls, host, item):
465 _contact = JID(item)
466 host.contact_panel.setContactMessageWaiting(_contact.bare, False)
467 _new_panel = ChatPanel(host, _contact) #XXX: pyjamas doesn't seems to support creating with cls directly
468 _new_panel.historyPrint()
469 return _new_panel
749 470
750 def onQuit(self): 471 def onQuit(self):
751 LiberviaWidget.onQuit(self) 472 base_widget.LiberviaWidget.onQuit(self)
752 if self.type == 'group': 473 if self.type == 'group':
753 self.host.bridge.call('mucLeave', None, self.target.bare) 474 self.host.bridge.call('mucLeave', None, self.target.bare)
754 475
755 476
756 def setUserNick(self, nick): 477 def setUserNick(self, nick):
831 if game_type=="Tarot": 552 if game_type=="Tarot":
832 return self.tarot_panel 553 return self.tarot_panel
833 elif game_type=="RadioCol": 554 elif game_type=="RadioCol":
834 return self.radiocol_panel 555 return self.radiocol_panel
835 556
836 class WebPanel(LiberviaWidget): 557 class WebPanel(base_widget.LiberviaWidget):
837 """ (mini)browser like widget """ 558 """ (mini)browser like widget """
838 559
839 def __init__(self, host, url=None): 560 def __init__(self, host, url=None):
840 """ 561 """
841 @param host: SatWebFrontend instance 562 @param host: SatWebFrontend instance
842 """ 563 """
843 LiberviaWidget.__init__(self, host) 564 base_widget.LiberviaWidget.__init__(self, host)
844 self._vpanel = VerticalPanel() 565 self._vpanel = VerticalPanel()
845 self._vpanel.setSize('100%', '100%') 566 self._vpanel.setSize('100%', '100%')
846 self._url = dialog.ExtTextBox(enter_cb = self.onUrlClick) 567 self._url = dialog.ExtTextBox(enter_cb = self.onUrlClick)
847 self._url.setText(url or "") 568 self._url.setText(url or "")
848 self._url.setWidth('100%') 569 self._url.setWidth('100%')
862 self.setWidget(self._vpanel) 583 self.setWidget(self._vpanel)
863 584
864 def onUrlClick(self, sender): 585 def onUrlClick(self, sender):
865 self._frame.setUrl(self._url.getText()) 586 self._frame.setUrl(self._url.getText())
866 587
867 class WidgetsPanel(ScrollPanelWrapper):
868
869 def __init__(self, host, locked = False):
870 ScrollPanelWrapper.__init__(self)
871 self.setSize('100%', '100%')
872 self.host = host
873 self.locked = locked #if True: tab will not be removed when there are no more widgets inside
874 self.selected = None
875 self.flextable = FlexTable()
876 self.flextable.setSize('100%','100%')
877 self.setWidget(self.flextable)
878 self.setStyleName('widgetsPanel')
879 _bottom = BottomBorderWidget(self.host)
880 self.flextable.setWidget(0, 0, _bottom) #There will be always an Empty widget on the last row,
881 #dropping a widget there will add a new row
882 td_elt = _bottom.getElement().parentNode
883 DOM.setStyleAttribute(td_elt, "height", "1px") #needed so the cell adapt to the size of the border (specially in webkit)
884 self._max_cols = 1 #give the maximum number of columns i a raw
885
886 def isLocked(self):
887 return self.locked
888
889 def changeWidget(self, row, col, wid):
890 """Change the widget in the given location, add row or columns when necessary"""
891 print "changing widget:", wid, row, col
892 last_row = max(0, self.flextable.getRowCount()-1)
893 try:
894 prev_wid = self.flextable.getWidget(row, col)
895 except:
896 print "ERROR: Trying to change an unexisting widget !"
897 return
898
899
900 cellFormatter = self.flextable.getFlexCellFormatter()
901
902 if isinstance(prev_wid, BorderWidget):
903 #We are on a border, we must create a row and/or columns
904 print "BORDER WIDGET"
905 prev_wid.removeStyleName('dragover')
906
907 if isinstance(prev_wid, BottomBorderWidget):
908 #We are on the bottom border, we create a new row
909 self.flextable.insertRow(last_row)
910 self.flextable.setWidget(last_row, 0, LeftBorderWidget(self.host))
911 self.flextable.setWidget(last_row, 1, wid)
912 self.flextable.setWidget(last_row, 2, RightBorderWidget(self.host))
913 cellFormatter.setHorizontalAlignment(last_row, 2, HasAlignment.ALIGN_RIGHT)
914 row = last_row
915
916 elif isinstance(prev_wid, LeftBorderWidget):
917 if col!=0:
918 print "ERROR: LeftBorderWidget must be on the first column !"
919 return
920 self.flextable.insertCell(row, col+1)
921 self.flextable.setWidget(row, 1, wid)
922
923 elif isinstance(prev_wid, RightBorderWidget):
924 if col!=self.flextable.getCellCount(row)-1:
925 print "ERROR: RightBorderWidget must be on the last column !"
926 return
927 self.flextable.insertCell(row, col)
928 self.flextable.setWidget(row, col, wid)
929
930 else:
931 prev_wid.removeFromParent()
932 self.flextable.setWidget(row, col, wid)
933
934 _max_cols = max(self._max_cols, self.flextable.getCellCount(row))
935 if _max_cols != self._max_cols:
936 self._max_cols = _max_cols
937 self._sizesAdjust()
938
939 def _sizesAdjust(self):
940 cellFormatter = self.flextable.getFlexCellFormatter()
941 width = 100.0/max(1, self._max_cols-2) #we don't count the borders
942
943 for row_idx in xrange(self.flextable.getRowCount()):
944 for col_idx in xrange(self.flextable.getCellCount(row_idx)):
945 _widget = self.flextable.getWidget(row_idx, col_idx)
946 if not isinstance(_widget, BorderWidget):
947 td_elt = _widget.getElement().parentNode
948 DOM.setStyleAttribute(td_elt, "width", "%.2f%%" % width)
949
950 last_row = max(0, self.flextable.getRowCount()-1)
951 cellFormatter.setColSpan(last_row, 0, self._max_cols)
952
953 def addWidget(self, wid):
954 """Add a widget to a new cell on the next to last row"""
955 last_row = max(0, self.flextable.getRowCount()-1)
956 print "putting widget %s at %d, %d" % (wid, last_row, 0)
957 self.changeWidget(last_row, 0, wid)
958
959 def removeWidget(self, wid):
960 """Remove a widget and the cell where it is"""
961 _row, _col = self.flextable.getIndex(wid)
962 self.flextable.remove(wid)
963 self.flextable.removeCell(_row, _col)
964 if self.flextable.getCellCount(_row) == 2: #we have only the borders left, we remove the row
965 self.flextable.removeRow(_row)
966 _max_cols = 1
967 for row_idx in xrange(self.flextable.getRowCount()):
968 _max_cols = max(_max_cols, self.flextable.getCellCount(row_idx))
969 if _max_cols != self._max_cols:
970 self._max_cols = _max_cols
971 self._sizesAdjust()
972 current = self
973
974 blank_page = not [wid for wid in self.flextable if isinstance(wid, LiberviaWidget)] # do we still have widgets on the page ?
975
976 if blank_page and not self.isLocked():
977 #we now notice the MainTabPanel that the WidgetsPanel is empty and need to be removed
978 while current is not None:
979 if isinstance(current, MainTabPanel):
980 current.onWidgetPanelRemove(self)
981 return
982 current = current.getParent()
983 print "Error: no MainTabPanel found !"
984
985 def getIndex(self, wid):
986 return self.flextable.getIndex(wid)
987
988 def getColSpan(self, row, col):
989 cellFormatter = self.flextable.getFlexCellFormatter()
990 return cellFormatter.getColSpan(row, col)
991
992 def setColSpan(self, row, col, value):
993 cellFormatter = self.flextable.getFlexCellFormatter()
994 return cellFormatter.setColSpan(row, col, value)
995
996 def getRowSpan(self, row, col):
997 cellFormatter = self.flextable.getFlexCellFormatter()
998 return cellFormatter.getRowSpan(row, col)
999
1000 def setRowSpan(self, row, col, value):
1001 cellFormatter = self.flextable.getFlexCellFormatter()
1002 return cellFormatter.setRowSpan(row, col, value)
1003
1004 class ContactTabPanel(HorizontalPanel): 588 class ContactTabPanel(HorizontalPanel):
1005 """ TabPanel with a contacts list which can be hidden """ 589 """ TabPanel with a contacts list which can be hidden """
1006 590
1007 def __init__(self, host, locked = False): 591 def __init__(self, host, locked = False):
1008 self.host=host 592 self.host=host
1010 self._left = VerticalPanel() 594 self._left = VerticalPanel()
1011 contacts_switch = Button('<<', self._contactsSwitch) 595 contacts_switch = Button('<<', self._contactsSwitch)
1012 contacts_switch.addStyleName('contactsSwitch') 596 contacts_switch.addStyleName('contactsSwitch')
1013 self._left.add(contacts_switch) 597 self._left.add(contacts_switch)
1014 self._left.add(self.host.contact_panel) 598 self._left.add(self.host.contact_panel)
1015 self._right = WidgetsPanel(host) 599 self._right = base_widget.WidgetsPanel(host)
1016 self._right.setWidth('100%') 600 self._right.setWidth('100%')
1017 self._right.setHeight('100%') 601 self._right.setHeight('100%')
1018 self.add(self._left) 602 self.add(self._left)
1019 self.add(self._right) 603 self.add(self._right)
1020 self.setCellWidth(self._right, "100%") 604 self.setCellWidth(self._right, "100%")
1022 def addWidget(self, wid): 606 def addWidget(self, wid):
1023 """Add a widget to the WidgetsPanel""" 607 """Add a widget to the WidgetsPanel"""
1024 print "main addWidget", wid 608 print "main addWidget", wid
1025 self._right.addWidget(wid) 609 self._right.addWidget(wid)
1026 610
1027 class MainTabPanel(TabPanel):
1028
1029 def __init__(self, host):
1030 TabPanel.__init__(self)
1031 self.host=host
1032 self.tabBar.setVisible(False)
1033 self.setStyleName('liberviaTabPanel')
1034 self.addStyleName('mainTabPanel')
1035 Window.addWindowResizeListener(self)
1036
1037 def getCurrentPanel(self):
1038 """ Get the panel of the currently selected tab """
1039 return self.deck.visibleWidget
1040
1041 def onWindowResized(self, width, height):
1042 tab_panel_elt = self.getElement()
1043 _elts = doc().getElementsByClassName('gwt-TabBar')
1044 if not _elts.length:
1045 print ("ERROR: no TabBar found, it should exist !")
1046 tab_bar_h = 0
1047 else:
1048 tab_bar_h = _elts.item(0).offsetHeight
1049 ideal_height = height - DOM.getAbsoluteTop(tab_panel_elt) - tab_bar_h - 5
1050 ideal_width = width - DOM.getAbsoluteLeft(tab_panel_elt) - 5
1051 self.setWidth("%s%s" % (ideal_width, "px"));
1052 self.setHeight("%s%s" % (ideal_height, "px"));
1053
1054 def add(self, widget, tabText=None, asHTML=False):
1055 TabPanel.add(self, widget, tabText, asHTML)
1056 if self.getWidgetCount()>1:
1057 self.tabBar.setVisible(True)
1058 self.host.resize()
1059
1060 def onWidgetPanelRemove(self, panel):
1061 """ Called when a child WidgetsPanel is empty and need to be removed """
1062 self.remove(panel)
1063 widgets_count = self.getWidgetCount()
1064 if widgets_count == 1:
1065 self.tabBar.setVisible(False)
1066 self.host.resize()
1067 self.selectTab(0)
1068 else:
1069 self.selectTab(widgets_count - 1)
1070
1071 class MainPanel(AbsolutePanel): 611 class MainPanel(AbsolutePanel):
1072 612
1073 def __init__(self, host): 613 def __init__(self, host):
1074 self.host=host 614 self.host=host
1075 AbsolutePanel.__init__(self) 615 AbsolutePanel.__init__(self)
1090 contacts_switch.addStyleName('contactsSwitch') 630 contacts_switch.addStyleName('contactsSwitch')
1091 _contacts.add(contacts_switch) 631 _contacts.add(contacts_switch)
1092 _contacts.add(self.host.contact_panel) 632 _contacts.add(self.host.contact_panel)
1093 633
1094 #tabs 634 #tabs
1095 self.tab_panel = MainTabPanel(host) 635 self.tab_panel = base_widget.MainTabPanel(host)
1096 self.discuss_panel = WidgetsPanel(self.host, locked=True) 636 self.discuss_panel = base_widget.WidgetsPanel(self.host, locked=True)
1097 self.tab_panel.add(self.discuss_panel, "Discussions") 637 self.tab_panel.add(self.discuss_panel, "Discussions")
1098 self.tab_panel.selectTab(0) 638 self.tab_panel.selectTab(0)
1099 639
1100 header=AbsolutePanel() 640 header=AbsolutePanel()
1101 header.add(menu) 641 header.add(menu)