Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sugar/graphics/icon.py238
-rw-r--r--sugar/graphics/menuitem.py2
-rw-r--r--sugar/graphics/radiotoolbutton.py2
-rw-r--r--sugar/graphics/toggletoolbutton.py2
-rw-r--r--sugar/graphics/toolbutton.py2
-rw-r--r--tests/graphics/iconwidget.py45
6 files changed, 199 insertions, 92 deletions
diff --git a/sugar/graphics/icon.py b/sugar/graphics/icon.py
index 13f6065..81c28da 100644
--- a/sugar/graphics/icon.py
+++ b/sugar/graphics/icon.py
@@ -31,6 +31,113 @@ from sugar.graphics.xocolor import XoColor
from sugar.graphics import style
from sugar.graphics.palette import Palette, CanvasInvoker
+_svg_loader = None
+
+def _get_svg_loader():
+ global _svg_loader
+ if _svg_loader == None:
+ _svg_loader = _SVGLoader()
+ return _svg_loader
+
+class _SVGLoader(object):
+ def load(self, file_name, entities):
+ icon_file = open(file_name, 'r')
+ data = icon_file.read()
+ icon_file.close()
+
+ for entity, value in entities.items():
+ xml = '<!ENTITY %s "%s">' % (entity, value)
+ data = re.sub('<!ENTITY %s .*>' % entity, xml, data)
+
+ return rsvg.Handle(data=data)
+
+class _IconBuffer(gobject.GObject):
+ def __init__(self):
+ gobject.GObject.__init__(self)
+
+ self._svg_loader = _get_svg_loader()
+ self._surface = None
+
+ self.icon_name = None
+ self.file_name = None
+ self.fill_color = None
+ self.stroke_color = None
+ self.width = None
+ self.height = None
+
+ 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._svg_loader.load(file_name, entities)
+
+ def _load_pixbuf(self, file_name):
+ if self.width is None or self.height is None:
+ pixbuf = gtk.gdk.pixbuf_new_from_file(file_name)
+ else:
+ pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(
+ file_name, self.width, self.height)
+ return hippo.cairo_surface_from_gdk_pixbuf(pixbuf)
+
+ def _get_file_name(self):
+ file_name = None
+
+ if self.file_name:
+ return self.file_name
+
+ if self.icon_name:
+ theme = gtk.icon_theme_get_default()
+
+ size = 0
+ if self.width != None:
+ size = self.width
+
+ info = theme.lookup_icon(self.icon_name, size, 0)
+ if info:
+ return info.get_filename()
+
+ return None
+
+ def get_surface(self):
+ if self._surface is not None:
+ return self._surface
+
+ file_name = self._get_file_name()
+ if file_name is None:
+ return None
+
+ if file_name.endswith('.svg'):
+ handle = self._load_svg(file_name)
+
+ dimensions = handle.get_dimension_data()
+ icon_width = int(dimensions[0])
+ icon_height = int(dimensions[1])
+
+ if self.width is not None and self.height is not None:
+ target_width = self.width
+ target_height = self.height
+ else:
+ target_width = icon_width
+ target_height = icon_height
+
+ self._surface = cairo.ImageSurface(cairo.FORMAT_ARGB32,
+ target_width, target_height)
+
+ context = cairo.Context(self._surface)
+ context.scale(float(target_width) / float(icon_width),
+ float(target_height) / float(icon_height))
+ handle.render_cairo(context)
+ else:
+ self._surface = self._load_pixbuf(file_name)
+
+ return self._surface
+
+ def invalidate(self):
+ self._surface = None
+
class Icon(gtk.Image):
__gtype_name__ = 'SugarIcon'
@@ -43,122 +150,77 @@ class Icon(gtk.Image):
gobject.PARAM_READWRITE)
}
- def __init__(self, name, **kwargs):
- self._constructed = False
- self._fill_color = None
- self._stroke_color = None
- self._icon_name = name
- self._theme = gtk.icon_theme_get_default()
- self._data = None
+ def __init__(self, **kwargs):
+ self._buffer = _IconBuffer()
gobject.GObject.__init__(self, **kwargs)
- self._constructed = True
- self._update_icon()
-
- def _get_pixbuf(self, data, width, height):
- loader = gtk.gdk.PixbufLoader('svg')
- loader.set_size(width, height)
- loader.write(data, len(data))
- loader.close()
- return loader.get_pixbuf()
-
- def _read_icon_data(self, icon_name):
- filename = self._get_real_name(icon_name)
- icon_file = open(filename, 'r')
- data = icon_file.read()
- icon_file.close()
-
- return data
-
- def _update_normal_icon(self):
- icon_theme = gtk.icon_theme_get_for_screen(self.get_screen())
- icon_set = gtk.IconSet()
-
- if icon_theme.has_icon(self._icon_name) or os.path.exists(self._icon_name):
- source = gtk.IconSource()
+ def _sync_image_properties(self):
+ if self._buffer.icon_name != self.props.icon_name:
+ self._buffer.icon_name = self.props.icon_name
+ self._buffer.invalidate()
- if os.path.exists(self._icon_name):
- source.set_filename(self._icon_name)
- else:
- source.set_icon_name(self._icon_name)
+ if self._buffer.file_name != self.props.file:
+ self._buffer.file_name = self.props.file
+ self._buffer.invalidate()
- icon_set.add_source(source)
+ width, height = gtk.icon_size_lookup(self.props.icon_size)
+ if self._buffer.width != width and self._buffer.height != height:
+ self._buffer.width = width
+ self._buffer.height = height
+ self._buffer.invalidate()
- inactive_name = self._icon_name + '-inactive'
- if icon_theme.has_icon(inactive_name) or os.path.exists(inactive_name):
- source = gtk.IconSource()
+ def _icon_size_changed_cb(self, image, pspec):
+ self._buffer.icon_size = self.props.icon_size
+ self._buffer.invalidate()
- if os.path.exists(inactive_name):
- source.set_filename(inactive_name)
- else:
- source.set_icon_name(inactive_name)
+ def _icon_name_changed_cb(self, image, pspec):
+ self._buffer.icon_name = self.props.icon_name
+ self._buffer.invalidate()
- source.set_state(gtk.STATE_INSENSITIVE)
- icon_set.add_source(source)
+ def _file_changed_cb(self, image, pspec):
+ self._buffer.file_name = self.props.file
+ self._buffer.invalidate()
- self.props.icon_set = icon_set
+ def _update_buffer_size(self):
+ width, height = gtk.icon_size_lookup(self.props.icon_size)
- def _update_icon(self):
- if not self._constructed:
- return
+ self._buffer.width = width
+ self._buffer.height = height
- if not self._fill_color and not self._stroke_color:
- self._update_normal_icon()
- return
+ self._buffer.invalidate()
- if not self._data:
- data = self._read_icon_data(self._icon_name)
- else:
- data = self._data
-
- if self._fill_color:
- entity = '<!ENTITY fill_color "%s">' % self._fill_color
- data = re.sub('<!ENTITY fill_color .*>', entity, data)
+ def do_expose_event(self, event):
+ self._sync_image_properties()
- if self._stroke_color:
- entity = '<!ENTITY stroke_color "%s">' % self._stroke_color
- data = re.sub('<!ENTITY stroke_color .*>', entity, data)
-
- self._data = data
-
- # Redraw pixbuf
- [w, h] = gtk.icon_size_lookup(self.props.icon_size)
- pixbuf = self._get_pixbuf(self._data, w, h)
- self.set_from_pixbuf(pixbuf)
+ surface = self._buffer.get_surface()
+ if surface is not None:
+ cr = self.window.cairo_create()
- def _get_real_name(self, name):
- if os.path.exists(name):
- return name
+ x = self.allocation.x
+ y = self.allocation.y
- info = self._theme.lookup_icon(name, self.props.icon_size, 0)
- if not info:
- raise ValueError("Icon '" + name + "' not found.")
- fname = info.get_filename()
- del info
- return fname
+ cr.set_source_surface(surface, x, y)
+ cr.paint()
def do_set_property(self, pspec, value):
if pspec.name == 'xo-color':
self.props.fill_color = value.get_fill_color()
self.props.stroke_color = value.get_stroke_color()
elif pspec.name == 'fill-color':
- self._fill_color = value
- self._update_icon()
+ self._buffer.fill_color = value
+ self._buffer.invalidate()
elif pspec.name == 'stroke-color':
- self._stroke_color = value
- self._update_icon()
+ self._buffer.fill_color = value
+ self._buffer.invalidate()
else:
gtk.Image.do_set_property(self, pspec, value)
- if pspec.name == 'icon-size':
- self._update_icon()
-
def do_get_property(self, pspec):
if pspec.name == 'fill-color':
- return self._fill_color
+ return self._buffer.fill_color
elif pspec.name == 'stroke-color':
- return self._stroke_color
+ return self._buffer.stroke_color
else:
return gtk.Image.do_get_property(self, pspec)
diff --git a/sugar/graphics/menuitem.py b/sugar/graphics/menuitem.py
index db4a293..5b457a5 100644
--- a/sugar/graphics/menuitem.py
+++ b/sugar/graphics/menuitem.py
@@ -22,7 +22,7 @@ class MenuItem(gtk.ImageMenuItem):
def __init__(self, text_label, icon_name=None):
gtk.ImageMenuItem.__init__(self, text_label)
if icon_name:
- icon = Icon(icon_name, icon_size=gtk.ICON_SIZE_MENU)
+ icon = Icon(icon_name=icon_name, icon_size=gtk.ICON_SIZE_MENU)
self.set_image(icon)
icon.show()
diff --git a/sugar/graphics/radiotoolbutton.py b/sugar/graphics/radiotoolbutton.py
index cf0cc64..a8269dd 100644
--- a/sugar/graphics/radiotoolbutton.py
+++ b/sugar/graphics/radiotoolbutton.py
@@ -31,7 +31,7 @@ class RadioToolButton(gtk.RadioToolButton):
self.set_named_icon(named_icon)
def set_named_icon(self, named_icon):
- icon = Icon(named_icon,
+ icon = Icon(icon_name=named_icon,
xo_color=self._xo_color,
icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
self.set_icon_widget(icon)
diff --git a/sugar/graphics/toggletoolbutton.py b/sugar/graphics/toggletoolbutton.py
index a83bebc..4fbfa66 100644
--- a/sugar/graphics/toggletoolbutton.py
+++ b/sugar/graphics/toggletoolbutton.py
@@ -29,7 +29,7 @@ class ToggleToolButton(gtk.ToggleToolButton):
self.set_named_icon(named_icon)
def set_named_icon(self, named_icon):
- icon = Icon(named_icon)
+ icon = Icon(icon_name=named_icon)
self.set_icon_widget(icon)
icon.show()
diff --git a/sugar/graphics/toolbutton.py b/sugar/graphics/toolbutton.py
index 2b90fd4..4db7d90 100644
--- a/sugar/graphics/toolbutton.py
+++ b/sugar/graphics/toolbutton.py
@@ -33,7 +33,7 @@ class ToolButton(gtk.ToolButton):
self.connect('clicked', self._button_clicked_cb)
def set_icon(self, icon_name):
- icon = Icon(icon_name)
+ icon = Icon(icon_name=icon_name)
self.set_icon_widget(icon)
icon.show()
diff --git a/tests/graphics/iconwidget.py b/tests/graphics/iconwidget.py
new file mode 100644
index 0000000..829ab85
--- /dev/null
+++ b/tests/graphics/iconwidget.py
@@ -0,0 +1,45 @@
+# Copyright (C) 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.
+
+"""
+Test the sugar.graphics.icon.Icon widget.
+"""
+
+import gtk
+
+from sugar.graphics.icon import Icon
+from sugar.graphics.xocolor import XoColor
+
+import common
+
+test = common.Test()
+
+icon = Icon(icon_name='go-previous')
+icon.props.icon_size = gtk.ICON_SIZE_LARGE_TOOLBAR
+test.pack_start(icon)
+icon.show()
+
+icon = Icon(icon_name='computer-xo',
+ icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR,
+ xo_color=XoColor())
+test.pack_start(icon)
+icon.show()
+
+test.show()
+
+if __name__ == "__main__":
+ common.main(test)