diff --git a/src/sugar/graphics/palette.py b/src/sugar/graphics/palette.py index 88145d24..a29a9412 100644 --- a/src/sugar/graphics/palette.py +++ b/src/sugar/graphics/palette.py @@ -127,11 +127,8 @@ class MouseSpeedDetector(gobject.GObject): return True -class Palette(gtk.Window): - PRIMARY = 0 - SECONDARY = 1 - - __gtype_name__ = 'SugarPalette' +class PaletteWindow(gtk.Window): + __gtype_name__ = 'SugarPaletteWindow' __gsignals__ = { 'popup' : (gobject.SIGNAL_RUN_FIRST, @@ -142,6 +139,261 @@ class Palette(gtk.Window): gobject.TYPE_NONE, ([])) } + def __init__(self, **kwargs): + self._group_id = None + self._invoker = None + self._invoker_hids = [] + self._cursor_x = 0 + self._cursor_y = 0 + self._alignment = None + self._up = False + self._old_alloc = None + + self._popup_anim = animator.Animator(.5, 10) + self._popup_anim.add(_PopupAnimation(self)) + + self._secondary_anim = animator.Animator(2.0, 10) + self._secondary_anim.add(_SecondaryAnimation(self)) + + self._popdown_anim = animator.Animator(0.6, 10) + self._popdown_anim.add(_PopdownAnimation(self)) + + gobject.GObject.__init__(self, **kwargs) + + self.set_decorated(False) + self.set_resizable(False) + # Just assume xthickness and ythickness are the same + self.set_border_width(self.get_style().xthickness) + + accel_group = gtk.AccelGroup() + self.set_data('sugar-accel-group', accel_group) + self.add_accel_group(accel_group) + + self.set_group_id("default") + + self.connect('show', self.__show_cb) + self.connect('hide', self.__hide_cb) + self.connect('realize', self.__realize_cb) + self.connect('destroy', self.__destroy_cb) + self.connect('enter-notify-event', self.__enter_notify_event_cb) + self.connect('leave-notify-event', self.__leave_notify_event_cb) + + self._mouse_detector = MouseSpeedDetector(self, 200, 5) + self._mouse_detector.connect('motion-slow', self._mouse_slow_cb) + + def __destroy_cb(self, palette): + self.set_group_id(None) + + def set_invoker(self, invoker): + for hid in self._invoker_hids[:]: + self._invoker.disconnect(hid) + self._invoker_hids.remove(hid) + + self._invoker = invoker + if invoker is not None: + self._invoker_hids.append(self._invoker.connect( + 'mouse-enter', self._invoker_mouse_enter_cb)) + self._invoker_hids.append(self._invoker.connect( + 'mouse-leave', self._invoker_mouse_leave_cb)) + self._invoker_hids.append(self._invoker.connect( + 'right-click', self._invoker_right_click_cb)) + + logging.debug(' Invoker set to %r' % self._invoker) + + def get_invoker(self): + return self._invoker + + invoker = gobject.property(type=object, + getter=get_invoker, + setter=set_invoker) + + def __realize_cb(self, widget): + self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) + + def _mouse_slow_cb(self, widget): + self._mouse_detector.stop() + self._palette_do_popup() + + def _palette_do_popup(self): + immediate = False + + if self.is_up(): + self._popdown_anim.stop() + return + + if self._group_id: + group = palettegroup.get_group(self._group_id) + if group and group.is_up(): + immediate = True + group.popdown() + + self.popup(immediate=immediate) + + def is_up(self): + return self._up + + def set_group_id(self, group_id): + if self._group_id: + group = palettegroup.get_group(self._group_id) + group.remove(self) + if group_id: + self._group_id = group_id + group = palettegroup.get_group(group_id) + group.add(self) + + def get_group_id(self): + return self._group_id + + group_id = gobject.property(type=str, + getter=get_group_id, + setter=set_group_id) + + def do_size_request(self, requisition): + gtk.Window.do_size_request(self, requisition) + requisition.width = max(requisition.width, style.GRID_CELL_SIZE * 2) + + def do_size_allocate(self, allocation): + gtk.Window.do_size_allocate(self, allocation) + + if self._old_alloc is None or \ + self._old_alloc.x != allocation.x or \ + self._old_alloc.y != allocation.y or \ + self._old_alloc.width != allocation.width or \ + self._old_alloc.height != allocation.height: + self.queue_draw() + + # We need to store old allocation because when size_allocate + # is called widget.allocation is already updated. + # gtk.Window resizing is different from normal containers: + # the X window is resized, widget.allocation is updated from + # the configure request handler and finally size_allocate is called. + self._old_alloc = allocation + + def do_expose_event(self, event): + # We want to draw a border with a beautiful gap + if self._invoker is not None and self._invoker.has_rectangle_gap(): + invoker = self._invoker.get_rect() + palette = self.get_rect() + + gap = _calculate_gap(palette, invoker) + else: + gap = False + + allocation = self.get_allocation() + wstyle = self.get_style() + + if gap: + wstyle.paint_box_gap(event.window, gtk.STATE_PRELIGHT, + gtk.SHADOW_IN, event.area, self, "palette", + 0, 0, allocation.width, allocation.height, + gap[0], gap[1], gap[2]) + else: + wstyle.paint_box(event.window, gtk.STATE_PRELIGHT, + gtk.SHADOW_IN, event.area, self, "palette", + 0, 0, allocation.width, allocation.height) + + # Fall trough to the container expose handler. + # (Leaving out the window expose handler which redraws everything) + gtk.Bin.do_expose_event(self, event) + + def update_position(self): + logging.debug(' update_position 1 %r %r' % (self._invoker, self._alignment)) + invoker = self._invoker + if invoker is None or self._alignment is None: + logging.error('Cannot update the palette position.') + return + + rect = self.size_request() + position = invoker.get_position_for_alignment(self._alignment, rect) + if position is None: + position = invoker.get_position(rect) + + logging.debug(' update_position %r %r' % (position.x, position.y)) + self.move(position.x, position.y) + + def get_full_size_request(self): + return self.size_request() + + def popup(self, immediate=False): + if self._invoker is not None: + full_size_request = self.get_full_size_request() + self._alignment = self._invoker.get_alignment(full_size_request) + + self.update_position() + self.set_transient_for(self._invoker.get_toplevel()) + + self._popdown_anim.stop() + + if not immediate: + self._popup_anim.start() + else: + self.show() + + def popdown(self, immediate=False): + logging.debug('Palette.popdown immediate %r' % immediate) + self._popup_anim.stop() + + self._mouse_detector.stop() + + if not immediate: + self._popdown_anim.start() + else: + self.hide() + + def _invoker_mouse_enter_cb(self, invoker): + self._mouse_detector.start() + + def _invoker_mouse_leave_cb(self, invoker): + self._mouse_detector.stop() + self.popdown() + + def _invoker_right_click_cb(self, invoker): + self.popup(immediate=True) + + def __enter_notify_event_cb(self, widget, event): + if event.detail != gtk.gdk.NOTIFY_INFERIOR and \ + event.mode == gtk.gdk.CROSSING_NORMAL: + self._popdown_anim.stop() + self._secondary_anim.start() + + def __leave_notify_event_cb(self, widget, event): + if event.detail != gtk.gdk.NOTIFY_INFERIOR and \ + event.mode == gtk.gdk.CROSSING_NORMAL: + self.popdown() + + def __show_cb(self, widget): + self._invoker.notify_popup() + + self._up = True + self.emit('popup') + + def __hide_cb(self, widget): + logging.debug('__hide_cb') + self._secondary_anim.stop() + + if self._invoker: + self._invoker.notify_popdown() + + self._up = False + self.emit('popdown') + + def get_rect(self): + win_x, win_y = self.window.get_origin() + rectangle = self.get_allocation() + + x = win_x + rectangle.x + y = win_y + rectangle.y + width = rectangle.width + height = rectangle.height + + return gtk.gdk.Rectangle(x, y, width, height) + +class Palette(PaletteWindow): + PRIMARY = 0 + SECONDARY = 1 + + __gtype_name__ = 'SugarPalette' + # DEPRECATED: label is passed with the primary-text property, accel_path # is set via the invoker property, and menu_after_content is not used def __init__(self, label=None, accel_path=None, menu_after_content=False, @@ -153,7 +405,6 @@ class Palette(gtk.Window): self._secondary_text = None self._icon = None self._icon_visible = True - self._group_id = None palette_box = gtk.VBox() @@ -200,49 +451,18 @@ class Palette(gtk.Window): self._menu_content_separator = gtk.HSeparator() - self._popup_anim = animator.Animator(.5, 10) - self._popup_anim.add(_PopupAnimation(self)) - self._secondary_anim = animator.Animator(2.0, 10) self._secondary_anim.add(_SecondaryAnimation(self)) - self._popdown_anim = animator.Animator(0.6, 10) - self._popdown_anim.add(_PopdownAnimation(self)) - # we init after initializing all of our containers - gobject.GObject.__init__(self, **kwargs) - - self.set_decorated(False) - self.set_resizable(False) - # Just assume xthickness and ythickness are the same - self.set_border_width(self.get_style().xthickness) - - accel_group = gtk.AccelGroup() - self.set_data('sugar-accel-group', accel_group) - self.add_accel_group(accel_group) + PaletteWindow.__init__(self, **kwargs) primary_box.set_size_request(-1, style.GRID_CELL_SIZE - 2 * self.get_border_width()) - - self.connect('show', self.__show_cb) - self.connect('hide', self.__hide_cb) - self.connect('realize', self.__realize_cb) - self.connect('destroy', self.__destroy_cb) - - self._alignment = None - self._old_alloc = None self._full_request = [0, 0] - self._cursor_x = 0 - self._cursor_y = 0 - self._invoker = None - self._group_id = None - self._up = False self._menu_box = None self._content = None - self._invoker_hids = [] - - self.set_group_id("default") # we set these for backward compatibility if label is not None: @@ -263,21 +483,63 @@ class Palette(gtk.Window): self.menu = _Menu(self) self.menu.connect('item-inserted', self.__menu_item_inserted_cb) - self.connect('enter-notify-event', self.__enter_notify_event_cb) - self.connect('leave-notify-event', self.__leave_notify_event_cb) + self.connect('realize', self.__realize_cb) + self.connect('show', self.__show_cb) + self.connect('hide', self.__hide_cb) + self.connect('notify::invoker', self.__notify_invoker_cb) + self.connect('destroy', self.__destroy_cb) - self._mouse_detector = MouseSpeedDetector(self, 200, 5) - self._mouse_detector.connect('motion-slow', self._mouse_slow_cb) + def _invoker_right_click_cb(self, invoker): + self.popup(immediate=True, state=self.SECONDARY) + + def do_style_set(self, previous_style): + # Prevent a warning from pygtk + if previous_style is not None: + gtk.Window.do_style_set(self, previous_style) + self.set_border_width(self.get_style().xthickness) def __menu_item_inserted_cb(self, menu): self._update_separators() def __destroy_cb(self, palette): - self.set_group_id(None) - # Break the reference cycle. It looks like the gc is not able to free # it, possibly because gtk.Menu memory handling is very special. self.menu = None + + def __show_cb(self, widget): + self.menu.set_active(True) + + def __hide_cb(self, widget): + logging.debug('__hide_cb') + self.menu.set_active(False) + + def __notify_invoker_cb(self, palette, pspec): + invoker = self.props.invoker + if invoker is not None and hasattr(invoker.props, 'widget'): + logging.debug(('Setup widget', invoker.props.widget)) + self._update_accel_widget() + self._invoker.connect('notify::widget', + self.__invoker_widget_changed_cb) + + def __invoker_widget_changed_cb(self, invoker, spec): + self._update_accel_widget() + + def get_full_size_request(self): + return self._full_request + + def popup(self, immediate=False, state=None): + logging.debug('Palette.popup immediate %r' % immediate) + + if self._invoker is not None: + self._update_full_request() + + PaletteWindow.popup(self, immediate) + + if state is None: + state = self.PRIMARY + self.set_state(state) + + self._secondary_anim.start() def _add_menu(self): self._menu_box = gtk.VBox() @@ -290,52 +552,6 @@ class Palette(gtk.Window): self._content.set_border_width(style.DEFAULT_SPACING) self._secondary_box.pack_start(self._content) - def do_style_set(self, previous_style): - # Prevent a warning from pygtk - if previous_style is not None: - gtk.Window.do_style_set(self, previous_style) - self.set_border_width(self.get_style().xthickness) - - def is_up(self): - return self._up - - def get_rect(self): - win_x, win_y = self.window.get_origin() - rectangle = self.get_allocation() - - x = win_x + rectangle.x - y = win_y + rectangle.y - width = rectangle.width - height = rectangle.height - - return gtk.gdk.Rectangle(x, y, width, height) - - def set_invoker(self, invoker): - for hid in self._invoker_hids[:]: - self._invoker.disconnect(hid) - self._invoker_hids.remove(hid) - - self._invoker = invoker - if invoker is not None: - self._invoker_hids.append(self._invoker.connect( - 'mouse-enter', self._invoker_mouse_enter_cb)) - self._invoker_hids.append(self._invoker.connect( - 'mouse-leave', self._invoker_mouse_leave_cb)) - self._invoker_hids.append(self._invoker.connect( - 'right-click', self._invoker_right_click_cb)) - if hasattr(invoker.props, 'widget'): - self._update_accel_widget() - logging.debug(('Setup widget', invoker.props.widget)) - self._invoker_hids.append(self._invoker.connect( - 'notify::widget', self._invoker_widget_changed_cb)) - - def get_invoker(self): - return self._invoker - - invoker = gobject.property(type=object, - getter=get_invoker, - setter=set_invoker) - def _update_accel_widget(self): assert self.props.invoker is not None self._label.props.accel_widget = self.props.invoker.props.widget @@ -438,24 +654,8 @@ class Palette(gtk.Window): self._update_accept_focus() self._update_separators() - def set_group_id(self, group_id): - if self._group_id: - group = palettegroup.get_group(self._group_id) - group.remove(self) - if group_id: - self._group_id = group_id - group = palettegroup.get_group(group_id) - group.add(self) - - def get_group_id(self): - return self._group_id - - group_id = gobject.property(type=str, - getter=get_group_id, - setter=set_group_id) - def do_size_request(self, requisition): - gtk.Window.do_size_request(self, requisition) + PaletteWindow.do_size_request(self, requisition) # gtk.AccelLabel request doesn't include the accelerator. label_width = self._label_alignment.size_request()[0] + \ @@ -463,54 +663,9 @@ class Palette(gtk.Window): 2 * self.get_border_width() requisition.width = max(requisition.width, - style.GRID_CELL_SIZE * 2, label_width, self._full_request[0]) - def do_size_allocate(self, allocation): - gtk.Window.do_size_allocate(self, allocation) - - if self._old_alloc is None or \ - self._old_alloc.x != allocation.x or \ - self._old_alloc.y != allocation.y or \ - self._old_alloc.width != allocation.width or \ - self._old_alloc.height != allocation.height: - self.queue_draw() - - # We need to store old allocation because when size_allocate - # is called widget.allocation is already updated. - # gtk.Window resizing is different from normal containers: - # the X window is resized, widget.allocation is updated from - # the configure request handler and finally size_allocate is called. - self._old_alloc = allocation - - def do_expose_event(self, event): - # We want to draw a border with a beautiful gap - if self._invoker is not None and self._invoker.has_rectangle_gap(): - invoker = self._invoker.get_rect() - palette = self.get_rect() - - gap = _calculate_gap(palette, invoker) - else: - gap = False - - allocation = self.get_allocation() - wstyle = self.get_style() - - if gap: - wstyle.paint_box_gap(event.window, gtk.STATE_PRELIGHT, - gtk.SHADOW_IN, event.area, self, "palette", - 0, 0, allocation.width, allocation.height, - gap[0], gap[1], gap[2]) - else: - wstyle.paint_box(event.window, gtk.STATE_PRELIGHT, - gtk.SHADOW_IN, event.area, self, "palette", - 0, 0, allocation.width, allocation.height) - - # Fall trough to the container expose handler. - # (Leaving out the window expose handler which redraws everything) - gtk.Bin.do_expose_event(self, event) - def _update_separators(self): visible = len(self.menu.get_children()) > 0 or \ len(self._content.get_children()) > 0 @@ -526,7 +681,6 @@ class Palette(gtk.Window): self.window.set_accept_focus(accept_focus) def __realize_cb(self, widget): - self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) self._update_accept_focus() def _update_full_request(self): @@ -540,52 +694,6 @@ class Palette(gtk.Window): self.menu.unembed() self._secondary_box.hide() - def _update_position(self): - invoker = self._invoker - if invoker is None or self._alignment is None: - logging.error('Cannot update the palette position.') - return - - rect = self.size_request() - position = invoker.get_position_for_alignment(self._alignment, rect) - if position is None: - position = invoker.get_position(rect) - - self.move(position.x, position.y) - - def popup(self, immediate=False, state=None): - logging.debug('Palette.popup immediate %r' % immediate) - - if state is None: - state = self.PRIMARY - self.set_state(state) - - if self._invoker is not None: - self._update_full_request() - self._alignment = self._invoker.get_alignment(self._full_request) - self._update_position() - self.set_transient_for(self._invoker.get_toplevel()) - - self._popdown_anim.stop() - - if not immediate: - self._popup_anim.start() - else: - self.show() - - self._secondary_anim.start() - - def popdown(self, immediate=False): - logging.debug('Palette.popdown immediate %r' % immediate) - self._popup_anim.stop() - - self._mouse_detector.stop() - - if not immediate: - self._popdown_anim.start() - else: - self.hide() - def set_state(self, state): if self.palette_state == state: return @@ -596,74 +704,10 @@ class Palette(gtk.Window): elif state == self.SECONDARY: self.menu.embed(self._menu_box) self._secondary_box.show() - self._update_position() + self.update_position() self.palette_state = state - def _mouse_slow_cb(self, widget): - self._mouse_detector.stop() - self._palette_do_popup() - - def _palette_do_popup(self): - immediate = False - - if self.is_up(): - self._popdown_anim.stop() - return - - if self._group_id: - group = palettegroup.get_group(self._group_id) - if group and group.is_up(): - immediate = True - group.popdown() - - self.popup(immediate=immediate) - - def _invoker_widget_changed_cb(self, invoker, spec): - self._update_accel_widget() - - def _invoker_mouse_enter_cb(self, invoker): - self._mouse_detector.start() - - def _invoker_mouse_leave_cb(self, invoker): - self._mouse_detector.stop() - self.popdown() - - def _invoker_right_click_cb(self, invoker): - self.popup(immediate=True, state=self.SECONDARY) - - def __enter_notify_event_cb(self, widget, event): - if event.detail != gtk.gdk.NOTIFY_INFERIOR and \ - event.mode == gtk.gdk.CROSSING_NORMAL: - self._popdown_anim.stop() - self._secondary_anim.start() - - def __leave_notify_event_cb(self, widget, event): - if event.detail != gtk.gdk.NOTIFY_INFERIOR and \ - event.mode == gtk.gdk.CROSSING_NORMAL: - self.popdown() - - def __show_cb(self, widget): - self.menu.set_active(True) - - self._invoker.notify_popup() - - self._up = True - self.emit('popup') - - def __hide_cb(self, widget): - logging.debug('__hide_cb') - self.menu.set_active(False) - - self._secondary_anim.stop() - - if self._invoker: - self._invoker.notify_popdown() - - self._up = False - self.emit('popdown') - - class PaletteActionBar(gtk.HButtonBox): def add_action(self, label, icon_name=None): button = gtk.Button(label) diff --git a/src/sugar/graphics/toolbarbox.py b/src/sugar/graphics/toolbarbox.py index e8c1b359..f2f3ef8a 100644 --- a/src/sugar/graphics/toolbarbox.py +++ b/src/sugar/graphics/toolbarbox.py @@ -15,25 +15,21 @@ # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. -import gtk -import gobject import logging + +import gobject from gobject import SIGNAL_RUN_FIRST, TYPE_NONE +import gtk from sugar.graphics import style +from sugar.graphics.palette import PaletteWindow, ToolInvoker from sugar.graphics.toolbutton import ToolButton -from sugar.graphics.palette import MouseSpeedDetector, Invoker -from sugar.graphics.animator import Animator, Animation -from sugar.graphics import palettegroup class ToolbarButton(ToolButton): - def __init__(self, **kwargs): - self.page_widget = None - + def __init__(self, page=None, **kwargs): ToolButton.__init__(self, **kwargs) - if self.palette is None: - self.palette = _Palette(self) + self.set_page(page) self.connect('clicked', lambda widget: self.set_expanded(not self.is_expanded())) @@ -51,19 +47,36 @@ class ToolbarButton(ToolButton): return self.page_widget.child.child def set_page(self, page): + if page is None: + self.page_widget = None + return self.page_widget = _embody_page(_Box, page) self.page_widget.toolbar_button = self page.show() + if self.props.palette is None: + self.props.palette = _ToolbarPalette(invoker=ToolInvoker(self)) + self.props.palette.toolbar_button = self + self._move_page_to_palette() page = gobject.property(type=object, getter=get_page, setter=set_page) + def _move_page_to_palette(self): + if self.page_widget is None: + return + + if self.toolbar_box is not None and \ + self.page_widget in self.toolbar_box.get_children(): + self.toolbar_box.remove(self.page_widget) + + if self.props.palette is not None: + self.props.palette.add(self.page_widget) + def is_expanded(self): return self.toolbar_box is not None and self.page_widget is not None \ and self.toolbar_box.expanded_button == self def popdown(self): - if isinstance(self.palette, _Palette) and self.palette.is_up(): - self.palette.popdown(immediate=True) + self.props.palette.popdown(immediate=True) def set_expanded(self, expanded): self.popdown() @@ -77,6 +90,7 @@ class ToolbarButton(ToolButton): if not expanded: box.remove(self.page_widget) box.expanded_button = None + self._move_page_to_palette() return if box.expanded_button is not None: @@ -86,7 +100,7 @@ class ToolbarButton(ToolButton): box.expanded_button.set_expanded(False) if self.page_widget.parent is not None: - self.palette.remove(self.page_widget) + self.props.palette.remove(self.page_widget) self.modify_bg(gtk.STATE_NORMAL, box.background) _setup_page(self.page_widget, box.background, box.props.padding) @@ -94,8 +108,8 @@ class ToolbarButton(ToolButton): box.expanded_button = self def do_expose_event(self, event): - if not self.is_expanded() or self.palette is not None and \ - self.palette.is_up(): + if not self.is_expanded() or self.props.palette is not None and \ + self.props.palette.is_up(): ToolButton.do_expose_event(self, event) _paint_arrow(self, event, gtk.ARROW_DOWN) return @@ -157,6 +171,26 @@ class ToolbarBox(gtk.VBox): self.toolbar.parent.parent.modify_bg(state, color) self.toolbar.modify_bg(state, color) +class _ToolbarPalette(PaletteWindow): + def __init__(self, **kwargs): + PaletteWindow.__init__(self, **kwargs) + self.toolbar_box = None + self.set_border_width(0) + + def do_size_request(self, requisition): + gtk.Window.do_size_request(self, requisition) + requisition.width = max(requisition.width, + gtk.gdk.screen_width()) + + def popup(self, immediate=False): + button = self.toolbar_button + if button.is_expanded(): + return + box = button.toolbar_box + _setup_page(button.page_widget, style.COLOR_BLACK.get_gdk_color(), + box.props.padding) + PaletteWindow.popup(self, immediate) + class _Box(gtk.EventBox): def __init__(self): gtk.EventBox.__init__(self) @@ -176,193 +210,6 @@ class _Box(gtk.EventBox): alloc.width - style._FOCUS_LINE_WIDTH*2, style._FOCUS_LINE_WIDTH) -class _Palette(gtk.Window): - def __init__(self, toolitem, **kwargs): - gobject.GObject.__init__(self, **kwargs) - - self.set_decorated(False) - self.set_resizable(False) - self.set_border_width(0) - - self._toolitem = toolitem - self._invoker = None - self._up = False - self._invoker_hids = [] - self._focus = 0 - - self._popup_anim = Animator(.5, 10) - self._popup_anim.add(_PopupAnimation(self)) - - self._popdown_anim = Animator(0.6, 10) - self._popdown_anim.add(_PopdownAnimation(self)) - - accel_group = gtk.AccelGroup() - self.set_data('sugar-accel-group', accel_group) - self.add_accel_group(accel_group) - - self.connect('show', self.__show_cb) - self.connect('hide', self.__hide_cb) - self.connect('realize', self.__realize_cb) - self.connect('enter-notify-event', self.__enter_notify_event_cb) - self.connect('leave-notify-event', self.__leave_notify_event_cb) - - self._mouse_detector = MouseSpeedDetector(self, 200, 5) - self._mouse_detector.connect('motion-slow', self._mouse_slow_cb) - - group = palettegroup.get_group('default') - group.connect('popdown', self.__group_popdown_cb) - - def is_up(self): - return self._up - - def get_rect(self): - win_x, win_y = self.window.get_origin() - rectangle = self.get_allocation() - - x = win_x + rectangle.x - y = win_y + rectangle.y - width = rectangle.width - height = rectangle.height - - return gtk.gdk.Rectangle(x, y, width, height) - - def set_invoker(self, invoker): - for hid in self._invoker_hids[:]: - self._invoker.disconnect(hid) - self._invoker_hids.remove(hid) - - self._invoker = invoker - if invoker is not None: - self._invoker_hids.append(self._invoker.connect( - 'mouse-enter', self.__invoker_mouse_enter_cb)) - self._invoker_hids.append(self._invoker.connect( - 'mouse-leave', self.__invoker_mouse_leave_cb)) - self._invoker_hids.append(self._invoker.connect( - 'right-click', self.__invoker_right_click_cb)) - - def get_invoker(self): - return self._invoker - - invoker = gobject.property(type=object, - getter=get_invoker, - setter=set_invoker) - - def do_size_request(self, requisition): - gtk.Window.do_size_request(self, requisition) - if self._toolitem.toolbar_box is not None: - requisition.width = self._toolitem.toolbar_box.allocation.width - - def __realize_cb(self, widget): - self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) - #accept_focus = len(self._content.get_children()) - #if self.window: - # self.window.set_accept_focus(accept_focus) - - def popup(self, immediate=False): - self._popdown_anim.stop() - - toolbar = self._toolitem.toolbar_box - page = self._toolitem.page_widget - - if not self._invoker or self._toolitem.is_expanded() or not toolbar: - return - - _setup_page(page, style.COLOR_BLACK.get_gdk_color(), - toolbar.props.padding) - if self.child is None: - self.add(page) - - x, y = toolbar.window.get_origin() - self.move(x + toolbar.allocation.x, - y + toolbar.toolbar.allocation.height) - self.set_transient_for(self._invoker.get_toplevel()) - - if not immediate: - self._popup_anim.start() - else: - self.show() - - def popdown(self, immediate=False): - self._popup_anim.stop() - self._mouse_detector.stop() - - if not immediate: - self._popdown_anim.start() - else: - self.hide() - - def _mouse_slow_cb(self, widget): - self._mouse_detector.stop() - - if self.is_up(): - self._popdown_anim.stop() - return - - self.popup(immediate=False) - - def _handle_focus(self, delta): - self._focus += delta - if self._focus not in (0, 1): - logging.error('_Palette._focus=%s not in (0, 1)' % self._focus) - - if self._focus == 0: - group = palettegroup.get_group('default') - if not group.is_up(): - self.popdown() - - def __group_popdown_cb(self, group): - if self._focus == 0: - self.popdown(immediate=True) - - def __invoker_mouse_enter_cb(self, invoker): - self._mouse_detector.start() - self._handle_focus(+1) - - def __invoker_mouse_leave_cb(self, invoker): - self._mouse_detector.stop() - self._handle_focus(-1) - - def __invoker_right_click_cb(self, invoker): - self.popup(immediate=True) - - def __enter_notify_event_cb(self, widget, event): - if event.detail != gtk.gdk.NOTIFY_INFERIOR and \ - event.mode == gtk.gdk.CROSSING_NORMAL: - self._popdown_anim.stop() - self._handle_focus(+1) - - def __leave_notify_event_cb(self, widget, event): - if event.detail != gtk.gdk.NOTIFY_INFERIOR and \ - event.mode == gtk.gdk.CROSSING_NORMAL: - self._handle_focus(-1) - - def __show_cb(self, widget): - self._invoker.notify_popup() - self._up = True - - def __hide_cb(self, widget): - if self._invoker: - self._invoker.notify_popdown() - self._up = False - -class _PopupAnimation(Animation): - def __init__(self, palette): - Animation.__init__(self, 0.0, 1.0) - self._palette = palette - - def next_frame(self, current): - if current == 1.0: - self._palette.show() - -class _PopdownAnimation(Animation): - def __init__(self, palette): - Animation.__init__(self, 0.0, 1.0) - self._palette = palette - - def next_frame(self, current): - if current == 1.0: - self._palette.hide() - def _setup_page(page, color, hpad): vpad = style._FOCUS_LINE_WIDTH page.child.set_padding(vpad, vpad, hpad, hpad)