Mercurial > libervia-web
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=' ' | |
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) |