From 2302c3d2cf1688a5d8b44c4f927f96a0e4d58c04 Mon Sep 17 00:00:00 2001 From: Aleksey Lim Date: Tue, 26 May 2009 11:16:01 +0000 Subject: Update sugar-port --- diff --git a/port/AUTHORS b/port/AUTHORS deleted file mode 100644 index 47ead6c..0000000 --- a/port/AUTHORS +++ /dev/null @@ -1 +0,0 @@ -Aleksey Lim diff --git a/port/NEWS b/port/NEWS deleted file mode 100644 index 0dcfeb5..0000000 --- a/port/NEWS +++ /dev/null @@ -1,8 +0,0 @@ -1 - -* Add tarball.py -* Add json import wrapper -* Add object chooser -* Add activity classes -* Add pixbuf methods -* Add TempoSlider and ScrolledBox widgets diff --git a/port/README b/port/README index bd0dade..256200d 100644 --- a/port/README +++ b/port/README @@ -7,6 +7,11 @@ Cornerstone purposes for this project: * Total backwards compatibility for sugar-port API * Run on all sugar platforms beginning from 0.82 +In most cases sugar-port could be embedded to activity's directory tree. +There is no need to include the whole sugar-port project only top level +files/directories you are using directly - sugar-port's top level entities +don't import each other. + Get it ------ diff --git a/port/TODO b/port/TODO deleted file mode 100644 index e69de29..0000000 --- a/port/TODO +++ /dev/null diff --git a/port/activity.py b/port/activity.py index dfe6a4a..e3b6fdd 100644 --- a/port/activity.py +++ b/port/activity.py @@ -17,6 +17,7 @@ import gtk import logging import telepathy +import gobject import sugar.activity.activity as toolkit from sugar.presence.sugartubeconn import SugarTubeConnection @@ -27,6 +28,18 @@ _NEW_INSTANCE = 1 _PRE_INSTANCE = 2 _POST_INSTANCE = 3 +class CursorFactory: + __shared_state = {"cursors": {}} + + def __init__(self): + self.__dict__ = self.__shared_state + + def get_cursor(self, cur_type): + if not self.cursors.has_key(cur_type): + cur = gtk.gdk.Cursor(cur_type) + self.cursors[cur_type] = cur + return self.cursors[cur_type] + class Activity(toolkit.Activity): """Basic activity class""" @@ -57,6 +70,10 @@ class Activity(toolkit.Activity): """ raise NotImplementedError + def on_save_instance(self, cb, *args): + """ Register callback which will be invoked before save_instance """ + self.__on_save_instance.append((cb, args)) + def share_instance(self, connection, is_initiator): """ Activity was shared/joined. @@ -98,6 +115,17 @@ class Activity(toolkit.Activity): alert.show_all() self.add_alert(alert) + def get_cursor(self): + return self._cursor + + def set_cursor(self, cursor): + if not isinstance(cursor, gtk.gdk.Cursor): + cursor = CursorFactory().get_cursor(cursor) + + if self._cursor != cursor: + self._cursor = cursor + self.window.set_cursor(self._cursor) + def __init__(self, canvas, handle): """ Initialise the Activity. @@ -120,6 +148,10 @@ class Activity(toolkit.Activity): self.__resume_filename = None self.__postponed_share = [] + self.__on_save_instance = [] + + self._cursor = None + self.set_cursor(gtk.gdk.LEFT_PTR) # XXX do it after(possible) read_file() invoking # have to rely on calling read_file() from map_cb in sugar-toolkit @@ -153,6 +185,8 @@ class Activity(toolkit.Activity): def write_file(self, filepath): """Subclass should not override this method""" + for cb, args in self.__on_save_instance: + cb(*args) self.save_instance(filepath) def __map_canvasactivity_cb(self, widget): diff --git a/port/chooser.py b/port/chooser.py index b7046be..e2df259 100644 --- a/port/chooser.py +++ b/port/chooser.py @@ -34,9 +34,9 @@ def pick(cb=None, default=None, parent=None, what=None): * cb(jobject), if object was choosen and cb is not None * jobject, if object was choosen and cb is None - * None, otherwise + * default, otherwise - NOTE: what make sense only for sugar >= 0.84 + NOTE: 'what' makes sense only for sugar >= 0.84 """ what = what and {'what_filter': what} or {} chooser = ObjectChooser(parent=parent, **what) diff --git a/port/epydoc b/port/epydoc deleted file mode 100644 index 3e4852c..0000000 --- a/port/epydoc +++ /dev/null @@ -1,21 +0,0 @@ -[epydoc] - -modules: *.py -output: html -target: html/ -verbosity: 0 -debug: 0 - -# Generation options -docformat: epytext -parse: yes -introspect: no -inheritance: listed -private: no -imports: no -sourcecode: yes - -# Output options -name: sugar-port -url: http://wiki.sugarlabs.org/go/Development_Team/sugar-port -frames: yes diff --git a/port/images/tempo1.svg b/port/images/tempo1.svg deleted file mode 100644 index bb9aeec..0000000 --- a/port/images/tempo1.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/port/images/tempo2.svg b/port/images/tempo2.svg deleted file mode 100644 index 4a98310..0000000 --- a/port/images/tempo2.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/port/images/tempo3.svg b/port/images/tempo3.svg deleted file mode 100644 index bd893bd..0000000 --- a/port/images/tempo3.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/port/images/tempo4.svg b/port/images/tempo4.svg deleted file mode 100644 index 6fa5afa..0000000 --- a/port/images/tempo4.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/port/images/tempo5.svg b/port/images/tempo5.svg deleted file mode 100644 index 9500e7e..0000000 --- a/port/images/tempo5.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/port/images/tempo6.svg b/port/images/tempo6.svg deleted file mode 100644 index 9844fd6..0000000 --- a/port/images/tempo6.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - diff --git a/port/images/tempo7.svg b/port/images/tempo7.svg deleted file mode 100644 index 54bed80..0000000 --- a/port/images/tempo7.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/port/images/tempo8.svg b/port/images/tempo8.svg deleted file mode 100644 index 2c0154f..0000000 --- a/port/images/tempo8.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - diff --git a/port/pixbuf.py b/port/pixbuf.py index 4390fef..ba6d0f5 100644 --- a/port/pixbuf.py +++ b/port/pixbuf.py @@ -14,10 +14,16 @@ """gtk.gdk.Pixbuf extensions""" +import re import os import cStringIO -import tempfile import gtk +import rsvg +import cairo +import logging + +from sugar.graphics.xocolor import XoColor +from sugar.util import LRU def to_file(pixbuf): """Convert pixbuf object to file object""" @@ -38,13 +44,170 @@ def to_str(pixbuf): def from_str(str): """Convert string to pixbuf object""" - file_d, path = tempfile.mkstemp() + loader = gtk.gdk.pixbuf_loader_new_with_mime_type('image/png') + loader.write(str) + loader.close() + + return loader.get_pixbuf() + +def from_svg_at_size(filename=None, width=None, height=None, handle=None, + keep_ratio=True): + """Scale and load SVG into pixbuf""" + + if not handle: + handle = rsvg.Handle(filename) + + dimensions = handle.get_dimension_data() + icon_width = dimensions[0] + icon_height = dimensions[1] + + if icon_width != width or icon_height != height: + ratio_width = float(width) / icon_width + ratio_height = float(height) / icon_height + + if keep_ratio: + ratio = min(ratio_width, ratio_height) + if ratio_width != ratio: + ratio_width = ratio + width = int(icon_width * ratio) + elif ratio_height != ratio: + ratio_height = ratio + height = int(icon_height * ratio) + else: + ratio_width = 1 + ratio_height = 1 + + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) + context = cairo.Context(surface) + context.scale(ratio_width, ratio_height) + handle.render_cairo(context) + + loader = gtk.gdk.pixbuf_loader_new_with_mime_type('image/png') + surface.write_to_png(loader) + loader.close() + + return loader.get_pixbuf() + +def sugar_icon(file_name=None, icon_name=None, + width=None, height=None, + color=None, + insensitive_widget=None): + """Load sugar icon into pixbuf + + NOTE: Function can load all image formats but makes sense only for SVG + (due to color argument, see load_svg()) + + NOTE: Function caches results + + Arguments: + file_name path to filename with image + (mutually exclusive for icon_name) + icon_name name of icon + (mutually exclusive for icon_name) + width width of final image + height height of final image + color defines stroke and fill colors for final SVG image + in string notion, could be: + * tuple of (stroke_color, fill_color) + * XoColor + * scalar value for stroke and fill colors + insensitive_widget render icon in insensitive mode + """ + def load_svg(): + entities = {} + if fill_color: + entities['fill_color'] = fill_color + if stroke_color: + entities['stroke_color'] = stroke_color + + f = open(icon_filename, 'r') + icon = f.read() + f.close() + + for entity, value in entities.items(): + xml = '' % (entity, value) + icon = re.sub('' % entity, xml, icon) + + return rsvg.Handle(data=icon) + + def get_insensitive_pixbuf(): + if not (insensitive_widget and insensitive_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) + + # Please note that the pixbuf returned by this function is leaked + # with current stable versions of pygtk. The relevant bug is + # http://bugzilla.gnome.org/show_bug.cgi?id=502871 + # -- 2007-12-14 Benjamin Berg + pixbuf = insensitive_widget.style.render_icon(icon_source, + insensitive_widget.get_direction(), gtk.STATE_INSENSITIVE, -1, + insensitive_widget, "sugar-icon") + + return pixbuf + + def get_cache_key(): + return (icon_filename, fill_color, stroke_color, width, height, + insensitive_widget is None) + + if isinstance(color, XoColor): + stroke_color = color.get_stroke_color() + fill_color = color.get_fill_color() + elif isinstance(color, tuple): + stroke_color = color[0] + fill_color = color[1] + else: + stroke_color = color + fill_color = color + + if file_name: + icon_filename = file_name + elif icon_name: + theme = gtk.icon_theme_get_default() + info = theme.lookup_icon(icon_name, width or 50, 0) + if info: + icon_filename = info.get_filename() + del info + else: + logging.warning('No icon with the name %s ' + 'was found in the theme.' % icon_name) + else: + return None + + cache_key = get_cache_key() + if cache_key in _sugar_icon_cache: + return _sugar_icon_cache[cache_key] + + logging.debug('sugar_icon: file_name=%s icon_name=%s width=%s height=%s ' \ + 'color=%s' % (file_name, icon_name, width, height, color)) + + is_svg = icon_filename.endswith('.svg') + + if is_svg: + handle = load_svg() + if width and height: + pixbuf = from_svg_at_size(handle=handle, width=width, height=height, + keep_ratio=True) + else: + pixbuf = handle.get_pixbuf() + else: + if width and height: + pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(icon_filename, + width, height) + else: + pixbuf = gtk.gdk.pixbuf_new_from_file(icon_filename) + + if insensitive_widget: + pixbuf = get_insensitive_pixbuf() - file_o = os.fdopen(file_d, 'w') - file_o.write(str) - file_o.close() + _sugar_icon_cache[cache_key] = pixbuf - out = gtk.gdk.pixbuf_new_from_file(path) - os.unlink(path) + return pixbuf - return out +_sugar_icon_cache = LRU(50) diff --git a/port/port b/port/port deleted file mode 120000 index 945c9b4..0000000 --- a/port/port +++ /dev/null @@ -1 +0,0 @@ -. \ No newline at end of file diff --git a/port/tarball.py b/port/tarball.py index d07a3e1..9e842b9 100644 --- a/port/tarball.py +++ b/port/tarball.py @@ -23,8 +23,6 @@ import zipfile import tempfile import shutil -import port.pixbuf as pixbuf - class TarballError(Exception): """Base Tarball exception.""" pass @@ -124,7 +122,10 @@ class Tarball: def read_pixbuf(self, arcname): """Returns pixbuf object of given file from tarball.""" - return pixbuf.from_str(self.read(arcname)) + loader = gtk.gdk.pixbuf_loader_new_with_mime_type('image/png') + loader.write(self.read(arcname)) + loader.close() + return loader.get_pixbuf() def write(self, arcname, data, mode=0644): """ @@ -147,8 +148,12 @@ class Tarball: self.__tar.addfile(info, cStringIO.StringIO(data)) def __write_pixbuf(self, info, data): - buffer = pixbuf.to_file(data) - buffer.seek(0, os.SEEK_END) + def push(pixbuf, buffer): + buffer.write(pixbuf) + + buffer = cStringIO.StringIO() + data.save_to_callback(push, 'png', user_data=buffer) + info.size = buffer.tell() buffer.seek(0) self.__tar.addfile(info, buffer) diff --git a/port/temposlider.py b/port/temposlider.py index 9d14be8..6950453 100644 --- a/port/temposlider.py +++ b/port/temposlider.py @@ -12,8 +12,9 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -import os import gtk +import rsvg +import cairo from sugar.graphics import style @@ -26,7 +27,7 @@ class TempoSlider(gtk.HBox): self._image.show() # used to store tempo updates while the slider is active - self._delayed = 0 + self._delayed = 0 self._active = False self.adjustment = gtk.Adjustment(min_value, min_value, max_value, @@ -68,10 +69,10 @@ class TempoSlider(gtk.HBox): self.adjustment.upper, 0, 7) if not self._pixbuf[img]: - self._pixbuf[img] = gtk.gdk.pixbuf_new_from_file_at_size( - os.path.join(os.path.dirname(__file__), 'images', - 'tempo' + str(img+1) + '.svg'), - style.STANDARD_ICON_SIZE, style.STANDARD_ICON_SIZE) + svg = rsvg.Handle(data=IMAGE[img]) + self._pixbuf[img] = _from_svg_at_size(handle=svg, + width=style.STANDARD_ICON_SIZE, + height=style.STANDARD_ICON_SIZE) self._image.set_from_pixbuf(self._pixbuf[img]) @@ -83,3 +84,132 @@ class TempoSlider(gtk.HBox): if self._delayed != 0: self.set_value(self._delayed, True) self._delayed = 0 + +def _from_svg_at_size(filename=None, width=None, height=None, handle=None, + keep_ratio=True): + """ import from pixbuf.py """ + + if not handle: + handle = rsvg.Handle(filename) + + dimensions = handle.get_dimension_data() + icon_width = dimensions[0] + icon_height = dimensions[1] + + if icon_width != width or icon_height != height: + ratio_width = float(width) / icon_width + ratio_height = float(height) / icon_height + + if keep_ratio: + ratio = min(ratio_width, ratio_height) + if ratio_width != ratio: + ratio_width = ratio + width = int(icon_width * ratio) + elif ratio_height != ratio: + ratio_height = ratio + height = int(icon_height * ratio) + else: + ratio_width = 1 + ratio_height = 1 + + surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) + context = cairo.Context(surface) + context.scale(ratio_width, ratio_height) + handle.render_cairo(context) + + loader = gtk.gdk.pixbuf_loader_new_with_mime_type('image/png') + surface.write_to_png(loader) + loader.close() + + return loader.get_pixbuf() + +IMAGE = [None] * 8 + +IMAGE[0] = """ + + + + + +""" + +IMAGE[1] = """ + + + + + +""" + +IMAGE[2] = """ + + + + + +""" + +IMAGE[3] = """ + + + + + +""" + +IMAGE[4] = """ + + + + + +""" + +IMAGE[5] = """ + + + + + + +""" + +IMAGE[6] = """ + + + + + +""" + +IMAGE[7] = """ + + + + + +""" -- cgit v0.9.1