Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Francis <francis@sugarlabs.org>2012-10-23 12:57:06 (GMT)
committer Daniel Francis <francis@sugarlabs.org>2012-10-23 12:57:06 (GMT)
commit9c7c915efa9d1815ea2e47f29ff549781d50c083 (patch)
tree12af6185de444135dc87573012f33afb6c8af7c9
Initial commit
Moved from git.sl.org/sweetener/sweetener Signed-off-by: Daniel Francis <francis@sugarlabs.org>
-rw-r--r--.gitignore3
-rw-r--r--__init__.py19
-rw-r--r--alerts.py44
-rw-r--r--basic_options.py71
-rw-r--r--coloritem.py71
-rw-r--r--colors.py88
-rw-r--r--help.py67
-rw-r--r--icon.py860
-rw-r--r--item.py87
-rw-r--r--itembox.py33
-rw-r--r--itemgroup.py85
-rw-r--r--profile.py33
-rw-r--r--radioitem.py48
-rw-r--r--settingsitem.py54
-rw-r--r--settingsradioitem.py46
-rw-r--r--shortcontentitem.py69
-rw-r--r--stock.py75
-rw-r--r--toggleitem.py59
18 files changed, 1812 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..eef29c1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+*~
+*.pyc
+
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..22ff499
--- /dev/null
+++ b/__init__.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2012 S. Daniel Francis <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
diff --git a/alerts.py b/alerts.py
new file mode 100644
index 0000000..7f90efb
--- /dev/null
+++ b/alerts.py
@@ -0,0 +1,44 @@
+# Copyright (C) 2012 S. Daniel Francis <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import gtk
+
+import pynotify
+import info
+pynotify.init(info.name)
+
+
+class Alert(gtk.MessageDialog):
+ def __init__(self, parent, title, content, mtype):
+ gtk.MessageDialog.__init__(self, parent, gtk.DIALOG_MODAL,
+ type=mtype,
+ buttons=gtk.BUTTONS_OK)
+ self.set_markup('<b>%s</b>' % title)
+ self.format_secondary_markup(content)
+
+ def run(self):
+ gtk.MessageDialog.run(self)
+ gtk.MessageDialog.destroy(self)
+
+
+class NotifyAlert(pynotify.Notification):
+ def __init__(self, parent, title, content, icon, timeout):
+ pynotify.Notification.__init__(self, title, message=content, icon=icon)
+ self.set_timeout(timeout)
+
+ def run(self):
+ self.show()
diff --git a/basic_options.py b/basic_options.py
new file mode 100644
index 0000000..83636e1
--- /dev/null
+++ b/basic_options.py
@@ -0,0 +1,71 @@
+"""
+This module provides a "File" menu at desktops and an ActivityToolbar at Sugar.
+See class BasicOptions.
+"""
+# Copyright (C) 2012 S. Daniel Francis <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+from gettext import gettext as _
+import gtk
+
+import stock
+from item import Item
+from itemgroup import ItemGroup
+
+DOCUMENT = 0
+CONFIG = 1
+
+
+class BasicOptions(ItemGroup):
+ """This class has the basic options for your program."""
+ def __init__(self, activity, box, export_formats=None):
+ """Create and append the basic items to a ItemBox.
+ activity -- The activity used as argument at Canvas and Options.
+ box -- sweetener.itembox.ItemBox of the activity.
+ export_formats -- list of tuples or none. Each tuple should have:
+ ['generic_type', 'mime_type', 'mime_filter', 'filter_name']
+ """
+ ItemGroup.__init__(self, box, _('_File'), None)
+
+ if activity.save_type != CONFIG:
+ new = Item(gtk.STOCK_NEW, True)
+ new.connect('activate', lambda w: activity.new())
+ self.append_item(new)
+ _open = Item(gtk.STOCK_OPEN, True)
+ _open.connect('activate', lambda w: activity.open())
+ self.append_item(_open)
+ self.append_separator()
+ save_option = Item(gtk.STOCK_SAVE, True)
+ save_option.connect('activate', lambda w: activity.save())
+ self.append_item(save_option)
+ save_as_option = Item(gtk.STOCK_SAVE_AS)
+ save_as_option.connect('activate', lambda w: activity.save_as())
+ self.append_item(save_as_option)
+ if export_formats != None:
+ if len(export_formats) == 1:
+ stock.register('sweetener-%s' % export_formats[0][1],
+ _('Export as %s') % export_formats[0][0],
+ None, export_formats[0][1].replace('/',
+ '-'))
+ export = Item('sweetener-%s' % export_formats[0][1])
+ export.connect('activate', activity.export,
+ export_formats[0])
+ self.append_item(export)
+ self.append_separator()
+ _quit = Item(gtk.STOCK_QUIT)
+ _quit.connect('activate', lambda w: activity.stop())
+ self.append_item(_quit)
diff --git a/coloritem.py b/coloritem.py
new file mode 100644
index 0000000..1db0e69
--- /dev/null
+++ b/coloritem.py
@@ -0,0 +1,71 @@
+"""The color item gives the user the posibility to choice a colour."""
+# Copyright (C) 2012 S. Daniel Francis <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('option')
+
+import gobject
+import gtk
+
+from colors import color2string
+from item import Item
+
+
+class ColorItem(Item):
+ """Color Selecting interface.
+ In Sugar it creates a sugar.graphics.colorbutton.ColorToolButton.
+ In other desktops it's a sweetener.item.Item connected to a
+ gtk.ColorSelectionDialog.
+ """
+ __gsignals__ = {'updated': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
+ (gobject.TYPE_STRING,))}
+
+ def __init__(self, parent=None, important=False):
+ """Constructor.
+ parent -- A window where focus the dialog or None.
+ important -- bool , if it's True, in desktops a ToolItem will be
+ appended to the Toolbar.
+ """
+ Item.__init__(self, gtk.STOCK_SELECT_COLOR, important)
+ self.parent = parent
+ self.color = '#FFFFFF'
+
+ def set_color(self, color):
+ """color -- RGB color"""
+ self.color = color
+
+ def _color_changed_cb(self, widget):
+ color_gdk = widget.get_current_color()
+ self.color = color2string(color_gdk)
+ self.emit('updated', self.color)
+
+ def do_activate(self):
+ if self.tooltip:
+ title = self.tooltip
+ else:
+ title = gtk.stock_lookup(self.stock_id)[1]
+ dialog = gtk.ColorSelectionDialog(title)
+ dialog.set_transient_for(self.parent)
+ color_selection = dialog.get_color_selection()
+ color_selection.connect('color-changed', self._color_changed_cb)
+ color_selection.set_current_color(gtk.gdk.color_parse(self.color))
+ dialog.props.cancel_button.hide()
+ #dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)
+ dialog.run()
+ dialog.destroy()
+ self.emit('updated', self.color)
diff --git a/colors.py b/colors.py
new file mode 100644
index 0000000..cff7ca1
--- /dev/null
+++ b/colors.py
@@ -0,0 +1,88 @@
+"""Color handling.
+Utilities for handle color data and conversions.
+"""
+# XOColor from Sugar Toolkit:
+# Copyright (C) 2006-2007 Red Hat, Inc.
+#
+# Adaptation and more:
+# Copyright (C) 2012 Daniel Francis
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('colors')
+
+# Part from Sugar Toolkit with very few adaptations.
+
+def _parse_string(color_string):
+ if not isinstance(color_string, (str, unicode)):
+ logging.error('Invalid color string: %r', color_string)
+ return None
+
+ if color_string == 'white':
+ return ['#ffffff', '#414141']
+ elif color_string == 'insensitive':
+ return ['#ffffff', '#e2e2e2']
+
+ splitted = color_string.split(',')
+ if len(splitted) == 2:
+ return [splitted[0], splitted[1]]
+ else:
+ return None
+
+
+def is_valid(color_string):
+ return (_parse_string(color_string) != None)
+
+
+class XoColor:
+
+ def __init__(self, color_string):
+ if not is_valid(color_string):
+ logger.debug('Color string is not valid: %s, '
+ 'fallback to default', color_string)
+ raise Exception
+
+ [self.stroke, self.fill] = _parse_string(color_string)
+
+ def __cmp__(self, other):
+ if isinstance(other, XoColor):
+ if self.stroke == other.stroke and self.fill == other.fill:
+ return 0
+ return -1
+
+ def get_stroke_color(self):
+ return self.stroke
+
+ def get_fill_color(self):
+ return self.fill
+
+ def to_string(self):
+ return '%s,%s' % (self.stroke, self.fill)
+
+
+# Added in Sweetener
+def color2string(color):
+ """Converts a GdkColor to a RGB string
+ color -- gtk.gdk.Color
+ """
+ color_string = ["#"]
+ color_string.append("%02x" % (color.red / 256))
+ color_string.append("%02x" % (color.green / 256))
+ color_string.append("%02x" % (color.blue / 256))
+ string = "".join(color_string)
+ #logger.debug(str(color) + ' ' + string)
+ return string
diff --git a/help.py b/help.py
new file mode 100644
index 0000000..7d45790
--- /dev/null
+++ b/help.py
@@ -0,0 +1,67 @@
+""" 'Help' and 'About' information."""
+# Copyright (C) 2012 S. Daniel Francis <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+from gettext import gettext as _
+import gtk
+
+import stock
+from item import Item
+from itemgroup import ItemGroup
+import info
+
+
+class AboutItem(Item):
+ def __init__(self, parent):
+ Item.__init__(self, gtk.STOCK_ABOUT)
+ self.parent = parent
+
+ def do_activate(self):
+ dialog = gtk.AboutDialog()
+ dialog.set_name(info.name)
+ dialog.set_version(info.version)
+ dialog.set_transient_for(self.parent)
+ dialog.set_comments(info.description)
+ dialog.set_copyright(info.copyright)
+ dialog.set_website(info.url)
+ dialog.set_authors(info.authors)
+ dialog.set_license(info.license)
+ dialog.set_wrap_license(True)
+ dialog.set_logo_icon_name('graph-plotter')
+ dialog.run()
+ dialog.destroy()
+
+
+class Help(ItemGroup):
+ """
+ This class makes the help menu on desktops and a HelpButton on Sugar.
+ """
+ def __init__(self, box):
+ """Constructor.
+ box -- sweetener.ItemBox where append it
+ """
+ title = gtk.stock_lookup(gtk.STOCK_HELP)[1]
+ ItemGroup.__init__(self, box, title, 'toolbar-help')
+ stock.register('sweetener-help-contents', _('Contents'),
+ 'F1', 'gtk-help')
+ contents = Item('sweetener-help-contents')
+ contents.connect('activate', lambda w: gtk.show_uri(None,
+ info.documentation,
+ gtk.get_current_event_time()))
+ self.append_item(contents)
+ about = AboutItem(box._parent)
+ self.append_item(about)
diff --git a/icon.py b/icon.py
new file mode 100644
index 0000000..3eb0515
--- /dev/null
+++ b/icon.py
@@ -0,0 +1,860 @@
+# 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.
+
+"""
+A small fixed size picture, typically used to decorate components.
+
+STABLE.
+"""
+
+import re
+import math
+import logging
+
+import gobject
+import gtk
+import cairo
+
+from colors import XoColor
+
+
+class Node(object):
+
+ __slots__ = ['prev', 'next', 'me']
+
+ def __init__(self, prev, me):
+ self.prev = prev
+ self.me = me
+ self.next = None
+
+
+class LRU:
+ """
+ Implementation of a length-limited O(1) LRU queue.
+ Built for and used by PyPE:
+ http://pype.sourceforge.net
+ Copyright 2003 Josiah Carlson.
+ """
+
+ def __init__(self, count, pairs=[]):
+ # pylint: disable=W0102,W0612
+ self.count = max(count, 1)
+ self.d = {}
+ self.first = None
+ self.last = None
+ for key, value in pairs:
+ self[key] = value
+
+ def __contains__(self, obj):
+ return obj in self.d
+
+ def __getitem__(self, obj):
+ a = self.d[obj].me
+ self[a[0]] = a[1]
+ return a[1]
+
+ def __setitem__(self, obj, val):
+ if obj in self.d:
+ del self[obj]
+ nobj = Node(self.last, (obj, val))
+ if self.first is None:
+ self.first = nobj
+ if self.last:
+ self.last.next = nobj
+ self.last = nobj
+ self.d[obj] = nobj
+ if len(self.d) > self.count:
+ if self.first == self.last:
+ self.first = None
+ self.last = None
+ return
+ a = self.first
+ a.next.prev = None
+ self.first = a.next
+ a.next = None
+ del self.d[a.me[0]]
+ del a
+
+ def __delitem__(self, obj):
+ nobj = self.d[obj]
+ if nobj.prev:
+ nobj.prev.next = nobj.next
+ else:
+ self.first = nobj.next
+ if nobj.next:
+ nobj.next.prev = nobj.prev
+ else:
+ self.last = nobj.prev
+ del self.d[obj]
+
+ def __iter__(self):
+ cur = self.first
+ while cur != None:
+ cur2 = cur.next
+ yield cur.me[1]
+ cur = cur2
+
+ def iteritems(self):
+ cur = self.first
+ while cur != None:
+ cur2 = cur.next
+ yield cur.me
+ cur = cur2
+
+ def iterkeys(self):
+ return iter(self.d)
+
+ def itervalues(self):
+ for i_, j in self.iteritems():
+ yield j
+
+ def keys(self):
+ return self.d.keys()
+
+
+_BADGE_SIZE = 0.45
+
+
+class _SVGLoader(object):
+
+ def __init__(self):
+ self._cache = LRU(50)
+
+ def load(self, file_name, entities, cache):
+ if file_name in self._cache:
+ icon = self._cache[file_name]
+ else:
+ icon_file = open(file_name, 'r')
+ icon = icon_file.read()
+ icon_file.close()
+
+ if cache:
+ self._cache[file_name] = icon
+
+ for entity, value in entities.items():
+ if isinstance(value, basestring):
+ xml = '<!ENTITY %s "%s">' % (entity, value)
+ icon = re.sub('<!ENTITY %s .*>' % entity, xml, icon)
+ else:
+ logging.error(
+ 'Icon %s, entity %s is invalid.', file_name, entity)
+
+ # XXX this is very slow! why?
+ import rsvg
+ return rsvg.Handle(data=icon)
+
+
+class _IconInfo(object):
+
+ def __init__(self):
+ self.file_name = None
+ self.attach_x = 0
+ self.attach_y = 0
+
+
+class _BadgeInfo(object):
+
+ def __init__(self):
+ self.attach_x = 0
+ self.attach_y = 0
+ self.size = 0
+ self.icon_padding = 0
+
+
+class _IconBuffer(object):
+
+ _surface_cache = LRU(50)
+ _loader = _SVGLoader()
+
+ def __init__(self):
+ self.icon_name = None
+ self.icon_size = None
+ self.file_name = None
+ self.fill_color = None
+ self.background_color = None
+ self.stroke_color = None
+ self.badge_name = None
+ self.width = None
+ self.height = None
+ self.cache = False
+ self.scale = 1.0
+
+ def _get_cache_key(self, sensitive):
+ if self.background_color is None:
+ color = None
+ else:
+ color = (self.background_color.red, self.background_color.green,
+ self.background_color.blue)
+ return (self.icon_name, self.file_name, self.fill_color,
+ self.stroke_color, self.badge_name, self.width, self.height,
+ color, sensitive)
+
+ def _load_svg(self, file_name):
+ entities = {}
+ if self.fill_color:
+ entities['fill_color'] = self.fill_color
+ if self.stroke_color:
+ entities['stroke_color'] = self.stroke_color
+
+ return self._loader.load(file_name, entities, self.cache)
+
+ def _get_attach_points(self, info, size_request):
+ attach_points = info.get_attach_points()
+
+ if attach_points:
+ attach_x = float(attach_points[0][0]) / size_request
+ attach_y = float(attach_points[0][1]) / size_request
+ else:
+ attach_x = attach_y = 0
+
+ return attach_x, attach_y
+
+ def _get_icon_info(self):
+ icon_info = _IconInfo()
+
+ if self.file_name:
+ icon_info.file_name = self.file_name
+ elif self.icon_name:
+ theme = gtk.icon_theme_get_default()
+
+ size = 50
+ if self.width != None:
+ size = self.width
+
+ info = theme.lookup_icon(self.icon_name, int(size), 0)
+ if info:
+ attach_x, attach_y = self._get_attach_points(info, size)
+
+ icon_info.file_name = info.get_filename()
+ icon_info.attach_x = attach_x
+ icon_info.attach_y = attach_y
+
+ del info
+ else:
+ logging.warning('No icon with the name %s was found in the '
+ 'theme.', self.icon_name)
+
+ return icon_info
+
+ def _draw_badge(self, context, size, sensitive, widget):
+ theme = gtk.icon_theme_get_default()
+ badge_info = theme.lookup_icon(self.badge_name, int(size), 0)
+ if badge_info:
+ badge_file_name = badge_info.get_filename()
+ if badge_file_name.endswith('.svg'):
+ handle = self._loader.load(badge_file_name, {}, self.cache)
+
+ dimensions = handle.get_dimension_data()
+ icon_width = int(dimensions[0])
+ icon_height = int(dimensions[1])
+
+ pixbuf = handle.get_pixbuf()
+ else:
+ pixbuf = gtk.gdk.pixbuf_new_from_file(badge_file_name)
+
+ icon_width = pixbuf.get_width()
+ icon_height = pixbuf.get_height()
+
+ context.scale(float(size) / icon_width,
+ float(size) / icon_height)
+
+ if not sensitive:
+ pixbuf = self._get_insensitive_pixbuf(pixbuf, widget)
+ gdkcontext = gtk.gdk.CairoContext(context)
+ gdkcontext.set_source_pixbuf(pixbuf, 0, 0)
+ gdkcontext.paint()
+
+ def _get_size(self, icon_width, icon_height, padding):
+ if self.width is not None and self.height is not None:
+ width = self.width + padding
+ height = self.height + padding
+ else:
+ width = icon_width + padding
+ height = icon_height + padding
+
+ return width, height
+
+ def _get_badge_info(self, icon_info, icon_width, icon_height):
+ info = _BadgeInfo()
+ if self.badge_name is None:
+ return info
+
+ info.size = int(_BADGE_SIZE * icon_width)
+ info.attach_x = int(icon_info.attach_x * icon_width - info.size / 2)
+ info.attach_y = int(icon_info.attach_y * icon_height - info.size / 2)
+
+ if info.attach_x < 0 or info.attach_y < 0:
+ info.icon_padding = max(-info.attach_x, -info.attach_y)
+ elif info.attach_x + info.size > icon_width or \
+ info.attach_y + info.size > icon_height:
+ x_padding = info.attach_x + info.size - icon_width
+ y_padding = info.attach_y + info.size - icon_height
+ info.icon_padding = max(x_padding, y_padding)
+
+ return info
+
+ def _get_xo_color(self):
+ if self.stroke_color and self.fill_color:
+ return XoColor('%s,%s' % (self.stroke_color, self.fill_color))
+ else:
+ return None
+
+ def _set_xo_color(self, xo_color):
+ if xo_color:
+ self.stroke_color = xo_color.get_stroke_color()
+ self.fill_color = xo_color.get_fill_color()
+ else:
+ self.stroke_color = None
+ self.fill_color = None
+
+ def _get_insensitive_pixbuf(self, pixbuf, widget):
+ if not (widget and widget.style):
+ return pixbuf
+
+ icon_source = gtk.IconSource()
+ # Special size meaning "don't touch"
+ icon_source.set_size(-1)
+ icon_source.set_pixbuf(pixbuf)
+ icon_source.set_state(gtk.STATE_INSENSITIVE)
+ icon_source.set_direction_wildcarded(False)
+ icon_source.set_size_wildcarded(False)
+
+ pixbuf = widget.style.render_icon(icon_source, widget.get_direction(),
+ gtk.STATE_INSENSITIVE, -1, widget,
+ 'sugar-icon')
+
+ return pixbuf
+
+ def get_surface(self, sensitive=True, widget=None):
+ cache_key = self._get_cache_key(sensitive)
+ if cache_key in self._surface_cache:
+ return self._surface_cache[cache_key]
+
+ icon_info = self._get_icon_info()
+ if icon_info.file_name is None:
+ return None
+
+ is_svg = icon_info.file_name.endswith('.svg')
+
+ if is_svg:
+ handle = self._load_svg(icon_info.file_name)
+ dimensions = handle.get_dimension_data()
+ icon_width = int(dimensions[0])
+ icon_height = int(dimensions[1])
+ else:
+ pixbuf = gtk.gdk.pixbuf_new_from_file(icon_info.file_name)
+ icon_width = pixbuf.get_width()
+ icon_height = pixbuf.get_height()
+
+ badge_info = self._get_badge_info(icon_info, icon_width, icon_height)
+
+ padding = badge_info.icon_padding
+ width, height = self._get_size(icon_width, icon_height, padding)
+ if self.background_color is None:
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width),
+ int(height))
+ context = cairo.Context(surface)
+ else:
+ surface = cairo.ImageSurface(cairo.FORMAT_RGB24, int(width),
+ int(height))
+ context = cairo.Context(surface)
+ context.set_source_rgb(self.background_color.red,
+ self.background_color.blue,
+ self.background_color.green)
+ context.paint()
+
+ context.scale(float(width) / (icon_width + padding * 2),
+ float(height) / (icon_height + padding * 2))
+ context.save()
+
+ context.translate(padding, padding)
+ if is_svg:
+ if sensitive:
+ handle.render_cairo(context)
+ else:
+ pixbuf = handle.get_pixbuf()
+ pixbuf = self._get_insensitive_pixbuf(pixbuf, widget)
+ gdkcontext = gtk.gdk.CairoContext(context)
+ gdkcontext.set_source_pixbuf(pixbuf, 0, 0)
+ gdkcontext.paint()
+ else:
+ if not sensitive:
+ pixbuf = self._get_insensitive_pixbuf(pixbuf, widget)
+ gdkcontext = gtk.gdk.CairoContext(context)
+ gdkcontext.set_source_pixbuf(pixbuf, 0, 0)
+ gdkcontext.paint()
+
+ if self.badge_name:
+ context.restore()
+ context.translate(badge_info.attach_x, badge_info.attach_y)
+ self._draw_badge(context, badge_info.size, sensitive, widget)
+
+ self._surface_cache[cache_key] = surface
+
+ return surface
+
+ xo_color = property(_get_xo_color, _set_xo_color)
+
+
+class Icon(gtk.Image):
+
+ __gtype_name__ = 'SugarIcon'
+
+ def __init__(self, **kwargs):
+ self._buffer = _IconBuffer()
+ # HACK: need to keep a reference to the path so it doesn't get garbage
+ # collected while it's still used if it's a sugar.util.TempFilePath.
+ # See #1175
+ self._file = None
+ self._alpha = 1.0
+ self._scale = 1.0
+
+ gobject.GObject.__init__(self, **kwargs)
+
+ def get_file(self):
+ return self._file
+
+ def set_file(self, file_name):
+ self._file = file_name
+ self._buffer.file_name = file_name
+
+ file = gobject.property(type=object, setter=set_file, getter=get_file)
+
+ def _sync_image_properties(self):
+ if self._buffer.icon_name != self.props.icon_name:
+ self._buffer.icon_name = self.props.icon_name
+
+ if self._buffer.file_name != self.props.file:
+ self._buffer.file_name = self.props.file
+
+ if self.props.pixel_size == -1:
+ width, height = gtk.icon_size_lookup(self.props.icon_size)
+ else:
+ width = height = self.props.pixel_size
+ if self._buffer.width != width or self._buffer.height != height:
+ self._buffer.width = width
+ self._buffer.height = height
+
+ def _icon_size_changed_cb(self, image, pspec):
+ self._buffer.icon_size = self.props.icon_size
+
+ def _icon_name_changed_cb(self, image, pspec):
+ self._buffer.icon_name = self.props.icon_name
+
+ def _file_changed_cb(self, image, pspec):
+ self._buffer.file_name = self.props.file
+
+ def do_size_request(self, requisition):
+ """
+ Parameters
+ ----------
+ requisition :
+
+ Returns
+ -------
+ None
+
+ """
+ self._sync_image_properties()
+ surface = self._buffer.get_surface()
+ if surface:
+ requisition[0] = surface.get_width()
+ requisition[1] = surface.get_height()
+ elif self._buffer.width and self._buffer.height:
+ requisition[0] = self._buffer.width
+ requisition[1] = self._buffer.width
+ else:
+ requisition[0] = requisition[1] = 0
+
+ def do_expose_event(self, event):
+ """
+ Parameters
+ ----------
+ event :
+
+ Returns:
+ --------
+ None
+
+ """
+ self._sync_image_properties()
+ sensitive = (self.state != gtk.STATE_INSENSITIVE)
+ surface = self._buffer.get_surface(sensitive, self)
+ if surface is None:
+ return
+
+ xpad, ypad = self.get_padding()
+ xalign, yalign = self.get_alignment()
+ requisition = self.get_child_requisition()
+ if self.get_direction() != gtk.TEXT_DIR_LTR:
+ xalign = 1.0 - xalign
+
+ allocation = self.get_allocation()
+ x = math.floor(allocation.x + xpad +
+ (allocation.width - requisition[0]) * xalign)
+ y = math.floor(allocation.y + ypad +
+ (allocation.height - requisition[1]) * yalign)
+
+ cr = self.window.cairo_create()
+
+ if self._scale != 1.0:
+ cr.scale(self._scale, self._scale)
+
+ margin = self._buffer.width * (1 - self._scale) / 2
+ x, y = x + margin, y + margin
+
+ x = x / self._scale
+ y = y / self._scale
+
+ cr.set_source_surface(surface, x, y)
+
+ if self._alpha == 1.0:
+ cr.paint()
+ else:
+ cr.paint_with_alpha(self._alpha)
+
+ def set_xo_color(self, value):
+ """
+ Parameters
+ ----------
+ value :
+
+ Returns
+ -------
+ None
+
+ """
+ if self._buffer.xo_color != value:
+ self._buffer.xo_color = value
+ self.queue_draw()
+
+ xo_color = gobject.property(
+ type=object, getter=None, setter=set_xo_color)
+
+ def set_fill_color(self, value):
+ """
+ Parameters
+ ----------
+ value :
+
+ Returns
+ -------
+ None
+
+ """
+ if self._buffer.fill_color != value:
+ self._buffer.fill_color = value
+ self.queue_draw()
+
+ def get_fill_color(self):
+ """
+ Parameters
+ ----------
+ None
+
+ Returns
+ -------
+ fill_color :
+
+ """
+ return self._buffer.fill_color
+
+ fill_color = gobject.property(
+ type=object, getter=get_fill_color, setter=set_fill_color)
+
+ def set_stroke_color(self, value):
+ """
+ Parameters
+ ----------
+ value :
+
+ Returns
+ -------
+ None
+
+ """
+ if self._buffer.stroke_color != value:
+ self._buffer.stroke_color = value
+ self.queue_draw()
+
+ def get_stroke_color(self):
+ """
+ Parameters
+ ----------
+ None
+
+ Returns
+ -------
+ stroke_color :
+
+ """
+ return self._buffer.stroke_color
+
+ stroke_color = gobject.property(
+ type=object, getter=get_stroke_color, setter=set_stroke_color)
+
+ def set_badge_name(self, value):
+ """
+ Parameters
+ ----------
+ value:
+
+ Returns
+ -------
+ None
+
+ """
+ if self._buffer.badge_name != value:
+ self._buffer.badge_name = value
+ self.queue_resize()
+
+ def get_badge_name(self):
+ return self._buffer.badge_name
+
+ badge_name = gobject.property(
+ type=str, getter=get_badge_name, setter=set_badge_name)
+
+ def set_alpha(self, value):
+ if self._alpha != value:
+ self._alpha = value
+ self.queue_draw()
+
+ alpha = gobject.property(
+ type=float, setter=set_alpha)
+
+ def set_scale(self, value):
+ if self._scale != value:
+ self._scale = value
+ self.queue_draw()
+
+ scale = gobject.property(
+ type=float, setter=set_scale)
+
+
+class CellRendererIcon(gtk.GenericCellRenderer):
+
+ __gtype_name__ = 'SugarCellRendererIcon'
+
+ __gsignals__ = {
+ 'clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, [object]),
+ }
+
+ def __init__(self, tree_view):
+
+ self._buffer = _IconBuffer()
+ self._buffer.cache = True
+ self._xo_color = None
+ self._fill_color = None
+ self._stroke_color = None
+ self._prelit_fill_color = None
+ self._prelit_stroke_color = None
+
+ gobject.GObject.__init__(self)
+
+ def set_file_name(self, value):
+ if self._buffer.file_name != value:
+ self._buffer.file_name = value
+
+ file_name = gobject.property(type=str, setter=set_file_name)
+
+ def set_icon_name(self, value):
+ if self._buffer.icon_name != value:
+ self._buffer.icon_name = value
+
+ icon_name = gobject.property(type=str, setter=set_icon_name)
+
+ def get_xo_color(self):
+ return self._xo_color
+
+ def set_xo_color(self, value):
+ self._xo_color = value
+
+ xo_color = gobject.property(type=object,
+ getter=get_xo_color, setter=set_xo_color)
+
+ def set_fill_color(self, value):
+ if self._fill_color != value:
+ self._fill_color = value
+
+ fill_color = gobject.property(type=object, setter=set_fill_color)
+
+ def set_stroke_color(self, value):
+ if self._stroke_color != value:
+ self._stroke_color = value
+
+ stroke_color = gobject.property(type=object, setter=set_stroke_color)
+
+ def set_prelit_fill_color(self, value):
+ if self._prelit_fill_color != value:
+ self._prelit_fill_color = value
+
+ prelit_fill_color = gobject.property(type=object,
+ setter=set_prelit_fill_color)
+
+ def set_prelit_stroke_color(self, value):
+ if self._prelit_stroke_color != value:
+ self._prelit_stroke_color = value
+
+ prelit_stroke_color = gobject.property(type=object,
+ setter=set_prelit_stroke_color)
+
+ def set_background_color(self, value):
+ if self._buffer.background_color != value:
+ self._buffer.background_color = value
+
+ background_color = gobject.property(type=object,
+ setter=set_background_color)
+
+ def set_size(self, value):
+ if self._buffer.width != value:
+ self._buffer.width = value
+ self._buffer.height = value
+
+ size = gobject.property(type=object, setter=set_size)
+
+ def on_get_size(self, widget, cell_area):
+ width = self._buffer.width + self.props.xpad * 2
+ height = self._buffer.height + self.props.ypad * 2
+ xoffset = 0
+ yoffset = 0
+
+ if width > 0 and height > 0 and cell_area is not None:
+
+ if widget.get_direction() == gtk.TEXT_DIR_RTL:
+ xoffset = 1.0 - self.props.xalign
+ else:
+ xoffset = self.props.xalign
+
+ xoffset = max(xoffset * (cell_area.width - width), 0)
+ yoffset = max(self.props.yalign * (cell_area.height - height), 0)
+
+ return xoffset, yoffset, width, height
+
+ def on_activate(self, event, widget, path, background_area, cell_area,
+ flags):
+ pass
+
+ def on_start_editing(self, event, widget, path, background_area, cell_area,
+ flags):
+ pass
+
+ def _is_prelit(self, tree_view):
+ x, y = tree_view.get_pointer()
+ x, y = tree_view.convert_widget_to_bin_window_coords(x, y)
+ pos = tree_view.get_path_at_pos(x, y)
+ if pos is None:
+ return False
+
+ path_, column, x, y = pos
+
+ for cell_renderer in column.get_cell_renderers():
+ if cell_renderer == self:
+ cell_x, cell_width = column.cell_get_position(cell_renderer)
+ if x > cell_x and x < (cell_x + cell_width):
+ return True
+ return False
+
+ return False
+
+ def on_render(self, window, widget, background_area, cell_area,
+ expose_area, flags):
+ if self._xo_color is not None:
+ stroke_color = self._xo_color.get_stroke_color()
+ fill_color = self._xo_color.get_fill_color()
+ prelit_fill_color = None
+ prelit_stroke_color = None
+ else:
+ stroke_color = self._stroke_color
+ fill_color = self._fill_color
+ prelit_fill_color = self._prelit_fill_color
+ prelit_stroke_color = self._prelit_stroke_color
+
+ has_prelit_colors = None not in [prelit_fill_color,
+ prelit_stroke_color]
+
+ if flags & gtk.CELL_RENDERER_PRELIT and has_prelit_colors and \
+ self._is_prelit(widget):
+
+ self._buffer.fill_color = prelit_fill_color
+ self._buffer.stroke_color = prelit_stroke_color
+ else:
+ self._buffer.fill_color = fill_color
+ self._buffer.stroke_color = stroke_color
+
+ surface = self._buffer.get_surface()
+ if surface is None:
+ return
+
+ xoffset, yoffset, width_, height_ = self.on_get_size(widget, cell_area)
+
+ x = cell_area.x + xoffset
+ y = cell_area.y + yoffset
+
+ cr = window.cairo_create()
+ cr.set_source_surface(surface, math.floor(x), math.floor(y))
+ cr.rectangle(expose_area)
+ cr.paint()
+
+
+def get_icon_state(base_name, perc, step=5):
+ strength = round(perc / step) * step
+ icon_theme = gtk.icon_theme_get_default()
+
+ while strength <= 100 and strength >= 0:
+ icon_name = '%s-%03d' % (base_name, strength)
+ if icon_theme.has_icon(icon_name):
+ return icon_name
+
+ strength = strength + step
+
+
+def get_icon_file_name(icon_name):
+ icon_theme = gtk.icon_theme_get_default()
+ info = icon_theme.lookup_icon(icon_name, gtk.ICON_SIZE_LARGE_TOOLBAR, 0)
+ if not info:
+ return None
+ filename = info.get_filename()
+ del info
+ return filename
+
+
+def get_surface(**kwargs):
+ """Get cached cairo surface.
+
+ Keyword arguments:
+ icon_name -- name of icon to load, default None
+ file_name -- path to image file, default None
+ fill_color -- for svg images, change default fill color
+ default None
+ stroke_color -- for svg images, change default stroke color
+ default None
+ background_color -- draw background or surface will be transparent
+ default None
+ badge_name -- name of icon which will be drawn on top of
+ original image, default None
+ width -- change image width, default None
+ height -- change image height, default None
+ cache -- if image is svg, keep svg file content for later
+ scale -- scale image, default 1.0
+
+ Return: cairo surface or None if image was not found
+
+ """
+ icon = _IconBuffer()
+ for key, value in kwargs.items():
+ icon.__setattr__(key, value)
+ return icon.get_surface()
diff --git a/item.py b/item.py
new file mode 100644
index 0000000..1b5fc8e
--- /dev/null
+++ b/item.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2012 S. Daniel Francis <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('option')
+
+import gobject
+import gtk
+
+import stock
+
+
+class Item(gobject.GObject):
+ __gsignals__ = {'activate': (gobject.SIGNAL_RUN_LAST,
+ gobject.TYPE_NONE,
+ tuple())}
+ menuitem = None
+ toolitem = None
+
+ def __init__(self, stock_id=None, important=False):
+ gobject.GObject.__init__(self)
+ self._stock_id = stock_id
+ self.accel_group = None
+ self.important = important
+ self.connection = None
+ self.connection_data = None
+ self.tooltip = None
+
+ def set_stock_id(self, stock_id):
+ self._stock_id = stock_id
+
+ def get_stock_id(self):
+ return self._stock_id
+
+ stock_id = property(get_stock_id, set_stock_id)
+
+ def get_menu_item(self):
+ self.menuitem = gtk.ImageMenuItem(self._stock_id)
+ self.menuitem.connect('activate', self.activate_cb)
+ self.setup_accelerator()
+ return self.menuitem
+
+ def activate_cb(self, widget):
+ self.emit('activate')
+
+ def setup_accelerator(self):
+ accelerator = stock.get_accelerator(self.stock_id)
+ logger.debug(str(accelerator))
+ if accelerator[1] > 0:
+ self.menuitem.add_accelerator('activate',
+ self.accel_group, accelerator[1], accelerator[0],
+ gtk.ACCEL_VISIBLE)
+
+ def get_tool_item(self):
+ self.toolitem = gtk.ToolButton(self._stock_id)
+ self.toolitem.connect('clicked', self.activate_cb)
+ self.setup_tooltip()
+ return self.toolitem
+
+ def setup_tooltip(self):
+ if self.tooltip:
+ self.toolitem.set_tooltip_text(self.tooltip)
+ else:
+ text = gtk.stock_lookup(self.stock_id)[1]
+ self.toolitem.set_tooltip_text(text.replace('_', ''))
+
+ def emit_signal(self, widget, signal_name):
+ print self.stock_id
+ print self.get_stock_id()
+ self.emit(signal_name)
diff --git a/itembox.py b/itembox.py
new file mode 100644
index 0000000..b7ff60d
--- /dev/null
+++ b/itembox.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2012 S. Daniel Francis <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import gtk
+
+
+class ItemBox(gtk.VBox):
+ def __init__(self, activity):
+ gtk.VBox.__init__(self)
+ self._parent = activity
+ self.menubar = gtk.MenuBar()
+ self.toolbar = gtk.Toolbar()
+ self.pack_start(self.menubar, False, True, 0)
+ self.pack_start(self.toolbar, False, True, 0)
+ self.menubar.show()
+ self.toolbar.show()
diff --git a/itemgroup.py b/itemgroup.py
new file mode 100644
index 0000000..3e4780e
--- /dev/null
+++ b/itemgroup.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2012 S. Daniel Francis <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import gobject
+import gtk
+
+
+class ItemGroup(gobject.GObject):
+ def __init__(self, box, name=None, icon=None):
+ gobject.GObject.__init__(self)
+ self.items = []
+ self.first_important = True
+ self.item = gtk.MenuItem(name)
+ box.menubar.append(self.item)
+ self.menu = gtk.Menu()
+ self.item.set_submenu(self.menu)
+ self.menu.show()
+ self.item.show()
+ self.activity = box._parent
+ self.accel_group = box._parent.accel_group
+ self.toolbar = box.toolbar
+
+ def append_item(self, item):
+ item.accel_group = self.accel_group
+ menuitem = item.get_menu_item()
+ menuitem.show()
+ self.menu.append(menuitem)
+ if item.important:
+ if self.first_important and len(self.toolbar):
+ separator = gtk.SeparatorToolItem()
+ separator.show()
+ self.toolbar.insert(separator, -1)
+ self.first_important = False
+ tool_item = item.get_tool_item()
+ self.toolbar.insert(tool_item, -1)
+ tool_item.show()
+ self.items.append(item)
+
+ def append_separator(self, important=False):
+ menuitem = gtk.SeparatorMenuItem()
+ menuitem.show()
+ self.menu.append(menuitem)
+ if important:
+ toolitem = gtk.SeparatorToolItem()
+ toolitem.show()
+ self.toolbar.insert(toolitem, -1)
+ return toolitem
+
+
+class GhostGroup(ItemGroup):
+
+ def __init__(self, box, name):
+ ItemGroup.__init__(self, box, name)
+
+
+class SubGroup(ItemGroup):
+ def __init__(self, group, name=None):
+ gobject.GObject.__init__(self)
+ self.items = []
+ self.item = gtk.MenuItem(name)
+ group.menu.append(self.item)
+ self.menu = gtk.Menu()
+ self.item.set_submenu(self.menu)
+ self.menu.show()
+ self.item.show()
+ self.accel_group = group.accel_group
+ self.toolbar = group.toolbar
+ self.first_important = True
diff --git a/profile.py b/profile.py
new file mode 100644
index 0000000..2be2e4a
--- /dev/null
+++ b/profile.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2012 S. Daniel Francis <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import os
+import pwd
+name = pwd.getpwuid(os.getuid()).pw_gecos
+import gtk
+
+
+def get_fill_color(widget):
+ color = widget.get_style().mid[gtk.STATE_SELECTED]
+ return color
+
+
+def get_stroke_color(widget):
+ return widget.get_style().dark[gtk.STATE_SELECTED]
diff --git a/radioitem.py b/radioitem.py
new file mode 100644
index 0000000..20f7b66
--- /dev/null
+++ b/radioitem.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2012 S. Daniel Francis <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('toggleoption')
+import gtk
+from toggleitem import ToggleItem
+
+
+class RadioItem(ToggleItem):
+ def __init__(self, group, default_value=True,
+ stock_id=None, important=False):
+ ToggleItem.__init__(self, default_value, stock_id, important)
+ self.group = group
+
+ def get_menu_item(self):
+ stock_info = gtk.stock_lookup(self.stock_id)
+ self.menuitem = gtk.RadioMenuItem(self.group.menuitem if self.group !=\
+ None else None, stock_info[1])
+ self.menuitem.set_active(self.default_value)
+ self.menuitem.connect('toggled', self.toggled_cb)
+ self.setup_accelerator()
+ return self.menuitem
+
+ def get_tool_item(self):
+ self.toolitem = gtk.RadioToolButton(self.group.toolitem if self.group !=\
+ None else None, self._stock_id)
+ self.toolitem.set_active(self.default_value)
+ self.toolitem.connect('toggled', self.toggled_cb)
+ self.setup_tooltip()
+ return self.toolitem
diff --git a/settingsitem.py b/settingsitem.py
new file mode 100644
index 0000000..a690597
--- /dev/null
+++ b/settingsitem.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2012 S. Daniel Francis <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('option')
+
+import gobject
+import gtk
+
+from item import Item
+
+
+class SettingsItem(Item):
+ __gsignals__ = {'closed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
+ tuple())}
+
+ def __init__(self, parent=None, stock_id=None, important=False):
+ Item.__init__(self, stock_id, important)
+ self.content = gtk.EventBox()
+ self.parent = parent
+ # For toggleoptions
+ self.active = True
+
+ def do_activate(self):
+ if self.active:
+ if self.tooltip:
+ title = self.tooltip
+ else:
+ title = gtk.stock_lookup(self.stock_id)[1]
+ dialog = gtk.Dialog(title, self.parent)
+ dialog.vbox.pack_start(self.content, True, True)
+ self.content.show()
+ dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)
+ dialog.run()
+ dialog.vbox.remove(self.content)
+ dialog.destroy()
+ self.emit('closed')
diff --git a/settingsradioitem.py b/settingsradioitem.py
new file mode 100644
index 0000000..b8c8a9f
--- /dev/null
+++ b/settingsradioitem.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2012 S. Daniel Francis <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('option')
+
+import gobject
+from radioitem import RadioItem
+from settingsitem import SettingsItem
+
+
+class SettingsRadioItem(SettingsItem, RadioItem):
+ __gsignals__ = {'toggled': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
+ (gobject.TYPE_BOOLEAN,))}
+
+ def __init__(self, group, default_value=True, parent=None,
+ stock_id=None, important=False):
+ SettingsItem.__init__(self, parent, stock_id, important)
+ RadioItem.__init__(self, group, default_value, stock_id, important)
+
+ def get_menu_item(self):
+ RadioItem.get_menu_item(self)
+ self.menuitem.connect('activate', self.activate_cb)
+ return self.menuitem
+
+ def get_tool_item(self):
+ RadioItem.get_tool_item(self)
+ self.toolitem.connect('clicked', self.activate_cb)
+ return self.toolitem
diff --git a/shortcontentitem.py b/shortcontentitem.py
new file mode 100644
index 0000000..7b4a5f4
--- /dev/null
+++ b/shortcontentitem.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2012 S. Daniel Francis <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('option')
+
+import gobject
+import gtk
+
+from item import Item
+
+
+class ShortContentItem(Item):
+
+ def __init__(self, parent=None, stock_id=None, important=False):
+ Item.__init__(self, stock_id, important)
+ self.content = gtk.EventBox()
+ self.parent = parent
+ self.separator = None
+
+ def get_tool_item(self):
+ self.toolitem = gtk.ToolItem()
+ self.toolitem.add(self.content)
+ self.setup_tooltip()
+ return self.toolitem
+
+ def do_activate(self):
+ if self.tooltip:
+ title = self.tooltip
+ else:
+ title = gtk.stock_lookup(self.stock_id)[1].replace('_', '')
+ window = gtk.Dialog(title, self.parent)
+ window.set_modal(False)
+ window.set_decorated(True)
+ window.set_has_separator(False)
+ window.set_border_width(10)
+ if self.toolitem:
+ self.toolitem.remove(self.content)
+ if self.separator:
+ self.separator.hide()
+ window.vbox.pack_start(self.content)
+ self.content.show()
+ window.connect('delete-event', self.destroy_window)
+ window.show()
+
+ def destroy_window(self, window, event):
+ window.vbox.remove(self.content)
+ window.destroy()
+ if self.toolitem:
+ self.toolitem.add(self.content)
+ if self.separator:
+ self.separator.show()
diff --git a/stock.py b/stock.py
new file mode 100644
index 0000000..4f3b213
--- /dev/null
+++ b/stock.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2012 S. Daniel Francis <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('stock')
+import gtk
+
+icon_factory = gtk.IconFactory()
+
+
+def register(name, label, accelerator, icon_name):
+ if accelerator == None:
+ keyval = 0
+ mask = 0
+ else:
+ keyval, mask = gtk.accelerator_parse(accelerator)
+ gtk.stock_add([(name, label, mask, keyval, '')])
+ if icon_name:
+ icon_source = gtk.IconSource()
+ icon_source.set_icon_name(icon_name)
+ icon = gtk.IconSet()
+ icon.add_source(icon_source)
+ icon_factory.add(name, icon)
+ icon_factory.add_default()
+
+
+def overwrite_stock(stock_id, new_accelerator):
+ info = list(gtk.stock_lookup(stock_id))
+ keyval, mask = gtk.accelerator_parse(new_accelerator)
+ info[2] = mask
+ info[3] = keyval
+ logger.debug(str(info))
+ gtk.stock_add([(info[0], info[1], info[2], info[3], info[4])])
+
+# Here we overwrite the key accelerators for some stock ids.
+# Feel free to add here any other stock id if you need it at your activity,
+# and send us a patch.
+
+overwrite_stock(gtk.STOCK_SAVE_AS, '<Shift><Ctrl>S')
+overwrite_stock(gtk.STOCK_ZOOM_IN, '<Ctrl>plus')
+overwrite_stock(gtk.STOCK_ZOOM_OUT, '<Ctrl>minus')
+overwrite_stock(gtk.STOCK_ZOOM_100, '<Ctrl>0')
+# Key accelerator will be F11 on desktops and <Alt>return on Sugar.
+overwrite_stock(gtk.STOCK_FULLSCREEN, 'F11')
+overwrite_stock(gtk.STOCK_ADD, '<Ctrl>A')
+overwrite_stock(gtk.STOCK_REMOVE, '<Shift>Delete')
+overwrite_stock(gtk.STOCK_SELECT_COLOR, '<Ctrl>L')
+
+
+def get_label(stock, underline):
+ text = gtk.stock_lookup(stock)[1]
+ if underline:
+ text = text.replace('_', '')
+ return text
+
+
+def get_accelerator(stock):
+ return gtk.stock_lookup(stock)[2:-1]
diff --git a/toggleitem.py b/toggleitem.py
new file mode 100644
index 0000000..d95aec9
--- /dev/null
+++ b/toggleitem.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2012 S. Daniel Francis <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('toggleoption')
+import gobject
+import gtk
+from item import Item
+
+
+class ToggleItem(Item):
+ __gsignals__ = {'toggled': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
+ (gobject.TYPE_BOOLEAN,))}
+
+ def __init__(self, default_value=True, stock_id=None, important=False):
+ Item.__init__(self, stock_id, important)
+ self.default_value = default_value
+ self.active = default_value
+
+ def get_menu_item(self):
+ stock_info = gtk.stock_lookup(self.stock_id)
+ self.menuitem = gtk.CheckMenuItem(stock_info[1])
+ self.menuitem.set_active(self.default_value)
+ self.menuitem.connect('toggled', self.toggled_cb)
+ self.setup_accelerator()
+ return self.menuitem
+
+ def toggled_cb(self, widget):
+ active = widget.get_active()
+ if self.menuitem:
+ self.menuitem.set_active(active)
+ if self.toolitem:
+ self.toolitem.set_active(active)
+ self.active = active
+ self.emit('toggled', active)
+
+ def get_tool_item(self):
+ self.toolitem = gtk.ToggleToolButton(self._stock_id)
+ self.toolitem.set_active(self.default_value)
+ self.toolitem.connect('toggled', self.toggled_cb)
+ self.setup_tooltip()
+ return self.toolitem