Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/sugar3/Makefile.am4
-rw-r--r--src/sugar3/graphics/palette.py228
-rw-r--r--src/sugar3/graphics/palettegroup.py5
-rw-r--r--src/sugar3/graphics/palettewindow.py489
-rw-r--r--src/sugar3/graphics/toolbarbox.py85
-rw-r--r--src/sugar3/sugar-menu.c63
-rw-r--r--src/sugar3/sugar-menu.h57
7 files changed, 515 insertions, 416 deletions
diff --git a/src/sugar3/Makefile.am b/src/sugar3/Makefile.am
index 1f073df..3aec8bf 100644
--- a/src/sugar3/Makefile.am
+++ b/src/sugar3/Makefile.am
@@ -53,9 +53,7 @@ libsugarext_la_SOURCES = \
sugar-grid.c \
sugar-grid.h \
sugar-key-grabber.c \
- sugar-key-grabber.h \
- sugar-menu.c \
- sugar-menu.h
+ sugar-key-grabber.h
sugar_LTLIBRARIES = _sugarbaseext.la
diff --git a/src/sugar3/graphics/palette.py b/src/sugar3/graphics/palette.py
index ee76674..1f95c11 100644
--- a/src/sugar3/graphics/palette.py
+++ b/src/sugar3/graphics/palette.py
@@ -1,6 +1,8 @@
# Copyright (C) 2007, Eduardo Silva <edsiper@gmail.com>
# Copyright (C) 2008, One Laptop Per Child
# Copyright (C) 2009, Tomeu Vizoso
+# Copyright (C) 2011, Benjamin Berg <benjamin@sipsolutions.net>
+# Copyright (C) 2011, Marco Pesenti Gritti <marco@marcopg.org>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -29,8 +31,8 @@ from sugar3.graphics import palettegroup
from sugar3.graphics import animator
from sugar3.graphics import style
from sugar3.graphics.icon import Icon
-from sugar3.graphics.palettewindow import PaletteWindow
-from gi.repository import SugarExt
+from sugar3.graphics.palettewindow import PaletteWindow, \
+ _PaletteWindowWidget, _PaletteMenuWidget
# DEPRECATED
# Import these for backwards compatibility
@@ -39,16 +41,37 @@ from sugar3.graphics.palettewindow import MouseSpeedDetector, Invoker, \
class Palette(PaletteWindow):
+ """
+ Floating palette implementation.
+
+ This class dynamically switches between one of two encapsulated child
+ widget types: a _PaletteWindowWidget or a _PaletteMenuWidget.
+
+ The window widget, created by default, acts as the container for any
+ type of widget the user may wish to add. It can optionally display primary
+ text, secondary text, and an icon at the top of the palette.
+
+ If the user attempts to access the 'menu' property, the window widget is
+ destroyed and the palette is dynamically switched to use a menu widget.
+ This is a GtkMenu that retains the same look and feel as a normal palette,
+ allowing submenus and so on. If primary text, secondary text and/or icons
+ were provided, an initial menu entry is created containing widgets to
+ display such information.
+ """
+
PRIMARY = 0
SECONDARY = 1
+ __gsignals__ = {
+ 'activate': (GObject.SignalFlags.RUN_FIRST, None, ([])),
+ }
+
__gtype_name__ = 'SugarPalette'
- def __init__(self, label=None, accel_path=None, menu_after_content=False,
+ def __init__(self, label=None, accel_path=None,
text_maxlen=60, **kwargs):
# DEPRECATED: label is passed with the primary-text property,
- # accel_path is set via the invoker property, and menu_after_content
- # is not used
+ # accel_path is set via the invoker property
self._primary_text = None
self._secondary_text = None
@@ -56,15 +79,12 @@ class Palette(PaletteWindow):
self._icon_visible = True
self._palette_state = self.PRIMARY
- palette_box = Gtk.VBox()
-
- primary_box = Gtk.HBox()
- palette_box.pack_start(primary_box, False, True, 0)
- primary_box.show()
+ self._primary_box = Gtk.HBox()
+ self._primary_box.show()
self._icon_box = Gtk.HBox()
self._icon_box.set_size_request(style.GRID_CELL_SIZE, -1)
- primary_box.pack_start(self._icon_box, False, True, 0)
+ self._primary_box.pack_start(self._icon_box, False, True, 0)
labels_box = Gtk.VBox()
self._label_alignment = Gtk.Alignment(xalign=0, yalign=0.5, xscale=1,
@@ -73,7 +93,7 @@ class Palette(PaletteWindow):
style.DEFAULT_SPACING)
self._label_alignment.add(labels_box)
self._label_alignment.show()
- primary_box.pack_start(self._label_alignment, True, True, 0)
+ self._primary_box.pack_start(self._label_alignment, True, True, 0)
labels_box.show()
self._label = Gtk.AccelLabel(label='')
@@ -94,77 +114,51 @@ class Palette(PaletteWindow):
labels_box.pack_start(self._secondary_label, True, True, 0)
self._secondary_box = Gtk.VBox()
- palette_box.pack_start(self._secondary_box, True, True, 0)
self._separator = Gtk.HSeparator()
self._secondary_box.pack_start(self._separator, True, True, 0)
- self._menu_content_separator = Gtk.HSeparator()
-
self._secondary_anim = animator.Animator(2.0, 10)
self._secondary_anim.add(_SecondaryAnimation(self))
# we init after initializing all of our containers
PaletteWindow.__init__(self, **kwargs)
- primary_box.set_size_request(-1, style.GRID_CELL_SIZE
- - 2 * self.get_border_width())
-
self._full_request = [0, 0]
- self._menu_box = None
self._content = None
# we set these for backward compatibility
if label is not None:
self.props.primary_text = label
- self._add_menu()
- self._secondary_box.pack_start(self._menu_content_separator, True, True, 0)
self._add_content()
self.action_bar = PaletteActionBar()
self._secondary_box.pack_start(self.action_bar, True, True, 0)
self.action_bar.show()
- self.add(palette_box)
- palette_box.show()
+ self.connect('notify::invoker', self.__notify_invoker_cb)
+ self.connect('popdown', self.__popdown_cb)
- # The menu is not shown here until an item is added
- self.menu = _Menu(self)
- self.menu.connect('item-inserted', self.__menu_item_inserted_cb)
+ # Default to a normal window palette
+ self._content_widget = None
+ self.set_content(None)
- 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)
+ def _setup_widget(self):
+ PaletteWindow._setup_widget(self)
+ self._widget.connect('destroy', self.__destroy_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._secondary_anim.stop()
self.popdown(immediate=True)
# 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.disconnect_by_func(self.__menu_item_inserted_cb)
- self.menu = None
+ self._widget = None
- def __show_cb(self, widget):
- self.menu.set_active(True)
-
- def __hide_cb(self, widget):
- self.menu.set_active(False)
- self.menu.cancel()
+ def __popdown_cb(self, widget):
self._secondary_anim.stop()
def __notify_invoker_cb(self, palette, pspec):
@@ -198,28 +192,16 @@ class Palette(PaletteWindow):
def popdown(self, immediate=False):
if immediate:
self._secondary_anim.stop()
- self._popdown_submenus()
# to suppress glitches while later re-opening
self.set_palette_state(self.PRIMARY)
+ if self._widget:
+ self._widget.size_request()
PaletteWindow.popdown(self, immediate)
- def _popdown_submenus(self):
- # TODO explicit hiding of subitems
- # should be removed after fixing #1301
- if self.menu is not None:
- for menu_item in self.menu.get_children():
- if menu_item.props.submenu is not None:
- menu_item.props.submenu.popdown()
-
- def on_enter(self, event):
- PaletteWindow.on_enter(self, event)
+ def on_enter(self):
+ PaletteWindow.on_enter(self)
self._secondary_anim.start()
- def _add_menu(self):
- self._menu_box = Gtk.VBox()
- self._secondary_box.pack_start(self._menu_box, True, True, 0)
- self._menu_box.show()
-
def _add_content(self):
# The content is not shown until a widget is added
self._content = Gtk.VBox()
@@ -315,6 +297,22 @@ class Palette(PaletteWindow):
setter=set_icon_visible)
def set_content(self, widget):
+ assert self._widget is None \
+ or isinstance(self._widget, _PaletteWindowWidget)
+
+ if self._widget is None:
+ self._widget = _PaletteWindowWidget()
+ self._setup_widget()
+
+ self._palette_box = Gtk.VBox()
+ self._palette_box.pack_start(self._primary_box, False, True, 0)
+ self._palette_box.pack_start(self._secondary_box, True, True, 0)
+
+ self._widget.add(self._palette_box)
+ self._palette_box.show()
+ height = style.GRID_CELL_SIZE - 2 * self._widget.get_border_width()
+ self._primary_box.set_size_request(-1, height)
+
if self._content.get_children():
self._content.remove(self._content.get_children()[0])
@@ -324,48 +322,37 @@ class Palette(PaletteWindow):
else:
self._content.hide()
+ self._content_widget = widget
+
self._update_accept_focus()
self._update_separators()
- def do_size_request(self, requisition):
- PaletteWindow.do_size_request(self, requisition)
+ def do_get_preferred_width(self):
+ minimum, natural = PaletteWindow.do_get_preferred_width(self)
# Gtk.AccelLabel request doesn't include the accelerator.
label_width = self._label_alignment.size_request()[0] + \
self._label.get_accel_width() + \
2 * self.get_border_width()
- requisition.width = max(requisition.width,
- label_width,
- self._full_request[0])
+ width = max(minimum, label_width, self._full_request[0])
+ return width, width
def _update_separators(self):
- visible = self.menu.get_children() or \
- self._content.get_children()
+ visible = self._content.get_children()
self._separator.props.visible = visible
- visible = self.menu.get_children() and \
- self._content.get_children()
- self._menu_content_separator.props.visible = visible
-
def _update_accept_focus(self):
accept_focus = len(self._content.get_children())
- window = self.get_window()
- if window:
- window.set_accept_focus(accept_focus)
-
- def __realize_cb(self, widget):
- self._update_accept_focus()
+ self._widget.set_accept_focus(accept_focus)
def _update_full_request(self):
if self._palette_state == self.PRIMARY:
- self.menu.embed(self._menu_box)
self._secondary_box.show()
- self._full_request = self.size_request()
+ self._full_request = self._widget.size_request()
if self._palette_state == self.PRIMARY:
- self.menu.unembed()
self._secondary_box.hide()
def _set_palette_state(self, state):
@@ -373,15 +360,46 @@ class Palette(PaletteWindow):
return
if state == self.PRIMARY:
- self.menu.unembed()
self._secondary_box.hide()
elif state == self.SECONDARY:
- self.menu.embed(self._menu_box)
self._secondary_box.show()
self.update_position()
self._palette_state = state
+ def get_menu(self):
+ assert self._content_widget is None
+
+ if self._widget is None \
+ or not isinstance(self._widget, _PaletteMenuWidget):
+ if self._widget is not None:
+ self._palette_box.remove(self._primary_box)
+ self._palette_box.remove(self._secondary_box)
+ self._teardown_widget()
+ self._widget.destroy()
+
+ self._widget = _PaletteMenuWidget()
+
+ self._label_menuitem = Gtk.MenuItem()
+ child = self._label_menuitem.get_child()
+ if child is not None:
+ self._label_menuitem.remove(child)
+ self._label_menuitem.add(self._primary_box)
+
+ # Mark the menuitem as insensitive so that it appears as an
+ # informational element, rather than a clickable item in the menu.
+ # TODO: see if we can do this better in GTK.
+ self._label_menuitem.set_sensitive(False)
+
+ self._label_menuitem.show()
+ self._widget.append(self._label_menuitem)
+
+ self._setup_widget()
+
+ return self._widget
+
+ menu = GObject.property(type=object, getter=get_menu)
+
class PaletteActionBar(Gtk.HButtonBox):
@@ -397,44 +415,6 @@ class PaletteActionBar(Gtk.HButtonBox):
button.show()
-class _Menu(SugarExt.Menu):
-
- __gtype_name__ = 'SugarPaletteMenu'
-
- __gsignals__ = {
- 'item-inserted': (GObject.SignalFlags.RUN_FIRST, None, ([])),
- }
-
- def __init__(self, palette):
- SugarExt.Menu.__init__(self)
- self._palette = palette
-
- def do_insert(self, item, position):
- SugarExt.Menu.do_insert(self, item, position)
- self.emit('item-inserted')
- self.show()
-
- def attach(self, child, left_attach, right_attach,
- top_attach, bottom_attach):
- SugarExt.Menu.attach(self, child, left_attach, right_attach,
- top_attach, bottom_attach)
- self.emit('item-inserted')
- self.show()
-
- def do_expose_event(self, event):
- # Ignore the Menu expose, just do the MenuShell expose to prevent any
- # border from being drawn here. A border is drawn by the palette object
- # around everything.
- Gtk.MenuShell.do_expose_event(self, event)
-
- def do_grab_notify(self, was_grabbed):
- # Ignore grab_notify as the menu would close otherwise
- pass
-
- def do_deactivate(self):
- self._palette.hide()
-
-
class _SecondaryAnimation(animator.Animation):
def __init__(self, palette):
diff --git a/src/sugar3/graphics/palettegroup.py b/src/sugar3/graphics/palettegroup.py
index cf38927..2d1537d 100644
--- a/src/sugar3/graphics/palettegroup.py
+++ b/src/sugar3/graphics/palettegroup.py
@@ -75,6 +75,11 @@ class Group(GObject.GObject):
self._sig_ids[palette].append(sid)
def remove(self, palette):
+ if not palette in self._palettes:
+ # This happens when converting a window based palette to a menu
+ # based one.
+ return
+
sig_ids = self._sig_ids[palette]
for sid in sig_ids:
palette.disconnect(sid)
diff --git a/src/sugar3/graphics/palettewindow.py b/src/sugar3/graphics/palettewindow.py
index 562f1d5..fe0b5a1 100644
--- a/src/sugar3/graphics/palettewindow.py
+++ b/src/sugar3/graphics/palettewindow.py
@@ -1,6 +1,8 @@
# Copyright (C) 2007, Eduardo Silva <edsiper@gmail.com>
# Copyright (C) 2008, One Laptop Per Child
# Copyright (C) 2009, Tomeu Vizoso
+# Copyright (C) 2011, Benjamin Berg <benjamin@sipsolutions.net>
+# Copyright (C) 2011, Marco Pesenti Gritti <marco@marcopg.org>
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -65,6 +67,269 @@ def _calculate_gap(a, b):
return False
+class _PaletteMenuWidget(Gtk.Menu):
+
+ __gtype_name__ = "SugarPaletteMenuWidget"
+
+ __gsignals__ = {
+ 'enter-notify': (GObject.SignalFlags.RUN_FIRST, None, ([])),
+ 'leave-notify': (GObject.SignalFlags.RUN_FIRST, None, ([])),
+ }
+
+ def __init__(self):
+ Gtk.Menu.__init__(self)
+
+ accel_group = Gtk.AccelGroup()
+ self.set_data('sugar-accel-group', accel_group)
+ self.get_toplevel().add_accel_group(accel_group)
+
+ self._popup_position = (0, 0)
+ self._entered = False
+ self._mouse_in_palette = False
+ self._mouse_in_invoker = False
+ self._up = False
+ self._invoker = None
+ self._menus = []
+
+ def set_accept_focus(self, focus):
+ pass
+
+ def get_origin(self):
+ res_, x, y = self.get_toplevel().get_window().get_origin()
+ return x, y
+
+ def do_size_request(self, requisition):
+ Gtk.Window.do_size_request(self, requisition)
+ requisition.width = max(requisition.width, style.GRID_CELL_SIZE * 2)
+
+ def move(self, x, y):
+ self._popup_position = (x, y)
+
+ def set_transient_for(self, window):
+ pass
+
+ def _position(self, widget, data):
+ return self._popup_position[0], self._popup_position[1], False
+
+ def popup(self, invoker):
+ if self._up:
+ return
+
+ # We need to track certain mouse events in order to close the palette
+ # when the mouse leaves the palette and the invoker widget, but
+ # GtkMenu makes our lives hard here.
+ #
+ # GtkMenu takes a grab on the root window, meaning that normal
+ # enter/leave events are not sent to the relevant widgets.
+ # However, connecting enter-notify and leave-notify events in this
+ # GtkMenu subclass mean that we get to see the events being grabbed.
+ # With certain filtering in place (see _enter_notify_cb and
+ # _leave_notify_cb) we are able to accurately determine when the
+ # mouse leaves/enters the palette menu. Some spurious events are
+ # generated but the important thing is that the last event generated
+ # in response to a user action is always reliable (i.e. we will
+ # always get a leave event last if the user left the menu,
+ # even if we get some strange enter events leading up to it).
+ #
+ # This is complicated with submenus; in this case the submenu takes
+ # the grab, so we must also listen for events on any submenus of
+ # the palette and apply the same considerations.
+ #
+ # The remaining challenge is tracking when the mouse enters or leaves
+ # the invoker area. While the appropriate GtkMenu grab is active,
+ # we do get informed of such events, however these events will only
+ # arrive if the user has entered the menu. If the user hovers over
+ # the invoker and then leaves the invoker without entering the palette,
+ # we get no enter/leave event.
+ # We work around this by tracking mouse motion events. When the mouse
+ # moves, we compare the mouse coordinates to the region occupied by the
+ # invoker, and this lets us track enter/leave for the invoker widget.
+
+ self._invoker = invoker
+ self._find_all_menus(self)
+ for menu in self._menus:
+ if self._invoker:
+ menu.connect('motion-notify-event', self._motion_notify_cb)
+ menu.connect('enter-notify-event', self._enter_notify_cb)
+ menu.connect('leave-notify-event', self._leave_notify_cb)
+ self._entered = False
+ self._mouse_in_palette = False
+ self._mouse_in_invoker = False
+ Gtk.Menu.popup(self, None, None, self._position, None, 0, 0)
+ self._up = True
+
+ def popdown(self):
+ if not self._up:
+ return
+ Gtk.Menu.popdown(self)
+
+ for menu in self._menus:
+ menu.disconnect_by_func(self._motion_notify_cb)
+ menu.disconnect_by_func(self._enter_notify_cb)
+ menu.disconnect_by_func(self._leave_notify_cb)
+
+ self._up = False
+ self._menus = []
+ self._invoker = None
+
+ def _find_all_menus(self, menu):
+ """
+ Recursively find all submenus of menu, adding them to self._menus.
+ """
+ self._menus.append(menu)
+ for child in menu.get_children():
+ if not isinstance(child, Gtk.MenuItem):
+ continue
+ submenu = child.get_submenu()
+ if submenu and isinstance(submenu, Gtk.Menu):
+ self._find_all_menus(submenu)
+
+ def _enter_notify_cb(self, widget, event):
+ if event.mode in (Gdk.CrossingMode.GRAB, Gdk.CrossingMode.GTK_GRAB):
+ return False
+ if Gtk.get_event_widget(event) not in self._menus:
+ return False
+
+ self._mouse_in_palette = True
+ self._reevaluate_state()
+ return False
+
+ def _leave_notify_cb(self, widget, event):
+ if event.mode in (Gdk.CrossingMode.GRAB, Gdk.CrossingMode.GTK_GRAB):
+ return False
+ if Gtk.get_event_widget(event) not in self._menus:
+ return False
+
+ self._mouse_in_palette = False
+ self._reevaluate_state()
+ return False
+
+ def _motion_notify_cb(self, widget, event):
+ rect = self._invoker.get_rect()
+ x = event.x_root
+ y = event.y_root
+ in_invoker = x >= rect.x and x < (rect.x + rect.width) \
+ and y >= rect.y and y < (rect.y + rect.height)
+ if in_invoker != self._mouse_in_invoker:
+ self._mouse_in_invoker = in_invoker
+ self._reevaluate_state()
+
+ def _reevaluate_state(self):
+ if self._entered:
+ # If we previously advised that the mouse was inside, but now the
+ # mouse is outside both the invoker and the palette, notify that
+ # the mouse has left.
+ if not self._mouse_in_palette and not self._mouse_in_invoker:
+ self._entered = False
+ self.emit('leave-notify')
+ else:
+ # If we previously advised that the mouse had left, but now the
+ # mouse is inside either the palette or the invoker, notify that
+ # the mouse has entered.
+ if self._mouse_in_palette or self._mouse_in_invoker:
+ self._entered = True
+ self.emit('enter-notify')
+
+
+class _PaletteWindowWidget(Gtk.Window):
+
+ __gtype_name__ = 'SugarPaletteWindowWidget'
+
+ __gsignals__ = {
+ 'enter-notify': (GObject.SignalFlags.RUN_FIRST, None, ([])),
+ 'leave-notify': (GObject.SignalFlags.RUN_FIRST, None, ([])),
+ }
+
+ def __init__(self):
+ Gtk.Window.__init__(self)
+
+ self.set_decorated(False)
+ self.set_resizable(False)
+ self.set_position(Gtk.WindowPosition.NONE)
+
+ accel_group = Gtk.AccelGroup()
+ self.set_data('sugar-accel-group', accel_group)
+ self.add_accel_group(accel_group)
+
+ self._old_alloc = None
+
+ self._should_accept_focus = True
+
+ def set_accept_focus(self, focus):
+ self._should_accept_focus = focus
+ if self.get_window() != None:
+ self.get_window().set_accept_focus(focus)
+
+ def get_origin(self):
+ res_, x, y = self.get_window().get_origin()
+ return x, y
+
+ def do_realize(self):
+ Gtk.Window.do_realize(self)
+
+ self.get_window().set_accept_focus(self._should_accept_focus)
+ self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
+
+ 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_button_release_event(self, event):
+ alloc = self.get_allocation()
+ x, y = self.get_window().get_position()
+
+ in_window = event.x_root >= x and event.x_root < x + alloc.width and \
+ event.y_root >= y and event.y_root < y + alloc.height
+
+ if not in_window:
+ self.popdown()
+ return True
+
+ def __enter_notify_event_cb(self, widget, event):
+ if event.mode == Gdk.CrossingMode.NORMAL and \
+ event.detail != Gdk.NotifyType.INFERIOR:
+ self.emit('enter-notify')
+ return False
+
+ def __leave_notify_event_cb(self, widget, event):
+ if event.mode != Gdk.CrossingMode.NORMAL:
+ return False
+
+ if event.detail != Gdk.NotifyType.INFERIOR:
+ self.emit('leave-notify')
+
+ def popup(self, invoker):
+ if self.get_visible():
+ return
+ self.connect('enter-notify-event', self.__enter_notify_event_cb)
+ self.connect('leave-notify-event', self.__leave_notify_event_cb)
+ self.show()
+
+ def popdown(self):
+ if not self.get_visible():
+ return
+ self.disconnect_by_func(self.__enter_notify_event_cb)
+ self.disconnect_by_func(self.__leave_notify_event_cb)
+ self.hide()
+
+
class MouseSpeedDetector(GObject.GObject):
__gsignals__ = {
@@ -75,15 +340,15 @@ class MouseSpeedDetector(GObject.GObject):
_MOTION_SLOW = 1
_MOTION_FAST = 2
- def __init__(self, parent, delay, thresh):
+ def __init__(self, delay, thresh):
"""Create MouseSpeedDetector object,
delay in msec
threshold in pixels (per tick of 'delay' msec)"""
GObject.GObject.__init__(self)
+ self.parent = None
self._threshold = thresh
- self._parent = parent
self._delay = delay
self._state = None
self._timeout_hid = None
@@ -101,8 +366,10 @@ class MouseSpeedDetector(GObject.GObject):
self._state = None
def _get_mouse_position(self):
- display = Gdk.Display.get_default()
- screen_, x, y, mask_ = display.get_pointer()
+ display = self.parent.get_display()
+ manager = display.get_device_manager()
+ pointer_device = manager.get_client_pointer()
+ screen, x, y = pointer_device.get_position()
return (x, y)
def _detect_motion(self):
@@ -128,14 +395,16 @@ class MouseSpeedDetector(GObject.GObject):
return True
-class PaletteWindow(Gtk.Window):
+class PaletteWindow(GObject.GObject):
+ """
+ Base class for _ToolbarPalette and Palette.
- __gtype_name__ = 'SugarPaletteWindow'
+ Provides basic management of child widget, invoker, and animation.
+ """
__gsignals__ = {
'popup': (GObject.SignalFlags.RUN_FIRST, None, ([])),
'popdown': (GObject.SignalFlags.RUN_FIRST, None, ([])),
- 'activate': (GObject.SignalFlags.RUN_FIRST, None, ([])),
}
def __init__(self, **kwargs):
@@ -146,8 +415,8 @@ class PaletteWindow(Gtk.Window):
self._cursor_y = 0
self._alignment = None
self._up = False
- self._old_alloc = None
self._palette_state = None
+ self._widget = None
self._popup_anim = animator.Animator(.5, 10)
self._popup_anim.add(_PopupAnimation(self))
@@ -157,29 +426,31 @@ class PaletteWindow(Gtk.Window):
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)
+ self.set_group_id('default')
- accel_group = Gtk.AccelGroup()
- self.set_data('sugar-accel-group', accel_group)
- self.add_accel_group(accel_group)
+ self._mouse_detector = MouseSpeedDetector(200, 5)
- self.set_group_id('default')
+ def _setup_widget(self):
+ self._widget.connect('show', self.__show_cb)
+ self._widget.connect('hide', self.__hide_cb)
+ self._widget.connect('destroy', self.__destroy_cb)
+ self._widget.connect('enter-notify', self.__enter_notify_cb)
+ self._widget.connect('leave-notify', self.__leave_notify_cb)
- 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._set_effective_group_id(self._group_id)
- self._mouse_detector = MouseSpeedDetector(self, 200, 5)
self._mouse_detector.connect('motion-slow', self._mouse_slow_cb)
+ self._mouse_detector.parent = self._widget
+
+ def _teardown_widget(self):
+ self._widget.disconnect_by_func(self.__show_cb)
+ self._widget.disconnect_by_func(self.__hide_cb)
+ self._widget.disconnect_by_func(self.__destroy_cb)
+ self._widget.disconnect_by_func(self.__enter_notify_cb)
+ self._widget.disconnect_by_func(self.__leave_notify_cb)
+ self._set_effective_group_id(None)
def __destroy_cb(self, palette):
- self.set_group_id(None)
self._mouse_detector.disconnect_by_func(self._mouse_slow_cb)
def set_invoker(self, invoker):
@@ -203,9 +474,6 @@ class PaletteWindow(Gtk.Window):
getter=get_invoker,
setter=set_invoker)
- def __realize_cb(self, widget):
- self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
-
def _mouse_slow_cb(self, widget):
self._mouse_detector.stop()
self._palette_do_popup()
@@ -228,15 +496,18 @@ class PaletteWindow(Gtk.Window):
def is_up(self):
return self._up
- def set_group_id(self, group_id):
+ def _set_effective_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 set_group_id(self, group_id):
+ self._set_effective_group_id(group_id)
+ self._group_id = group_id
+
def get_group_id(self):
return self._group_id
@@ -244,69 +515,21 @@ class PaletteWindow(Gtk.Window):
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.StateType.PRELIGHT,
- Gtk.ShadowType.IN, event.area, self, 'palette',
- 0, 0, allocation.width, allocation.height,
- gap[0], gap[1], gap[2])
- else:
- wstyle.paint_box(event.window, Gtk.StateType.PRELIGHT,
- Gtk.ShadowType.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):
invoker = self._invoker
if invoker is None or self._alignment is None:
logging.error('Cannot update the palette position.')
return
- rect = self.size_request()
+ rect = self._widget.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)
+ self._widget.move(position.x, position.y)
def get_full_size_request(self):
- return self.size_request()
+ return self._widget.size_request()
def popup(self, immediate=False):
if self._invoker is not None:
@@ -314,7 +537,7 @@ class PaletteWindow(Gtk.Window):
self._alignment = self._invoker.get_alignment(full_size_request)
self.update_position()
- self.set_transient_for(self._invoker.get_toplevel())
+ self._widget.set_transient_for(self._invoker.get_toplevel())
self._popdown_anim.stop()
@@ -322,7 +545,7 @@ class PaletteWindow(Gtk.Window):
self._popup_anim.start()
else:
self._popup_anim.stop()
- self.show()
+ self._widget.popup(self._invoker)
# we have to invoke update_position() twice
# since WM could ignore first move() request
self.update_position()
@@ -335,8 +558,8 @@ class PaletteWindow(Gtk.Window):
self._popdown_anim.start()
else:
self._popdown_anim.stop()
- self.size_request()
- self.hide()
+ if self._widget is not None:
+ self._widget.popdown()
def on_invoker_enter(self):
self._popdown_anim.stop()
@@ -346,10 +569,10 @@ class PaletteWindow(Gtk.Window):
self._mouse_detector.stop()
self.popdown()
- def on_enter(self, event):
+ def on_enter(self):
self._popdown_anim.stop()
- def on_leave(self, event):
+ def on_leave(self):
self.popdown()
def _invoker_mouse_enter_cb(self, invoker):
@@ -361,15 +584,11 @@ class PaletteWindow(Gtk.Window):
def _invoker_right_click_cb(self, invoker):
self.popup(immediate=True)
- def __enter_notify_event_cb(self, widget, event):
- if event.detail != Gdk.NOTIFY_INFERIOR and \
- event.mode == Gdk.CROSSING_NORMAL:
- self.on_enter(event)
+ def __enter_notify_cb(self, widget):
+ self.on_enter()
- def __leave_notify_event_cb(self, widget, event):
- if event.detail != Gdk.NOTIFY_INFERIOR and \
- event.mode == Gdk.CROSSING_NORMAL:
- self.on_leave(event)
+ def __leave_notify_cb(self, widget):
+ self.on_leave()
def __show_cb(self, widget):
if self._invoker is not None:
@@ -386,14 +605,20 @@ class PaletteWindow(Gtk.Window):
self.emit('popdown')
def get_rect(self):
- win_x, win_y = self.get_window().get_origin()
+ win_x, win_y = self._widget.get_origin()
rectangle = self.get_allocation()
x = win_x + rectangle.x
y = win_y + rectangle.y
- width, height = self.size_request()
+ requisition = self._widget.size_request()
- return (x, y, width, height)
+ rect = Gdk.Rectangle()
+ rect.x = x
+ rect.y = y
+ rect.width = requisition.width
+ rect.height = requisition.height
+
+ return rect
def get_palette_state(self):
return self._palette_state
@@ -453,8 +678,10 @@ class Invoker(GObject.GObject):
self.parent = None
- self._screen_area = (0, 0, Gdk.Screen.width(),
- Gdk.Screen.height())
+ self._screen_area = Gdk.Rectangle()
+ self._screen_area.x = self._screen_area.y = 0
+ self._screen_area.width = Gdk.Screen.width()
+ self._screen_area.height = Gdk.Screen.height()
self._position_hint = self.ANCHORED
self._cursor_x = -1
self._cursor_y = -1
@@ -477,8 +704,10 @@ class Invoker(GObject.GObject):
invoker_valign = alignment[3]
if self._cursor_x == -1 or self._cursor_y == -1:
- display = Gdk.Display.get_default()
- screen_, x, y, mask_ = display.get_pointer()
+ display = self.parent.get_display()
+ manager = display.get_device_manager()
+ pointer_device = manager.get_client_pointer()
+ screen, x, y = pointer_device.get_position()
self._cursor_x = x
self._cursor_y = y
@@ -486,11 +715,12 @@ class Invoker(GObject.GObject):
rect = self.get_rect()
else:
dist = style.PALETTE_CURSOR_DISTANCE
- rect = (self._cursor_x - dist,
- self._cursor_y - dist,
- dist * 2, dist * 2)
+ rect = Gdk.Rectangle()
+ rect.x = self._cursor_x - dist
+ rect.y = self._cursor_y - dist
+ rect.width = rect.height = dist * 2
- palette_width, palette_height = palette_dim
+ palette_width, palette_height = palette_dim.width, palette_dim.height
x = rect.x + rect.width * invoker_halign + \
palette_width * palette_halign
@@ -498,8 +728,12 @@ class Invoker(GObject.GObject):
y = rect.y + rect.height * invoker_valign + \
palette_height * palette_valign
- return (int(x), int(y),
- palette_width, palette_height)
+ rect = Gdk.Rectangle()
+ rect.x = int(x)
+ rect.y = int(y)
+ rect.width = palette_width
+ rect.height = palette_height
+ return rect
def _in_screen(self, rect):
return rect.x >= self._screen_area.x and \
@@ -582,12 +816,12 @@ class Invoker(GObject.GObject):
# Set palette_valign to align to screen on the top
if dtop > dbottom:
- pv = -float(dtop) / palette_dim[1]
+ pv = -float(dtop) / palette_dim.height
# Set palette_valign to align to screen on the bottom
else:
- pv = -float(palette_dim[1] - dbottom - rect.height) \
- / palette_dim[1]
+ pv = -float(palette_dim.height - dbottom - rect.height) \
+ / palette_dim.height
elif best_alignment in self.TOP or best_alignment in self.BOTTOM:
dleft = rect.x - screen_area.x
@@ -597,12 +831,12 @@ class Invoker(GObject.GObject):
# Set palette_halign to align to screen on left
if dleft > dright:
- ph = -float(dleft) / palette_dim[0]
+ ph = -float(dleft) / palette_dim.width
# Set palette_halign to align to screen on right
else:
- ph = -float(palette_dim[0] - dright - rect.width) \
- / palette_dim[0]
+ ph = -float(palette_dim.width - dright - rect.width) \
+ / palette_dim.width
return (ph, pv, ih, iv)
@@ -717,30 +951,31 @@ class WidgetInvoker(Invoker):
allocation = self._widget.get_allocation()
window = self._widget.get_window()
if window is not None:
- x, y = window.get_origin()
+ res, x, y = window.get_origin()
else:
logging.warning(
"Trying to position palette with invoker that's not realized.")
x = 0
y = 0
- if self._widget.flags() & Gtk.NO_WINDOW:
- x += allocation.x
- y += allocation.y
+ x += allocation.x
+ y += allocation.y
width = allocation.width
height = allocation.height
- return (x, y, width, height)
+ rect = Gdk.Rectangle()
+ rect.x = x
+ rect.y = y
+ rect.width = width
+ rect.height = height
+ return rect
def has_rectangle_gap(self):
return True
def draw_rectangle(self, event, palette):
- if self._widget.flags() & Gtk.NO_WINDOW:
- x, y = self._widget.allocation.x, self._widget.allocation.y
- else:
- x = y = 0
+ x, y = self._widget.allocation.x, self._widget.allocation.y
wstyle = self._widget.get_style()
gap = _calculate_gap(self.get_rect(), palette.get_rect())
@@ -763,7 +998,8 @@ class WidgetInvoker(Invoker):
self.notify_mouse_enter()
def __leave_notify_event_cb(self, widget, event):
- self.notify_mouse_leave()
+ if event.mode == Gdk.CrossingMode.NORMAL:
+ self.notify_mouse_leave()
def __button_release_event_cb(self, widget, event):
if event.button == 3:
@@ -908,16 +1144,15 @@ class CellRendererInvoker(Invoker):
allocation = self._tree_view.get_allocation()
window = self._tree_view.get_window()
if window is not None:
- x, y = window.get_origin()
+ res, x, y = window.get_origin()
else:
logging.warning(
"Trying to position palette with invoker that's not realized.")
x = 0
y = 0
- if self._tree_view.flags() & Gtk.NO_WINDOW:
- x += allocation.x
- y += allocation.y
+ x += allocation.x
+ y += allocation.y
width = allocation.width
height = allocation.height
diff --git a/src/sugar3/graphics/toolbarbox.py b/src/sugar3/graphics/toolbarbox.py
index b321715..5ec2db9 100644
--- a/src/sugar3/graphics/toolbarbox.py
+++ b/src/sugar3/graphics/toolbarbox.py
@@ -15,11 +15,14 @@
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
+import math
+
from gi.repository import Gtk
from gi.repository import GObject
from sugar3.graphics import style
-from sugar3.graphics.palette import PaletteWindow, ToolInvoker
+from sugar3.graphics.palettewindow import PaletteWindow, ToolInvoker, \
+ _PaletteWindowWidget
from sugar3.graphics.toolbutton import ToolButton
from sugar3.graphics import palettegroup
@@ -74,7 +77,7 @@ class ToolbarButton(ToolButton):
def is_in_palette(self):
return self.page is not None and \
- self.page_widget.get_parent() == self.props.palette
+ self.page_widget.get_parent() == self.props.palette._widget
def is_expanded(self):
return self.page is not None and \
@@ -97,10 +100,7 @@ class ToolbarButton(ToolButton):
box = self.toolbar_box
if box.expanded_button is not None:
- button_window = box.expanded_button.get_window()
- if button_window is not None:
- # need to redraw it to erase arrow
- button_window.invalidate_rect(None, True)
+ box.expanded_button.queue_draw()
box.expanded_button.set_expanded(False)
box.expanded_button = self
@@ -117,7 +117,7 @@ class ToolbarButton(ToolButton):
self._unparent()
if isinstance(self.props.palette, _ToolbarPalette):
- self.props.palette.add(self.page_widget)
+ self.props.palette._widget.add(self.page_widget)
def _unparent(self):
page_parent = self.page_widget.get_parent()
@@ -125,28 +125,22 @@ class ToolbarButton(ToolButton):
return
page_parent.remove(self.page_widget)
- def do_expose_event(self, event):
+ def do_draw(self, cr):
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.ArrowType.DOWN)
+ Gtk.ToolButton.do_draw(self, cr)
+ _paint_arrow(self, cr, math.pi)
return
- alloc = self.allocation
-
- self.get_style().paint_box(event.window,
- Gtk.StateType.NORMAL, Gtk.ShadowType.IN, event.area, self,
- 'palette-invoker', alloc.x, 0,
- alloc.width, alloc.height + style.FOCUS_LINE_WIDTH)
+ alloc = self.get_allocation()
- if self.get_child().state != Gtk.StateType.PRELIGHT:
- self.get_style().paint_box(event.window,
- Gtk.StateType.NORMAL, Gtk.ShadowType.NONE, event.area, self, None,
- alloc.x + style.FOCUS_LINE_WIDTH, style.FOCUS_LINE_WIDTH,
- alloc.width - style.FOCUS_LINE_WIDTH * 2, alloc.height)
+ context = self.get_style_context()
+ context.add_class('toolitem')
- Gtk.ToolButton.do_expose_event(self, event)
- _paint_arrow(self, event, Gtk.ArrowType.UP)
+ Gtk.render_frame_gap(context, cr, 0, 0, alloc.width, alloc.height,
+ Gtk.PositionType.BOTTOM, 0, alloc.width)
+ Gtk.ToolButton.do_draw(self, cr)
+ _paint_arrow(self, cr, 0)
class ToolbarBox(Gtk.VBox):
@@ -214,15 +208,20 @@ class _ToolbarPalette(PaletteWindow):
def __init__(self, **kwargs):
PaletteWindow.__init__(self, **kwargs)
- self.set_border_width(0)
self._has_focus = False
group = palettegroup.get_group('default')
group.connect('popdown', self.__group_popdown_cb)
self.set_group_id('toolbarbox')
+ self._widget = _PaletteWindowWidget()
+ self._widget.set_border_width(0)
+ self._setup_widget()
+
+ self._widget.connect('realize', self._realize_cb)
+
def get_expanded_button(self):
- return self.invoker.get_parent()
+ return self.invoker.parent
expanded_button = property(get_expanded_button)
@@ -234,12 +233,12 @@ class _ToolbarPalette(PaletteWindow):
PaletteWindow.on_invoker_leave(self)
self._set_focus(False)
- def on_enter(self, event):
- PaletteWindow.on_enter(self, event)
+ def on_enter(self):
+ PaletteWindow.on_enter(self)
self._set_focus(True)
- def on_leave(self, event):
- PaletteWindow.on_enter(self, event)
+ def on_leave(self):
+ PaletteWindow.on_enter(self)
self._set_focus(False)
def _set_focus(self, new_focus):
@@ -249,10 +248,10 @@ class _ToolbarPalette(PaletteWindow):
if not group.is_up():
self.popdown()
- def do_size_request(self, requisition):
- Gtk.Window.do_size_request(self, requisition)
- requisition.width = max(requisition.width,
- Gdk.Screen.width())
+ def _realize_cb(self, widget):
+ screen = self._widget.get_screen()
+ width = screen.width()
+ self._widget.set_size_request(width, -1)
def popup(self, immediate=False):
button = self.expanded_button
@@ -272,10 +271,10 @@ class _Box(Gtk.EventBox):
def __init__(self):
GObject.GObject.__init__(self)
- self.connect('expose-event', self.do_expose_event)
self.set_app_paintable(True)
def do_expose_event(self, widget, event):
+ # TODO: reimplement this in the theme
expanded_button = self.get_parent().expanded_button
if expanded_button is None:
return
@@ -326,12 +325,14 @@ def _get_embedded_page(page_widget):
return page_widget.get_child().get_child()
-def _paint_arrow(widget, event, arrow_type):
- alloc = widget.allocation
- x = alloc.x + alloc.width / 2 - style.TOOLBAR_ARROW_SIZE / 2
- y = alloc.y + alloc.height - int(style.TOOLBAR_ARROW_SIZE * .85)
+def _paint_arrow(widget, cr, angle):
+ alloc = widget.get_allocation()
+
+ arrow_size = style.TOOLBAR_ARROW_SIZE / 2
+ y = alloc.height - arrow_size
+ x = (alloc.width - arrow_size) / 2
+
+ context = widget.get_style_context()
+ context.add_class('toolitem')
- widget.get_style().paint_arrow(event.window,
- Gtk.StateType.NORMAL, Gtk.ShadowType.NONE, event.area, widget,
- None, arrow_type, True,
- x, y, style.TOOLBAR_ARROW_SIZE, style.TOOLBAR_ARROW_SIZE)
+ Gtk.render_arrow(context, cr, angle, x, y, arrow_size)
diff --git a/src/sugar3/sugar-menu.c b/src/sugar3/sugar-menu.c
deleted file mode 100644
index f19dc4b..0000000
--- a/src/sugar3/sugar-menu.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2006-2007, Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#include <gtk/gtkwindow.h>
-
-#include "sugar-menu.h"
-
-static void sugar_menu_class_init (SugarMenuClass *menu_class);
-static void sugar_menu_init (SugarMenu *menu);
-
-
-G_DEFINE_TYPE(SugarMenu, sugar_menu, GTK_TYPE_MENU)
-
-void
-sugar_menu_set_active(SugarMenu *menu, gboolean active)
-{
- GTK_MENU_SHELL(menu)->active = active;
-}
-
-void
-sugar_menu_embed(SugarMenu *menu, GtkContainer *parent)
-{
- menu->orig_toplevel = GTK_MENU(menu)->toplevel;
-
- GTK_MENU(menu)->toplevel = gtk_widget_get_toplevel(GTK_WIDGET(parent));
- gtk_widget_reparent(GTK_WIDGET(menu), GTK_WIDGET(parent));
-}
-
-void
-sugar_menu_unembed(SugarMenu *menu)
-{
- if (menu->orig_toplevel) {
- GTK_MENU(menu)->toplevel = menu->orig_toplevel;
- gtk_widget_reparent(GTK_WIDGET(menu), GTK_WIDGET(menu->orig_toplevel));
- }
-}
-
-static void
-sugar_menu_class_init(SugarMenuClass *menu_class)
-{
-}
-
-static void
-sugar_menu_init(SugarMenu *menu)
-{
- menu->orig_toplevel = NULL;
-}
diff --git a/src/sugar3/sugar-menu.h b/src/sugar3/sugar-menu.h
deleted file mode 100644
index c3bb3d0..0000000
--- a/src/sugar3/sugar-menu.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2006-2007, Red Hat, Inc.
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-#ifndef __SUGAR_MENU_H__
-#define __SUGAR_MENU_H__
-
-#include <gtk/gtk.h>
-
-G_BEGIN_DECLS
-
-typedef struct _SugarMenu SugarMenu;
-typedef struct _SugarMenuClass SugarMenuClass;
-
-#define SUGAR_TYPE_MENU (sugar_menu_get_type())
-#define SUGAR_MENU(object) (G_TYPE_CHECK_INSTANCE_CAST((object), SUGAR_TYPE_MENU, SugarMenu))
-#define SUGAR_MENU_CLASS(klass) (G_TYPE_CHACK_CLASS_CAST((klass), SUGAR_TYPE_MENU, SugarMenuClass))
-#define SUGAR_IS_MENU(object) (G_TYPE_CHECK_INSTANCE_TYPE((object), SUGAR_TYPE_MENU))
-#define SUGAR_IS_MENU_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SUGAR_TYPE_MENU))
-#define SUGAR_MENU_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS((object), SUGAR_TYPE_MENU, SugarMenuClass))
-
-struct _SugarMenu {
- GtkMenu base_instance;
-
- GtkWidget *orig_toplevel;
- int min_width;
-};
-
-struct _SugarMenuClass {
- GtkMenuClass base_class;
-};
-
-GType sugar_menu_get_type (void);
-void sugar_menu_set_active (SugarMenu *menu,
- gboolean active);
-void sugar_menu_embed (SugarMenu *menu,
- GtkContainer *parent);
-void sugar_menu_unembed (SugarMenu *menu);
-
-G_END_DECLS
-
-#endif /* __SUGAR_MENU_H__ */