From 02c3fed9dc41f2304270a34c0748c278f9fe6931 Mon Sep 17 00:00:00 2001 From: Daniel Francis Date: Thu, 30 Aug 2012 23:55:34 +0000 Subject: Update Sweetener --- diff --git a/activity.py b/activity.py index 6f1b6c9..7fdf2e1 100644 --- a/activity.py +++ b/activity.py @@ -20,25 +20,23 @@ import tempfile import os - -try: - import sweetener -except ImportError: - import sys - sys.path.append(os.path.abspath('./sugar')) - import sweetener -import info - import logging logging.basicConfig(level=logging.DEBUG) -logger = logging.getLogger(info.lower_name) - +logger = logging.getLogger('graph-plotter') from gettext import gettext as _ + import gtk from sugar.datastore import datastore from sugar.activity import activity +try: + import sweetener +except ImportError: + import sys + sys.path.append(os.path.abspath('./sugar')) + import sweetener + os.environ['DATA_DIR'] = os.path.abspath('./data') from toolbars import Options diff --git a/activity/.directory b/activity/.directory new file mode 100644 index 0000000..4beb1f1 --- /dev/null +++ b/activity/.directory @@ -0,0 +1,3 @@ +[Dolphin] +PreviewsShown=true +Timestamp=2012,8,30,20,31,41 diff --git a/activity/Sugar-iconify.py b/activity/Sugar-iconify.py new file mode 100644 index 0000000..7d7b017 --- /dev/null +++ b/activity/Sugar-iconify.py @@ -0,0 +1,678 @@ +#!/usr/bin/python + +import sys +import xml.dom.minidom +import getopt +import re +import os +import string + +#define help output +def usage(): + print '\nUsage: sugar-iconify.py [options] input.svg\n' + print 'Options:\n' + print '\t -c\t\tApply default color entities (#666666, #ffffff) to output' + print '\t -d directory\tThe preferred output directory' + print '\t -e\t\tDo not insert entities for strokes and fills' + print '\t -f hex\t\tHex value to replace with fill entity' + print '\t -g\t\tAutomatically accept guesses for stroke and fill entities' + print '\t -h\t\tDisplay this help message' + print '\t -i\t\tInsert "isolated stroke" entities' + print '\t -m\t\tMultiple export; export top level groups as separate icons' + print '\t -o\t\tOverwrite the input file; overridden by -m' + print '\t -p pattern\tOnly export icons whose name matches pattern; for use with -m' + print '\t -s hex\t\tHex value to replace with stroke entity' + print '\t -x\t\tOutput example SVGs, for previewing their appearance in Sugar; ignored with -m' + print '\t -v\t\tverbose' + +#check for valid arguments +try: + opts,arg = getopt.getopt(sys.argv[1:], "s:f:gcd:imp:oehvx") +except: + usage() + sys.exit(2) + +if len(arg) < 1: + usage() + sys.exit(2) + +#declare variables and constants +default_stroke_color = '#666666' +default_fill_color = '#ffffff' +transparent_color = '#00000000' +stroke_color = default_stroke_color +fill_color = default_fill_color +stroke_entity = "stroke_color" +fill_entity = "fill_color" +iso_stroke_entity = "iso_stroke_color" + +output_path = '' +pattern = '' +entities_passed = 0 +use_default_colors = False +confirm_guess = True +use_entities = True +multiple = False +verbose = False +overwrite_input = False +output_examples = False +use_iso_strokes = False + +#interpret arguments +for o, a in opts: + + if o == '-s': + stroke_color = '#' + a.lstrip('#').lower() + entities_passed += 1 + elif o == '-f': + fill_color = '#' + a.lstrip('#').lower() + entities_passed += 1 + elif o == '-g': + confirm_guess = False + elif o == '-c': + use_default_colors = True + elif o == '-o': + overwrite_input = True + elif o == '-d': + output_path = a.rstrip('/') + '/' + elif o == '-e': + use_entities = False + elif o == '-v': + verbose = True + elif o == '-p': + pattern = a + elif o == '-h': + usage() + sys.exit(2) + elif o == '-m': + multiple = True + elif o == '-x': + output_examples = True + elif o == '-i': + use_iso_strokes = True + +#isolate important parts of the input path +svgfilepath = arg[0].rstrip('/') +svgdirpath, sep, svgfilename = svgfilepath.rpartition('/') +svgbasename = re.sub(r'(.*)\.([^.]+)', r'\1', svgfilename) + +#load the SVG as text +try: + svgfile = open(svgfilepath, 'r') +except: + sys.exit('Error: Could not locate ' + svgfilepath) + +try: + svgtext = svgfile.read() + svgfile.close() +except: + svgfile.close() + sys.exit('Error: Could not read ' + svgfilepath) + +#determine the creator of the SVG (we only care about Inkscape and Illustrator) +creator = 'unknown' + +if re.search('illustrator', svgtext, re.I): + creator = 'illustrator' +elif re.search('inkscape', svgtext, re.I): + creator = 'inkscape' + +if verbose: + print 'The creator of this svg is ' + creator + '.' + +#hack the entities into the readonly DTD +if use_entities: + + #before replacing them, we read the stroke/fill values out, should they have previously been defined, + #to prevent needing to make guesses for them later + stroke_match = re.search(r'stroke_color\s*\"([^"]*)\"', svgtext) + fill_match = re.search(r'fill_color\s*\"([^"]*)\"', svgtext) + if stroke_match is not None: + stroke_color = stroke_match.group(1).lower() + entities_passed += 1 + if fill_match is not None: + fill_color = fill_match.group(1).lower() + entities_passed += 1 + + #define the entities + if fill_match and stroke_match: + entities = '\t\n' + entities += '\t\n' + if use_iso_strokes: + entities += '\t\n' + else: + entities = '\t\n' + entities += '\t\n' + if use_iso_strokes: + entities += '\t\n' + + #for simplicity, we simply replace the entire entity declaration block; this obviously would remove + #any other custom entities declared within the SVG, but we assume that's an extreme edge case + + svgtext, n = re.subn(r'(\[]*)(\[[^\]]*\])*\>', r'\1 \n[\n' + entities + ']>\n', svgtext) + + #add a doctype if none already exists, adding the appropriate entities as well + if n == 0: + svgtext,n = re.subn("\n\n\ +\n\ +\n\ +\t\n\ +\tSugar Icon Preview: ~~~\n\ +\t\n\ +\t\n\ +\n\ +\n\ +\t\t
    \n\ +\t\t

  • stroke\n\ +\t\t

  • fill\n\ +\t\t

  • both\n\ +\t\t
