Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/sugar
diff options
context:
space:
mode:
Diffstat (limited to 'sugar')
-rw-r--r--sugar/Makefile.am24
-rw-r--r--sugar/_sugaruiext.defs20
-rw-r--r--sugar/_sugaruiext.override1
-rw-r--r--sugar/activity/activityfactory.py7
-rw-r--r--sugar/activity/bundle.py14
-rw-r--r--sugar/activity/bundlebuilder.py3
-rw-r--r--sugar/activity/registry.py37
-rw-r--r--sugar/graphics/canvasicon.py8
-rw-r--r--sugar/graphics/iconbutton.py2
-rw-r--r--sugar/graphics/objectchooser.py6
-rw-r--r--sugar/graphics/palette.py342
-rw-r--r--sugar/graphics/radiotoolbutton.py18
-rw-r--r--sugar/graphics/style.py7
-rw-r--r--sugar/graphics/toggletoolbutton.py18
-rw-r--r--sugar/graphics/toolbutton.py17
15 files changed, 358 insertions, 166 deletions
diff --git a/sugar/Makefile.am b/sugar/Makefile.am
index 5fcb387..dffca33 100644
--- a/sugar/Makefile.am
+++ b/sugar/Makefile.am
@@ -12,21 +12,19 @@ sugar_PYTHON = \
util.py \
wm.py
-INCLUDES = \
- $(LIB_CFLAGS) \
- $(LIB_BINDINGS_CFLAGS) \
- $(PYTHON_INCLUDES) \
- -I$(top_srcdir)/lib \
- -I$(top_srcdir)/lib/ui
-
pkgpyexecdir = $(pythondir)/sugar
pkgpyexec_LTLIBRARIES = _sugarext.la _sugaruiext.la
+_sugarext_la_CFLAGS = \
+ $(LIB_CFLAGS) \
+ $(LIB_BINDINGS_CFLAGS) \
+ $(PYTHON_INCLUDES) \
+ -I$(top_srcdir)/lib
+
_sugarext_la_LDFLAGS = -module -avoid-version
_sugarext_la_LIBADD = \
$(LIB_BINDINGS_LIBS) \
- $(LIB_LIBS) \
$(top_builddir)/lib/libsugar.la
_sugarext_la_SOURCES = \
@@ -36,10 +34,16 @@ nodist__sugarext_la_SOURCES = _sugarext.c
_sugarext.c: _sugarext.defs _sugarext.override
+_sugaruiext_la_CFLAGS = \
+ $(LIBUI_CFLAGS) \
+ $(LIBUI_BINDINGS_CFLAGS) \
+ $(PYTHON_INCLUDES) \
+ -I$(top_srcdir)/lib/ui
+
_sugaruiext_la_LDFLAGS = -module -avoid-version
_sugaruiext_la_LIBADD = \
- $(LIB_BINDINGS_LIBS) \
- $(LIB_LIBS) \
+ $(LIBUI_BINDINGS_LIBS) \
+ $(LIBUI_LIBS) \
$(top_builddir)/lib/ui/libsugarui.la
_sugaruiext_la_SOURCES = \
diff --git a/sugar/_sugaruiext.defs b/sugar/_sugaruiext.defs
index c4efbbc..3c011e1 100644
--- a/sugar/_sugaruiext.defs
+++ b/sugar/_sugaruiext.defs
@@ -26,19 +26,27 @@
;; From sugar-menu.h
-(define-method popup
+(define-method set_active
(of-object "SugarMenu")
- (c-name "sugar_menu_popup")
+ (c-name "sugar_menu_set_active")
(return-type "none")
(parameters
- '("gint" "x")
- '("gint" "y")
+ '("gboolean" "active")
)
)
-(define-method popdown
+(define-method embed
(of-object "SugarMenu")
- (c-name "sugar_menu_popdown")
+ (c-name "sugar_menu_embed")
+ (return-type "none")
+ (parameters
+ '("GtkContainer" "container")
+ )
+)
+
+(define-method unembed
+ (of-object "SugarMenu")
+ (c-name "sugar_menu_unembed")
(return-type "none")
)
diff --git a/sugar/_sugaruiext.override b/sugar/_sugaruiext.override
index 02e900e..6daafc3 100644
--- a/sugar/_sugaruiext.override
+++ b/sugar/_sugaruiext.override
@@ -18,6 +18,7 @@ modulename _sugarext
import gobject.GObject as PyGObject_Type
import gtk.Entry as PyGtkEntry_Type
import gtk.Menu as PyGtkMenu_Type
+import gtk.Container as PyGtkContainer_Type
import gtk.gdk.Window as PyGdkWindow_Type
%%
ignore-glob
diff --git a/sugar/activity/activityfactory.py b/sugar/activity/activityfactory.py
index 404e5f4..b1d55eb 100644
--- a/sugar/activity/activityfactory.py
+++ b/sugar/activity/activityfactory.py
@@ -114,7 +114,7 @@ class ActivityCreationHandler(gobject.GObject):
self._factory.create(self._activity_handle.get_dict(),
timeout=120 * 1000,
- reply_handler=self._no_reply_handler,
+ reply_handler=self._create_reply_handler,
error_handler=self._create_error_handler)
def get_activity_id(self):
@@ -137,7 +137,10 @@ class ActivityCreationHandler(gobject.GObject):
def _activate_error_handler(self, err):
logging.debug("Activity activation request failed %s" % err)
- def _create_reply_handler(self, xid):
+ def _create_reply_handler(self, xid=None):
+ if xid is None:
+ self._create_error_handler('D-Bus error')
+ return
logging.debug("Activity created %s (%s)." %
(self._activity_handle.activity_id, self._service_name))
diff --git a/sugar/activity/bundle.py b/sugar/activity/bundle.py
index a9c246d..d361c62 100644
--- a/sugar/activity/bundle.py
+++ b/sugar/activity/bundle.py
@@ -41,6 +41,7 @@ class NotInstalledException(Exception): pass
class InvalidPathException(Exception): pass
class ZipExtractException(Exception): pass
class RegistrationException(Exception): pass
+class MalformedBundleException(Exception): pass
class Bundle:
"""Metadata description of a given application/activity
@@ -265,10 +266,12 @@ class Bundle:
if not bundle_root_dir:
bundle_root_dir = file_name.split('/')[0]
if not bundle_root_dir.endswith('.activity'):
- raise 'Incorrect bundle.'
+ raise MalformedBundleException(
+ 'The activity directory name must end with .activity')
else:
if not file_name.startswith(bundle_root_dir):
- raise 'Incorrect bundle.'
+ raise MalformedBundleException(
+ 'All files in the bundle must be inside the activity directory')
return bundle_root_dir
@@ -293,11 +296,8 @@ class Bundle:
raise ZipExtractException
self._init_with_path(bundle_path)
-
- bus = dbus.SessionBus()
- proxy_obj = bus.get_object(_DBUS_SHELL_SERVICE, _DBUS_SHELL_PATH)
- dbus_service = dbus.Interface(proxy_obj, _DBUS_ACTIVITY_REGISTRY_IFACE)
- if not dbus_service.AddBundle(bundle_path):
+
+ if not activity.get_registry().add_bundle(bundle_path):
raise RegistrationException
def deinstall(self):
diff --git a/sugar/activity/bundlebuilder.py b/sugar/activity/bundlebuilder.py
index b255cfb..3bbe454 100644
--- a/sugar/activity/bundlebuilder.py
+++ b/sugar/activity/bundlebuilder.py
@@ -162,8 +162,7 @@ def _get_mo_list(manifest):
for lang in _get_po_list(manifest).keys():
filename = _get_service_name() + '.mo'
- mo_list.append(os.path.join(_get_source_path(), 'locale',
- lang, 'LC_MESSAGES', filename))
+ mo_list.append(os.path.join('locale', lang, 'LC_MESSAGES', filename))
return mo_list
diff --git a/sugar/activity/registry.py b/sugar/activity/registry.py
index b19abee..1483a78 100644
--- a/sugar/activity/registry.py
+++ b/sugar/activity/registry.py
@@ -1,4 +1,5 @@
# Copyright (C) 2006-2007 Red Hat, Inc.
+# Copyright (C) 2007 One Laptop Per Child
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
@@ -18,29 +19,39 @@
import logging
import dbus
+import gobject
-_SHELL_SERVICE = "org.laptop.Shell"
-_SHELL_PATH = "/org/laptop/Shell"
-_REGISTRY_IFACE = "org.laptop.Shell.ActivityRegistry"
+_ACTIVITY_REGISTRY_SERVICE_NAME = 'org.laptop.ActivityRegistry'
+_ACTIVITY_REGISTRY_IFACE = 'org.laptop.ActivityRegistry'
+_ACTIVITY_REGISTRY_PATH = '/org/laptop/ActivityRegistry'
def _activity_info_from_dict(info_dict):
if not info_dict:
return None
return ActivityInfo(info_dict['name'], info_dict['icon'],
- info_dict['service_name'], info_dict['path'])
+ info_dict['service_name'], info_dict['path'],
+ info_dict['show_launcher'])
class ActivityInfo(object):
- def __init__(self, name, icon, service_name, path):
+ def __init__(self, name, icon, service_name, path, show_launcher):
self.name = name
self.icon = icon
self.service_name = service_name
self.path = path
+ self.show_launcher = show_launcher
-class ActivityRegistry(object):
+class ActivityRegistry(gobject.GObject):
+ __gsignals__ = {
+ 'activity-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([gobject.TYPE_PYOBJECT]))
+ }
def __init__(self):
+ gobject.GObject.__init__(self)
+
bus = dbus.SessionBus()
- bus_object = bus.get_object(_SHELL_SERVICE, _SHELL_PATH)
- self._registry = dbus.Interface(bus_object, _REGISTRY_IFACE)
+ bus_object = bus.get_object(_ACTIVITY_REGISTRY_SERVICE_NAME,
+ _ACTIVITY_REGISTRY_PATH)
+ self._registry = dbus.Interface(bus_object, _ACTIVITY_REGISTRY_IFACE)
self._registry.connect_to_signal('ActivityAdded', self._activity_added_cb)
# Two caches fo saving some travel across dbus.
@@ -55,6 +66,10 @@ class ActivityRegistry(object):
return result
+ def get_activities(self):
+ info_list = self._registry.GetActivities()
+ return self._convert_info_list(info_list)
+
def get_activity(self, service_name):
if self._service_name_to_activity_info.has_key(service_name):
return self._service_name_to_activity_info[service_name]
@@ -79,10 +94,14 @@ class ActivityRegistry(object):
self._mime_type_to_activities[mime_type] = activities
return activities
- def _activity_added_cb(self, bundle):
+ def add_bundle(self, bundle_path):
+ return self._registry.AddBundle(bundle_path)
+
+ def _activity_added_cb(self, info_dict):
logging.debug('ActivityRegistry._activity_added_cb: flushing caches')
self._service_name_to_activity_info.clear()
self._mime_type_to_activities.clear()
+ self.emit('activity-added', _activity_info_from_dict(info_dict))
_registry = None
diff --git a/sugar/graphics/canvasicon.py b/sugar/graphics/canvasicon.py
index e879f05..39f1358 100644
--- a/sugar/graphics/canvasicon.py
+++ b/sugar/graphics/canvasicon.py
@@ -231,13 +231,13 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
stroke_color = None
if self._active:
if self._fill_color:
- fill_color = self._fill_color.get_html()
+ fill_color = self._fill_color.get_svg()
if self._stroke_color:
- stroke_color = self._stroke_color.get_html()
+ stroke_color = self._stroke_color.get_svg()
else:
- stroke_color = color.ICON_STROKE_INACTIVE.get_html()
+ stroke_color = color.ICON_STROKE_INACTIVE.get_svg()
if self._fill_color:
- fill_color = self._fill_color.get_html()
+ fill_color = self._fill_color.get_svg()
return [fill_color, stroke_color]
def _get_handle(self, name, handle):
diff --git a/sugar/graphics/iconbutton.py b/sugar/graphics/iconbutton.py
index 85ea4e4..ba9fad8 100644
--- a/sugar/graphics/iconbutton.py
+++ b/sugar/graphics/iconbutton.py
@@ -53,7 +53,7 @@ class IconButton(CanvasIcon, hippo.CanvasItem):
if self.props.active:
self.props.background_color = 0x000000FF
else:
- self.props.background_color = 0x404040FF
+ self.props.background_color = 0x00000000
def _icon_clicked_cb(self, button):
if self._palette:
diff --git a/sugar/graphics/objectchooser.py b/sugar/graphics/objectchooser.py
index c75cec0..5b09e13 100644
--- a/sugar/graphics/objectchooser.py
+++ b/sugar/graphics/objectchooser.py
@@ -21,12 +21,12 @@ import time
import gtk
import hippo
-from sugar.graphics.frame import Frame
from sugar.activity.bundle import Bundle
from sugar.date import Date
from sugar.graphics import style
from sugar.graphics.canvasicon import CanvasIcon
from sugar.graphics.xocolor import XoColor
+from sugar.graphics.canvasroundbox import CanvasRoundBox
from sugar.datastore import datastore
from sugar import activity
from sugar.objects import objecttype
@@ -95,12 +95,12 @@ class ObjectChooser(gtk.Dialog):
else:
return None
-class CollapsedEntry(Frame):
+class CollapsedEntry(CanvasRoundBox):
_DATE_COL_WIDTH = style.zoom(100)
_BUDDIES_COL_WIDTH = style.zoom(50)
def __init__(self, jobject):
- Frame.__init__(self)
+ CanvasRoundBox.__init__(self)
self.props.box_height = style.zoom(75)
self.props.spacing = style.DEFAULT_SPACING
self.props.border_color = style.COLOR_BLACK.get_int()
diff --git a/sugar/graphics/palette.py b/sugar/graphics/palette.py
index 45ac057..368a0f6 100644
--- a/sugar/graphics/palette.py
+++ b/sugar/graphics/palette.py
@@ -36,7 +36,40 @@ _RIGHT_TOP = 5
_TOP_LEFT = 6
_TOP_RIGHT = 7
-class Palette(gobject.GObject):
+
+# Helper function to find the gap position and size of widget a
+def _calculate_gap(a, b):
+ # Test for each side if the palette and invoker are
+ # adjacent to each other.
+ gap = True
+
+ if a.y + a.height == b.y:
+ gap_side = gtk.POS_BOTTOM
+ elif a.x + a.width == b.x:
+ gap_side = gtk.POS_RIGHT
+ elif a.x == b.x + b.width:
+ gap_side = gtk.POS_LEFT
+ elif a.y == b.y + b.height:
+ gap_side = gtk.POS_TOP
+ else:
+ gap = False
+
+ if gap:
+ if gap_side == gtk.POS_BOTTOM or gap_side == gtk.POS_TOP:
+ gap_start = min(a.width, max(0, b.x - a.x))
+ gap_size = max(0, min(a.width,
+ (b.x + b.width) - a.x) - gap_start)
+ elif gap_side == gtk.POS_RIGHT or gap_side == gtk.POS_LEFT:
+ gap_start = min(a.height, max(0, b.y - a.y))
+ gap_size = max(0, min(a.height,
+ (b.y + b.height) - a.y) - gap_start)
+
+ if gap and gap_size > 0:
+ return (gap_side, gap_start, gap_size)
+ else:
+ return False
+
+class Palette(gtk.Window):
DEFAULT = 0
AT_CURSOR = 1
AROUND = 2
@@ -54,7 +87,9 @@ class Palette(gobject.GObject):
'invoker' : (object, None, None,
gobject.PARAM_READWRITE),
'position' : (gobject.TYPE_INT, None, None, 0, 6,
- 0, gobject.PARAM_READWRITE)
+ 0, gobject.PARAM_READWRITE),
+ 'draw-gap' : (bool, None, None, False,
+ gobject.PARAM_READWRITE)
}
__gsignals__ = {
@@ -65,16 +100,21 @@ class Palette(gobject.GObject):
}
def __init__(self, label, accel_path=None):
- gobject.GObject.__init__(self)
+ gtk.Window.__init__(self)
+
+ self.set_decorated(False)
+ self.set_resizable(False)
+ self.connect('realize', self._realize_cb)
self._full_request = [0, 0]
self._cursor_x = 0
self._cursor_y = 0
- self._state = self._SECONDARY
+ self._state = self._PRIMARY
self._invoker = None
self._group_id = None
self._up = False
self._position = self.DEFAULT
+ self._draw_gap = False
self._palette_popup_sid = None
self._popup_anim = animator.Animator(0.3, 10)
@@ -86,60 +126,70 @@ class Palette(gobject.GObject):
self._popdown_anim = animator.Animator(0.6, 10)
self._popdown_anim.add(_PopdownAnimation(self))
- self._menu = _sugaruiext.Menu()
+ vbox = gtk.VBox()
+ vbox.set_border_width(style.DEFAULT_PADDING)
- self._primary = _PrimaryMenuItem(label, accel_path)
- self._menu.append(self._primary)
- self._primary.show()
+ self._label = gtk.Label()
+ vbox.pack_start(self._label, False)
- self._separator = gtk.SeparatorMenuItem()
- self._menu.append(self._separator)
+ self._secondary_box = gtk.VBox()
+ vbox.pack_start(self._secondary_box)
- self._content = _ContentMenuItem()
- self._menu.append(self._content)
+ self._separator = gtk.HSeparator()
+ self._secondary_box.pack_start(self._separator)
- self._button_bar = _ButtonBarMenuItem()
- self._menu.append(self._button_bar)
+ self._menu_box = gtk.VBox()
+ self._secondary_box.pack_start(self._menu_box)
+ self._menu_box.show()
- self._menu.connect('enter-notify-event',
- self._enter_notify_event_cb)
- self._menu.connect('leave-notify-event',
- self._leave_notify_event_cb)
+ self._content = gtk.VBox()
+ self._secondary_box.pack_start(self._content)
+ self._content.show()
- def is_up(self):
- return self._up
+ self.action_bar = PaletteActionBar()
+ self._secondary_box.pack_start(self.action_bar)
+ self.action_bar.show()
- def set_primary_text(self, label, accel_path=None):
- self._primary.set_label(label, accel_path)
+ self.add(vbox)
+ vbox.show()
- def append_menu_item(self, item):
- self._separator.show()
- self._menu.insert(item, len(self._menu.get_children()) - 2)
+ self.menu = _Menu(self)
+ self.menu.show()
- def insert_menu_item(self, item, index=-1):
- self._separator.show()
- if index < 0:
- self._menu.insert(item, len(self._menu.get_children()) - 2)
- else:
- self._menu.insert(item, index + 2)
+ self.connect('enter-notify-event',
+ self._enter_notify_event_cb)
+ self.connect('leave-notify-event',
+ self._leave_notify_event_cb)
+
+ self.set_primary_text(label, accel_path)
- def remove_menu_item(self, index):
- if index > len(self._menu.get_children()) - 4:
- raise ValueError('index %i out of range' % index)
- self._menu.remove(self._menu.get_children()[index + 2])
- if len(self._menu.get_children()) == 0:
- self._separator.hide()
+ def is_up(self):
+ return self._up
+
+ def get_rect(self):
+ win_x, win_y = self.window.get_origin()
+ rectangle = self.get_allocation()
- def menu_item_count(self):
- return len(self._menu.get_children()) - 4
+ 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_primary_text(self, label, accel_path=None):
+ self._label.set_text(label)
+ self._label.show()
+
def set_content(self, widget):
- self._content.set_widget(widget)
- self._content.show()
+ if len(self._content.get_children()) > 0:
+ self.remove(self._content.get_children()[0])
+
+ if widget is not None:
+ self._content.add(widget)
- def append_button(self, button):
- self._button_bar.append_button(button)
- self._button_bar.show()
+ self._update_accept_focus()
+ self._update_separator()
def set_group_id(self, group_id):
if self._group_id:
@@ -154,9 +204,11 @@ class Palette(gobject.GObject):
self._invoker = value
self._invoker.connect('mouse-enter', self._invoker_mouse_enter_cb)
self._invoker.connect('mouse-leave', self._invoker_mouse_leave_cb)
- self._invoker.connect('focus-out', self._invoker_focus_out_cb)
elif pspec.name == 'position':
self._position = value
+ elif pspec.name == 'draw-gap':
+ self._draw_gap = value
+ self.queue_draw()
else:
raise AssertionError
@@ -165,9 +217,57 @@ class Palette(gobject.GObject):
return self._invoker
elif pspec.name == 'position':
return self._position
+ elif pspec.name == 'draw-gap':
+ return self._draw_gap
else:
raise AssertionError
+ def do_size_allocate(self, allocation):
+ gtk.Window.do_size_allocate(self, allocation)
+ self.queue_draw()
+
+ def do_expose_event(self, event):
+ # We want to draw a border with a beautiful gap
+ if self._draw_gap:
+ invoker = self._invoker.get_rect()
+ palette = self.get_rect()
+
+ gap = _calculate_gap(palette, invoker)
+ else:
+ gap = False
+
+ if gap:
+ self.style.paint_box_gap(event.window, gtk.STATE_PRELIGHT,
+ gtk.SHADOW_IN, event.area, self, "palette",
+ 0, 0,
+ self.allocation.width,
+ self.allocation.height,
+ gap[0], gap[1], gap[2])
+ else:
+ self.style.paint_box(event.window, gtk.STATE_PRELIGHT,
+ gtk.SHADOW_IN, event.area, self, "palette",
+ 0, 0,
+ self.allocation.width,
+ self.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_separator(self):
+ visible = len(self.menu.get_children()) > 0 or \
+ len(self._content.get_children()) > 0
+ self._separator.props.visible = visible
+
+ def _update_accept_focus(self):
+ accept_focus = len(self._content.get_children())
+ if self.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 _in_screen(self, x, y):
[width, height] = self._full_request
screen_area = self._invoker.get_screen_area()
@@ -182,7 +282,7 @@ class Palette(gobject.GObject):
if inv_rect == None:
inv_rect = self._invoker.get_rect()
- palette_width, palette_height = self._menu.size_request()
+ palette_width, palette_height = self.size_request()
x = inv_rect.x + inv_rect.width * invoker_halign + \
palette_width * palette_halign
@@ -241,12 +341,12 @@ class Palette(gobject.GObject):
def _update_full_request(self):
state = self._state
- self._menu.set_size_request(-1, -1)
+ self.set_size_request(-1, -1)
self._set_state(self._SECONDARY)
- self._full_request = self._menu.size_request()
+ self._full_request = self.size_request()
- self._menu.set_size_request(self._full_request[0], -1)
+ self.set_size_request(self._full_request[0], -1)
self._set_state(state)
@@ -282,7 +382,7 @@ class Palette(gobject.GObject):
elif position == self.TOP:
x, y = self._get_top_position()
- self._menu.popup(x, y)
+ self.move(x, y)
def _show(self):
if self._up:
@@ -291,11 +391,12 @@ class Palette(gobject.GObject):
self._update_cursor_position()
self._update_full_request()
- self._invoker.connect_to_parent()
- self._palette_popup_sid = _palette_observer.connect('popup',
- self._palette_observer_popup_cb)
+ self._palette_popup_sid = _palette_observer.connect(
+ 'popup', self._palette_observer_popup_cb)
self._update_position()
+ self.menu.set_active(True)
+ self.show()
self._up = True
_palette_observer.emit('popup', self)
@@ -305,7 +406,8 @@ class Palette(gobject.GObject):
if not self._palette_popup_sid is None:
_palette_observer.disconnect(self._palette_popup_sid)
self._palette_popup_sid = None
- self._menu.popdown()
+ self.menu.set_active(False)
+ self.hide()
self._up = False
self.emit('popdown')
@@ -329,25 +431,11 @@ class Palette(gobject.GObject):
return
if state == self._PRIMARY:
- self._primary.show()
- for menu_item in self._menu.get_children()[1:]:
- menu_item.hide()
+ self.menu.unembed()
+ self._secondary_box.hide()
elif state == self._SECONDARY:
- middle_menu_items = self._menu.get_children()
- middle_menu_items = middle_menu_items[2:len(middle_menu_items) - 2]
- if middle_menu_items or \
- not self._content.is_empty() or \
- not self._button_bar.is_empty():
- self._separator.show()
-
- for menu_item in middle_menu_items:
- menu_item.show()
-
- if not self._content.is_empty():
- self._content.show()
-
- if not self._button_bar.is_empty():
- self._button_bar.show()
+ self.menu.embed(self._menu_box)
+ self._secondary_box.show()
self._state = state
@@ -357,68 +445,54 @@ class Palette(gobject.GObject):
def _invoker_mouse_leave_cb(self, invoker):
self.popdown()
- def _invoker_focus_out_cb(self, invoker):
- self._hide()
-
def _enter_notify_event_cb(self, widget, event):
- if event.detail == gtk.gdk.NOTIFY_NONLINEAR:
+ if event.detail != gtk.gdk.NOTIFY_INFERIOR:
self._popdown_anim.stop()
self._secondary_anim.start()
def _leave_notify_event_cb(self, widget, event):
- if event.detail == gtk.gdk.NOTIFY_NONLINEAR:
+ if event.detail != gtk.gdk.NOTIFY_INFERIOR:
self.popdown()
def _palette_observer_popup_cb(self, observer, palette):
if self != palette:
self._hide()
-class _PrimaryMenuItem(gtk.MenuItem):
- def __init__(self, label, accel_path):
- gtk.MenuItem.__init__(self)
- self.set_border_width(style.DEFAULT_PADDING)
- self._set_label(label, accel_path)
+class PaletteActionBar(gtk.HButtonBox):
+ def add_action(label, icon_name=None):
+ button = Button(label)
- def set_label(self, label, accel_path):
- self.remove(self._label)
- self._set_label(label, accel_path)
+ if icon_name:
+ icon = Icon(icon_name)
+ button.set_image(icon)
+ icon.show()
- def _set_label(self, label, accel_path):
- self._label = gtk.AccelLabel(label)
- self._label.set_accel_widget(self)
+ self.pack_start(button)
+ button.show()
- if accel_path:
- self.set_accel_path(accel_path)
- self._label.set_alignment(0.0, 0.5)
+class _Menu(_sugaruiext.Menu):
+ __gtype_name__ = 'SugarPaletteMenu'
- self.add(self._label)
- self._label.show()
-
-class _ContentMenuItem(gtk.MenuItem):
- def __init__(self):
- gtk.MenuItem.__init__(self)
-
- def set_widget(self, widget):
- if self.child:
- self.remove(self.child)
- self.add(widget)
-
- def is_empty(self):
- return self.child is None or not self.child.props.visible
+ def __init__(self, palette):
+ _sugaruiext.Menu.__init__(self)
+ self._palette = palette
-class _ButtonBarMenuItem(gtk.MenuItem):
- def __init__(self):
- gtk.MenuItem.__init__(self)
+ def do_insert(self, item, position):
+ _sugaruiext.Menu.do_insert(self, item, position)
+ self._palette._update_separator()
- self._hbar = gtk.HButtonBox()
- self.add(self._hbar)
- self._hbar.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 append_button(self, button):
- self._hbar.pack_start(button)
+ def do_grab_notify(self, was_grabbed):
+ # Ignore grab_notify as the menu would close otherwise
+ pass
- def is_empty(self):
- return len(self._hbar.get_children()) == 0
+ def do_deactivate(self):
+ self._palette._hide()
class _PopupAnimation(animator.Animation):
def __init__(self, palette):
@@ -469,13 +543,6 @@ class Invoker(gobject.GObject):
height = gtk.gdk.screen_height()
return gtk.gdk.Rectangle(0, 0, width, height)
- def connect_to_parent(self):
- window = self.get_toplevel()
- window.connect('focus-out-event', self._window_focus_out_event_cb)
-
- def _window_focus_out_event_cb(self, widget, event):
- self.emit('focus-out')
-
class WidgetInvoker(Invoker):
def __init__(self, widget):
Invoker.__init__(self)
@@ -495,6 +562,37 @@ class WidgetInvoker(Invoker):
return gtk.gdk.Rectangle(x, y, width, height)
+ def draw_invoker_rect(self, event, palette):
+ style = self._widget.style
+ if palette.is_up():
+ gap = _calculate_gap(self.get_rect(), palette.get_rect())
+
+ if gap:
+ style.paint_box_gap(event.window, gtk.STATE_PRELIGHT,
+ gtk.SHADOW_IN, event.area, self._widget,
+ "palette-invoker",
+ self._widget.allocation.x,
+ self._widget.allocation.y,
+ self._widget.allocation.width,
+ self._widget.allocation.height,
+ gap[0], gap[1], gap[2])
+ else:
+ style.paint_box(event.window, gtk.STATE_PRELIGHT,
+ gtk.SHADOW_IN, event.area, self._widget,
+ "palette-invoker",
+ self._widget.allocation.x,
+ self._widget.allocation.y,
+ self._widget.allocation.width,
+ self._widget.allocation.height)
+ else:
+ style.paint_box(event.window, gtk.STATE_PRELIGHT,
+ gtk.SHADOW_NONE, event.area, self._widget,
+ "palette-invoker",
+ self._widget.allocation.x,
+ self._widget.allocation.y,
+ self._widget.allocation.width,
+ self._widget.allocation.height)
+
def _enter_notify_event_cb(self, widget, event):
self.emit('mouse-enter')
diff --git a/sugar/graphics/radiotoolbutton.py b/sugar/graphics/radiotoolbutton.py
index 94ff6ba..fb584ee 100644
--- a/sugar/graphics/radiotoolbutton.py
+++ b/sugar/graphics/radiotoolbutton.py
@@ -22,6 +22,8 @@ from sugar.graphics.icon import Icon
from sugar.graphics.palette import Palette, WidgetInvoker
class RadioToolButton(gtk.RadioToolButton):
+ __gtype_name__ = "SugarRadioToolButton"
+
def __init__(self, named_icon=None, group=None):
gtk.RadioToolButton.__init__(self, group=group)
self._palette = None
@@ -38,9 +40,25 @@ class RadioToolButton(gtk.RadioToolButton):
def set_palette(self, palette):
self._palette = palette
self._palette.props.invoker = WidgetInvoker(self.child)
+ self._palette.props.draw_gap = True
+
+ self._palette.connect("popup", self._palette_changed)
+ self._palette.connect("popdown", self._palette_changed)
def set_tooltip(self, text):
self._palette = Palette(text)
self._palette.props.invoker = WidgetInvoker(self.child)
+
+ def do_expose_event(self, event):
+ if self._palette and self._palette.props.draw_gap:
+ if self._palette.is_up() or self.child.state == gtk.STATE_PRELIGHT:
+ invoker = self._palette.props.invoker
+ invoker.draw_invoker_rect(event, self._palette)
+
+ gtk.RadioToolButton.do_expose_event(self, event)
+
+ def _palette_changed(self, palette):
+ # Force a redraw to update the invoker rectangle
+ self.queue_draw()
palette = property(get_palette, set_palette)
diff --git a/sugar/graphics/style.py b/sugar/graphics/style.py
index 55b4a4b..a5186da 100644
--- a/sugar/graphics/style.py
+++ b/sugar/graphics/style.py
@@ -81,6 +81,12 @@ class Color(object):
return (r, g, b)
+ def get_svg(self):
+ if self._a == 0.0:
+ return 'none'
+ else:
+ return self.get_html()
+
_XO_DPI = 200.0
_FOCUS_LINE_WIDTH = 2
@@ -113,6 +119,7 @@ TOOLBOX_TAB_LABEL_WIDTH = zoom(150 - 15 * 2)
COLOR_BLACK = Color('#000000')
COLOR_WHITE = Color('#FFFFFF')
+COLOR_TRANSPARENT = Color('#FFFFFF', alpha=0.0)
COLOR_PANEL_GREY = Color('#C0C0C0')
COLOR_SELECTION_GREY = Color('#A6A6A6')
COLOR_INACTIVE_FILL = Color('#9D9FA1')
diff --git a/sugar/graphics/toggletoolbutton.py b/sugar/graphics/toggletoolbutton.py
index 3684e9c..41050e2 100644
--- a/sugar/graphics/toggletoolbutton.py
+++ b/sugar/graphics/toggletoolbutton.py
@@ -21,6 +21,8 @@ from sugar.graphics.icon import Icon
from sugar.graphics.palette import Palette, WidgetInvoker
class ToggleToolButton(gtk.ToggleToolButton):
+ __gtype_name__ = "SugarToggleToolButton"
+
def __init__(self, named_icon=None):
gtk.ToggleToolButton.__init__(self)
self._palette = None
@@ -37,9 +39,25 @@ class ToggleToolButton(gtk.ToggleToolButton):
def set_palette(self, palette):
self._palette = palette
self._palette.props.invoker = WidgetInvoker(self.child)
+ self._palette.props.draw_gap = True
+
+ self._palette.connect("popup", self._palette_changed)
+ self._palette.connect("popdown", self._palette_changed)
def set_tooltip(self, text):
self._palette = Palette(text)
self._palette.props.invoker = WidgetInvoker(self.child)
+ def do_expose_event(self, event):
+ if self._palette and self._palette.props.draw_gap:
+ if self._palette.is_up() or self.child.state == gtk.STATE_PRELIGHT:
+ invoker = self._palette.props.invoker
+ invoker.draw_invoker_rect(event, self._palette)
+
+ gtk.ToggleToolButton.do_expose_event(self, event)
+
+ def _palette_changed(self, palette):
+ # Force a redraw to update the invoker rectangle
+ self.queue_draw()
+
palette = property(get_palette, set_palette)
diff --git a/sugar/graphics/toolbutton.py b/sugar/graphics/toolbutton.py
index e5d90ab..52a5d62 100644
--- a/sugar/graphics/toolbutton.py
+++ b/sugar/graphics/toolbutton.py
@@ -23,6 +23,7 @@ from sugar.graphics.icon import Icon
from sugar.graphics.palette import Palette, WidgetInvoker
class ToolButton(gtk.ToolButton):
+ __gtype_name__ = "SugarToolButton"
def __init__(self, icon_name=None):
gtk.ToolButton.__init__(self)
@@ -41,12 +42,28 @@ class ToolButton(gtk.ToolButton):
def set_palette(self, palette):
self._palette = palette
self._palette.props.invoker = WidgetInvoker(self.child)
+ self._palette.props.draw_gap = True
+
+ self._palette.connect("popup", self._palette_changed)
+ self._palette.connect("popdown", self._palette_changed)
def set_tooltip(self, text):
self.set_palette(Palette(text))
+ def do_expose_event(self, event):
+ if self._palette and self._palette.props.draw_gap:
+ if self._palette.is_up() or self.child.state == gtk.STATE_PRELIGHT:
+ invoker = self._palette.props.invoker
+ invoker.draw_invoker_rect(event, self._palette)
+
+ gtk.ToolButton.do_expose_event(self, event)
+
def _button_clicked_cb(self, widget):
if self._palette:
self._palette.popdown(True)
+ def _palette_changed(self, palette):
+ # Force a redraw to update the invoker rectangle
+ self.queue_draw()
+
palette = property(get_palette, set_palette)