Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Francis <francis@sugarlabs.org>2012-08-30 23:55:34 (GMT)
committer Daniel Francis <francis@sugarlabs.org>2012-08-30 23:55:34 (GMT)
commit02c3fed9dc41f2304270a34c0748c278f9fe6931 (patch)
tree7621c09f70028c38dfaf95982a704ba91416cc16
parent87458b0aea14739324c822aaa4cea11722b347df (diff)
Update Sweetener
-rw-r--r--activity.py20
-rw-r--r--activity/.directory3
-rw-r--r--activity/Sugar-iconify.py678
-rw-r--r--activity/activity-hello.svg22
-rw-r--r--activity/activity.info9
-rw-r--r--activity/evaluate.sugar.svg35
-rw-r--r--activity/evaluate.svg192
-rw-r--r--desktop/sweetener/basic_options.py30
-rw-r--r--desktop/sweetener/coloritem.py62
-rw-r--r--desktop/sweetener/colors.py32
-rw-r--r--desktop/sweetener/help.py63
-rw-r--r--desktop/sweetener/icon.py860
-rw-r--r--desktop/sweetener/profile.py30
-rw-r--r--desktop/sweetener/radioitem.py48
-rw-r--r--desktop/sweetener/settingsitem.py54
-rw-r--r--desktop/sweetener/settingsradioitem.py46
-rw-r--r--desktop/sweetener/shortcontentitem.py69
-rw-r--r--desktop/sweetener/stock.py1
-rw-r--r--desktop/sweetener/toggleitem.py59
-rw-r--r--desktop/sweetener/xocolor.py70
-rwxr-xr-xevaluate.py18
-rw-r--r--info.py4
-rwxr-xr-xsetup.py21
-rw-r--r--sugar/sweetener/basic_options.py2
-rw-r--r--sugar/sweetener/coloritem.py64
-rw-r--r--sugar/sweetener/colors.py32
-rw-r--r--sugar/sweetener/help.py91
-rw-r--r--sugar/sweetener/icon.py18
-rw-r--r--sugar/sweetener/item.py2
-rw-r--r--sugar/sweetener/itembox.py1
-rw-r--r--sugar/sweetener/profile.py27
-rw-r--r--sugar/sweetener/radioitem.py45
-rw-r--r--sugar/sweetener/settingsitem.py53
-rw-r--r--sugar/sweetener/settingsradioitem.py44
-rw-r--r--sugar/sweetener/shortcontentitem.py41
-rw-r--r--sugar/sweetener/stock.py2
-rw-r--r--sugar/sweetener/toggleitem.py56
-rw-r--r--sugar/sweetener/xocolor.py18
38 files changed, 2886 insertions, 36 deletions
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<!ENTITY ' + stroke_entity + ' "' + stroke_color + '">\n'
+ entities += '\t<!ENTITY ' + fill_entity + ' "' + fill_color + '">\n'
+ if use_iso_strokes:
+ entities += '\t<!ENTITY ' + iso_stroke_entity + ' "' + stroke_color + '">\n'
+ else:
+ entities = '\t<!ENTITY ' + stroke_entity + ' "' + default_stroke_color + '">\n'
+ entities += '\t<!ENTITY ' + fill_entity + ' "' + default_fill_color + '">\n'
+ if use_iso_strokes:
+ entities += '\t<!ENTITY ' + iso_stroke_entity + ' "' + default_stroke_color + '">\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'(<!DOCTYPE[^>\[]*)(\[[^\]]*\])*\>', 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("<svg", "<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [\n" + entities + "]>\n<svg", svgtext)
+ if n == 0:
+ sys.exit('Error: Could not insert entities into DTD')
+
+#convert entities to references
+stroke_entity = '&' + stroke_entity + ';'
+fill_entity = '&' + fill_entity + ';'
+
+#create the SVG DOM
+try:
+ svgxml = xml.dom.minidom.parseString(svgtext);
+except Exception, e:
+ sys.exit('Error: Could not parse ' + svgfilename + str(e))
+
+#extract top level nodes
+i = 0
+svgindex = 0;
+docindex = 0;
+for element in svgxml.childNodes:
+ if element.nodeType == 10:
+ docindex = i;
+ elif element.localName == 'svg':
+ svgindex = i;
+ break;
+ i += 1;
+
+doctype = svgxml.childNodes[docindex]
+svg = svgxml.childNodes[svgindex]
+icons = svg.childNodes;
+
+#validate canvas size
+w = svg.getAttribute('width')
+h = svg.getAttribute('height')
+
+if w != '55px' or h != '55px':
+ print "Warning: invalid canvas size (%s, %s); Should be (55px, 55px)" % (w, h)
+
+#define utility functions
+def getStroke(node):
+ s = node.getAttribute('stroke')
+ if s:
+ return s.lower()
+ else:
+ if re.search(r'stroke:', node.getAttribute('style')):
+ s = re.sub(r'.*stroke:\s*(#*[^;]*).*', r'\1', node.getAttribute('style'))
+ return s.lower()
+ else:
+ return 'none'
+
+def setStroke(node, value):
+ s = node.getAttribute('stroke')
+ if s:
+ node.setAttribute('stroke', value)
+ else:
+ s = re.sub(r'stroke:\s*#*[^;]*', 'stroke:' + value, node.getAttribute('style'))
+ node.setAttribute('style', s)
+
+def getFill(node):
+ f = node.getAttribute('fill')
+ if f:
+ return f.lower()
+ else:
+ if re.search(r'fill:', node.getAttribute('style')):
+ f = re.sub(r'.*fill:\s*(#*[^;]*).*', r'\1', node.getAttribute('style'))
+ return f.lower()
+ else:
+ return 'none'
+
+def setFill(node, value):
+ f = node.getAttribute('fill')
+ if f:
+ node.setAttribute('fill', value)
+ else:
+ s = re.sub(r'fill:\s*#*[^;]*', 'fill:' + value, node.getAttribute('style'))
+ node.setAttribute('style', s)
+
+def replaceEntities(node, indent=''):
+
+ strokes_replaced = 0
+ fills_replaced = 0
+
+ if node.localName:
+ str = indent + node.localName
+
+ if node.nodeType == 1: #only element nodes have attrs
+
+ #replace entities for matches
+ if getStroke(node) == stroke_color:
+ setStroke(node, stroke_entity)
+ strokes_replaced += 1
+
+ if getStroke(node) == fill_color:
+ setStroke(node, fill_entity)
+ strokes_replaced += 1
+
+ if getFill(node) == fill_color:
+ setFill(node, fill_entity)
+ fills_replaced += 1
+
+ if getFill(node) == stroke_color:
+ setFill(node, stroke_entity)
+ fills_replaced += 1
+
+ str = str + " (" + getStroke(node) + ", " + getFill(node) + ")"
+ if verbose:
+ print str
+
+ #recurse on DOM
+ for n in node.childNodes:
+ sr, fr = replaceEntities(n, indent + " ")
+ strokes_replaced += sr
+ fills_replaced += fr
+
+ #return the number of replacements made
+ return (strokes_replaced, fills_replaced)
+
+def fix_isolated_strokes(node):
+
+ strokes_fixed = 0
+ #recurse on DOM
+ last_n = None
+ for n in node.childNodes:
+ sf = fix_isolated_strokes(n)
+ strokes_fixed += sf
+
+ if node.nodeType == 1: #only element nodes have attrs
+
+ #find strokes with no associated fill
+ if getStroke(node) != 'none' and getFill(node) == 'none':
+ strokes_fixed += 1
+ setStroke(node, "&iso_stroke_color;")
+
+ #return the number of strokes fixed
+ return strokes_fixed
+
+
+#these functions attempt to guess the hex values for the stroke and fill entities
+
+def getColorPairs(node, pairs=[]):
+
+ if node.nodeType == 1:
+
+ #skip masks
+ if node.localName == 'mask':
+ return pairs
+
+ node_name = ''
+ 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
+ except:
+ pass
+
+ #skip the template layers
+ if node_name.startswith('_'):
+ return pairs
+
+ pair = (getStroke(node), getFill(node))
+ if pair[0] != pair[1]:
+ pairs.append(pair)
+
+ #recurse on DOM
+ for n in node.childNodes:
+ getColorPairs(n, pairs)
+
+ return pairs
+
+def guessEntities(node):
+
+ guesses = getColorPairs(node)
+ #print guesses
+
+ stroke_guess = 'none'
+ fill_guess = 'none'
+
+ for guess in guesses:
+ if stroke_guess == 'none':
+ stroke_guess = guess[0]
+ if fill_guess == 'none' and stroke_guess != guess[1]:
+ fill_guess = guess[1]
+ if guess[0] == fill_guess and guess[1] != 'none':
+ fill_guess = stroke_guess
+ stroke_guess = guess[0]
+ if fill_guess == 'none':
+ fill_guess = guess[1]
+
+ return (stroke_guess, fill_guess)
+
+
+#guess the entity values, if they aren't passed in
+
+if use_entities:
+ if entities_passed < 2:
+ stroke_color, fill_color = guessEntities(svg)
+
+ if confirm_guess or verbose:
+ print '\nentity definitions:'
+ print ' stroke_entity = ' + stroke_color
+ print ' fill_entity = ' + fill_color
+
+ if entities_passed < 2:
+ if confirm_guess:
+ response = raw_input("\nAre these entities correct? [y/n] ")
+ if response.lower() != 'y':
+ print 'Please run this script again, passing the proper colors with the -s and -f flags.'
+ sys.exit(1)
+
+#define the HTML for preview output
+
+previewHTML = "\
+<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\n\
+\t\"http://www.w3.org/TR/html4/strict.dtd\">\n\
+<html>\n\
+<head>\n\
+\t<meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\">\n\
+\t<title>Sugar Icon Preview: ~~~</title>\n\
+\t<script type=\"text/javascript\" charset=\"utf-8\">\n\
+\t\tvar bordered = false;\n\
+\t\tvar bgcolor = \"#FFF\"\n\
+\n\
+\t\tfunction toggleIconBorder(reset)\n\
+\t\t{\n\
+\t\t\tif(!reset) bordered = !bordered;\n\
+\t\t\t\n\
+\t\t\tvar objects = document.getElementsByTagName('object')\n\
+\t\t\tfor(var i = 0; i < objects.length; i++)\n\
+\t\t\t{\n\
+\t\t\t\tif(bordered) objects[i].style.border = 'solid 1px gray';\n\
+\t\t\t\telse objects[i].style.border = 'solid 1px ' + bgcolor;\n\
+\t\t\t}\n\
+\t\t}\n\
+\t\tfunction setBackgroundColor(color)\n\
+\t\t{\n\
+\t\t\tbgcolor = color;\n\
+\t\t\tvar objects = document.getElementsByTagName('div');\n\
+\t\t\tfor(var i = 0; i < objects.length; i++)\n\
+\t\t\t\tif(objects[i].className == 'cell')\n\
+\t\t\t\t\tobjects[i].style.backgroundColor = color;\n\
+\t\t\t\n\
+\t\t\ttoggleIconBorder(true)\n\
+\t\t}\n\
+\t</script>\n\
+\t<style type=\"text/css\" media=\"screen\">\n\
+\t\thtml, body {\n\
+\t\t\tmargin: 0px;\n\
+\t\t\tborder: 0px;\n\
+\t\t\tbackground-color: white;\n\
+\t\t\t\n\
+\t\t\tfont: 14px Helvetica;\n\
+\t\t\tcolor: gray;\n\
+\t\t}\n\
+\t\t.cell {\n\
+\t\t\twidth: 57px;\n\
+\t\t\theight: 57px;\n\
+\t\t\tpadding: 8px;\n\
+\t\t\tborder: solid 1px gray;\n\
+\t\t}\n\
+\t\t.icon {\n\
+\t\t\tmargin: 0px;\n\
+\t\t\tpadding: 0px;\n\
+\t\t\tborder: solid 1px #FFF;\n\
+\t\t\twidth: 55px;\n\
+\t\t\theight: 55px;\n\
+\t\t}\n\
+\t\t#icons {\n\
+\t\t\tmargin-top: 30px;\n\
+\t\t}\n\
+\n\
+\t\t#icons > li {\n\
+\t\t\tdisplay: block;\n\
+\t\t\tmargin: 0px;\n\
+\t\t\tmargin-bottom: 20px;\n\
+\t\t}\n\
+\t\t#description {\n\
+\t\t\twidth: 300px;\n\
+\t\t\tposition: absolute;\n\
+\t\t\ttop: 0px;\n\
+\t\t\tleft: 200px;\n\
+\t\t\tborder-left: solid 1px gray;\n\
+\t\t\tmargin-top: 30px;\n\
+\t\t\tpadding-left: 30px;\n\
+\t\t\tfont-size: 11px;\n\
+\t\t}\n\
+\t\t#description ul {\n\
+\t\t\tmargin: 0px;\n\
+\t\t\tpadding: 0px;\n\
+\t\t\tdisplay: block;\n\
+\t\t\tlist-style: square;\n\
+\t\t}\n\
+\t\tli { margin-bottom: 10px; }\n\
+\t\ta, a:visited { color: gray; }\n\
+\t\ta:hover { color: black; }\n\
+\t</style>\n\
+</head>\n\
+<body>\n\
+\t\t<ul id='icons'>\n\
+\t\t<li><div class='cell'><object class='icon' id='stroke' data=\"~~~.stroke.svg\" type=\"image/svg+xml\"></object></div><br>stroke\n\
+\t\t<li><div class='cell'><object class='icon' id='fill' data=\"~~~.fill.svg\" type=\"image/svg+xml\"></object></div><br>fill\n\
+\t\t<li><div class='cell'><object class='icon' id='both' data=\"~~~.both.svg\" type=\"image/svg+xml\"></object></div><br>both\n\
+\t\t</ul>\n\
+\t\t<div id='description'>\n\
+\t\t\t<h3>Icon Validation</h3>\n\
+\t\t\t<ul>\n\
+\t\t\t\t<li>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 <a href='javascript:toggleIconBorder();'>toggle</a> the 55px canvas border.\n\
+\t\t\t\t<li>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<!--li>Click to see your icon on <a href=\"javascript:setBackgroundColor('#000');\">black</a>, \n\
+\t\t\t\t\t<a href=\"javascript:setBackgroundColor('#FFF');\">white</a>, <a href=\"javascript:setBackgroundColor('#282828');\">gray</a>.<!-->\n\
+\t\t\t\t<li>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 <b>-s</b> and <b>-f</b> options of sugar-iconify, and be sure that your input SVG doesn't have extra colors in its palette.\n\
+\t\t\t\t<li>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<li>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 <b>-i</b> option of the sugar-iconify script.\n\
+\t\t\t</ul>\n\
+\t\t\t<i>For more information, please see the OLPC wiki page on <a href='http://wiki.laptop.org/go/Making_Sugar_Icons' target='_blank'>making sugar icons</a>.</i>\n\
+\t\t</div>\n\
+</body>\n\
+</html>\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('&amp;', '&', 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('&amp;', '&', 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('&amp;', '&', 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 @@
+<?xml version="1.0" ?><!-- Created with Inkscape (http://www.inkscape.org/) --><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ <!ENTITY stroke_color "#000000">
+ <!ENTITY fill_color "#ffffff">
+]><svg height="72.824974" id="svg2" inkscape:version="0.48.3.1 r9886" sodipodi:docname="activity-illustrate-sugar.svg" version="1.1" width="73.833656" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg">
+ <sodipodi:namedview bordercolor="#666666" borderopacity="1" fit-margin-bottom="10" fit-margin-left="10" fit-margin-right="10" fit-margin-top="10" gridtolerance="10" guidetolerance="10" id="namedview12" inkscape:current-layer="layer1" inkscape:cx="28.289239" inkscape:cy="24.18596" inkscape:guide-bbox="true" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:window-height="480" inkscape:window-maximized="0" inkscape:window-width="640" inkscape:window-x="281" inkscape:window-y="99" inkscape:zoom="1.845" objecttolerance="10" pagecolor="#ffffff" showgrid="false" showguides="true"/>
+ <defs id="defs4"/>
+ <metadata id="metadata7">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="layer1" transform="translate(8.1694087,-987.72323)">
+ <path d="m 53.54055,1024.1398 a 24.793131,24.288791 0 0 1 -49.5862613,0 24.793131,24.288791 0 1 1 49.5862613,0 z" id="path3755" inkscape:connector-curvature="0" style="fill:&fill_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:4.24739408;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/>
+ <text id="text3757" sodipodi:linespacing="125%" style="font-size:19.87748528px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:&stroke_color;;fill-opacity:1;stroke:none;font-family:Sans" transform="scale(1.1602098,0.86191307)" x="17.57967" xml:space="preserve" y="1195.9161"><tspan id="tspan3759" x="17.57967" y="1195.9161">X</tspan></text>
+ <path d="m 32.247574,1014.3027 a 3.2501449,3.2028739 0 0 1 -6.500289,0 3.2501449,3.2028739 0 1 1 6.500289,0 z" id="path3761" inkscape:connector-curvature="0" style="fill:&fill_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:1.28571522;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/>
+ <path d="m 30.747506,1032.8339 a 2.2501002,2.0589904 0 0 1 -4.5002,0 2.2501002,2.0589904 0 1 1 4.5002,0 z" id="path3763" inkscape:connector-curvature="0" style="fill:&stroke_color;;fill-opacity:1;stroke:&stroke_color;;stroke-width:1.28571522;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/>
+ </g>
+</svg> \ 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 @@
+<?xml version="1.0" ?><!-- Created with Inkscape (http://www.inkscape.org/) --><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ <!ENTITY stroke_color "#ffffff">
+ <!ENTITY fill_color "#000000">
+]><svg height="48px" id="svg2985" inkscape:version="0.48.2 r9819" sodipodi:docname="evaluate.svg" version="1.1" width="48px" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:svg="http://www.w3.org/2000/svg">
+ <defs id="defs2987"/>
+ <sodipodi:namedview bordercolor="#666666" borderopacity="1.0" id="base" inkscape:current-layer="layer1" inkscape:cx="22.367011" inkscape:cy="36.986049" inkscape:document-units="px" inkscape:grid-bbox="true" inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:window-height="706" inkscape:window-maximized="1" inkscape:window-width="1366" inkscape:window-x="0" inkscape:window-y="27" inkscape:zoom="7" pagecolor="#ffffff" showgrid="true"/>
+ <metadata id="metadata2990">
+ <rdf:RDF>
+ <cc:Work rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
+ <dc:title/>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g id="layer1" inkscape:groupmode="layer" inkscape:label="Layer 1">
+ <path d="m 40.718631,4.2962212 a 4.5714288,5.2142859 0 0 0 -0.113444,-0.00661" id="path3785" sodipodi:cx="40.42857" sodipodi:cy="9.5" sodipodi:end="4.7510337" sodipodi:open="true" sodipodi:rx="4.5714288" sodipodi:ry="5.2142859" sodipodi:start="4.7758826" sodipodi:type="arc" style="fill:&stroke_color;;fill-opacity:1;fill-rule:nonzero;stroke:&fill_color;;stroke-width:0.93099999;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"/>
+ <rect height="2.8571429" id="rect3787" style="fill:&stroke_color;;fill-opacity:1;fill-rule:nonzero;stroke:&fill_color;;stroke-width:0.93099999;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" width="32" x="2" y="5.7142859"/>
+ <rect height="14.428572" id="rect3789" style="fill:&stroke_color;;fill-opacity:1;fill-rule:nonzero;stroke:&fill_color;;stroke-width:0.93099999000000000;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" width="16.714285" x="2.1428571" y="11.285714"/>
+ <rect height="1.3427573" id="rect3791" style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:0.51636136;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" width="11.77133" x="22.542906" y="12.828621"/>
+ <rect height="1.5544633" id="rect3800" style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:0.6599071;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" width="23.125889" x="22.365627" y="17.365629"/>
+ <rect height="1.6122955" id="rect3800-27" style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:0.71175671;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" transform="scale(1,-1)" width="43.813625" x="2.0763655" y="-29.613859"/>
+ <rect height="1.3427573" id="rect3791-0" style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:0.51636136;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" width="11.77133" x="22.542906" y="15.042907"/>
+ <rect height="1.5544633" id="rect3800-5" style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:0.6599071;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" width="23.125889" x="22.294199" y="19.937054"/>
+ <rect height="1.5544633" id="rect3800-8" style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:0.6599071;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" width="23.125889" x="22.294199" y="22.794197"/>
+ <rect height="1.5544633" id="rect3800-56" style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:0.6599071;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" width="23.125889" x="22.151342" y="25.079912"/>
+ <rect height="1.6122955" id="rect3800-27-38" style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:0.71175671;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" transform="scale(1,-1)" width="43.813625" x="1.9503301" y="-31.949003"/>
+ <rect height="1.6122955" id="rect3800-27-9" style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:0.71175671;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" transform="scale(1,-1)" width="43.813625" x="1.9503307" y="-34.520435"/>
+ <rect height="1.6122955" id="rect3800-27-30" style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:0.71175671;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" transform="scale(1,-1)" width="43.813625" x="1.9503303" y="-36.949005"/>
+ <rect height="1.6122955" id="rect3800-27-4" style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:0.71175671;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" transform="scale(1,-1)" width="43.813625" x="1.9503301" y="-39.520435"/>
+ <rect height="1.6122955" id="rect3800-27-2" style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:0.71175671;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" transform="scale(1,-1)" width="43.813625" x="2.0931873" y="-42.091858"/>
+ <rect height="1.6122955" id="rect3800-27-92" style="fill:&fill_color;;fill-opacity:1;fill-rule:nonzero;stroke:&stroke_color;;stroke-width:0.71175671;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" transform="scale(1,-1)" width="43.813625" x="2.0931873" y="-44.377575"/>
+ <text id="text4016" sodipodi:linespacing="125%" style="font-size:8.86945343px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:&fill_color;;fill-opacity:1;stroke:none;font-family:Sans" transform="scale(1.1574149,0.86399443)" x="32.70092" xml:space="preserve" y="14.715704"><tspan id="tspan4018" sodipodi:role="line" x="32.70092" y="14.715704">?</tspan></text>
+ </g>
+</svg> \ 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 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="48px"
+ height="48px"
+ id="svg2985"
+ version="1.1"
+ inkscape:version="0.48.2 r9819"
+ sodipodi:docname="evaluate.svg">
+ <defs
+ id="defs2987" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="7"
+ inkscape:cx="22.367011"
+ inkscape:cy="36.986049"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:grid-bbox="true"
+ inkscape:document-units="px"
+ inkscape:window-width="1366"
+ inkscape:window-height="706"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata2990">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <path
+ sodipodi:type="arc"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.93099999;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="path3785"
+ sodipodi:cx="40.42857"
+ sodipodi:cy="9.5"
+ sodipodi:rx="4.5714288"
+ sodipodi:ry="5.2142859"
+ d="m 40.718631,4.2962212 a 4.5714288,5.2142859 0 0 0 -0.113444,-0.00661"
+ sodipodi:start="4.7758826"
+ sodipodi:end="4.7510337"
+ sodipodi:open="true" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.93099999;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3787"
+ width="32"
+ height="2.8571429"
+ x="2"
+ y="5.7142859" />
+ <rect
+ style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.93099999000000000;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3789"
+ width="16.714285"
+ height="14.428572"
+ x="2.1428571"
+ y="11.285714" />
+ <rect
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.51636136;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3791"
+ width="11.77133"
+ height="1.3427573"
+ x="22.542906"
+ y="12.828621" />
+ <rect
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.6599071;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3800"
+ width="23.125889"
+ height="1.5544633"
+ x="22.365627"
+ y="17.365629" />
+ <rect
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.71175671;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3800-27"
+ width="43.813625"
+ height="1.6122955"
+ x="2.0763655"
+ y="-29.613859"
+ transform="scale(1,-1)" />
+ <rect
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.51636136;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3791-0"
+ width="11.77133"
+ height="1.3427573"
+ x="22.542906"
+ y="15.042907" />
+ <rect
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.6599071;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3800-5"
+ width="23.125889"
+ height="1.5544633"
+ x="22.294199"
+ y="19.937054" />
+ <rect
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.6599071;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3800-8"
+ width="23.125889"
+ height="1.5544633"
+ x="22.294199"
+ y="22.794197" />
+ <rect
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.6599071;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3800-56"
+ width="23.125889"
+ height="1.5544633"
+ x="22.151342"
+ y="25.079912" />
+ <rect
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.71175671;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3800-27-38"
+ width="43.813625"
+ height="1.6122955"
+ x="1.9503301"
+ y="-31.949003"
+ transform="scale(1,-1)" />
+ <rect
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.71175671;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3800-27-9"
+ width="43.813625"
+ height="1.6122955"
+ x="1.9503307"
+ y="-34.520435"
+ transform="scale(1,-1)" />
+ <rect
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.71175671;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3800-27-30"
+ width="43.813625"
+ height="1.6122955"
+ x="1.9503303"
+ y="-36.949005"
+ transform="scale(1,-1)" />
+ <rect
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.71175671;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3800-27-4"
+ width="43.813625"
+ height="1.6122955"
+ x="1.9503301"
+ y="-39.520435"
+ transform="scale(1,-1)" />
+ <rect
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.71175671;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3800-27-2"
+ width="43.813625"
+ height="1.6122955"
+ x="2.0931873"
+ y="-42.091858"
+ transform="scale(1,-1)" />
+ <rect
+ style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.71175671;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ id="rect3800-27-92"
+ width="43.813625"
+ height="1.6122955"
+ x="2.0931873"
+ y="-44.377575"
+ transform="scale(1,-1)" />
+ <text
+ xml:space="preserve"
+ style="font-size:8.86945343px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans"
+ x="32.70092"
+ y="14.715704"
+ id="text4016"
+ sodipodi:linespacing="125%"
+ transform="scale(1.1574149,0.86399443)"><tspan
+ sodipodi:role="line"
+ id="tspan4018"
+ x="32.70092"
+ y="14.715704">?</tspan></text>
+ </g>
+</svg>
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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('option')
+
+import gobject
+import gtk
+
+from colors import color2string
+from item import Item
+
+
+class ColorItem(Item):
+ __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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+from gettext import gettext as _
+import gtk
+
+import stock
+from item import Item
+from itemgroup import ItemGroup
+import info
+
+
+class AboutItem(Item):
+ def __init__(self, parent):
+ Item.__init__(self, gtk.STOCK_ABOUT)
+ self.parent = parent
+
+ def do_activate(self):
+ dialog = gtk.AboutDialog()
+ dialog.set_name(info.name)
+ dialog.set_version(info.version)
+ dialog.set_transient_for(self.parent)
+ dialog.set_comments(info.description)
+ dialog.set_copyright(info.copyright)
+ dialog.set_website(info.url)
+ dialog.set_authors(info.authors)
+ dialog.set_license(info.license)
+ dialog.set_wrap_license(True)
+ dialog.set_logo_icon_name('graph-plotter')
+ dialog.run()
+ dialog.destroy()
+
+
+class Help(ItemGroup):
+ 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 %s "%s">' % (entity, value)
+ icon = re.sub('<!ENTITY %s .*>' % entity, xml, icon)
+ else:
+ logging.error(
+ 'Icon %s, entity %s is invalid.', file_name, entity)
+
+ # XXX this is very slow! why?
+ import rsvg
+ return rsvg.Handle(data=icon)
+
+
+class _IconInfo(object):
+
+ def __init__(self):
+ self.file_name = None
+ self.attach_x = 0
+ self.attach_y = 0
+
+
+class _BadgeInfo(object):
+
+ def __init__(self):
+ self.attach_x = 0
+ self.attach_y = 0
+ self.size = 0
+ self.icon_padding = 0
+
+
+class _IconBuffer(object):
+
+ _surface_cache = LRU(50)
+ _loader = _SVGLoader()
+
+ def __init__(self):
+ self.icon_name = None
+ self.icon_size = None
+ self.file_name = None
+ self.fill_color = None
+ self.background_color = None
+ self.stroke_color = None
+ self.badge_name = None
+ self.width = None
+ self.height = None
+ self.cache = False
+ self.scale = 1.0
+
+ def _get_cache_key(self, sensitive):
+ if self.background_color is None:
+ color = None
+ else:
+ color = (self.background_color.red, self.background_color.green,
+ self.background_color.blue)
+ return (self.icon_name, self.file_name, self.fill_color,
+ self.stroke_color, self.badge_name, self.width, self.height,
+ color, sensitive)
+
+ def _load_svg(self, file_name):
+ entities = {}
+ if self.fill_color:
+ entities['fill_color'] = self.fill_color
+ if self.stroke_color:
+ entities['stroke_color'] = self.stroke_color
+
+ return self._loader.load(file_name, entities, self.cache)
+
+ def _get_attach_points(self, info, size_request):
+ attach_points = info.get_attach_points()
+
+ if attach_points:
+ attach_x = float(attach_points[0][0]) / size_request
+ attach_y = float(attach_points[0][1]) / size_request
+ else:
+ attach_x = attach_y = 0
+
+ return attach_x, attach_y
+
+ def _get_icon_info(self):
+ icon_info = _IconInfo()
+
+ if self.file_name:
+ icon_info.file_name = self.file_name
+ elif self.icon_name:
+ theme = gtk.icon_theme_get_default()
+
+ size = 50
+ if self.width != None:
+ size = self.width
+
+ info = theme.lookup_icon(self.icon_name, int(size), 0)
+ if info:
+ attach_x, attach_y = self._get_attach_points(info, size)
+
+ icon_info.file_name = info.get_filename()
+ icon_info.attach_x = attach_x
+ icon_info.attach_y = attach_y
+
+ del info
+ else:
+ logging.warning('No icon with the name %s was found in the '
+ 'theme.', self.icon_name)
+
+ return icon_info
+
+ def _draw_badge(self, context, size, sensitive, widget):
+ theme = gtk.icon_theme_get_default()
+ badge_info = theme.lookup_icon(self.badge_name, int(size), 0)
+ if badge_info:
+ badge_file_name = badge_info.get_filename()
+ if badge_file_name.endswith('.svg'):
+ handle = self._loader.load(badge_file_name, {}, self.cache)
+
+ dimensions = handle.get_dimension_data()
+ icon_width = int(dimensions[0])
+ icon_height = int(dimensions[1])
+
+ pixbuf = handle.get_pixbuf()
+ else:
+ pixbuf = gtk.gdk.pixbuf_new_from_file(badge_file_name)
+
+ icon_width = pixbuf.get_width()
+ icon_height = pixbuf.get_height()
+
+ context.scale(float(size) / icon_width,
+ float(size) / icon_height)
+
+ if not sensitive:
+ pixbuf = self._get_insensitive_pixbuf(pixbuf, widget)
+ gdkcontext = gtk.gdk.CairoContext(context)
+ gdkcontext.set_source_pixbuf(pixbuf, 0, 0)
+ gdkcontext.paint()
+
+ def _get_size(self, icon_width, icon_height, padding):
+ if self.width is not None and self.height is not None:
+ width = self.width + padding
+ height = self.height + padding
+ else:
+ width = icon_width + padding
+ height = icon_height + padding
+
+ return width, height
+
+ def _get_badge_info(self, icon_info, icon_width, icon_height):
+ info = _BadgeInfo()
+ if self.badge_name is None:
+ return info
+
+ info.size = int(_BADGE_SIZE * icon_width)
+ info.attach_x = int(icon_info.attach_x * icon_width - info.size / 2)
+ info.attach_y = int(icon_info.attach_y * icon_height - info.size / 2)
+
+ if info.attach_x < 0 or info.attach_y < 0:
+ info.icon_padding = max(-info.attach_x, -info.attach_y)
+ elif info.attach_x + info.size > icon_width or \
+ info.attach_y + info.size > icon_height:
+ x_padding = info.attach_x + info.size - icon_width
+ y_padding = info.attach_y + info.size - icon_height
+ info.icon_padding = max(x_padding, y_padding)
+
+ return info
+
+ def _get_xo_color(self):
+ if self.stroke_color and self.fill_color:
+ return XoColor('%s,%s' % (self.stroke_color, self.fill_color))
+ else:
+ return None
+
+ def _set_xo_color(self, xo_color):
+ if xo_color:
+ self.stroke_color = xo_color.get_stroke_color()
+ self.fill_color = xo_color.get_fill_color()
+ else:
+ self.stroke_color = None
+ self.fill_color = None
+
+ def _get_insensitive_pixbuf(self, pixbuf, widget):
+ if not (widget and widget.style):
+ return pixbuf
+
+ icon_source = gtk.IconSource()
+ # Special size meaning "don't touch"
+ icon_source.set_size(-1)
+ icon_source.set_pixbuf(pixbuf)
+ icon_source.set_state(gtk.STATE_INSENSITIVE)
+ icon_source.set_direction_wildcarded(False)
+ icon_source.set_size_wildcarded(False)
+
+ pixbuf = widget.style.render_icon(icon_source, widget.get_direction(),
+ gtk.STATE_INSENSITIVE, -1, widget,
+ 'sugar-icon')
+
+ return pixbuf
+
+ def get_surface(self, sensitive=True, widget=None):
+ cache_key = self._get_cache_key(sensitive)
+ if cache_key in self._surface_cache:
+ return self._surface_cache[cache_key]
+
+ icon_info = self._get_icon_info()
+ if icon_info.file_name is None:
+ return None
+
+ is_svg = icon_info.file_name.endswith('.svg')
+
+ if is_svg:
+ handle = self._load_svg(icon_info.file_name)
+ dimensions = handle.get_dimension_data()
+ icon_width = int(dimensions[0])
+ icon_height = int(dimensions[1])
+ else:
+ pixbuf = gtk.gdk.pixbuf_new_from_file(icon_info.file_name)
+ icon_width = pixbuf.get_width()
+ icon_height = pixbuf.get_height()
+
+ badge_info = self._get_badge_info(icon_info, icon_width, icon_height)
+
+ padding = badge_info.icon_padding
+ width, height = self._get_size(icon_width, icon_height, padding)
+ if self.background_color is None:
+ surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, int(width),
+ int(height))
+ context = cairo.Context(surface)
+ else:
+ surface = cairo.ImageSurface(cairo.FORMAT_RGB24, int(width),
+ int(height))
+ context = cairo.Context(surface)
+ context.set_source_rgb(self.background_color.red,
+ self.background_color.blue,
+ self.background_color.green)
+ context.paint()
+
+ context.scale(float(width) / (icon_width + padding * 2),
+ float(height) / (icon_height + padding * 2))
+ context.save()
+
+ context.translate(padding, padding)
+ if is_svg:
+ if sensitive:
+ handle.render_cairo(context)
+ else:
+ pixbuf = handle.get_pixbuf()
+ pixbuf = self._get_insensitive_pixbuf(pixbuf, widget)
+ gdkcontext = gtk.gdk.CairoContext(context)
+ gdkcontext.set_source_pixbuf(pixbuf, 0, 0)
+ gdkcontext.paint()
+ else:
+ if not sensitive:
+ pixbuf = self._get_insensitive_pixbuf(pixbuf, widget)
+ gdkcontext = gtk.gdk.CairoContext(context)
+ gdkcontext.set_source_pixbuf(pixbuf, 0, 0)
+ gdkcontext.paint()
+
+ if self.badge_name:
+ context.restore()
+ context.translate(badge_info.attach_x, badge_info.attach_y)
+ self._draw_badge(context, badge_info.size, sensitive, widget)
+
+ self._surface_cache[cache_key] = surface
+
+ return surface
+
+ xo_color = property(_get_xo_color, _set_xo_color)
+
+
+class Icon(gtk.Image):
+
+ __gtype_name__ = 'SugarIcon'
+
+ def __init__(self, **kwargs):
+ self._buffer = _IconBuffer()
+ # HACK: need to keep a reference to the path so it doesn't get garbage
+ # collected while it's still used if it's a sugar.util.TempFilePath.
+ # See #1175
+ self._file = None
+ self._alpha = 1.0
+ self._scale = 1.0
+
+ gobject.GObject.__init__(self, **kwargs)
+
+ def get_file(self):
+ return self._file
+
+ def set_file(self, file_name):
+ self._file = file_name
+ self._buffer.file_name = file_name
+
+ file = gobject.property(type=object, setter=set_file, getter=get_file)
+
+ def _sync_image_properties(self):
+ if self._buffer.icon_name != self.props.icon_name:
+ self._buffer.icon_name = self.props.icon_name
+
+ if self._buffer.file_name != self.props.file:
+ self._buffer.file_name = self.props.file
+
+ if self.props.pixel_size == -1:
+ width, height = gtk.icon_size_lookup(self.props.icon_size)
+ else:
+ width = height = self.props.pixel_size
+ if self._buffer.width != width or self._buffer.height != height:
+ self._buffer.width = width
+ self._buffer.height = height
+
+ def _icon_size_changed_cb(self, image, pspec):
+ self._buffer.icon_size = self.props.icon_size
+
+ def _icon_name_changed_cb(self, image, pspec):
+ self._buffer.icon_name = self.props.icon_name
+
+ def _file_changed_cb(self, image, pspec):
+ self._buffer.file_name = self.props.file
+
+ def do_size_request(self, requisition):
+ """
+ Parameters
+ ----------
+ requisition :
+
+ Returns
+ -------
+ None
+
+ """
+ self._sync_image_properties()
+ surface = self._buffer.get_surface()
+ if surface:
+ requisition[0] = surface.get_width()
+ requisition[1] = surface.get_height()
+ elif self._buffer.width and self._buffer.height:
+ requisition[0] = self._buffer.width
+ requisition[1] = self._buffer.width
+ else:
+ requisition[0] = requisition[1] = 0
+
+ def do_expose_event(self, event):
+ """
+ Parameters
+ ----------
+ event :
+
+ Returns:
+ --------
+ None
+
+ """
+ self._sync_image_properties()
+ sensitive = (self.state != gtk.STATE_INSENSITIVE)
+ surface = self._buffer.get_surface(sensitive, self)
+ if surface is None:
+ return
+
+ xpad, ypad = self.get_padding()
+ xalign, yalign = self.get_alignment()
+ requisition = self.get_child_requisition()
+ if self.get_direction() != gtk.TEXT_DIR_LTR:
+ xalign = 1.0 - xalign
+
+ allocation = self.get_allocation()
+ x = math.floor(allocation.x + xpad +
+ (allocation.width - requisition[0]) * xalign)
+ y = math.floor(allocation.y + ypad +
+ (allocation.height - requisition[1]) * yalign)
+
+ cr = self.window.cairo_create()
+
+ if self._scale != 1.0:
+ cr.scale(self._scale, self._scale)
+
+ margin = self._buffer.width * (1 - self._scale) / 2
+ x, y = x + margin, y + margin
+
+ x = x / self._scale
+ y = y / self._scale
+
+ cr.set_source_surface(surface, x, y)
+
+ if self._alpha == 1.0:
+ cr.paint()
+ else:
+ cr.paint_with_alpha(self._alpha)
+
+ def set_xo_color(self, value):
+ """
+ Parameters
+ ----------
+ value :
+
+ Returns
+ -------
+ None
+
+ """
+ if self._buffer.xo_color != value:
+ self._buffer.xo_color = value
+ self.queue_draw()
+
+ xo_color = gobject.property(
+ type=object, getter=None, setter=set_xo_color)
+
+ def set_fill_color(self, value):
+ """
+ Parameters
+ ----------
+ value :
+
+ Returns
+ -------
+ None
+
+ """
+ if self._buffer.fill_color != value:
+ self._buffer.fill_color = value
+ self.queue_draw()
+
+ def get_fill_color(self):
+ """
+ Parameters
+ ----------
+ None
+
+ Returns
+ -------
+ fill_color :
+
+ """
+ return self._buffer.fill_color
+
+ fill_color = gobject.property(
+ type=object, getter=get_fill_color, setter=set_fill_color)
+
+ def set_stroke_color(self, value):
+ """
+ Parameters
+ ----------
+ value :
+
+ Returns
+ -------
+ None
+
+ """
+ if self._buffer.stroke_color != value:
+ self._buffer.stroke_color = value
+ self.queue_draw()
+
+ def get_stroke_color(self):
+ """
+ Parameters
+ ----------
+ None
+
+ Returns
+ -------
+ stroke_color :
+
+ """
+ return self._buffer.stroke_color
+
+ stroke_color = gobject.property(
+ type=object, getter=get_stroke_color, setter=set_stroke_color)
+
+ def set_badge_name(self, value):
+ """
+ Parameters
+ ----------
+ value:
+
+ Returns
+ -------
+ None
+
+ """
+ if self._buffer.badge_name != value:
+ self._buffer.badge_name = value
+ self.queue_resize()
+
+ def get_badge_name(self):
+ return self._buffer.badge_name
+
+ badge_name = gobject.property(
+ type=str, getter=get_badge_name, setter=set_badge_name)
+
+ def set_alpha(self, value):
+ if self._alpha != value:
+ self._alpha = value
+ self.queue_draw()
+
+ alpha = gobject.property(
+ type=float, setter=set_alpha)
+
+ def set_scale(self, value):
+ if self._scale != value:
+ self._scale = value
+ self.queue_draw()
+
+ scale = gobject.property(
+ type=float, setter=set_scale)
+
+
+class CellRendererIcon(gtk.GenericCellRenderer):
+
+ __gtype_name__ = 'SugarCellRendererIcon'
+
+ __gsignals__ = {
+ 'clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, [object]),
+ }
+
+ def __init__(self, tree_view):
+
+ self._buffer = _IconBuffer()
+ self._buffer.cache = True
+ self._xo_color = None
+ self._fill_color = None
+ self._stroke_color = None
+ self._prelit_fill_color = None
+ self._prelit_stroke_color = None
+
+ gobject.GObject.__init__(self)
+
+ def set_file_name(self, value):
+ if self._buffer.file_name != value:
+ self._buffer.file_name = value
+
+ file_name = gobject.property(type=str, setter=set_file_name)
+
+ def set_icon_name(self, value):
+ if self._buffer.icon_name != value:
+ self._buffer.icon_name = value
+
+ icon_name = gobject.property(type=str, setter=set_icon_name)
+
+ def get_xo_color(self):
+ return self._xo_color
+
+ def set_xo_color(self, value):
+ self._xo_color = value
+
+ xo_color = gobject.property(type=object,
+ getter=get_xo_color, setter=set_xo_color)
+
+ def set_fill_color(self, value):
+ if self._fill_color != value:
+ self._fill_color = value
+
+ fill_color = gobject.property(type=object, setter=set_fill_color)
+
+ def set_stroke_color(self, value):
+ if self._stroke_color != value:
+ self._stroke_color = value
+
+ stroke_color = gobject.property(type=object, setter=set_stroke_color)
+
+ def set_prelit_fill_color(self, value):
+ if self._prelit_fill_color != value:
+ self._prelit_fill_color = value
+
+ prelit_fill_color = gobject.property(type=object,
+ setter=set_prelit_fill_color)
+
+ def set_prelit_stroke_color(self, value):
+ if self._prelit_stroke_color != value:
+ self._prelit_stroke_color = value
+
+ prelit_stroke_color = gobject.property(type=object,
+ setter=set_prelit_stroke_color)
+
+ def set_background_color(self, value):
+ if self._buffer.background_color != value:
+ self._buffer.background_color = value
+
+ background_color = gobject.property(type=object,
+ setter=set_background_color)
+
+ def set_size(self, value):
+ if self._buffer.width != value:
+ self._buffer.width = value
+ self._buffer.height = value
+
+ size = gobject.property(type=object, setter=set_size)
+
+ def on_get_size(self, widget, cell_area):
+ width = self._buffer.width + self.props.xpad * 2
+ height = self._buffer.height + self.props.ypad * 2
+ xoffset = 0
+ yoffset = 0
+
+ if width > 0 and height > 0 and cell_area is not None:
+
+ if widget.get_direction() == gtk.TEXT_DIR_RTL:
+ xoffset = 1.0 - self.props.xalign
+ else:
+ xoffset = self.props.xalign
+
+ xoffset = max(xoffset * (cell_area.width - width), 0)
+ yoffset = max(self.props.yalign * (cell_area.height - height), 0)
+
+ return xoffset, yoffset, width, height
+
+ def on_activate(self, event, widget, path, background_area, cell_area,
+ flags):
+ pass
+
+ def on_start_editing(self, event, widget, path, background_area, cell_area,
+ flags):
+ pass
+
+ def _is_prelit(self, tree_view):
+ x, y = tree_view.get_pointer()
+ x, y = tree_view.convert_widget_to_bin_window_coords(x, y)
+ pos = tree_view.get_path_at_pos(x, y)
+ if pos is None:
+ return False
+
+ path_, column, x, y = pos
+
+ for cell_renderer in column.get_cell_renderers():
+ if cell_renderer == self:
+ cell_x, cell_width = column.cell_get_position(cell_renderer)
+ if x > cell_x and x < (cell_x + cell_width):
+ return True
+ return False
+
+ return False
+
+ def on_render(self, window, widget, background_area, cell_area,
+ expose_area, flags):
+ if self._xo_color is not None:
+ stroke_color = self._xo_color.get_stroke_color()
+ fill_color = self._xo_color.get_fill_color()
+ prelit_fill_color = None
+ prelit_stroke_color = None
+ else:
+ stroke_color = self._stroke_color
+ fill_color = self._fill_color
+ prelit_fill_color = self._prelit_fill_color
+ prelit_stroke_color = self._prelit_stroke_color
+
+ has_prelit_colors = None not in [prelit_fill_color,
+ prelit_stroke_color]
+
+ if flags & gtk.CELL_RENDERER_PRELIT and has_prelit_colors and \
+ self._is_prelit(widget):
+
+ self._buffer.fill_color = prelit_fill_color
+ self._buffer.stroke_color = prelit_stroke_color
+ else:
+ self._buffer.fill_color = fill_color
+ self._buffer.stroke_color = stroke_color
+
+ surface = self._buffer.get_surface()
+ if surface is None:
+ return
+
+ xoffset, yoffset, width_, height_ = self.on_get_size(widget, cell_area)
+
+ x = cell_area.x + xoffset
+ y = cell_area.y + yoffset
+
+ cr = window.cairo_create()
+ cr.set_source_surface(surface, math.floor(x), math.floor(y))
+ cr.rectangle(expose_area)
+ cr.paint()
+
+
+def get_icon_state(base_name, perc, step=5):
+ strength = round(perc / step) * step
+ icon_theme = gtk.icon_theme_get_default()
+
+ while strength <= 100 and strength >= 0:
+ icon_name = '%s-%03d' % (base_name, strength)
+ if icon_theme.has_icon(icon_name):
+ return icon_name
+
+ strength = strength + step
+
+
+def get_icon_file_name(icon_name):
+ icon_theme = gtk.icon_theme_get_default()
+ info = icon_theme.lookup_icon(icon_name, gtk.ICON_SIZE_LARGE_TOOLBAR, 0)
+ if not info:
+ return None
+ filename = info.get_filename()
+ del info
+ return filename
+
+
+def get_surface(**kwargs):
+ """Get cached cairo surface.
+
+ Keyword arguments:
+ icon_name -- name of icon to load, default None
+ file_name -- path to image file, default None
+ fill_color -- for svg images, change default fill color
+ default None
+ stroke_color -- for svg images, change default stroke color
+ default None
+ background_color -- draw background or surface will be transparent
+ default None
+ badge_name -- name of icon which will be drawn on top of
+ original image, default None
+ width -- change image width, default None
+ height -- change image height, default None
+ cache -- if image is svg, keep svg file content for later
+ scale -- scale image, default 1.0
+
+ Return: cairo surface or None if image was not found
+
+ """
+ icon = _IconBuffer()
+ for key, value in kwargs.items():
+ icon.__setattr__(key, value)
+ return icon.get_surface()
diff --git a/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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import gtk
+
+
+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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('toggleoption')
+import gtk
+from toggleitem import ToggleItem
+
+
+class RadioItem(ToggleItem):
+ def __init__(self, group, default_value=True,
+ stock_id=None, important=False):
+ ToggleItem.__init__(self, default_value, stock_id, important)
+ self.group = group
+
+ def get_menu_item(self):
+ stock_info = gtk.stock_lookup(self.stock_id)
+ self.menuitem = gtk.RadioMenuItem(self.group.menuitem if self.group !=\
+ None else None, stock_info[1])
+ self.menuitem.set_active(self.default_value)
+ self.menuitem.connect('toggled', self.toggled_cb)
+ self.setup_accelerator()
+ return self.menuitem
+
+ def get_tool_item(self):
+ self.toolitem = gtk.RadioToolButton(self.group.toolitem,
+ 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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('option')
+
+import gobject
+import gtk
+
+from item import Item
+
+
+class SettingsItem(Item):
+ __gsignals__ = {'closed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
+ tuple())}
+
+ def __init__(self, parent=None, stock_id=None, important=False):
+ Item.__init__(self, stock_id, important)
+ self.content = gtk.EventBox()
+ self.parent = parent
+ # For toggleoptions
+ self.active = True
+
+ def do_activate(self):
+ if self.active:
+ if self.tooltip:
+ title = self.tooltip
+ else:
+ title = gtk.stock_lookup(self.stock_id)[1]
+ dialog = gtk.Dialog(title, self.parent)
+ dialog.vbox.pack_start(self.content, True, True)
+ self.content.show()
+ dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)
+ dialog.run()
+ dialog.vbox.remove(self.content)
+ dialog.destroy()
+ self.emit('closed')
diff --git a/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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('option')
+
+import gobject
+from radioitem import RadioItem
+from settingsitem import SettingsItem
+
+
+class SettingsRadioItem(SettingsItem, RadioItem):
+ __gsignals__ = {'toggled': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
+ (gobject.TYPE_BOOLEAN,))}
+
+ def __init__(self, group, default_value=True, parent=None,
+ stock_id=None, important=False):
+ SettingsItem.__init__(self, parent, stock_id, important)
+ RadioItem.__init__(self, group, default_value, stock_id, important)
+
+ def get_menu_item(self):
+ RadioItem.get_menu_item(self)
+ self.menuitem.connect('activate', self.activate_cb)
+ return self.menuitem
+
+ def get_tool_item(self):
+ RadioItem.get_tool_item(self)
+ self.toolitem.connect('clicked', self.activate_cb)
+ return self.toolitem
diff --git a/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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('option')
+
+import gobject
+import gtk
+
+from item import Item
+
+
+class ShortContentItem(Item):
+
+ def __init__(self, parent=None, stock_id=None, important=False):
+ Item.__init__(self, stock_id, important)
+ self.content = gtk.EventBox()
+ self.parent = parent
+ self.separator = None
+
+ def get_tool_item(self):
+ self.toolitem = gtk.ToolItem()
+ self.toolitem.add(self.content)
+ self.setup_tooltip()
+ return self.toolitem
+
+ def do_activate(self):
+ if self.tooltip:
+ title = self.tooltip
+ else:
+ title = gtk.stock_lookup(self.stock_id)[1].replace('_', '')
+ window = gtk.Dialog(title, self.parent)
+ window.set_modal(False)
+ window.set_decorated(True)
+ window.set_has_separator(False)
+ window.set_border_width(10)
+ if self.toolitem:
+ self.toolitem.remove(self.content)
+ if self.separator:
+ self.separator.hide()
+ window.vbox.pack_start(self.content)
+ self.content.show()
+ window.connect('delete-event', self.destroy_window)
+ window.show()
+
+ def destroy_window(self, window, event):
+ window.vbox.remove(self.content)
+ window.destroy()
+ if self.toolitem:
+ self.toolitem.add(self.content)
+ if self.separator:
+ self.separator.show()
diff --git a/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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('toggleoption')
+import gobject
+import gtk
+from item import Item
+
+
+class ToggleItem(Item):
+ __gsignals__ = {'toggled': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
+ (gobject.TYPE_BOOLEAN,))}
+
+ def __init__(self, default_value=True, stock_id=None, important=False):
+ Item.__init__(self, stock_id, important)
+ self.default_value = default_value
+ self.active = default_value
+
+ def get_menu_item(self):
+ stock_info = gtk.stock_lookup(self.stock_id)
+ self.menuitem = gtk.CheckMenuItem(stock_info[1])
+ self.menuitem.set_active(self.default_value)
+ self.menuitem.connect('toggled', self.toggled_cb)
+ self.setup_accelerator()
+ return self.menuitem
+
+ def toggled_cb(self, widget):
+ active = widget.get_active()
+ if self.menuitem:
+ self.menuitem.set_active(active)
+ if self.toolitem:
+ self.toolitem.set_active(active)
+ self.active = active
+ self.emit('toggled', active)
+
+ def get_tool_item(self):
+ self.toolitem = gtk.ToggleToolButton(self._stock_id)
+ self.toolitem.set_active(self.default_value)
+ self.toolitem.connect('toggled', self.toggled_cb)
+ self.setup_tooltip()
+ return self.toolitem
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 <francis@sugarlabs.org>
+#
+# 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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('option')
+
+import gobject
+import gtk
+
+from 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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('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 <francis@sugarlabs.org>
+# Based on helpbutton by Gonzalo Odiard <gonzalo@laptop.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+from gettext import gettext as _
+import gtk
+
+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,
+ '<Ctrl>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('<b>%s</b>' % 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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import gtk
+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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('toggleoption')
+import gtk
+from 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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('option')
+
+import gobject
+import gtk
+
+from item import Item
+
+
+class SettingsItem(Item):
+ __gsignals__ = {'closed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
+ tuple())}
+
+ def __init__(self, parent=None, stock_id=None, important=False):
+ Item.__init__(self, stock_id, important)
+ self.content = gtk.EventBox()
+ self.parent = parent
+ 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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('option')
+
+import gobject
+from radioitem import RadioItem
+from settingsitem import SettingsItem
+
+
+class SettingsRadioItem(SettingsItem, RadioItem):
+ __gsignals__ = {'toggled': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
+ (gobject.TYPE_BOOLEAN,))}
+
+ def __init__(self, group, default_value=True, parent=None,
+ stock_id=None, important=False):
+ SettingsItem.__init__(self, parent, stock_id, important)
+ RadioItem.__init__(self, group, default_value, stock_id, important)
+
+ def get_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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('option')
+
+import gobject
+import gtk
+
+from item import Item
+
+
+class ShortContentItem(Item):
+
+ def __init__(self, parent=None, stock_id=None, important=False):
+ Item.__init__(self, stock_id, important)
+ self.content = gtk.EventBox()
+ self.parent = parent
+ self.separator = None
+
+ def get_tool_item(self):
+ self.toolitem = gtk.ToolItem()
+ self.toolitem.add(self.content)
+ 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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+import logging
+logger = logging.getLogger('toggleoption')
+import gobject
+import gtk
+from 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 <francis@sugarlabs.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+from sugar.graphics.xocolor import *