Mercurial > libervia-web
diff src/browser/sat_browser/base_widget.py @ 648:6d3142b782c3 frontends_multi_profiles
browser_side: classes reorganisation:
- moved widgets in dedicated modules (base, contact, editor, libervia) and a widget module for single classes
- same thing for panels (base, main, contact)
- libervia_widget mix main panels and widget and drag n drop for technical reasons (see comments)
- renamed WebPanel to WebWidget
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 26 Feb 2015 18:10:54 +0100 |
parents | e0021d571eef |
children | 849ffb24d5bf |
line wrap: on
line diff
--- a/src/browser/sat_browser/base_widget.py Thu Feb 26 13:10:46 2015 +0100 +++ b/src/browser/sat_browser/base_widget.py Thu Feb 26 18:10:54 2015 +0100 @@ -20,197 +20,18 @@ import pyjd # this is dummy in pyjs from sat.core.log import getLogger log = getLogger(__name__) -from sat.core import exceptions -from sat.core.i18n import _ -from sat_frontends.quick_frontend import quick_widgets +import base_menu -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.Image import Image -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 constants import Const as C -from __pyjamas__ import doc - -import dialog -import base_menu -import html_tools - -unicode = str # XXX: pyjama doesn't manage unicode +### Exceptions ### class NoLiberviaWidgetException(Exception): + """A Libervia widget was expected""" pass -class DragLabel(DragWidget): - - def __init__(self, text, type_, host=None): - """Base of Drag n Drop mecanism in Libervia - - @param text: data embedded with in drag n drop operation - @param type_: type of data that we are dragging - @param host: if not None, the host will be use to highlight BorderWidgets - """ - DragWidget.__init__(self) - self.host = host - 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) - if self.host is not None: - current_panel = self.host.tab_panel.getCurrentPanel() - for widget in current_panel.widgets: - if isinstance(widget, BorderWidget): - widget.addStyleName('borderWidgetOnDrag') - - def onDragEnd(self, event): - if self.host is not None: - current_panel = self.host.tab_panel.getCurrentPanel() - for widget in current_panel.widgets: - if isinstance(widget, BorderWidget): - widget.removeStyleName('borderWidgetOnDrag') - - -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_, widget.host) - self.widget = widget - - def onDragStart(self, event): - LiberviaDragWidget.current = self.widget - DragLabel.onDragStart(self, event) - - def onDragEnd(self, event): - DragLabel.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, cb): - """Add a association between a key and a class to create on drop. - - @param key: key to be associated (e.g. "CONTACT", "CHAT") - @param cb: a callable (either a class or method) returning a - LiberviaWidget instance - """ - DropCell.drop_keys[key] = cb - - 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): - """ - @raise NoLiberviaWidgetException: something else than a LiberviaWidget - has been returned by the callback. - """ - 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.getParent(WidgetsPanel, expect=True) - 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) - if not isinstance(_new_panel, LiberviaWidget): - raise NoLiberviaWidgetException - else: - log.warning("unmanaged item type") - return - if isinstance(self, LiberviaWidget): - # self.host.unregisterWidget(self) # FIXME - 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)""" - if isinstance(self, quick_widgets.QuickWidget): - self.host.widgets.deleteWidget(self) +### Menus ### class WidgetMenuBar(base_menu.GenericMenuBar): @@ -259,744 +80,3 @@ @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 - header = WidgetHeader(self, host, self._title, self._info) - self.add(header) - self.setSize('100%', '100%') - self.addStyleName('widget') - if self._selectable: - self.addClickListener(self) - - # FIXME - # 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 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 (raise an error if not found) - @return: the parent/ancestor or None if it has not been found - @raise exceptions.InternalError: expect is True and no parent is 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: - raise exceptions.InternalError("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 """ - widgets_panel = self.getParent(WidgetsPanel, expect=True) - widgets_panel.removeWidget(self) - self.onQuit() - self.host.widgets.deleteWidget(self) - - def onQuit(self): - """ Called when the widget is actually ending """ - pass - - 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.getParent(WidgetsPanel, expect=True) - 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): - """ - - @param host (SatWebFrontend): host instance - @param locked (bool): If True, the tab containing self will not be - removed when there are no more widget inside self. If False, the - tab will be removed with self's last widget. - """ - ScrollPanelWrapper.__init__(self) - self.setSize('100%', '100%') - self.host = host - self.locked = locked - 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 in a raw - - @property - def widgets(self): - return iter(self.flextable) - - 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: # FIXME: except without exception specified ! - 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 - 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.parent.addStyleName('dragover') - DOM.eventPreventDefault(event) - - def onDragLeave(self, event): - self.parent.removeStyleName('dragover') - - def onDragOver(self, event): - DOM.eventPreventDefault(event) - - def onDrop(self, event): - DOM.eventPreventDefault(event) - self.parent.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.getParent(WidgetsPanel, expect=True).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, ClickHandler): - - def __init__(self, host): - TabPanel.__init__(self) - ClickHandler.__init__(self) - self.host = host - self.setStyleName('liberviaTabPanel') - self.addStyleName('mainTabPanel') - Window.addWindowResizeListener(self) - - self.tabBar.addTab(u'✚', True) - - def onTabSelected(self, sender, tabIndex): - if tabIndex < self.getWidgetCount(): - TabPanel.onTabSelected(self, sender, tabIndex) - return - # user clicked the "+" tab - default_label = _(u'new tab') - try: - label = Window.prompt(_(u'Name of the new tab'), default_label) - if not label: - label = default_label - except: # this happens when the user prevents the page to open the prompt dialog - label = default_label - self.addWidgetsTab(label, select=True) - - def getCurrentPanel(self): - """ Get the panel of the currently selected tab - - @return: WidgetsPanel - """ - 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 addTab(self, widget, label, select=False): - """Create a new tab for the given widget. - - @param widget (Widget): widget to associate to the tab - @param label (unicode): label of the tab - @param select (bool): True to select the added tab - """ - TabPanel.add(self, widget, DropTab(self, label), False) - if select: - self.selectTab(self.getWidgetCount() - 1) - - def addWidgetsTab(self, label, select=False, locked=False): - """Create a new tab for containing LiberviaWidgets. - - @param label (unicode): label of the tab - @param select (bool): True to select the added tab - @param locked (bool): If True, the tab will not be removed when there - are no more widget inside. If False, the tab will be removed with - the last widget. - @return: WidgetsPanel - """ - widgets_panel = WidgetsPanel(self, locked=locked) - self.addTab(widgets_panel, label, select) - return widgets_panel - - def onWidgetPanelRemove(self, panel): - """ Called when a child WidgetsPanel is empty and need to be removed """ - widget_index = self.getWidgetIndex(panel) - self.remove(panel) - widgets_count = self.getWidgetCount() - self.selectTab(widget_index if widget_index < widgets_count else widgets_count - 1) - - -class ContactLabel(HTML): - """Display a contact in HTML, selecting best display (jid/nick/etc)""" - - def __init__(self, host, jid_): - # TODO: add a listener for nick changes - HTML.__init__(self) - self.host = host - self.jid = jid_.bare - self.nick = self.host.contact_lists[C.PROF_KEY_NONE].getCache(self.jid, "nick") - self.alert = False - self.refresh() - self.setStyleName('contactLabel') - - def refresh(self): - alert_html = "<strong>(*)</strong> " if self.alert else "" - contact_html = html_tools.html_sanitize(self.nick or unicode(self.jid)) - html = "%(alert)s%(contact)s" % {'alert': alert_html, - 'contact': contact_html} - self.setHTML(html) - - def updateNick(self, new_nick): - """Change the current nick - - @param new_nick(unicode): new nick to use - """ - self.nick = new_nick - self.refresh() - - def setAlert(self, alert): - """Show a visual indicator - - @param alert: True if alert must be shown - """ - self.alert = alert - self.refresh() - - -class ContactMenuBar(WidgetMenuBar): - - def onBrowserEvent(self, event): - WidgetMenuBar.onBrowserEvent(self, event) - event.stopPropagation() # prevent opening the chat dialog - - @classmethod - def getCategoryHTML(cls, menu_name_i18n, type_): - return '<img src="%s"/>' % C.DEFAULT_AVATAR_URL - - def setUrl(self, url): - """Set the URL of the contact avatar.""" - self.items[0].setHTML('<img src="%s" />' % url) - - -class ContactBox(VerticalPanel, ClickHandler, DragLabel): - - def __init__(self, parent, jid_): - """ - @param parent (ContactPanel): ContactPanel hosting this box - @param jid_ (jid.JID): contact JID - """ - VerticalPanel.__init__(self, StyleName='contactBox', VerticalAlignment='middle') - ClickHandler.__init__(self) - DragLabel.__init__(self, jid_, "CONTACT", parent.host) - self.jid = jid_.bare - self.label = ContactLabel(parent.host, self.jid) - self.avatar = ContactMenuBar(self, parent.host) if parent.handle_menu else Image() - self.updateAvatar(parent.host.getAvatarURL(self.jid)) - self.add(self.avatar) - self.add(self.label) - self.addClickListener(self) - - def addMenus(self, menu_bar): - menu_bar.addCachedMenus(C.MENU_ROSTER_JID_CONTEXT, {'jid': unicode(self.jid)}) - menu_bar.addCachedMenus(C.MENU_JID_CONTEXT, {'jid': unicode(self.jid)}) - - def setAlert(self, alert): - """Show a visual indicator - - @param alert: True if alert indicator show be shown""" - self.label.setAlert(alert) - - def updateAvatar(self, url): - """Update the avatar. - - @param url (unicode): image url - """ - self.avatar.setUrl(url) - - def updateNick(self, new_nick): - """Update the nickname. - - @param new_nick (unicode): new nickname to use - """ - self.label.updateNick(new_nick) - - def onClick(self, sender): - try: - self.parent.onClick(self.jid) - except AttributeError: - pass - else: - self.setAlert(False)