\n\ +\t\t
\n\ +\t\t\t

Icon Validation

\n\ +\t\t\t
    \n\ +\t\t\t\t
  • Ensure that your icons appear to be centered within their boxes.\n\ +\t\t\t\t\tIf they appear off-center or cropped, you may have created your icon on canvas other than the required 55px square.\n\ +\t\t\t\t\tClick to toggle the 55px canvas border.\n\ +\t\t\t\t
  • If your icon appears off-center but has the correct 55px canvas, it may simply have uneven visual balance.\n\ +\t\t\t\t\tThis means, though it may be technically centered, differences in the distribution of \"mass\" cause it to appear otherwise. \n\ +\t\t\t\t\tTry shifting the icon slightly on your canvas, while ensuring that you don't accidentally exceed the 55px boundary.\n\ +\t\t\t\t\n\ +\t\t\t\t
  • Ensure that the first two icons appear entirely in gray, and that all of the third icon is colored blue and green, with the latter being the fill color.\n\ +\t\t\t\t\tIf any fail to meet these requirements, your icon does not have proper stroke and/or fill entities defined.\n\ +\t\t\t\t\tInvestigate the -s and -f options of sugar-iconify, and be sure that your input SVG doesn't have extra colors in its palette.\n\ +\t\t\t\t
  • Ensure that your icon reads clearly when viewed only as strokes.\n\ +\t\t\t\t\tThis visual style will be used to represent activities/objects which are inactive, or uninstantiated.\n\ +\t\t\t\t\tConsider applying outlining strokes to any filled shapes that do not already have them.\n\ +\t\t\t\t
  • Ensure that your icon reads clearly when viewed only as fills.\n\ +\t\t\t\t\tThis visual style will be used for representing activity types within other icons, such as invitations, transfers, and objects. \n\ +\t\t\t\t\tIf you have strokes which are isolated from fills, neither outlining them nor sitting against a filled background, please \n\ +\t\t\t\t\tinvestigate the -i option of the sugar-iconify script.\n\ +\t\t\t
