Mercurial > libervia-web
view src/browser/sat_browser/base_widget.py @ 589:a5019e62c3e9 frontends_multi_profiles
browser side: big refactoring to base Libervia on QuickFrontend, first draft:
/!\ not finished, partially working and highly instable
- add collections module with an OrderedDict like class
- SatWebFrontend inherit from QuickApp
- general sat_frontends tools.jid module is used
- bridge/json methods have moved to json module
- UniBox is partially removed (should be totally removed before merge to trunk)
- Signals are now register with the generic registerSignal method (which is called mainly in QuickFrontend)
- the generic getOrCreateWidget method from QuickWidgetsManager is used instead of Libervia's specific methods
- all Widget are now based more or less directly on QuickWidget
- with the new QuickWidgetsManager.getWidgets method, it's no more necessary to check all widgets which are instance of a particular class
- ChatPanel and related moved to chat module
- MicroblogPanel and related moved to blog module
- global and overcomplicated send method has been disabled: each class should manage its own sending
- for consistency with other frontends, former ContactPanel has been renamed to ContactList and vice versa
- for the same reason, ChatPanel has been renamed to Chat
- for compatibility with QuickFrontend, a fake profile is used in several places, it is set to C.PROF_KEY_NONE (real profile is managed server side for obvious security reasons)
- changed default url for web panel to SàT website, and contact address to generic SàT contact address
- ContactList is based on QuickContactList, UI changes are done in update method
- bride call (now json module) have been greatly improved, in particular call can be done in the same way as for other frontends (bridge.method_name(arg1, arg2, ..., callback=cb, errback=eb). Blocking method must be called like async methods due to javascript architecture
- in bridge calls, a callback can now exists without errback
- hard reload on BridgeSignals remote error has been disabled, a better option should be implemented
- use of constants where that make sens, some style improvments
- avatars are temporarily disabled
- lot of code disabled, will be fixed or removed before merge
- various other changes, check diff for more details
server side: manage remote exception on getEntityData, removed getProfileJid call, added getWaitingConf, added getRoomsSubjects
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 24 Jan 2015 01:45:39 +0100 |
parents | b07f0fe2763a |
children | a099990f77a6 |
line wrap: on
line source
#!/usr/bin/python # -*- coding: utf-8 -*- # Libervia: a Salut à Toi frontend # Copyright (C) 2011, 2012, 2013, 2014 Jérôme Poisson <goffi@goffi.org> # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import pyjd # this is dummy in pyjs from sat.core.log import getLogger log = getLogger(__name__) from pyjamas.ui.SimplePanel import SimplePanel from pyjamas.ui.AbsolutePanel import AbsolutePanel from pyjamas.ui.VerticalPanel import VerticalPanel from pyjamas.ui.HorizontalPanel import HorizontalPanel from pyjamas.ui.ScrollPanel import ScrollPanel from pyjamas.ui.FlexTable import FlexTable from pyjamas.ui.TabPanel import TabPanel from pyjamas.ui.HTMLPanel import HTMLPanel from pyjamas.ui.Label import Label from pyjamas.ui.HTML import HTML from pyjamas.ui.Button import Button from pyjamas.ui.Widget import Widget from pyjamas.ui.DragWidget import DragWidget from pyjamas.ui.DropWidget import DropWidget from pyjamas.ui.ClickListener import ClickHandler from pyjamas.ui import HasAlignment from pyjamas import DOM from pyjamas import Window from __pyjamas__ import doc import dialog import base_menu class DragLabel(DragWidget): def __init__(self, text, _type): DragWidget.__init__(self) self._text = text self._type = _type def onDragStart(self, event): dt = event.dataTransfer dt.setData('text/plain', "%s\n%s" % (self._text, self._type)) dt.setDragImage(self.getElement(), 15, 15) class LiberviaDragWidget(DragLabel): """ A DragLabel which keep the widget being dragged as class value """ current = None # widget currently dragged def __init__(self, text, _type, widget): DragLabel.__init__(self, text, _type) self.widget = widget def onDragStart(self, event): LiberviaDragWidget.current = self.widget DragLabel.onDragStart(self, event) def onDragEnd(self, event): LiberviaDragWidget.current = None class DropCell(DropWidget): """Cell in the middle grid which replace itself with the dropped widget on DnD""" drop_keys = {} def __init__(self, host): DropWidget.__init__(self) self.host = host self.setStyleName('dropCell') @classmethod def addDropKey(cls, key, callback): DropCell.drop_keys[key] = callback def onDragEnter(self, event): if self == LiberviaDragWidget.current: return self.addStyleName('dragover') DOM.eventPreventDefault(event) def onDragLeave(self, event): if event.clientX <= self.getAbsoluteLeft() or event.clientY <= self.getAbsoluteTop() or\ event.clientX >= self.getAbsoluteLeft() + self.getOffsetWidth() - 1 or event.clientY >= self.getAbsoluteTop() + self.getOffsetHeight() - 1: # We check that we are inside widget's box, and we don't remove the style in this case because # if the mouse is over a widget inside the DropWidget, if will leave the DropWidget, and we # don't want that self.removeStyleName('dragover') def onDragOver(self, event): DOM.eventPreventDefault(event) def _getCellAndRow(self, grid, event): """Return cell and row index where the event is occuring""" cell = grid.getEventTargetCell(event) row = DOM.getParent(cell) return (row.rowIndex, cell.cellIndex) def onDrop(self, event): self.removeStyleName('dragover') DOM.eventPreventDefault(event) dt = event.dataTransfer # 'text', 'text/plain', and 'Text' are equivalent. try: item, item_type = dt.getData("text/plain").split('\n') # Workaround for webkit, only text/plain seems to be managed if item_type and item_type[-1] == '\0': # Workaround for what looks like a pyjamas bug: the \0 should not be there, and item_type = item_type[:-1] # .strip('\0') and .replace('\0','') don't work. TODO: check this and fill a bug report # item_type = dt.getData("type") log.debug("message: %s" % item) log.debug("type: %s" % item_type) except: log.debug("no message found") item = ' ' item_type = None if item_type == "WIDGET": if not LiberviaDragWidget.current: log.error("No widget registered in LiberviaDragWidget !") return _new_panel = LiberviaDragWidget.current if self == _new_panel: # We can't drop on ourself return # we need to remove the widget from the panel as it will be inserted elsewhere widgets_panel = _new_panel.getWidgetsPanel() wid_row = widgets_panel.getWidgetCoords(_new_panel)[0] row_wids = widgets_panel.getLiberviaRowWidgets(wid_row) if len(row_wids) == 1 and wid_row == widgets_panel.getWidgetCoords(self)[0]: # the dropped widget is the only one in the same row # as the target widget (self), we don't do anything return widgets_panel.removeWidget(_new_panel) elif item_type in self.drop_keys: _new_panel = self.drop_keys[item_type](self.host, item) else: log.warning("unmanaged item type") return if isinstance(self, LiberviaWidget): self.host.unregisterWidget(self) self.onQuit() if not isinstance(_new_panel, LiberviaWidget): log.warning("droping an object which is not a class of LiberviaWidget") _flextable = self.getParent() _widgetspanel = _flextable.getParent().getParent() row_idx, cell_idx = self._getCellAndRow(_flextable, event) if self.host.getSelected == self: self.host.setSelected(None) _widgetspanel.changeWidget(row_idx, cell_idx, _new_panel) """_unempty_panels = filter(lambda wid:not isinstance(wid,EmptyWidget),list(_flextable)) _width = 90/float(len(_unempty_panels) or 1) #now we resize all the cell of the column for panel in _unempty_panels: td_elt = panel.getElement().parentNode DOM.setStyleAttribute(td_elt, "width", "%s%%" % _width)""" #FIXME: delete object ? Check the right way with pyjamas class WidgetMenuBar(base_menu.GenericMenuBar): ITEM_TPL = "<img src='media/icons/misc/%s.png' />" def __init__(self, parent, host, vertical=False, styles=None): menu_styles = {'menu_bar': 'widgetHeader_buttonGroup'} if styles: menu_styles.update(styles) base_menu.GenericMenuBar.__init__(self, host, vertical=vertical, styles=menu_styles) # FIXME # if hasattr(parent, 'addMenus'): # # regroup all the dynamic menu categories in a sub-menu # sub_menu = WidgetSubMenuBar(host, vertical=True) # parent.addMenus(sub_menu) # if len(sub_menu.getCategories()) > 0: # self.addCategory('', '', 'plugins', sub_menu) @classmethod def getCategoryHTML(cls, menu_name_i18n, type_): return cls.ITEM_TPL % type_ class WidgetSubMenuBar(base_menu.GenericMenuBar): def __init__(self, host, vertical=True): base_menu.GenericMenuBar.__init__(self, host, vertical=vertical, flat_level=1) @classmethod def getCategoryHTML(cls, menu_name_i18n, type_): return menu_name_i18n class WidgetHeader(AbsolutePanel, LiberviaDragWidget): def __init__(self, parent, host, title, info=None): """ @param parent (LiberviaWidget): LiberWidget instance @param host (SatWebFrontend): SatWebFrontend instance @param title (Label, HTML): text widget instance @param info (Widget): text widget instance """ AbsolutePanel.__init__(self) self.add(title) if info: # FIXME: temporary design to display the info near the menu button_group_wrapper = HorizontalPanel() button_group_wrapper.add(info) else: button_group_wrapper = SimplePanel() button_group_wrapper.setStyleName('widgetHeader_buttonsWrapper') button_group = WidgetMenuBar(parent, host) button_group.addItem('<img src="media/icons/misc/settings.png"/>', True, base_menu.MenuCmd(parent, 'onSetting')) button_group.addItem('<img src="media/icons/misc/close.png"/>', True, base_menu.MenuCmd(parent, 'onClose')) button_group_wrapper.add(button_group) self.add(button_group_wrapper) self.addStyleName('widgetHeader') LiberviaDragWidget.__init__(self, "", "WIDGET", parent) class LiberviaWidget(DropCell, VerticalPanel, ClickHandler): """Libervia's widget which can replace itself with a dropped widget on DnD""" def __init__(self, host, title='', info=None, selectable=False): """Init the widget @param host (SatWebFrontend): SatWebFrontend instance @param title (str): title shown in the header of the widget @param info (str, callable): info shown in the header of the widget @param selectable (bool): True is widget can be selected by user """ VerticalPanel.__init__(self) DropCell.__init__(self, host) ClickHandler.__init__(self) self._selectable = selectable self._title_id = HTMLPanel.createUniqueId() self._setting_button_id = HTMLPanel.createUniqueId() self._close_button_id = HTMLPanel.createUniqueId() self._title = Label(title) self._title.setStyleName('widgetHeader_title') if info is not None: if isinstance(info, str): self._info = HTML(info) else: # the info will be set by a callback assert callable(info) self._info = HTML() info(self._info.setHTML) self._info.setStyleName('widgetHeader_info') else: self._info = None self._close_listeners = [] header = WidgetHeader(self, host, self._title, self._info) self.add(header) self.setSize('100%', '100%') self.addStyleName('widget') if self._selectable: self.addClickListener(self) def onClose(sender): """Check dynamically if the unibox is enable or not""" if self.host.uni_box: self.host.uni_box.onWidgetClosed(sender) self.addCloseListener(onClose) # self.host.registerWidget(self) # FIXME def getDebugName(self): return "%s (%s)" % (self, self._title.getText()) def getWidgetsPanel(self, expect=True): return self.getParent(WidgetsPanel, expect) def getParent(self, class_=None, expect=True): """Return the closest ancestor of the specified class. Note: this method overrides pyjamas.ui.Widget.getParent @param class_: class of the ancestor to look for or None to return the first parent @param expect: set to True if the parent is expected (print a message if not found) @return: the parent/ancestor or None if it has not been found """ current = Widget.getParent(self) if class_ is None: return current # this is the default behavior while current is not None and not isinstance(current, class_): current = Widget.getParent(current) if current is None and expect: log.error("Can't find parent %s for %s" % (class_, self)) return current def onClick(self, sender): self.host.setSelected(self) def onClose(self, sender): """ Called when the close button is pushed """ _widgetspanel = self.getWidgetsPanel() _widgetspanel.removeWidget(self) for callback in self._close_listeners: callback(self) self.onQuit() def onQuit(self): """ Called when the widget is actually ending """ pass def addCloseListener(self, callback): """Add a close listener to this widget @param callback: function to be called from self.onClose""" self._close_listeners.append(callback) def refresh(self): """This can be overwritten by a child class to refresh the display when, instead of creating a new one, an existing widget is found and reused. """ pass def onSetting(self, sender): widpanel = self.getWidgetsPanel() row, col = widpanel.getIndex(self) body = VerticalPanel() # colspan & rowspan colspan = widpanel.getColSpan(row, col) rowspan = widpanel.getRowSpan(row, col) def onColSpanChange(value): widpanel.setColSpan(row, col, value) def onRowSpanChange(value): widpanel.setRowSpan(row, col, value) colspan_setter = dialog.IntSetter("Columns span", colspan) colspan_setter.addValueChangeListener(onColSpanChange) colspan_setter.setWidth('100%') rowspan_setter = dialog.IntSetter("Rows span", rowspan) rowspan_setter.addValueChangeListener(onRowSpanChange) rowspan_setter.setWidth('100%') body.add(colspan_setter) body.add(rowspan_setter) # size width_str = self.getWidth() if width_str.endswith('px'): width = int(width_str[:-2]) else: width = 0 height_str = self.getHeight() if height_str.endswith('px'): height = int(height_str[:-2]) else: height = 0 def onWidthChange(value): if not value: self.setWidth('100%') else: self.setWidth('%dpx' % value) def onHeightChange(value): if not value: self.setHeight('100%') else: self.setHeight('%dpx' % value) width_setter = dialog.IntSetter("width (0=auto)", width) width_setter.addValueChangeListener(onWidthChange) width_setter.setWidth('100%') height_setter = dialog.IntSetter("height (0=auto)", height) height_setter.addValueChangeListener(onHeightChange) height_setter.setHeight('100%') body.add(width_setter) body.add(height_setter) # reset def onReset(sender): colspan_setter.setValue(1) rowspan_setter.setValue(1) width_setter.setValue(0) height_setter.setValue(0) reset_bt = Button("Reset", onReset) body.add(reset_bt) body.setCellHorizontalAlignment(reset_bt, HasAlignment.ALIGN_CENTER) _dialog = dialog.GenericDialog("Widget setting", body) _dialog.show() def setTitle(self, text): """change the title in the header of the widget @param text: text of the new title""" self._title.setText(text) def setHeaderInfo(self, text): """change the info in the header of the widget @param text: text of the new title""" try: self._info.setHTML(text) except TypeError: log.error("LiberviaWidget.setInfo: info widget has not been initialized!") def isSelectable(self): return self._selectable def setSelectable(self, selectable): if not self._selectable: try: self.removeClickListener(self) except ValueError: pass if self.selectable and not self in self._clickListeners: self.addClickListener(self) self._selectable = selectable def getWarningData(self): """ Return exposition warning level when this widget is selected and something is sent to it This method should be overriden by children @return: tuple (warning level type/HTML msg). Type can be one of: - PUBLIC - GROUP - ONE2ONE - MISC - NONE """ if not self._selectable: log.error("getWarningLevel must not be called for an unselectable widget") raise Exception # TODO: cleaner warning types (more general constants) return ("NONE", None) def setWidget(self, widget, scrollable=True): """Set the widget that will be in the body of the LiberviaWidget @param widget: widget to put in the body @param scrollable: if true, the widget will be in a ScrollPanelWrapper""" if scrollable: _scrollpanelwrapper = ScrollPanelWrapper() _scrollpanelwrapper.setStyleName('widgetBody') _scrollpanelwrapper.setWidget(widget) body_wid = _scrollpanelwrapper else: body_wid = widget self.add(body_wid) self.setCellHeight(body_wid, '100%') def doDetachChildren(self): # We need to force the use of a panel subclass method here, # for the same reason as doAttachChildren VerticalPanel.doDetachChildren(self) def doAttachChildren(self): # We need to force the use of a panel subclass method here, else # the event will not propagate to children VerticalPanel.doAttachChildren(self) def matchEntity(self, item): """Check if this widget corresponds to the given entity. This method should be overwritten by child classes. @return: True if the widget matches the entity""" raise NotImplementedError def addMenus(self, menu_bar): """Add menus to the header. This method can be overwritten by child classes. @param menu_bar (GenericMenuBar): menu bar of the widget's header """ pass class ScrollPanelWrapper(SimplePanel): """Scroll Panel like component, wich use the full available space to work around percent size issue, it use some of the ideas found here: http://code.google.com/p/google-web-toolkit/issues/detail?id=316 specially in code given at comment #46, thanks to Stefan Bachert""" def __init__(self, *args, **kwargs): SimplePanel.__init__(self) self.spanel = ScrollPanel(*args, **kwargs) SimplePanel.setWidget(self, self.spanel) DOM.setStyleAttribute(self.getElement(), "position", "relative") DOM.setStyleAttribute(self.getElement(), "top", "0px") DOM.setStyleAttribute(self.getElement(), "left", "0px") DOM.setStyleAttribute(self.getElement(), "width", "100%") DOM.setStyleAttribute(self.getElement(), "height", "100%") DOM.setStyleAttribute(self.spanel.getElement(), "position", "absolute") DOM.setStyleAttribute(self.spanel.getElement(), "width", "100%") DOM.setStyleAttribute(self.spanel.getElement(), "height", "100%") def setWidget(self, widget): self.spanel.setWidget(widget) def setScrollPosition(self, position): self.spanel.setScrollPosition(position) def scrollToBottom(self): self.setScrollPosition(self.spanel.getElement().scrollHeight) class EmptyWidget(DropCell, SimplePanel): """Empty dropable panel""" def __init__(self, host): SimplePanel.__init__(self) DropCell.__init__(self, host) #self.setWidget(HTML('')) self.setSize('100%', '100%') class BorderWidget(EmptyWidget): def __init__(self, host): EmptyWidget.__init__(self, host) self.addStyleName('borderPanel') class LeftBorderWidget(BorderWidget): def __init__(self, host): BorderWidget.__init__(self, host) self.addStyleName('leftBorderWidget') class RightBorderWidget(BorderWidget): def __init__(self, host): BorderWidget.__init__(self, host) self.addStyleName('rightBorderWidget') class BottomBorderWidget(BorderWidget): def __init__(self, host): BorderWidget.__init__(self, host) self.addStyleName('bottomBorderWidget') class WidgetsPanel(ScrollPanelWrapper): def __init__(self, host, locked=False): ScrollPanelWrapper.__init__(self) self.setSize('100%', '100%') self.host = host self.locked = locked # if True: tab will not be removed when there are no more widgets inside self.selected = None self.flextable = FlexTable() self.flextable.setSize('100%', '100%') self.setWidget(self.flextable) self.setStyleName('widgetsPanel') _bottom = BottomBorderWidget(self.host) self.flextable.setWidget(0, 0, _bottom) # There will be always an Empty widget on the last row, # dropping a widget there will add a new row td_elt = _bottom.getElement().parentNode DOM.setStyleAttribute(td_elt, "height", "1px") # needed so the cell adapt to the size of the border (specially in webkit) self._max_cols = 1 # give the maximum number of columns i a raw def isLocked(self): return self.locked def changeWidget(self, row, col, wid): """Change the widget in the given location, add row or columns when necessary""" log.debug("changing widget: %s %s %s" % (wid.getDebugName(), row, col)) last_row = max(0, self.flextable.getRowCount() - 1) try: prev_wid = self.flextable.getWidget(row, col) except: log.error("Trying to change an unexisting widget !") return cellFormatter = self.flextable.getFlexCellFormatter() if isinstance(prev_wid, BorderWidget): # We are on a border, we must create a row and/or columns log.debug("BORDER WIDGET") prev_wid.removeStyleName('dragover') if isinstance(prev_wid, BottomBorderWidget): # We are on the bottom border, we create a new row self.flextable.insertRow(last_row) self.flextable.setWidget(last_row, 0, LeftBorderWidget(self.host)) self.flextable.setWidget(last_row, 1, wid) self.flextable.setWidget(last_row, 2, RightBorderWidget(self.host)) cellFormatter.setHorizontalAlignment(last_row, 2, HasAlignment.ALIGN_RIGHT) row = last_row elif isinstance(prev_wid, LeftBorderWidget): if col != 0: log.error("LeftBorderWidget must be on the first column !") return self.flextable.insertCell(row, col + 1) self.flextable.setWidget(row, 1, wid) elif isinstance(prev_wid, RightBorderWidget): if col != self.flextable.getCellCount(row) - 1: log.error("RightBorderWidget must be on the last column !") return self.flextable.insertCell(row, col) self.flextable.setWidget(row, col, wid) else: prev_wid.removeFromParent() self.flextable.setWidget(row, col, wid) _max_cols = max(self._max_cols, self.flextable.getCellCount(row)) if _max_cols != self._max_cols: self._max_cols = _max_cols self._sizesAdjust() def _sizesAdjust(self): cellFormatter = self.flextable.getFlexCellFormatter() width = 100.0 / max(1, self._max_cols - 2) # we don't count the borders for row_idx in xrange(self.flextable.getRowCount()): for col_idx in xrange(self.flextable.getCellCount(row_idx)): _widget = self.flextable.getWidget(row_idx, col_idx) if not isinstance(_widget, BorderWidget): td_elt = _widget.getElement().parentNode DOM.setStyleAttribute(td_elt, "width", "%.2f%%" % width) last_row = max(0, self.flextable.getRowCount() - 1) cellFormatter.setColSpan(last_row, 0, self._max_cols) def addWidget(self, wid): """Add a widget to a new cell on the next to last row""" last_row = max(0, self.flextable.getRowCount() - 1) log.debug("putting widget %s at %d, %d" % (wid.getDebugName(), last_row, 0)) self.changeWidget(last_row, 0, wid) def removeWidget(self, wid): """Remove a widget and the cell where it is""" _row, _col = self.flextable.getIndex(wid) self.flextable.remove(wid) self.flextable.removeCell(_row, _col) if not self.getLiberviaRowWidgets(_row): # we have no more widgets, we remove the row self.flextable.removeRow(_row) _max_cols = 1 for row_idx in xrange(self.flextable.getRowCount()): _max_cols = max(_max_cols, self.flextable.getCellCount(row_idx)) if _max_cols != self._max_cols: self._max_cols = _max_cols self._sizesAdjust() current = self blank_page = self.getLiberviaWidgetsCount() == 0 # do we still have widgets on the page ? if blank_page and not self.isLocked(): # we now notice the MainTabPanel that the WidgetsPanel is empty and need to be removed while current is not None: if isinstance(current, MainTabPanel): current.onWidgetPanelRemove(self) return current = current.getParent() log.error("no MainTabPanel found !") def getWidgetCoords(self, wid): return self.flextable.getIndex(wid) def getLiberviaRowWidgets(self, row): """ Return all the LiberviaWidget in the row """ return [wid for wid in self.getRowWidgets(row) if isinstance(wid, LiberviaWidget)] def getRowWidgets(self, row): """ Return all the widgets in the row """ widgets = [] cols = self.flextable.getCellCount(row) for col in xrange(cols): widgets.append(self.flextable.getWidget(row, col)) return widgets def getLiberviaWidgetsCount(self): """ Get count of contained widgets """ return len([wid for wid in self.flextable if isinstance(wid, LiberviaWidget)]) def getIndex(self, wid): return self.flextable.getIndex(wid) def getColSpan(self, row, col): cellFormatter = self.flextable.getFlexCellFormatter() return cellFormatter.getColSpan(row, col) def setColSpan(self, row, col, value): cellFormatter = self.flextable.getFlexCellFormatter() return cellFormatter.setColSpan(row, col, value) def getRowSpan(self, row, col): cellFormatter = self.flextable.getFlexCellFormatter() return cellFormatter.getRowSpan(row, col) def setRowSpan(self, row, col, value): cellFormatter = self.flextable.getFlexCellFormatter() return cellFormatter.setRowSpan(row, col, value) class DropTab(Label, DropWidget): def __init__(self, tab_panel, text): Label.__init__(self, text) DropWidget.__init__(self, tab_panel) self.tab_panel = tab_panel self.setStyleName('dropCell') self.setWordWrap(False) DOM.setStyleAttribute(self.getElement(), "min-width", "30px") def _getIndex(self): """ get current index of the DropTab """ # XXX: awful hack, but seems the only way to get index return self.tab_panel.tabBar.panel.getWidgetIndex(self.getParent().getParent()) - 1 def onDragEnter(self, event): #if self == LiberviaDragWidget.current: # return self.addStyleName('dragover') DOM.eventPreventDefault(event) def onDragLeave(self, event): self.removeStyleName('dragover') def onDragOver(self, event): DOM.eventPreventDefault(event) def onDrop(self, event): DOM.eventPreventDefault(event) self.removeStyleName('dragover') if self._getIndex() == self.tab_panel.tabBar.getSelectedTab(): # the widget come from the DragTab, so nothing to do, we let it there return # FIXME: quite the same stuff as in DropCell, need some factorisation dt = event.dataTransfer # 'text', 'text/plain', and 'Text' are equivalent. try: item, item_type = dt.getData("text/plain").split('\n') # Workaround for webkit, only text/plain seems to be managed if item_type and item_type[-1] == '\0': # Workaround for what looks like a pyjamas bug: the \0 should not be there, and item_type = item_type[:-1] # .strip('\0') and .replace('\0','') don't work. TODO: check this and fill a bug report # item_type = dt.getData("type") log.debug("message: %s" % item) log.debug("type: %s" % item_type) except: log.debug("no message found") item = ' ' item_type = None if item_type == "WIDGET": if not LiberviaDragWidget.current: log.error("No widget registered in LiberviaDragWidget !") return _new_panel = LiberviaDragWidget.current _new_panel.getWidgetsPanel().removeWidget(_new_panel) elif item_type in DropCell.drop_keys: _new_panel = DropCell.drop_keys[item_type](self.tab_panel.host, item) else: log.warning("unmanaged item type") return widgets_panel = self.tab_panel.getWidget(self._getIndex()) widgets_panel.addWidget(_new_panel) class MainTabPanel(TabPanel): def __init__(self, host): TabPanel.__init__(self) self.host = host self.tabBar.setVisible(False) self.setStyleName('liberviaTabPanel') self.addStyleName('mainTabPanel') Window.addWindowResizeListener(self) def getCurrentPanel(self): """ Get the panel of the currently selected tab """ return self.deck.visibleWidget def onWindowResized(self, width, height): tab_panel_elt = self.getElement() _elts = doc().getElementsByClassName('gwt-TabBar') if not _elts.length: log.error("no TabBar found, it should exist !") tab_bar_h = 0 else: tab_bar_h = _elts.item(0).offsetHeight ideal_height = height - DOM.getAbsoluteTop(tab_panel_elt) - tab_bar_h - 5 ideal_width = width - DOM.getAbsoluteLeft(tab_panel_elt) - 5 self.setWidth("%s%s" % (ideal_width, "px")) self.setHeight("%s%s" % (ideal_height, "px")) def add(self, widget, text=''): tab = DropTab(self, text) TabPanel.add(self, widget, tab, False) if self.getWidgetCount() > 1: self.tabBar.setVisible(True) self.host.resize() def onWidgetPanelRemove(self, panel): """ Called when a child WidgetsPanel is empty and need to be removed """ self.remove(panel) widgets_count = self.getWidgetCount() if widgets_count == 1: self.tabBar.setVisible(False) self.host.resize() self.selectTab(0) else: self.selectTab(widgets_count - 1)