\n\ +\t\t\tFor more information, please see the OLPC wiki page on making sugar icons.\n\ +\t\t
\n\ +\n\ +\n\ +" + +#finally, do the icon conversion and export + +if multiple: + #export each icon as a separate file by top level group + n_icons_exported = 0 + n_warnings = 0 + for icon in icons: + + try: + #skip whitespace and unnamed icons + if icon.localName == 'g' and icon.attributes: + + icon_name = '' + try: + if creator == 'inkscape' and icon.attributes.getNamedItem('inkscape:label'): + icon_name = icon.attributes.getNamedItem('inkscape:label').nodeValue + else: + icon_name = icon.attributes.getNamedItem('id').nodeValue + except: + pass + + #skip the template layers + if not icon_name.startswith('_'): + + #skip non-matches + if pattern == '' or re.search(pattern, icon_name): + + if verbose: + print '\nExporting ' + icon_name + '.svg...' + icon_xml = xml.dom.minidom.Document(); + + #construct the SVG + icon_xml.appendChild(doctype) + icon_xml.appendChild(svg.cloneNode(0)) + + icon_xml.childNodes[1].appendChild(icon) + icon_xml.childNodes[1].childNodes[0].setAttribute('display', 'block') + + if use_entities: + strokes_replaced, fills_replaced = replaceEntities(icon_xml.childNodes[1]) + + if not strokes_replaced and not fills_replaced: + print 'Warning: no entity replacements were made in %s' % icon_name + elif not strokes_replaced: + print 'Warning: no stroke entity replacements were made in %s' % icon_name + elif not fills_replaced: + print 'Warning: no fill entity replacements were made in %s' % icon_name + + if not strokes_replaced or not fills_replaced: + n_warnings += 1 + + #write the file + try: + f = open(output_path + icon_name + '.svg', 'w') + except: + sys.exit('Error: Could not locate directory ' + output_path) + + try: + #had to hack here to remove the automatic encoding of '&' by toxml() in entity refs + #I'm sure there is a way to prevent need for this if I knew the XML DOM better + icon_svgtext = icon_xml.toxml() + icon_svgtext = re.sub('&', '&', icon_svgtext) + if not use_default_colors: + icon_svgtext = re.sub(r'ENTITY stroke_color "[^"]*"', r'ENTITY stroke_color "' + stroke_color + '"', icon_svgtext) + icon_svgtext = re.sub(r'ENTITY fill_color "[^"]*"', r'ENTITY fill_color "' + fill_color + '"', icon_svgtext) + f.write(icon_svgtext) + f.close() + except: + sys.exit('Error: Could not write file ' + icon_name + '.svg') + + n_icons_exported += 1 + except: + #catch any errors we may have missed, so the rest of the icons can export normally + if(icon_name): + print 'Error: Could not export' + icon_name + '.svg' + + if verbose: + if n_icons_exported == 1: + print 'Successfully exported 1 icon' + else: + print 'Successfully exported %d icons' % n_icons_exported + + if n_warnings == 1: + print 'Warnings were reported for 1 icon' + elif n_warnings > 1: + print 'Warnings were reported for %d icons' % n_warnings + +else: + #output a single converted icon + + if not overwrite_input: + outfilename = re.sub(r'(.*\.)([^.]+)', r'\1sugar.\2', svgfilename) + if verbose: + print 'Exporting ' + outfilename + ' ...' + else: + outfilename = svgfilename + if verbose: + print 'Overwriting ' + outfilename + ' ...' + + #remove the template layers + for node in svg.childNodes: + + #only check named nodes + if node.localName == 'g' and node.attributes: + try: + if creator == 'inkscape' and node.attributes.getNamedItem('inkscape:label'): + node_name = node.attributes.getNamedItem('inkscape:label').nodeValue + else: + node_name = node.attributes.getNamedItem('id').nodeValue + + if node_name.startswith('_'): + node.parentNode.removeChild(node) + except: + pass + + if use_entities: + strokes_replaced, fills_replaced = replaceEntities(svgxml) + if not strokes_replaced and not fills_replaced: + print 'Warning: no entity replacements were made' + elif not strokes_replaced: + print 'Warning: no stroke entity replacements were made' + elif not fills_replaced: + print 'Warning: no fill entity replacements were made' + + if use_iso_strokes: + strokes_fixed = fix_isolated_strokes(svgxml) + if strokes_fixed > 0 and verbose: + print "%d isolated strokes fixed" % strokes_fixed + + #create the output file(s) + if output_examples: + + example_path = output_path + re.sub(r'(.*\.)([^.]+)', r'\1preview', svgfilename) + '/' + try: + os.mkdir(example_path) + except: + pass + + try: + f = open(example_path + 'preview.html', 'w') + except: + print "Error: could not create HTML preview file" + + try: + f.write(re.sub(r'~~~', svgbasename, previewHTML)) + f.close() + except: + sys.exit('Error: could not write to HTML preview file') + + example_colors = [(default_stroke_color, "#FFFFFF", default_stroke_color), + ("#FFFFFF", default_stroke_color, default_stroke_color), + ("#0000AA", "#00DD00", "#0000AA")] + example_filenames = [re.sub(r'(.*\.)([^.]+)', r'\1stroke.\2', svgfilename), + re.sub(r'(.*\.)([^.]+)', r'\1fill.\2', svgfilename), + re.sub(r'(.*\.)([^.]+)', r'\1both.\2', svgfilename) ] + + icon_svgtext = svgxml.toxml() + icon_svgtext = re.sub('&', '&', icon_svgtext) + + for i in range(0, len(example_filenames)): + try: + f = open(example_path + example_filenames[i], 'w') + except: + sys.exit('Error: Could not save to ' + example_path + example_filenames[i]) + try: + icon_svgtext = re.sub(r'ENTITY stroke_color "[^"]*"', r'ENTITY stroke_color "' + example_colors[i][0] + '"', icon_svgtext) + icon_svgtext = re.sub(r'ENTITY fill_color "[^"]*"', r'ENTITY fill_color "' + example_colors[i][1] + '"', icon_svgtext) + if use_iso_strokes: + icon_svgtext = re.sub(r'ENTITY iso_stroke_color "[^"]*"', r'ENTITY iso_stroke_color "' + example_colors[i][2] + '"', icon_svgtext) + f.write(icon_svgtext) + f.close() + except: + sys.exit('Error: Could not write file ' + output_path + example_filenames[i]) + + try: + f = open(output_path + outfilename, 'w') + except: + sys.exit('Error: Could not save to ' + output_path + outfilename) + + try: + icon_svgtext = svgxml.toxml() + icon_svgtext = re.sub('&', '&', icon_svgtext) + if not use_default_colors: + icon_svgtext = re.sub(r'ENTITY stroke_color "[^"]*"', r'ENTITY stroke_color "' + stroke_color + '"', icon_svgtext) + icon_svgtext = re.sub(r'ENTITY fill_color "[^"]*"', r'ENTITY fill_color "' + fill_color + '"', icon_svgtext) + if use_iso_strokes: + icon_svgtext = re.sub(r'ENTITY iso_stroke_color "[^"]*"', r'ENTITY iso_stroke_color "' + stroke_color + '"', icon_svgtext) + f.write(icon_svgtext) + f.close() + + except: + sys.exit('Error: Could not write file ' + output_path + outfilename) + diff --git a/activity/activity-hello.svg b/activity/activity-hello.svg new file mode 100644 index 0000000..59abffd --- /dev/null +++ b/activity/activity-hello.svg @@ -0,0 +1,22 @@ + + +]> + + + + + + image/svg+xml + + + + + + + + X + + + + \ No newline at end of file diff --git a/activity/activity.info b/activity/activity.info new file mode 100644 index 0000000..57fee8a --- /dev/null +++ b/activity/activity.info @@ -0,0 +1,9 @@ +[Activity] +name = Evaluate +activity_version = 1 +show_launcher = 1 +bundle_id = org.sugarlabs.Evaluate +exec = sugar-activity activity.Activity -s +icon = activity-hello +license = GPLv3+ + diff --git a/activity/evaluate.sugar.svg b/activity/evaluate.sugar.svg new file mode 100644 index 0000000..c01b623 --- /dev/null +++ b/activity/evaluate.sugar.svg @@ -0,0 +1,35 @@ + + +]> + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + ? + + \ No newline at end of file diff --git a/activity/evaluate.svg b/activity/evaluate.svg new file mode 100644 index 0000000..29b266f --- /dev/null +++ b/activity/evaluate.svg @@ -0,0 +1,192 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + ? + + diff --git a/desktop/sweetener/basic_options.py b/desktop/sweetener/basic_options.py index bdb5bfc..7619743 100644 --- a/desktop/sweetener/basic_options.py +++ b/desktop/sweetener/basic_options.py @@ -31,7 +31,6 @@ CONFIG = 1 class BasicOptions(ItemGroup): def __init__(self, activity, box, export_formats=None): - # TRANS: This string could be already translated at hello-integration, if not, please translate there, not only here. ItemGroup.__init__(self, box, _('_File'), None) if activity.save_type != CONFIG: @@ -48,18 +47,17 @@ class BasicOptions(ItemGroup): 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], - # TRANS: This string could be already translated at hello-integration, if not, please translate there, not only here. - _('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) + 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/desktop/sweetener/coloritem.py b/desktop/sweetener/coloritem.py new file mode 100644 index 0000000..d71d37e --- /dev/null +++ b/desktop/sweetener/coloritem.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. 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('option') + +import gobject +import gtk + +from colors import color2string +from item import Item + + +class ColorItem(Item): + __gsignals__ = {'updated': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_STRING,))} + + def __init__(self, parent=None, important=False): + Item.__init__(self, gtk.STOCK_SELECT_COLOR, important) + self.parent = parent + self.color = '#FFFFFF' + + def set_color(self, 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/desktop/sweetener/colors.py b/desktop/sweetener/colors.py new file mode 100644 index 0000000..3b5d938 --- /dev/null +++ b/desktop/sweetener/colors.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. 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') + + +def color2string(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/desktop/sweetener/help.py b/desktop/sweetener/help.py new file mode 100644 index 0000000..d214392 --- /dev/null +++ b/desktop/sweetener/help.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. 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. + +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): + def __init__(self, box): + 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/desktop/sweetener/icon.py b/desktop/sweetener/icon.py new file mode 100644 index 0000000..446c603 --- /dev/null +++ b/desktop/sweetener/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 xocolor 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, value) + icon = re.sub('' % 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/desktop/sweetener/profile.py b/desktop/sweetener/profile.py new file mode 100644 index 0000000..81d4558 --- /dev/null +++ b/desktop/sweetener/profile.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. 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 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/desktop/sweetener/radioitem.py b/desktop/sweetener/radioitem.py new file mode 100644 index 0000000..16199df --- /dev/null +++ b/desktop/sweetener/radioitem.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. 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('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, + 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/desktop/sweetener/settingsitem.py b/desktop/sweetener/settingsitem.py new file mode 100644 index 0000000..a690597 --- /dev/null +++ b/desktop/sweetener/settingsitem.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. 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('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/desktop/sweetener/settingsradioitem.py b/desktop/sweetener/settingsradioitem.py new file mode 100644 index 0000000..b8c8a9f --- /dev/null +++ b/desktop/sweetener/settingsradioitem.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. 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('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/desktop/sweetener/shortcontentitem.py b/desktop/sweetener/shortcontentitem.py new file mode 100644 index 0000000..7b4a5f4 --- /dev/null +++ b/desktop/sweetener/shortcontentitem.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. 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('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/desktop/sweetener/stock.py b/desktop/sweetener/stock.py index a315e6e..6947510 100644 --- a/desktop/sweetener/stock.py +++ b/desktop/sweetener/stock.py @@ -40,6 +40,7 @@ def register(name, label, accelerator, icon_name): 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) diff --git a/desktop/sweetener/toggleitem.py b/desktop/sweetener/toggleitem.py new file mode 100644 index 0000000..d95aec9 --- /dev/null +++ b/desktop/sweetener/toggleitem.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. 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('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 diff --git a/desktop/sweetener/xocolor.py b/desktop/sweetener/xocolor.py new file mode 100644 index 0000000..ab05cb2 --- /dev/null +++ b/desktop/sweetener/xocolor.py @@ -0,0 +1,70 @@ +# Copyright (C) 2006-2007 Red Hat, Inc. +# Copyright (C) 2012 Daniel Francis +# +# 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. + +""" +STABLE. +""" + +import logging + + +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): + logging.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) diff --git a/evaluate.py b/evaluate.py index b000d4e..818f378 100755 --- a/evaluate.py +++ b/evaluate.py @@ -32,7 +32,7 @@ glib.set_prgname(appname) glib.set_application_name(appname) import gtk -from options import Options +from toolbars import Options from canvas import Canvas this_dir = os.path.split(__file__)[:-1] @@ -100,14 +100,19 @@ class UnfullscreenButton(gtk.Window): self._reposition() -class Application(gtk.Window): +class Window(gtk.Window): def __init__(self): gtk.Window.__init__(self) - self.save_type = None + self.save_type = info.io_mode self.file_path = None + self.filter = gtk.FileFilter() + self.filter.set_name('%s files' % info.name) + self.filter.add_mime_type('application/x-%s' % info.lower_name) + self.filter.add_pattern('*.gpt') self.accel_group = gtk.AccelGroup() self.add_accel_group(self.accel_group) self.set_title(appname) + logger.debug(info.lower_name) self.set_icon(gtk.gdk.pixbuf_new_from_file(os.path.join(datadir, '%s.svg' % info.lower_name))) self.connect('delete-event', lambda w, e: self.stop()) @@ -158,7 +163,6 @@ class Application(gtk.Window): format_name = data[3] filter_mime = data[2] mime_type = data[1] - # TRANS: This string could be already translated at hello-integration, if not, please translate there, not only here. filechooser = gtk.FileChooserDialog(_("Export..."), self, gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, @@ -181,7 +185,6 @@ class Application(gtk.Window): gtk.main_quit() def open(self): - # TRANS: This string could be already translated at hello-integration, if not, please translate there, not only here. filechooser = gtk.FileChooserDialog(_('Open...'), self, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, @@ -203,7 +206,6 @@ class Application(gtk.Window): self.canvas.write_file(self.file_path) def save_as(self): - # TRANS: This string could be already translated at hello-integration, if not, please translate there, not only here. filechooser = gtk.FileChooserDialog(_('Save...'), self, gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, @@ -218,7 +220,9 @@ class Application(gtk.Window): self.set_title('%s - %s' % (self.file_path, appname)) if __name__ == '__main__': - window = Application() + logger.debug('Initializing %s' % info.name) + window = Window() window.show() gtk.main() + logger.debug('Closing %s' % info.name) exit() diff --git a/info.py b/info.py index 1358182..ad858fe 100644 --- a/info.py +++ b/info.py @@ -20,6 +20,10 @@ from gettext import gettext as _ +DOCUMENT = 0 +CONFIG = 1 +io_mode = DOCUMENT + lower_name = 'evaluate' name = _('Evaluate') # Cualquier cosa si uno de nosotros no hace nada, lo sacamos de la lista. diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..530f97c --- /dev/null +++ b/setup.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python + +# Copyright (C) 2006, Red Hat, Inc. +# +# 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 2 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 St, Fifth Floor, Boston, MA 02110-1301 USA + +from sugar.activity import bundlebuilder + +bundlebuilder.start() diff --git a/sugar/sweetener/basic_options.py b/sugar/sweetener/basic_options.py index a2221f6..52f4ebd 100644 --- a/sugar/sweetener/basic_options.py +++ b/sugar/sweetener/basic_options.py @@ -36,7 +36,7 @@ class BasicOptions(ActivityToolbarButton): if export_formats != None: if len(export_formats) == 1: stock.register('sweetener-%s' % export_formats[0][1], - _('Save as %s') % export_formats[0][0], + _('Export as %s') % export_formats[0][0], None, export_formats[0][1].replace('/', '-')) export = Item('sweetener-%s' % export_formats[0][1]) diff --git a/sugar/sweetener/coloritem.py b/sugar/sweetener/coloritem.py new file mode 100644 index 0000000..30a3ad3 --- /dev/null +++ b/sugar/sweetener/coloritem.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. 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('option') + +import gobject +import gtk + +from sugar.graphics.colorbutton import ColorToolButton + +from colors import color2string +from item import Item + + +class ColorItem(Item): + __gsignals__ = {'updated': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, + (gobject.TYPE_STRING,))} + + def __init__(self, parent=None, important=False): + Item.__init__(self, gtk.STOCK_SELECT_COLOR) + self.color = '#FFFFFF' + + def set_color(self, color): + self.color = color + if self.toolitem and self.color: + self.toolitem.set_color(gtk.gdk.Color(self.color)) + + def get_tool_item(self): + self.toolitem = ColorToolButton() + self.toolitem.connect('notify::color', self._color_changed_cb) + self.setup_tooltip() + self.set_color(self.color) + return self.toolitem + + def setup_tooltip(self): + if self.tooltip: + self.toolitem.set_title(self.tooltip) + else: + text = gtk.stock_lookup(self.stock_id)[1] + self.toolitem.set_title(text.replace('_', '')) + self.setup_accelerator() + + def _color_changed_cb(self, widget, pspec): + color_gdk = widget.get_color() + self.color = color2string(color_gdk) + self.emit('updated', self.color) diff --git a/sugar/sweetener/colors.py b/sugar/sweetener/colors.py new file mode 100644 index 0000000..3b5d938 --- /dev/null +++ b/sugar/sweetener/colors.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. 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') + + +def color2string(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/sugar/sweetener/help.py b/sugar/sweetener/help.py new file mode 100644 index 0000000..c2250ff --- /dev/null +++ b/sugar/sweetener/help.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. Daniel Francis +# Based on helpbutton by Gonzalo Odiard +# +# 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 + +from sugar.graphics import style +from sugar.graphics.icon import Icon + +import stock +from settingsitem import SettingsItem +from itemgroup import ItemGroup +import info +import helpcontent + + +class Help(SettingsItem): + def __init__(self, box): + title = gtk.stock_lookup(gtk.STOCK_HELP)[1] + stock.register('sweetener-help-contents', title, + 'H', 'toolbar-help') + SettingsItem.__init__(self, None, 'sweetener-help-contents') + + sw = gtk.ScrolledWindow() + sw.set_size_request(int(gtk.gdk.screen_width() / 2.8), + gtk.gdk.screen_height() - style.GRID_CELL_SIZE * 3) + sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + self._max_text_width = int(gtk.gdk.screen_width() / 3) - 20 + self._vbox = gtk.VBox() + self._vbox.set_homogeneous(False) + hbox = gtk.HBox() + hbox.pack_start(self._vbox, False, True, 0) + sw.add_with_viewport(hbox) + sw.show() + self.content = sw + item = self.get_tool_item() + item.show_all() + separator = gtk.SeparatorToolItem() + separator.show() + box.toolbar.insert(separator, + len(box.toolbar.get_children()[:-2])) + box.toolbar.insert(item, + len(box.toolbar.get_children()[:-2])) + for i in helpcontent.help: + self.add_section(i[0]) + for text, icon in i[1:]: + self.add_paragraph(text, icon) + + def add_section(self, section_text): + hbox = gtk.HBox() + label = gtk.Label() + label.set_use_markup(True) + label.set_markup('%s' % section_text) + label.set_line_wrap(True) + label.set_size_request(self._max_text_width, -1) + hbox.add(label) + hbox.show_all() + self._vbox.pack_start(hbox, False, False, padding=5) + + def add_paragraph(self, text, icon=None): + hbox = gtk.HBox() + label = gtk.Label(text) + label.set_justify(gtk.JUSTIFY_LEFT) + label.set_line_wrap(True) + hbox.add(label) + if icon is not None: + _icon = Icon(icon_name=icon) + hbox.add(_icon) + label.set_size_request(self._max_text_width - 20, -1) + else: + label.set_size_request(self._max_text_width, -1) + + hbox.show_all() + self._vbox.pack_start(hbox, False, False, padding=5) diff --git a/sugar/sweetener/icon.py b/sugar/sweetener/icon.py new file mode 100644 index 0000000..59be87a --- /dev/null +++ b/sugar/sweetener/icon.py @@ -0,0 +1,18 @@ +# 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. + +from sugar.graphics.icon import * diff --git a/sugar/sweetener/item.py b/sugar/sweetener/item.py index f9c39d0..291204a 100644 --- a/sugar/sweetener/item.py +++ b/sugar/sweetener/item.py @@ -66,7 +66,7 @@ class Item(gobject.GObject): accelerator[1], accelerator[0]) except: logger.error( - 'Could not set up accelerator; if toogletoolbutton, update your sugar version') +'Could not set up accelerator; if toogletoolbutton, update your sugar version') def get_tool_item(self): if self._stock_id in stock.icons: diff --git a/sugar/sweetener/itembox.py b/sugar/sweetener/itembox.py index 8c6467f..d861c52 100644 --- a/sugar/sweetener/itembox.py +++ b/sugar/sweetener/itembox.py @@ -21,6 +21,7 @@ import gtk from sugar.graphics.toolbarbox import ToolbarBox from sugar.activity.widgets import StopButton + class ItemBox(ToolbarBox): def __init__(self, activity): ToolbarBox.__init__(self) diff --git a/sugar/sweetener/profile.py b/sugar/sweetener/profile.py new file mode 100644 index 0000000..39d1c97 --- /dev/null +++ b/sugar/sweetener/profile.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. 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 gtk +from sugar import profile +profile_color = profile.get_color() +fill_color = gtk.gdk.color_parse(profile_color.get_fill_color()) +stroke_color = gtk.gdk.color_parse(profile_color.get_stroke_color()) + +get_fill_color = lambda widget: fill_color +get_stroke_color = lambda widget: stroke_color diff --git a/sugar/sweetener/radioitem.py b/sugar/sweetener/radioitem.py new file mode 100644 index 0000000..93c640b --- /dev/null +++ b/sugar/sweetener/radioitem.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. 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('toggleoption') +import gtk +from sugar.graphics.radiotoolbutton import RadioToolButton +import stock +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_tool_item(self): + self.toolitem = RadioToolButton() + if self.group: + self.toolitem.set_group(self.group.toolitem) + self.toolitem.set_named_icon(stock.icons[self._stock_id] + if self._stock_id in stock.icons + else 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/sugar/sweetener/settingsitem.py b/sugar/sweetener/settingsitem.py new file mode 100644 index 0000000..4dcfdf5 --- /dev/null +++ b/sugar/sweetener/settingsitem.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. 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('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 + self.created = False + # For toggleoptions + self.active = True + + def get_tool_item(self): + self.tool_item = Item.get_tool_item(self) + self.palette = self.toolitem.get_palette() + self.palette.set_content(self.content) + self.content.show_all() + return self.tool_item + + def do_activate(self): + if self.active: + self.toolitem.props.palette.popup(immediate=True, state=1) + #TODO: Send close event when de palette is closed. + #self.emit('close') diff --git a/sugar/sweetener/settingsradioitem.py b/sugar/sweetener/settingsradioitem.py new file mode 100644 index 0000000..cf1492d --- /dev/null +++ b/sugar/sweetener/settingsradioitem.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. 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('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_tool_item(self): + RadioItem.get_tool_item(self) + self.toolitem.connect('clicked', self.activate_cb) + self.palette = self.toolitem.get_palette() + self.palette.set_content(self.content) + self.content.show_all() + return self.toolitem diff --git a/sugar/sweetener/shortcontentitem.py b/sugar/sweetener/shortcontentitem.py new file mode 100644 index 0000000..82fceba --- /dev/null +++ b/sugar/sweetener/shortcontentitem.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. 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('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) + return self.toolitem diff --git a/sugar/sweetener/stock.py b/sugar/sweetener/stock.py index 3ba898a..8fbca2e 100644 --- a/sugar/sweetener/stock.py +++ b/sugar/sweetener/stock.py @@ -26,6 +26,7 @@ icon_factory = gtk.IconFactory() # Set the icon name for the stock items, this is used only in Sugar. icons = {gtk.STOCK_ADD: 'list-add'} + def register(name, label, accelerator, icon_name): if accelerator == None: keyval = 0 @@ -42,6 +43,7 @@ def register(name, label, accelerator, icon_name): icon_factory.add_default() icons[name] = icon_name + def overwrite_stock(stock_id, new_accelerator): info = list(gtk.stock_lookup(stock_id)) keyval, mask = gtk.accelerator_parse(new_accelerator) diff --git a/sugar/sweetener/toggleitem.py b/sugar/sweetener/toggleitem.py new file mode 100644 index 0000000..d7b8c86 --- /dev/null +++ b/sugar/sweetener/toggleitem.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2012 S. 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('toggleoption') +import gobject +import gtk +from sugar.graphics.toggletoolbutton import ToggleToolButton +import stock +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): + return None + + def toggled_cb(self, widget): + active = widget.get_active() + self.toolitem.set_active(active) + self.active = active + self.emit('toggled', active) + + def get_tool_item(self): + self.toolitem = ToggleToolButton() + self.toolitem.set_named_icon(stock.icons[self._stock_id] + if self._stock_id in stock.icons + else 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/sugar/sweetener/xocolor.py b/sugar/sweetener/xocolor.py new file mode 100644 index 0000000..a5344cc --- /dev/null +++ b/sugar/sweetener/xocolor.py @@ -0,0 +1,18 @@ +# 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. + +from sugar.graphics.xocolor import * -- cgit v0.9.1