Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AbiWordActivity.py108
-rw-r--r--icons/list-bullet.svg96
-rw-r--r--icons/list-dashed.svg91
-rw-r--r--icons/list-lower-case.svg97
-rw-r--r--icons/list-none.svg84
-rw-r--r--icons/list-numbered.svg97
-rw-r--r--icons/list-upper-case.svg97
-rw-r--r--icons/search-bar.svg103
-rw-r--r--icons/text-bar.svg65
-rw-r--r--icons/view-bar.svg113
-rw-r--r--port/COPYING340
-rw-r--r--port/README18
-rw-r--r--port/__init__.py0
-rw-r--r--port/chooser.py66
-rw-r--r--toolbar.py530
-rw-r--r--widgets.py242
16 files changed, 1672 insertions, 475 deletions
diff --git a/AbiWordActivity.py b/AbiWordActivity.py
index 9e9ec84..4e5e25e 100644
--- a/AbiWordActivity.py
+++ b/AbiWordActivity.py
@@ -31,73 +31,103 @@ import gtk
import telepathy
import telepathy.client
-from sugar.activity.activity import Activity, ActivityToolbox, EditToolbar
+from sugar.graphics.toolcombobox import ToolComboBox
+from sugar.graphics.toolbar import ToolbarButton, Toolbar
+from sugar.activity import activity
from sugar.presence import presenceservice
from sugar.graphics import style
from abiword import Canvas
import toolbar
-from toolbar import WriteActivityToolbarExtension, WriteEditToolbar, TextToolbar, ImageToolbar, TableToolbar, FormatToolbar, ViewToolbar
+import widgets
from sugar.activity.activity import get_bundle_path
logger = logging.getLogger('write-activity')
-class AbiWordActivity (Activity):
+class AbiWordActivity (activity.Activity):
def __init__ (self, handle):
- Activity.__init__ (self, handle)
+ activity.Activity.__init__ (self, handle)
- # abiword uses the current directory for all its file dialogs
+ # abiword uses the current directory for all its file dialogs
os.chdir(os.path.expanduser('~'))
# create our main abiword canvas
self.abiword_canvas = Canvas()
- self.abiword_canvas.connect('text-selected', self._selection_cb)
- self.abiword_canvas.connect('image-selected', self._selection_cb)
- self.abiword_canvas.connect('selection-cleared', self._selection_cleared_cb)
- # create our toolbars
- toolbox = ActivityToolbox(self)
- self.set_toolbox(toolbox)
- toolbox.show()
+ main_toolbar = Toolbar()
- activity_toolbar_ext = WriteActivityToolbarExtension(self, toolbox, self.abiword_canvas)
+ main_toolbar.top.insert(activity.toolbar(self), 0)
- text_toolbar = TextToolbar(toolbox, self.abiword_canvas)
+ main_toolbar.top.insert(
+ ToolComboBox(widgets.FontCombo(self.abiword_canvas)), -1)
+ main_toolbar.top.insert(
+ ToolComboBox(widgets.FontSizeCombo(self.abiword_canvas)), -1)
- self._edit_toolbar = WriteEditToolbar(toolbox, self.abiword_canvas, text_toolbar)
- toolbox.add_toolbar(_('Edit'), self._edit_toolbar)
- self._edit_toolbar.show()
+ text_toolbar = ToolbarButton(
+ page=toolbar.TextToolbar(self.abiword_canvas),
+ icon_name='text-bar')
+ main_toolbar.top.insert(text_toolbar, -1)
- toolbox.add_toolbar(_('Text'), text_toolbar)
- text_toolbar.show()
+ main_toolbar.top.insert(activity.separator(), -1)
- image_toolbar = ImageToolbar(toolbox, self.abiword_canvas, self)
- toolbox.add_toolbar(_('Image'), image_toolbar)
- image_toolbar.show()
+ undo = activity.undo_button(sensitive=False)
+ undo.connect('clicked', lambda button: self.abiword_canvas.undo())
+ self.abiword_canvas.connect("can-undo", lambda abi, can_undo:
+ undo.set_sensitive(can_undo))
+ main_toolbar.top.insert(undo, -1)
- table_toolbar = TableToolbar(toolbox, self.abiword_canvas)
- toolbox.add_toolbar(_('Table'), table_toolbar)
- table_toolbar.show()
+ redo = activity.redo_button(sensitive=False)
+ redo.connect('clicked', lambda button: self.abiword_canvas.redo())
+ self.abiword_canvas.connect("can-redo", lambda abi, can_redo:
+ redo.set_sensitive(can_redo))
+ main_toolbar.top.insert(redo, -1)
- format_toolbar = FormatToolbar(toolbox, self.abiword_canvas)
- toolbox.add_toolbar(_('Format'), format_toolbar)
- format_toolbar.show()
+ copy = activity.copy_button()
+ copy.connect('clicked', lambda button: self.abiword_canvas.copy())
+ main_toolbar.top.insert(copy, -1)
- view_toolbar = ViewToolbar(self.abiword_canvas)
- toolbox.add_toolbar(_('View'), view_toolbar)
- view_toolbar.show()
+ paste = activity.paste_button()
+ paste.connect('clicked', lambda button: self.abiword_canvas.paste())
+ main_toolbar.top.insert(paste, -1)
- # the text toolbar should be our default toolbar
- toolbox.set_current_toolbar(toolbar.TOOLBAR_TEXT)
+ self.abiword_canvas.connect('text-selected', lambda abi, b:
+ copy.set_sensitive(True))
+ self.abiword_canvas.connect('image-selected', lambda abi, b:
+ copy.set_sensitive(True))
+ self.abiword_canvas.connect('selection-cleared', lambda abi, b:
+ copy.set_sensitive(False))
+
+ main_toolbar.top.insert(activity.separator(), -1)
+
+ insert_toolbar = ToolbarButton(
+ page=toolbar.InsertToolbar(self.abiword_canvas),
+ icon_name='transfer-from')
+ main_toolbar.top.insert(insert_toolbar, -1)
+
+ search_toolbar = ToolbarButton(
+ page=toolbar.SearchToolbar(self.abiword_canvas, main_toolbar),
+ icon_name='search-bar')
+ main_toolbar.top.insert(search_toolbar, -1)
+
+ view_toolbar = ToolbarButton(
+ page=toolbar.ViewToolbar(self.abiword_canvas),
+ icon_name='view-bar')
+ main_toolbar.top.insert(view_toolbar, -1)
+
+ main_toolbar.top.insert(activity.expander(), -1)
+ main_toolbar.top.insert(activity.stop_button(self), -1)
+
+ main_toolbar.show_all()
+ self.set_toolbox(main_toolbar)
self.set_canvas(self.abiword_canvas)
- self.abiword_canvas.connect_after('map-event', self._map_event_cb)
+ #self.abiword_canvas.connect_after('map-event', self._map_event_cb)
self.abiword_canvas.show()
def _map_event_cb(self, event, activity):
logger.debug('_map_event_cb')
-
+
# set custom keybindings for Write
logger.debug("Loading keybindings")
keybindings_file = os.path.join( get_bundle_path(), "keybindings.xml" )
@@ -141,7 +171,7 @@ class AbiWordActivity (Activity):
def get_preview(self):
if not hasattr(self.abiword_canvas, 'render_page_to_image'):
- return Activity.get_preview(self)
+ return activity.Activity.get_preview(self)
pixbuf = self.abiword_canvas.render_page_to_image(1)
pixbuf = pixbuf.scale_simple(style.zoom(300), style.zoom(225),
@@ -349,9 +379,3 @@ class AbiWordActivity (Activity):
self.metadata['fulltext'] = self.abiword_canvas.get_content(extension_or_mimetype=".txt")[:3000]
self.abiword_canvas.save('file://' + file_path, actual_mimetype, '');
-
- def _selection_cb(self, abi, b):
- self._edit_toolbar.copy.set_sensitive(True)
-
- def _selection_cleared_cb(self, abi, b):
- self._edit_toolbar.copy.set_sensitive(False)
diff --git a/icons/list-bullet.svg b/icons/list-bullet.svg
new file mode 100644
index 0000000..02f1981
--- /dev/null
+++ b/icons/list-bullet.svg
@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<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"
+ enable-background="new 0 0 55 54.696"
+ height="54.696px"
+ version="1.1"
+ viewBox="0 0 55 54.696"
+ width="55px"
+ x="0px"
+ xml:space="preserve"
+ y="0px"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="list-bullet.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
+ id="metadata16"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs14"><inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 27.348 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="55 : 27.348 : 1"
+ inkscape:persp3d-origin="27.5 : 18.232 : 1"
+ id="perspective18" /></defs><sodipodi:namedview
+ inkscape:window-height="655"
+ inkscape:window-width="1278"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.40392157"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#909090"
+ id="base"
+ showgrid="false"
+ inkscape:zoom="8.4101215"
+ inkscape:cx="6.6917394"
+ inkscape:cy="27.348"
+ inkscape:window-x="0"
+ inkscape:window-y="16"
+ inkscape:current-layer="svg2" /><g
+ display="block"
+ id="format-justify-fill"
+ transform="translate(7.4268516,0)"
+ style="display:block">
+ <line
+ display="inline"
+ x1="11.737"
+ x2="42.737"
+ y1="14.47"
+ y2="14.47"
+ id="line5"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" />
+ <line
+ display="inline"
+ x1="11.737"
+ x2="42.737"
+ y1="23.056"
+ y2="23.056"
+ id="line7"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" />
+ <line
+ display="inline"
+ x1="11.737"
+ x2="42.737"
+ y1="31.641001"
+ y2="31.641001"
+ id="line9"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" />
+ <line
+ display="inline"
+ x1="11.737"
+ x2="42.737"
+ y1="40.227001"
+ y2="40.227001"
+ id="line11"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" />
+</g><path
+ sodipodi:type="arc"
+ style="fill:#f5f5f5;fill-opacity:0.96078431;fill-rule:evenodd;stroke:#f5f5f5;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.96078431"
+ id="path2383"
+ sodipodi:cx="-26.694027"
+ sodipodi:cy="23.126896"
+ sodipodi:rx="2.7942522"
+ sodipodi:ry="2.7942522"
+ d="M -23.899775,23.126896 A 2.7942522,2.7942522 0 1 1 -29.488279,23.126896 A 2.7942522,2.7942522 0 1 1 -23.899775,23.126896 z"
+ transform="matrix(0.87874,0,0,0.8787401,31.083695,-2.2657739)" /></svg> \ No newline at end of file
diff --git a/icons/list-dashed.svg b/icons/list-dashed.svg
new file mode 100644
index 0000000..7ee2ca8
--- /dev/null
+++ b/icons/list-dashed.svg
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<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"
+ enable-background="new 0 0 55 54.696"
+ height="54.696px"
+ version="1.1"
+ viewBox="0 0 55 54.696"
+ width="55px"
+ x="0px"
+ xml:space="preserve"
+ y="0px"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="list-dashed.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
+ id="metadata16"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs14"><inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 27.348 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="55 : 27.348 : 1"
+ inkscape:persp3d-origin="27.5 : 18.232 : 1"
+ id="perspective18" />
+
+
+
+
+</defs><sodipodi:namedview
+ inkscape:window-height="655"
+ inkscape:window-width="1278"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.40392157"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#909090"
+ id="base"
+ showgrid="false"
+ inkscape:zoom="8.4101215"
+ inkscape:cx="6.6917394"
+ inkscape:cy="27.348"
+ inkscape:window-x="0"
+ inkscape:window-y="32"
+ inkscape:current-layer="svg2" /><g
+ id="g3212"><line
+ display="inline"
+ x1="19.163853"
+ x2="50.163853"
+ y1="14.47"
+ y2="14.47"
+ id="line5"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" /><line
+ display="inline"
+ x1="19.163853"
+ x2="50.163853"
+ y1="23.056"
+ y2="23.056"
+ id="line7"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" /><line
+ display="inline"
+ x1="19.163853"
+ x2="50.163853"
+ y1="31.641001"
+ y2="31.641001"
+ id="line9"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" /><line
+ display="inline"
+ x1="19.163853"
+ x2="50.163853"
+ y1="40.227001"
+ y2="40.227001"
+ id="line11"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" /></g><line
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;display:inline"
+ id="line3210"
+ y2="18.90579"
+ y1="18.90579"
+ x2="13.115018"
+ x1="3.0559719"
+ display="inline" /></svg> \ No newline at end of file
diff --git a/icons/list-lower-case.svg b/icons/list-lower-case.svg
new file mode 100644
index 0000000..8857a7c
--- /dev/null
+++ b/icons/list-lower-case.svg
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<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"
+ enable-background="new 0 0 55 54.696"
+ height="54.696px"
+ version="1.1"
+ viewBox="0 0 55 54.696"
+ width="55px"
+ x="0px"
+ xml:space="preserve"
+ y="0px"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="list-lower-case.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
+ id="metadata16"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs14"><inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 27.348 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="55 : 27.348 : 1"
+ inkscape:persp3d-origin="27.5 : 18.232 : 1"
+ id="perspective18" /></defs><sodipodi:namedview
+ inkscape:window-height="655"
+ inkscape:window-width="1278"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.40392157"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#909090"
+ id="base"
+ showgrid="false"
+ inkscape:zoom="8.4101215"
+ inkscape:cx="27.5"
+ inkscape:cy="27.348"
+ inkscape:window-x="0"
+ inkscape:window-y="48"
+ inkscape:current-layer="svg2" /><g
+ display="block"
+ id="format-justify-fill"
+ transform="translate(7.4268516,0)"
+ style="display:block">
+ <line
+ display="inline"
+ x1="11.737"
+ x2="42.737"
+ y1="14.47"
+ y2="14.47"
+ id="line5"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" />
+ <line
+ display="inline"
+ x1="11.737"
+ x2="42.737"
+ y1="23.056"
+ y2="23.056"
+ id="line7"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" />
+ <line
+ display="inline"
+ x1="11.737"
+ x2="42.737"
+ y1="31.641001"
+ y2="31.641001"
+ id="line9"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" />
+ <line
+ display="inline"
+ x1="11.737"
+ x2="42.737"
+ y1="40.227001"
+ y2="40.227001"
+ id="line11"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" />
+</g><text
+ xml:space="preserve"
+ style="font-size:18px;font-style:normal;font-weight:bold;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+ x="2.615895"
+ y="23.543058"
+ id="text3778"><tspan
+ sodipodi:role="line"
+ id="tspan3780"
+ x="2.615895"
+ y="23.543058">a</tspan></text>
+</svg> \ No newline at end of file
diff --git a/icons/list-none.svg b/icons/list-none.svg
new file mode 100644
index 0000000..91ffebe
--- /dev/null
+++ b/icons/list-none.svg
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<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"
+ enable-background="new 0 0 55 54.696"
+ height="54.696px"
+ version="1.1"
+ viewBox="0 0 55 54.696"
+ width="55px"
+ x="0px"
+ xml:space="preserve"
+ y="0px"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="list-none.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
+ id="metadata16"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs14"><inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 27.348 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="55 : 27.348 : 1"
+ inkscape:persp3d-origin="27.5 : 18.232 : 1"
+ id="perspective18" />
+
+
+
+
+</defs><sodipodi:namedview
+ inkscape:window-height="719"
+ inkscape:window-width="1278"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.40392157"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#909090"
+ id="base"
+ showgrid="false"
+ inkscape:zoom="8.4101215"
+ inkscape:cx="24.745584"
+ inkscape:cy="27.348"
+ inkscape:window-x="0"
+ inkscape:window-y="16"
+ inkscape:current-layer="svg2" /><g
+ id="g3162"><line
+ display="inline"
+ x1="5.3933511"
+ x2="49.428024"
+ y1="14.47"
+ y2="14.47"
+ id="line5"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.50000005;display:inline;stroke-miterlimit:4;stroke-dasharray:none" /><line
+ display="inline"
+ x1="5.3933511"
+ x2="49.428024"
+ y1="23.056"
+ y2="23.056"
+ id="line7"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.50000005;display:inline;stroke-miterlimit:4;stroke-dasharray:none" /><line
+ display="inline"
+ x1="5.3933511"
+ x2="49.428024"
+ y1="31.641001"
+ y2="31.641001"
+ id="line9"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.50000005;display:inline;stroke-miterlimit:4;stroke-dasharray:none" /><line
+ display="inline"
+ x1="5.3933511"
+ x2="49.428024"
+ y1="40.227001"
+ y2="40.227001"
+ id="line11"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.50000005;display:inline;stroke-miterlimit:4;stroke-dasharray:none" /></g></svg> \ No newline at end of file
diff --git a/icons/list-numbered.svg b/icons/list-numbered.svg
new file mode 100644
index 0000000..acd0b51
--- /dev/null
+++ b/icons/list-numbered.svg
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<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"
+ enable-background="new 0 0 55 54.696"
+ height="54.696px"
+ version="1.1"
+ viewBox="0 0 55 54.696"
+ width="55px"
+ x="0px"
+ xml:space="preserve"
+ y="0px"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="list-numbered.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
+ id="metadata16"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs14"><inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 27.348 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="55 : 27.348 : 1"
+ inkscape:persp3d-origin="27.5 : 18.232 : 1"
+ id="perspective18" /></defs><sodipodi:namedview
+ inkscape:window-height="655"
+ inkscape:window-width="1278"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.40392157"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#909090"
+ id="base"
+ showgrid="false"
+ inkscape:zoom="8.4101215"
+ inkscape:cx="27.5"
+ inkscape:cy="27.348"
+ inkscape:window-x="0"
+ inkscape:window-y="64"
+ inkscape:current-layer="svg2" /><g
+ display="block"
+ id="format-justify-fill"
+ transform="translate(7.4268516,0)"
+ style="display:block">
+ <line
+ display="inline"
+ x1="11.737"
+ x2="42.737"
+ y1="14.47"
+ y2="14.47"
+ id="line5"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" />
+ <line
+ display="inline"
+ x1="11.737"
+ x2="42.737"
+ y1="23.056"
+ y2="23.056"
+ id="line7"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" />
+ <line
+ display="inline"
+ x1="11.737"
+ x2="42.737"
+ y1="31.641001"
+ y2="31.641001"
+ id="line9"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" />
+ <line
+ display="inline"
+ x1="11.737"
+ x2="42.737"
+ y1="40.227001"
+ y2="40.227001"
+ id="line11"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" />
+</g><text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:bold;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+ x="1.7835652"
+ y="24.969913"
+ id="text3845"><tspan
+ sodipodi:role="line"
+ id="tspan3847"
+ x="1.7835652"
+ y="24.969913">1</tspan></text>
+</svg> \ No newline at end of file
diff --git a/icons/list-upper-case.svg b/icons/list-upper-case.svg
new file mode 100644
index 0000000..d94c2dc
--- /dev/null
+++ b/icons/list-upper-case.svg
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<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"
+ enable-background="new 0 0 55 54.696"
+ height="54.696px"
+ version="1.1"
+ viewBox="0 0 55 54.696"
+ width="55px"
+ x="0px"
+ xml:space="preserve"
+ y="0px"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="list-upper-case.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
+ id="metadata16"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs14"><inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 27.348 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="55 : 27.348 : 1"
+ inkscape:persp3d-origin="27.5 : 18.232 : 1"
+ id="perspective18" /></defs><sodipodi:namedview
+ inkscape:window-height="655"
+ inkscape:window-width="1278"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.40392157"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#909090"
+ id="base"
+ showgrid="false"
+ inkscape:zoom="8.4101215"
+ inkscape:cx="27.5"
+ inkscape:cy="27.348"
+ inkscape:window-x="0"
+ inkscape:window-y="80"
+ inkscape:current-layer="svg2" /><g
+ display="block"
+ id="format-justify-fill"
+ transform="translate(7.4268516,0)"
+ style="display:block">
+ <line
+ display="inline"
+ x1="11.737"
+ x2="42.737"
+ y1="14.47"
+ y2="14.47"
+ id="line5"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" />
+ <line
+ display="inline"
+ x1="11.737"
+ x2="42.737"
+ y1="23.056"
+ y2="23.056"
+ id="line7"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" />
+ <line
+ display="inline"
+ x1="11.737"
+ x2="42.737"
+ y1="31.641001"
+ y2="31.641001"
+ id="line9"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" />
+ <line
+ display="inline"
+ x1="11.737"
+ x2="42.737"
+ y1="40.227001"
+ y2="40.227001"
+ id="line11"
+ style="fill:#4c4d4f;stroke:#ffffff;stroke-width:3.5;display:inline" />
+</g><text
+ xml:space="preserve"
+ style="font-size:16px;font-style:normal;font-weight:bold;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
+ x="2.2591829"
+ y="24.494293"
+ id="text3912"><tspan
+ sodipodi:role="line"
+ id="tspan3914"
+ x="2.2591829"
+ y="24.494293">A</tspan></text>
+</svg> \ No newline at end of file
diff --git a/icons/search-bar.svg b/icons/search-bar.svg
new file mode 100644
index 0000000..34e4d5e
--- /dev/null
+++ b/icons/search-bar.svg
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 13.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
+<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"
+ version="1.0"
+ id="Layer_1"
+ x="0px"
+ y="0px"
+ width="22.16"
+ height="22.156"
+ viewBox="0 0 22.156 22.156"
+ enable-background="new 0 0 22.156 22.156"
+ xml:space="preserve"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="white-search.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
+ id="metadata12"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs10"><inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 11.078 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="22.156 : 11.078 : 1"
+ inkscape:persp3d-origin="11.078 : 7.3853334 : 1"
+ id="perspective14" /></defs><sodipodi:namedview
+ inkscape:window-height="719"
+ inkscape:window-width="1278"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.90196078"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#a1a1a1"
+ id="base"
+ showgrid="false"
+ inkscape:zoom="41.704278"
+ inkscape:cx="17.054208"
+ inkscape:cy="9.0460986"
+ inkscape:window-x="0"
+ inkscape:window-y="16"
+ inkscape:current-layer="g3"
+ borderlayer="false"
+ showborder="true"
+ inkscape:showpageshadow="true" />
+<g
+ id="g3">
+
+ <rect
+ style="opacity:1;fill:#ffffff;fill-opacity:0.9;fill-rule:nonzero;stroke:#3f3f3f;stroke-width:0.99999964;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.9"
+ id="rect3245"
+ width="3.1647084"
+ height="7.8021984"
+ x="-1.8735586"
+ y="20.465549"
+ transform="matrix(0.6999914,-0.7141512,0.7141512,0.6999914,0,0)" /><path
+ d="M 2.7712159,9.5270226 C 2.7712159,13.233154 5.7738562,16.23849 9.480886,16.23849 C 13.183424,16.23849 16.188759,13.233154 16.188759,9.5270226 C 16.188759,5.8226881 13.183424,2.8191494 9.480886,2.8191494 C 5.7738562,2.8191494 2.7712159,5.8226881 2.7712159,9.5270226 z"
+ id="path5"
+ style="fill:none;stroke:#ffffff;stroke-width:3.14459634000000010;stroke-opacity:0.89999998" />
+<path
+ sodipodi:type="arc"
+ style="opacity:1;fill:#ffffff;fill-opacity:0;fill-rule:nonzero;stroke:#3f3f3f;stroke-width:0.37737417000000001;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.89999998"
+ id="path3758"
+ sodipodi:cx="-5.7568049"
+ sodipodi:cy="10.262735"
+ sodipodi:rx="3.2130997"
+ sodipodi:ry="2.8774025"
+ d="M -2.5437052,10.262735 A 3.2130997,2.8774025 0 1 1 -8.9699047,10.262735 A 3.2130997,2.8774025 0 1 1 -2.5437052,10.262735 z"
+ transform="matrix(2.5076449,0,0,2.8002036,23.987204,-19.204298)" /><path
+ sodipodi:type="arc"
+ style="opacity:1;fill:#ffffff;fill-opacity:0;fill-rule:nonzero;stroke:#3f3f3f;stroke-width:0.6316339;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.90000000000000002"
+ id="path3760"
+ sodipodi:cx="-5.7568049"
+ sodipodi:cy="10.262735"
+ sodipodi:rx="3.2130997"
+ sodipodi:ry="2.8774025"
+ d="M -2.5437052,10.262735 A 3.2130997,2.8774025 0 1 1 -8.9699047,10.262735 A 3.2130997,2.8774025 0 1 1 -2.5437052,10.262735 z"
+ transform="matrix(1.4982103,0,0,1.6730016,18.022419,-7.6261876)" /><path
+ sodipodi:type="arc"
+ style="opacity:1;fill:#ffffff;fill-opacity:0;fill-rule:nonzero;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:0.9"
+ id="path2385"
+ sodipodi:cx="-18.08168"
+ sodipodi:cy="9.3755369"
+ sodipodi:rx="1.9182684"
+ sodipodi:ry="1.7024633"
+ d="M -16.163412,9.3755369 A 1.9182684,1.7024633 0 0 1 -17.387355,10.962566"
+ sodipodi:start="0"
+ sodipodi:end="1.2004329"
+ sodipodi:open="true" /><path
+ style="fill:none;fill-rule:evenodd;stroke:#f5f5f5;stroke-width:2.15084887px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.96078431"
+ d="M 14.456402,14.583424 L 16.158553,16.233575"
+ id="path3163"
+ inkscape:connector-type="polyline" /></g>
+</svg> \ No newline at end of file
diff --git a/icons/text-bar.svg b/icons/text-bar.svg
new file mode 100644
index 0000000..9890d97
--- /dev/null
+++ b/icons/text-bar.svg
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<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"
+ enable-background="new 0 0 55 54.696"
+ height="54.696px"
+ version="1.1"
+ viewBox="0 0 55 54.696"
+ width="55px"
+ x="0px"
+ xml:space="preserve"
+ y="0px"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ sodipodi:docname="text-bar.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"><metadata
+ id="metadata22"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
+ id="defs20"><inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 27.348 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="55 : 27.348 : 1"
+ inkscape:persp3d-origin="27.5 : 18.232 : 1"
+ id="perspective24" />
+
+
+
+
+ </defs><sodipodi:namedview
+ inkscape:window-height="719"
+ inkscape:window-width="1278"
+ inkscape:pageshadow="2"
+ inkscape:pageopacity="0.96078431"
+ guidetolerance="10.0"
+ gridtolerance="10.0"
+ objecttolerance="10.0"
+ borderopacity="1.0"
+ bordercolor="#666666"
+ pagecolor="#767676"
+ id="base"
+ showgrid="false"
+ inkscape:zoom="8.4101215"
+ inkscape:cx="27.5"
+ inkscape:cy="27.348"
+ inkscape:window-x="0"
+ inkscape:window-y="16"
+ inkscape:current-layer="svg2" /><g
+ id="g7"
+ transform="matrix(1.3315067,0,0,1.3315067,-22.643371,-8.8784595)">
+ <g
+ id="g9">
+ <path
+ d="M 25.263,12.435 L 49.919,12.435 L 49.919,15.997 L 39.575,15.997 L 39.575,41.59 L 35.606,41.59 L 35.606,15.996 L 25.263,15.996 L 25.263,12.435 z"
+ id="path11"
+ style="fill:#ffffff" />
+ </g>
+ </g></svg> \ No newline at end of file
diff --git a/icons/view-bar.svg b/icons/view-bar.svg
new file mode 100644
index 0000000..f97d45c
--- /dev/null
+++ b/icons/view-bar.svg
@@ -0,0 +1,113 @@
+<?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="42"
+ height="42"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.46"
+ version="1.0"
+ sodipodi:docbase="/home/msgodoi/olpc/workspace/Memorize.activity/images"
+ sodipodi:docname="view.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape">
+ <defs
+ id="defs4">
+ <inkscape:perspective
+ sodipodi:type="inkscape:persp3d"
+ inkscape:vp_x="0 : 21 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_z="42 : 21 : 1"
+ inkscape:persp3d-origin="21 : 14 : 1"
+ id="perspective15" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="11.313708"
+ inkscape:cx="8.5837325"
+ inkscape:cy="21"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ width="42px"
+ height="42px"
+ inkscape:window-width="1278"
+ inkscape:window-height="719"
+ inkscape:window-x="0"
+ inkscape:window-y="16"
+ showgrid="false" />
+ <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" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <g
+ id="g3164"
+ transform="translate(0.4419419,0.3535535)">
+ <path
+ style="fill:#ffffff;fill-opacity:1;stroke:#404040;stroke-width:2.5;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;overflow:visible"
+ id="path9"
+ d="M 20.673867,9.8120817 C 10.544487,9.8120817 2.1236741,19.900784 2.1236741,19.900784 C 2.1236741,19.900784 10.544487,30.030166 20.673867,30.030166 C 30.803249,30.030166 39.22406,19.860104 39.22406,19.860104 C 39.22406,19.860104 30.803249,9.8120817 20.673867,9.8120817 z M 20.673867,27.019828 C 16.768563,27.019828 13.554823,23.846769 13.554823,19.900784 C 13.554823,15.99548 16.727884,12.781739 20.673867,12.781739 C 24.579169,12.781739 27.79291,15.954799 27.79291,19.900784 C 27.79291,23.846769 24.619851,27.019828 20.673867,27.019828 z"
+ class="st0" />
+ <circle
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.00508869;stroke-opacity:1;overflow:visible"
+ sodipodi:ry="7.9000001"
+ sodipodi:rx="7.9000001"
+ sodipodi:cy="25.4"
+ sodipodi:cx="46.299999"
+ id="circle2386"
+ r="7.9000001"
+ cy="25.4"
+ cx="46.299999"
+ class="st0"
+ transform="matrix(1.192052,-3.4298789e-3,4.2357937e-3,1.0633869,-34.174785,-6.9925058)" />
+ </g>
+ <circle
+ transform="matrix(0.8617299,0,0,0.8617299,-18.746536,-1.4676242)"
+ class="st0"
+ cx="46.299999"
+ cy="25.4"
+ r="7.9000001"
+ id="circle2362"
+ sodipodi:cx="46.299999"
+ sodipodi:cy="25.4"
+ sodipodi:rx="7.9000001"
+ sodipodi:ry="7.9000001"
+ style="fill:#404040;fill-opacity:1;stroke:#404040;stroke-width:0.44639024;stroke-opacity:1;overflow:visible" />
+ <circle
+ transform="matrix(0.4165405,0,0,0.4165405,1.8602249,9.8155409)"
+ class="st0"
+ cx="46.299999"
+ cy="25.4"
+ r="7.9000001"
+ id="circle11"
+ sodipodi:cx="46.299999"
+ sodipodi:cy="25.4"
+ sodipodi:rx="7.9000001"
+ sodipodi:ry="7.9000001"
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.00508869;stroke-opacity:1;overflow:visible" />
+ </g>
+</svg>
diff --git a/port/COPYING b/port/COPYING
new file mode 100644
index 0000000..623b625
--- /dev/null
+++ b/port/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/port/README b/port/README
new file mode 100644
index 0000000..256200d
--- /dev/null
+++ b/port/README
@@ -0,0 +1,18 @@
+About
+-----
+
+A set of sugar components/libraries/etc to simplify writing activities.
+
+Cornerstone purposes for this project:
+* Total backwards compatibility for sugar-port API
+* Run on all sugar platforms beginning from 0.82
+
+In most cases sugar-port could be embedded to activity's directory tree.
+There is no need to include the whole sugar-port project only top level
+files/directories you are using directly - sugar-port's top level entities
+don't import each other.
+
+Get it
+------
+
+http://wiki.sugarlabs.org/go/Development_Team/sugar-port
diff --git a/port/__init__.py b/port/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/port/__init__.py
diff --git a/port/chooser.py b/port/chooser.py
new file mode 100644
index 0000000..e2df259
--- /dev/null
+++ b/port/chooser.py
@@ -0,0 +1,66 @@
+# 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
+
+"""Object chooser method"""
+
+import gtk
+import logging
+
+from sugar import mime
+from sugar.graphics.objectchooser import ObjectChooser
+
+TEXT = hasattr(mime, 'GENERIC_TYPE_TEXT') and mime.GENERIC_TYPE_TEXT or None
+IMAGE = hasattr(mime, 'GENERIC_TYPE_IMAGE') and mime.GENERIC_TYPE_IMAGE or None
+AUDIO = hasattr(mime, 'GENERIC_TYPE_AUDIO') and mime.GENERIC_TYPE_AUDIO or None
+VIDEO = hasattr(mime, 'GENERIC_TYPE_VIDEO') and mime.GENERIC_TYPE_VIDEO or None
+LINK = hasattr(mime, 'GENERIC_TYPE_LINK') and mime.GENERIC_TYPE_LINK or None
+
+def pick(cb=None, default=None, parent=None, what=None):
+ """
+ Opens object chooser.
+
+ Method returns:
+
+ * cb(jobject), if object was choosen and cb is not None
+ * jobject, if object was choosen and cb is None
+ * default, otherwise
+
+ NOTE: 'what' makes sense only for sugar >= 0.84
+ """
+ what = what and {'what_filter': what} or {}
+ chooser = ObjectChooser(parent=parent, **what)
+
+ jobject = None
+ out = None
+
+ try:
+ if chooser.run() == gtk.RESPONSE_ACCEPT:
+ jobject = chooser.get_selected_object()
+ logging.debug('ObjectChooser: %r' % jobject)
+
+ if jobject and jobject.file_path:
+ if cb:
+ out = cb(jobject)
+ else:
+ out = jobject
+ finally:
+ if jobject and id(jobject) != id(out):
+ jobject.destroy()
+ chooser.destroy()
+ del chooser
+
+ if out:
+ return out
+ else:
+ return default
diff --git a/toolbar.py b/toolbar.py
index 5625adc..09d8ccb 100644
--- a/toolbar.py
+++ b/toolbar.py
@@ -15,41 +15,36 @@
# 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 gettext import gettext as _
import logging
import os
import time
+import dbus
import abiword
import gtk
+from sugar.graphics.radiopalette import RadioMenuButton
from sugar.graphics.icon import Icon
from sugar.graphics.toolbutton import ToolButton
from sugar.graphics.toggletoolbutton import ToggleToolButton
-from sugar.graphics.combobox import ComboBox
from sugar.graphics.colorbutton import ColorToolButton
from sugar.graphics.toolcombobox import ToolComboBox
from sugar.graphics.objectchooser import ObjectChooser
from sugar.graphics import iconentry
-from sugar.activity.activity import ActivityToolbar
-from sugar.activity.activity import EditToolbar
+from sugar.activity import activity
from sugar.graphics.menuitem import MenuItem
+from sugar.graphics.palette import Palette
from sugar.datastore import datastore
from sugar import mime
+from port import chooser
import sugar.profile
-import dbus
+import widgets
logger = logging.getLogger('write-activity')
-#ick
-TOOLBAR_ACTIVITY = 0
-TOOLBAR_EDIT = 1
-TOOLBAR_TEXT = 2
-TOOLBAR_IMAGE = 3
-TOOLBAR_TABLE = 4
-TOOLBAR_VIEW = 5
-
class WriteActivityToolbarExtension:
# file mime type, abiword exporter properties, drop down name, journal entry postfix
@@ -108,33 +103,15 @@ class WriteActivityToolbarExtension:
fileObject.destroy()
del fileObject
-class WriteEditToolbar(EditToolbar):
+class SearchToolbar(gtk.Toolbar):
- def __init__(self, toolbox, abiword_canvas, text_toolbar):
+ def __init__(self, abiword_canvas, text_toolbar):
- EditToolbar.__init__(self)
+ gtk.Toolbar.__init__(self)
- self._toolbox = toolbox
self._abiword_canvas = abiword_canvas
self._text_toolbar = text_toolbar
- # connect existing buttons
- self.undo.set_sensitive(False)
- self.redo.set_sensitive(False)
- self.undo.connect('clicked', self._undo_cb)
- self.redo.connect('clicked', self._redo_cb)
- self.copy.connect('clicked', self._copy_cb)
- self.paste.connect('clicked', self._paste_cb)
- self._abiword_canvas.connect("can-undo", self._can_undo_cb)
- self._abiword_canvas.connect("can-redo", self._can_redo_cb)
-
- # make expanded non-drawn visible separator to make the search stuff right-align
- separator = gtk.SeparatorToolItem()
- separator.props.draw = False
- separator.set_expand(True)
- self.insert(separator, -1)
- separator.show()
-
# setup the search options
self._search_entry = iconentry.IconEntry()
self._search_entry.set_icon_from_name(iconentry.ICON_ENTRY_PRIMARY,
@@ -164,38 +141,18 @@ class WriteEditToolbar(EditToolbar):
self._findprev.set_sensitive(False)
self._findnext.set_sensitive(False)
- def _undo_cb(self, button):
- self._abiword_canvas.undo()
-
- def _redo_cb(self, button):
- self._abiword_canvas.redo()
-
- def _copy_cb(self, button):
- self._abiword_canvas.copy()
-
- def _paste_cb(self, button):
- self._abiword_canvas.paste()
-
- def _can_undo_cb(self, canvas, can_undo):
- self.undo.set_sensitive(can_undo)
-
- def _can_redo_cb(self, canvas, can_redo):
- self.redo.set_sensitive(can_redo)
-
def _search_entry_activated_cb(self, entry):
logger.debug('_search_entry_activated_cb')
if not self._search_entry.props.text:
return
# find the next entry
- id = self._text_toolbar.get_text_selected_handler();
- self._abiword_canvas.handler_block(id)
self._abiword_canvas.find_next(False)
- self._abiword_canvas.handler_unblock(id)
def _search_entry_changed_cb(self, entry):
- logger.debug('_search_entry_changed_cb search for \'%s\'', self._search_entry.props.text)
-
+ logger.debug('_search_entry_changed_cb search for \'%s\'',
+ self._search_entry.props.text)
+
if not self._search_entry.props.text:
self._search_entry.activate()
# set the button contexts
@@ -210,28 +167,19 @@ class WriteEditToolbar(EditToolbar):
self._findnext.set_sensitive(True)
# immediately start seaching
- id = self._text_toolbar.get_text_selected_handler();
- self._abiword_canvas.handler_block(id)
self._abiword_canvas.find_next(True)
- self._abiword_canvas.handler_unblock(id)
def _findprev_cb(self, button):
logger.debug('_findprev_cb')
if self._search_entry.props.text:
- id = self._text_toolbar.get_text_selected_handler();
- self._abiword_canvas.handler_block(id)
self._abiword_canvas.find_prev()
- self._abiword_canvas.handler_unblock(id)
else:
logger.debug('nothing to search for!')
def _findnext_cb(self, button):
logger.debug('_findnext_cb')
if self._search_entry.props.text:
- id = self._text_toolbar.get_text_selected_handler();
- self._abiword_canvas.handler_block(id)
self._abiword_canvas.find_next(False)
- self._abiword_canvas.handler_unblock(id)
else:
logger.debug('nothing to search for!')
@@ -246,298 +194,15 @@ class WriteEditToolbar(EditToolbar):
self.insert(tool_item, -1)
tool_item.show()
-class TextToolbar(gtk.Toolbar):
- _ACTION_ALIGNMENT_LEFT = 0
- _ACTION_ALIGNMENT_CENTER = 1
- _ACTION_ALIGNMENT_RIGHT = 2
- _ACTION_ALIGNMENT_JUSTIFY = 3
-
- def __init__(self, toolbox, abiword_canvas):
- self._colorseldlg = None
-
- gtk.Toolbar.__init__(self)
-
- self._toolbox = toolbox
- self._abiword_canvas = abiword_canvas
-
- self._bold = ToggleToolButton('format-text-bold')
- self._bold.set_tooltip(_('Bold'))
- self._bold_id = self._bold.connect('clicked', self._bold_cb)
- self._abiword_canvas.connect('bold', self._isBold_cb)
- self.insert(self._bold, -1)
- self._bold.show()
-
- self._italic = ToggleToolButton('format-text-italic')
- self._italic.set_tooltip(_('Italic'))
- self._italic_id = self._italic.connect('clicked', self._italic_cb)
- self._abiword_canvas.connect('italic', self._isItalic_cb)
- self.insert(self._italic, -1)
- self._italic.show()
-
- self._underline = ToggleToolButton('format-text-underline')
- self._underline.set_tooltip(_('Underline'))
- self._underline_id = self._underline.connect('clicked', self._underline_cb)
- self._abiword_canvas.connect('underline', self._isUnderline_cb)
- self.insert(self._underline, -1)
- self._underline.show()
-
- self._text_color = ColorToolButton()
- self._text_color_id = self._text_color.connect('color-set', self._text_color_cb)
- tool_item = gtk.ToolItem()
- tool_item.add(self._text_color)
- self.insert(tool_item, -1)
- tool_item.show_all()
-
- separator = gtk.SeparatorToolItem()
- separator.set_draw(True)
- separator.show()
- self.insert(separator, -1)
-
- self._font_size_icon = Icon(icon_name="format-text-size", icon_size=gtk.ICON_SIZE_LARGE_TOOLBAR)
- tool_item = gtk.ToolItem()
- tool_item.add(self._font_size_icon)
- self.insert(tool_item, -1)
- tool_item.show_all()
-
- self._font_size_combo = ComboBox()
- self._font_sizes = ['8', '9', '10', '11', '12', '14', '16', '20', '22', '24', '26', '28', '36', '48', '72']
- self._font_size_changed_id = self._font_size_combo.connect('changed', self._font_size_changed_cb)
- for i, s in enumerate(self._font_sizes):
- self._font_size_combo.append_item(i, s, None)
- if s == '12':
- self._font_size_combo.set_active(i)
- tool_item = ToolComboBox(self._font_size_combo)
- self.insert(tool_item, -1);
- tool_item.show()
-
- self._has_custom_fonts = False
-
- self._font_combo = ComboBox()
- self._fonts = sorted(self._abiword_canvas.get_font_names())
- self._fonts_changed_id = self._font_combo.connect('changed', self._font_changed_cb)
- for i, f in enumerate(self._fonts):
- self._font_combo.append_item(i, f, None)
- if f == 'Times New Roman':
- self._font_combo.set_active(i)
- tool_item = ToolComboBox(self._font_combo)
- self.insert(tool_item, -1);
- tool_item.show()
-
- separator = gtk.SeparatorToolItem()
- separator.set_draw(True)
- self.insert(separator, -1)
- separator.show()
-
- self._alignment = ComboBox()
- self._alignment.append_item(self._ACTION_ALIGNMENT_LEFT, None,
- 'format-justify-left')
- self._alignment.append_item(self._ACTION_ALIGNMENT_CENTER, None,
- 'format-justify-center')
- self._alignment.append_item(self._ACTION_ALIGNMENT_RIGHT, None,
- 'format-justify-right')
- self._alignment.append_item(self._ACTION_ALIGNMENT_JUSTIFY, None,
- 'format-justify-fill')
- self._alignment_changed_id = \
- self._alignment.connect('changed', self._alignment_changed_cb)
- tool_item = ToolComboBox(self._alignment)
- self.insert(tool_item, -1);
- tool_item.show()
-
- self._abiword_canvas.connect('color', self._color_cb)
-
- self._abiword_canvas.connect('font-size', self._font_size_cb)
- self._abiword_canvas.connect('font-family', self._font_family_cb)
-
- self._abiword_canvas.connect('left-align', self._isLeftAlign_cb)
- self._abiword_canvas.connect('center-align', self._isCenterAlign_cb)
- self._abiword_canvas.connect('right-align', self._isRightAlign_cb)
- self._abiword_canvas.connect('justify-align', self._isJustifyAlign_cb)
-
- self._text_selected_handler = self._abiword_canvas.connect('text-selected', self._text_selected_cb)
-
- def get_text_selected_handler(self):
- return self._text_selected_handler
-
- def _add_widget(self, widget, expand=False):
- tool_item = gtk.ToolItem()
- tool_item.set_expand(expand)
-
- tool_item.add(widget)
- widget.show()
-
- self.insert(tool_item, -1)
- tool_item.show()
-
- def setToggleButtonState(self,button,b,id):
- button.handler_block(id)
- button.set_active(b)
- button.handler_unblock(id)
-
- def _bold_cb(self, button):
- self._abiword_canvas.toggle_bold()
-
- def _isBold_cb(self, abi, b):
- self.setToggleButtonState(self._bold,b,self._bold_id)
-
- def _italic_cb(self, button):
- self._abiword_canvas.toggle_italic()
-
- def _isItalic_cb(self, abi, b):
- self.setToggleButtonState(self._italic, b, self._italic_id)
-
- def _underline_cb(self, button):
- self._abiword_canvas.toggle_underline()
-
- def _isUnderline_cb(self, abi, b):
- self.setToggleButtonState(self._underline, b, self._underline_id)
-
- def _color_cb(self, abi, r, g, b):
- self._text_color.set_color(gtk.gdk.Color(r * 256, g * 256, b * 256))
-
- def _text_color_cb(self, button):
- newcolor = self._text_color.get_color()
- self._abiword_canvas.set_text_color(int(newcolor.red / 256.0),
- int(newcolor.green / 256.0),
- int(newcolor.blue / 256.0))
-
- def _font_size_cb(self, abi, size):
- for i, s in enumerate(self._font_sizes):
- if int(s) == int(size):
- self._font_size_combo.handler_block(self._font_size_changed_id)
- self._font_size_combo.set_active(i)
- self._font_size_combo.handler_unblock(self._font_size_changed_id)
- break;
-
- def _font_size_changed_cb(self, combobox):
- if self._font_size_combo.get_active() != -1:
- logger.debug('Setting font size: %d', int(self._font_sizes[self._font_size_combo.get_active()]))
- self._abiword_canvas.set_font_size(self._font_sizes[self._font_size_combo.get_active()])
-
- def _font_family_cb(self, abi, font_family):
- font_index = -1
-
- # search for the font name in our font list
- for i, f in enumerate(self._fonts):
- if f == font_family:
- font_index = i
- break;
-
- # if we don't know this font yet, then add it (temporary) to the list
- if font_index == -1:
- logger.debug('Font not found in font list: %s', font_family)
- if not self._has_custom_fonts:
- # add a separator to seperate the non-available fonts from
- # the available ones
- self._fonts.append('') # ugly
- self._font_combo.append_separator()
- self._has_custom_fonts = True
- # add the new font
- self._fonts.append(font_family)
- self._font_combo.append_item(0, font_family, None)
- # see how many fonts we have now, so we can select the last one
- model = self._font_combo.get_model()
- num_children = model.iter_n_children(None)
- logger.debug('Number of fonts in the list: %d', num_children)
- font_index = num_children-1
-
- # activate the found font
- if (font_index > -1):
- self._font_combo.handler_block(self._fonts_changed_id)
- self._font_combo.set_active(font_index)
- self._font_combo.handler_unblock(self._fonts_changed_id)
-
- def _font_changed_cb(self, combobox):
- if self._font_combo.get_active() != -1:
- logger.debug('Setting font name: %s', self._fonts[self._font_combo.get_active()])
- self._abiword_canvas.set_font_name(self._fonts[self._font_combo.get_active()])
-
- def _alignment_changed_cb(self, combobox):
- if self._alignment.get_active() == self._ACTION_ALIGNMENT_LEFT:
- self._abiword_canvas.align_left()
- elif self._alignment.get_active() == self._ACTION_ALIGNMENT_CENTER:
- self._abiword_canvas.align_center()
- elif self._alignment.get_active() == self._ACTION_ALIGNMENT_RIGHT:
- self._abiword_canvas.align_right()
- elif self._alignment.get_active() == self._ACTION_ALIGNMENT_JUSTIFY:
- self._abiword_canvas.align_justify()
- else:
- raise ValueError, 'Unknown option in alignment combobox.'
-
- def _update_alignment_icon(self, index):
- self._alignment.handler_block(self._alignment_changed_id)
- try:
- self._alignment.set_active(index)
- finally:
- self._alignment.handler_unblock(self._alignment_changed_id)
-
- def _isLeftAlign_cb(self, abi, b):
- if b:
- self._update_alignment_icon(self._ACTION_ALIGNMENT_LEFT)
-
- def _isCenterAlign_cb(self, abi, b):
- if b:
- self._update_alignment_icon(self._ACTION_ALIGNMENT_CENTER)
-
- def _isRightAlign_cb(self, abi, b):
- if b:
- self._update_alignment_icon(self._ACTION_ALIGNMENT_RIGHT)
-
- def _isJustifyAlign_cb(self, abi, b):
- if b:
- self._update_alignment_icon(self._ACTION_ALIGNMENT_JUSTIFY)
-
- def _text_selected_cb(self, abi, b):
- if b:
- self._toolbox.set_current_toolbar(TOOLBAR_TEXT)
- self._abiword_canvas.grab_focus() # hack: bad toolbox, bad!
-
-class ImageToolbar(gtk.Toolbar):
- def __init__(self, toolbox, abiword_canvas, parent):
- gtk.Toolbar.__init__(self)
-
- self._toolbox = toolbox
- self._abiword_canvas = abiword_canvas
- self._parent = parent
-
- self._image = ToolButton('insert-image')
- self._image.set_tooltip(_('Insert Image'))
- self._image_id = self._image.connect('clicked', self._image_cb)
- self.insert(self._image, -1)
- self._image.show()
-
- self._abiword_canvas.connect('image-selected', self._image_selected_cb)
-
- def _image_cb(self, button):
- chooser = ObjectChooser(_('Choose image'), self._parent,
- gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
- what_filter=mime.GENERIC_TYPE_IMAGE)
- try:
- result = chooser.run()
- if result == gtk.RESPONSE_ACCEPT:
- logging.debug('ObjectChooser: %r' % chooser.get_selected_object())
- jobject = chooser.get_selected_object()
- if jobject and jobject.file_path:
- self._abiword_canvas.insert_image(jobject.file_path, True)
- finally:
- chooser.destroy()
- del chooser
-
- def _image_selected_cb(self, abi, b):
- if b:
- self._toolbox.set_current_toolbar(TOOLBAR_IMAGE)
- self._abiword_canvas.grab_focus() # hack: bad toolbox, bad!
-
-class TableToolbar(gtk.Toolbar):
- def __init__(self, toolbox, abiword_canvas):
+class InsertToolbar(gtk.Toolbar):
+ def __init__(self, abiword_canvas):
gtk.Toolbar.__init__(self)
- self._toolbox = toolbox
self._abiword_canvas = abiword_canvas
self._table = abiword.TableCreator()
self._table.set_labels(_('Table'), _('Cancel'))
self._table_id = self._table.connect('selected', self._table_cb)
- self._table.show()
tool_item = gtk.ToolItem()
tool_item.add(self._table)
self.insert(tool_item, -1)
@@ -547,27 +212,40 @@ class TableToolbar(gtk.Toolbar):
self._table_rows_after.set_tooltip(_('Insert Row'))
self._table_rows_after_id = self._table_rows_after.connect('clicked', self._table_rows_after_cb)
self.insert(self._table_rows_after, -1)
- self._table_rows_after.show()
self._table_delete_rows = ToolButton('row-remove')
self._table_delete_rows.set_tooltip(_('Delete Row'))
self._table_delete_rows_id = self._table_delete_rows.connect('clicked', self._table_delete_rows_cb)
self.insert(self._table_delete_rows, -1)
- self._table_delete_rows.show()
self._table_cols_after = ToolButton('column-insert')
self._table_cols_after.set_tooltip(_('Insert Column'))
self._table_cols_after_id = self._table_cols_after.connect('clicked', self._table_cols_after_cb)
self.insert(self._table_cols_after, -1)
- self._table_cols_after.show()
self._table_delete_cols = ToolButton('column-remove')
self._table_delete_cols.set_tooltip(_('Delete Column'))
self._table_delete_cols_id = self._table_delete_cols.connect('clicked', self._table_delete_cols_cb)
self.insert(self._table_delete_cols, -1)
- self._table_delete_cols.show()
+
+ separator = gtk.SeparatorToolItem()
+ self.insert(separator, -1)
+
+ image = ToolButton('insert-image')
+ image.set_tooltip(_('Insert Image'))
+ self._image_id = image.connect('clicked', self._image_cb)
+ self.insert(image, -1)
+
+ self.show_all()
self._abiword_canvas.connect('table-state', self._isTable_cb)
+ #self._abiword_canvas.connect('image-selected', self._image_selected_cb)
+
+ def _image_cb(self, button):
+ def cb(object):
+ logging.debug('ObjectChooser: %r' % object)
+ self._abiword_canvas.insert_image(object.file_path, True)
+ chooser.pick(what=chooser.IMAGE, cb=cb)
def _table_cb(self, abi, rows, cols):
self._abiword_canvas.insert_table(rows,cols)
@@ -589,84 +267,6 @@ class TableToolbar(gtk.Toolbar):
self._table_delete_rows.set_sensitive(b)
self._table_cols_after.set_sensitive(b)
self._table_delete_cols.set_sensitive(b)
- if b:
- self._toolbox.set_current_toolbar(TOOLBAR_TABLE)
- self._abiword_canvas.grab_focus() # hack: bad toolbox, bad!
-
-class FormatToolbar(gtk.Toolbar):
- def __init__(self, toolbox, abiword_canvas):
- gtk.Toolbar.__init__(self)
-
- self._toolbox = toolbox
- self._abiword_canvas = abiword_canvas
-
- style_label = gtk.Label(_("Style: "))
- style_label.show()
- tool_item_style_label = gtk.ToolItem()
- tool_item_style_label.add(style_label)
- self.insert(tool_item_style_label, -1)
- tool_item_style_label.show()
-
- self._has_custom_styles = False
-
- self._style_combo = ComboBox()
- self._styles = [['Heading 1',_('Heading 1')],
- ['Heading 2',_('Heading 2')],
- ['Heading 3',_('Heading 3')],
- ['Heading 4',_('Heading 4')],
- ['Bullet List',_('Bullet List')],
- ['Dashed List',_('Dashed List')],
- ['Numbered List',_('Numbered List')],
- ['Lower Case List',_('Lower Case List')],
- ['Upper Case List',_('Upper Case List')],
- ['Block Text',_('Block Text')],
- ['Normal',_('Normal')],
- ['Plain Text',_('Plain Text')]]
- self._style_changed_id = self._style_combo.connect('changed', self._style_changed_cb)
- for i, s in enumerate(self._styles):
- self._style_combo.append_item(i, s[1], None)
- if s[0] == 'Normal':
- self._style_combo.set_active(i)
- tool_item = ToolComboBox(self._style_combo)
- self.insert(tool_item, -1);
- tool_item.show()
-
- self._abiword_canvas.connect('style-name', self._style_cb)
-
- def _style_cb(self, abi, style_name):
- style_index = -1
- for i, s in enumerate(self._styles):
- if s[0] == style_name:
- style_index = i
- break;
-
- # if we don't know this style yet, then add it (temporary) to the list
- if style_index == -1:
- logger.debug('Style not found in style list: %s', style_name)
- if not self._has_custom_styles:
- # add a separator to seperate the non-available styles from
- # the available ones
- self._styles.append(['','']) # ugly
- self._style_combo.append_separator()
- self._has_custom_styles = True
- # add the new style
- self._styles.append([style_name, style_name])
- self._style_combo.append_item(0, style_name, None)
- # see how many styles we have now, so we can select the last one
- model = self._style_combo.get_model()
- num_children = model.iter_n_children(None)
- logger.debug('Number of styles in the list: %d', num_children)
- style_index = num_children-1
-
- if style_index > -1:
- self._style_combo.handler_block(self._style_changed_id)
- self._style_combo.set_active(style_index)
- self._style_combo.handler_unblock(self._style_changed_id)
-
- def _style_changed_cb(self, combobox):
- if self._style_combo.get_active() != -1:
- logger.debug('Setting style name: %s', self._styles[self._style_combo.get_active()][0])
- self._abiword_canvas.set_style(self._styles[self._style_combo.get_active()][0])
class ViewToolbar(gtk.Toolbar):
def __init__(self, abiword_canvas):
@@ -741,7 +341,7 @@ class ViewToolbar(gtk.Toolbar):
def set_zoom_percentage(self, zoom):
self._zoom_percentage = zoom
self._abiword_canvas.set_zoom_percentage(self._zoom_percentage)
-
+
def _zoom_cb(self, canvas, zoom):
self._zoom_spin.handler_block(self._zoom_spin_id)
try:
@@ -782,3 +382,67 @@ class ViewToolbar(gtk.Toolbar):
finally:
self._page_spin.handler_unblock(self._page_spin_id)
+class TextToolbar(gtk.Toolbar):
+ def __init__(self, abiword_canvas):
+ gtk.Toolbar.__init__(self)
+
+ self._abiword_canvas = abiword_canvas
+
+ self.insert(ToolComboBox(widgets.StyleCombo(abiword_canvas)), -1)
+ self.insert(activity.separator(), -1)
+
+ bold = ToggleToolButton('format-text-bold')
+ bold.set_tooltip(_('Bold'))
+ bold_id = bold.connect('clicked', lambda sender:
+ abiword_canvas.toggle_bold())
+ self._abiword_canvas.connect('bold', lambda abi, b:
+ self.setToggleButtonState(bold, b, bold_id))
+ self.insert(bold, -1)
+
+ italic = ToggleToolButton('format-text-italic')
+ italic.set_tooltip(_('Italic'))
+ italic_id = italic.connect('clicked', lambda sender:
+ abiword_canvas.toggle_italic())
+ self._abiword_canvas.connect('italic', lambda abi, b:
+ self.setToggleButtonState(italic, b, italic_id))
+ self.insert(italic, -1)
+
+ underline = ToggleToolButton('format-text-underline')
+ underline.set_tooltip(_('Underline'))
+ underline_id = underline.connect('clicked', lambda sender:
+ abiword_canvas.toggle_underline())
+ self._abiword_canvas.connect('underline', lambda abi, b:
+ self.setToggleButtonState(underline, b, underline_id))
+ self.insert(underline, -1)
+
+ self.insert(activity.separator(), -1)
+
+ alignment = RadioMenuButton(palette=widgets.Alignment(abiword_canvas))
+ self.insert(alignment, -1)
+
+ lists = RadioMenuButton(palette=widgets.Lists(abiword_canvas))
+ self.insert(lists, -1)
+
+ self.insert(activity.separator(), -1)
+
+ color = ColorToolButton()
+ color.connect('color-set', self._text_color_cb)
+ tool_item = gtk.ToolItem()
+ tool_item.add(color)
+ self.insert(tool_item, -1)
+ self._abiword_canvas.connect('color', lambda abi, r, g, b:
+ color.set_color(gtk.gdk.Color(r * 256, g * 256, b * 256)))
+
+ self.show_all()
+
+ def setToggleButtonState(self,button,b,id):
+ button.handler_block(id)
+ button.set_active(b)
+ button.handler_unblock(id)
+
+ def _text_color_cb(self, button):
+ newcolor = button.get_color()
+ self._abiword_canvas.set_text_color(int(newcolor.red / 256.0),
+ int(newcolor.green / 256.0),
+ int(newcolor.blue / 256.0))
+
diff --git a/widgets.py b/widgets.py
new file mode 100644
index 0000000..594ddd8
--- /dev/null
+++ b/widgets.py
@@ -0,0 +1,242 @@
+# 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
+
+import gtk
+from gettext import gettext as _
+
+import logging
+logger = logging.getLogger('write-activity')
+
+from sugar.graphics.radiotoolbutton import RadioToolButton
+from sugar.graphics.combobox import ComboBox
+from sugar.graphics.palette import Palette
+from sugar.graphics.radiopalette import RadioPalette
+
+class FontCombo(ComboBox):
+ def __init__(self, abi):
+ ComboBox.__init__(self)
+
+ self._has_custom_fonts = False
+ self._fonts = sorted(abi.get_font_names())
+ self._fonts_changed_id = self.connect('changed', self._font_changed_cb,
+ abi)
+
+ for i, f in enumerate(self._fonts):
+ self.append_item(i, f, None)
+ if f == 'Times New Roman':
+ self.set_active(i)
+
+ abi.connect('font-family', self._font_family_cb)
+
+ def _font_changed_cb(self, combobox, abi):
+ if self.get_active() != -1:
+ logger.debug('Setting font: %s', self._fonts[self.get_active()])
+ abi.set_font_name(self._fonts[self.get_active()])
+
+ def _font_family_cb(self, abi, font_family):
+ font_index = -1
+
+ # search for the font name in our font list
+ for i, f in enumerate(self._fonts):
+ if f == font_family:
+ font_index = i
+ break;
+
+ # if we don't know this font yet, then add it (temporary) to the list
+ if font_index == -1:
+ logger.debug('Font not found in font list: %s', font_family)
+ if not self._has_custom_fonts:
+ # add a separator to seperate the non-available fonts from
+ # the available ones
+ self._fonts.append('') # ugly
+ self.append_separator()
+ self._has_custom_fonts = True
+ # add the new font
+ self._fonts.append(font_family)
+ self.append_item(0, font_family, None)
+ # see how many fonts we have now, so we can select the last one
+ model = self.get_model()
+ num_children = model.iter_n_children(None)
+ logger.debug('Number of fonts in the list: %d', num_children)
+ font_index = num_children-1
+
+ # activate the found font
+ if (font_index > -1):
+ self.handler_block(self._fonts_changed_id)
+ self.set_active(font_index)
+ self.handler_unblock(self._fonts_changed_id)
+
+class FontSizeCombo(ComboBox):
+ def __init__(self, abi):
+ ComboBox.__init__(self)
+
+ self._font_sizes = ['8', '9', '10', '11', '12', '14', '16', '20', \
+ '22', '24', '26', '28', '36', '48', '72']
+ self._changed_id = self.connect('changed', self._font_size_changed_cb,
+ abi)
+
+ for i, s in enumerate(self._font_sizes):
+ self.append_item(i, s, None)
+ if s == '12':
+ self.set_active(i)
+
+ abi.connect('font-size', self._font_size_cb)
+
+ def _font_size_changed_cb(self, combobox, abi):
+ if self.get_active() != -1:
+ logger.debug('Setting font size: %d',
+ int(self._font_sizes[self.get_active()]))
+ abi.set_font_size(self._font_sizes[self.get_active()])
+
+ def _font_size_cb(self, abi, size):
+ for i, s in enumerate(self._font_sizes):
+ if int(s) == int(size):
+ self.handler_block(self._changed_id)
+ self.set_active(i)
+ self.handler_unblock(self._changed_id)
+ break;
+
+class StyleCombo(ComboBox):
+ def __init__(self, abi):
+ ComboBox.__init__(self)
+
+ self._styles = [ ['Heading 1', _('Heading 1')],
+ ['Heading 2', _('Heading 2')],
+ ['Heading 3', _('Heading 3')],
+ ['Heading 4', _('Heading 4')],
+ ['Bullet List', _('Bullet List')],
+ ['Dashed List', _('Dashed List')],
+ ['Numbered List', _('Numbered List')],
+ ['Lower Case List', _('Lower Case List')],
+ ['Upper Case List', _('Upper Case List')],
+ ['Block Text', _('Block Text')],
+ ['Normal', _('Normal')],
+ ['Plain Text', _('Plain Text')] ]
+
+ self._has_custom_styles = False
+ self._style_changed_id = self.connect('changed', self._style_changed_cb,
+ abi)
+
+ for i, s in enumerate(self._styles):
+ self.append_item(i, s[1], None)
+ if s[0] == 'Normal':
+ self.set_active(i)
+
+ abi.connect('style-name', self._style_cb)
+
+ def _style_changed_cb(self, combobox, abi):
+ if self.get_active() != -1:
+ logger.debug('Set style: %s', self._styles[self.get_active()][0])
+ abi.set_style(self._styles[self.get_active()][0])
+
+ def _style_cb(self, abi, style_name):
+ if style_name is None or style_name == 'None':
+ style_name = 'Normal'
+
+ style_index = -1
+ for i, s in enumerate(self._styles):
+ if s[0] == style_name:
+ style_index = i
+ break;
+
+ # if we don't know this style yet, then add it (temporary) to the list
+ if style_index == -1:
+ logger.debug('Style not found in style list: %s', style_name)
+
+ if not self._has_custom_styles:
+ # add a separator to seperate the non-available styles from
+ # the available ones
+ self._styles.append(['','']) # ugly
+ self.append_separator()
+ self._has_custom_styles = True
+
+ # add the new style
+ self._styles.append([style_name, style_name])
+ self.append_item(0, style_name, None)
+
+ # see how many styles we have now, so we can select the last one
+ model = self.get_model()
+ num_children = model.iter_n_children(None)
+ logger.debug('Number of styles in the list: %d', num_children)
+ style_index = num_children-1
+
+ if style_index > -1:
+ self.handler_block(self._style_changed_id)
+ self.set_active(style_index)
+ self.handler_unblock(self._style_changed_id)
+
+class AbiPalette(RadioPalette):
+ def __init__(self, abi):
+ RadioPalette.__init__(self)
+ self.abi = abi
+
+ def append(self, icon_name, tooltip, clicked_cb, abi_signal, abi_cb):
+ button = RadioPalette.append(self,
+ icon_name=icon_name,
+ tooltip=tooltip,
+ toggled_cb=lambda: clicked_cb())
+
+ def cb(abi, prop):
+ if abi_cb(abi, prop):
+ button.set_active(True)
+ self.abi.connect(abi_signal, cb)
+
+class Alignment(AbiPalette):
+ def __init__(self, abi):
+ AbiPalette.__init__(self, abi)
+
+ self.append('format-justify-left', _('Left justify'),
+ lambda: abi.align_left(), 'left-align', lambda abi, b: b)
+
+ self.append('format-justify-center', _('Center justify'),
+ lambda: abi.align_center(), 'center-align', lambda abi, b: b)
+
+ self.append('format-justify-right', _('Right justify'),
+ lambda: abi.align_right(), 'right-align', lambda abi, b: b)
+
+ self.append('format-justify-fill', _('Fill justify'),
+ lambda: abi.align_justify(), 'justify-align', lambda abi, b: b)
+
+class Lists(AbiPalette):
+ def __init__(self, abi):
+ AbiPalette.__init__(self, abi)
+
+ self.append('list-none', _('Normal'),
+ lambda: abi.set_style('Normal'),
+ 'style-name', lambda abi, style:
+ style not in ['Bullet List',
+ 'Dashed List',
+ 'Numbered List',
+ 'Lower Case List',
+ 'Upper Case List'])
+
+ self.append('list-bullet', _('Bullet List'),
+ lambda: abi.set_style('Bullet List'),
+ 'style-name', lambda abi, style: style == 'Bullet List')
+
+ self.append('list-dashed', _('Dashed List'),
+ lambda: abi.set_style('Dashed List'),
+ 'style-name', lambda abi, style: style == 'Dashed List')
+
+ self.append('list-numbered', _('Numbered List'),
+ lambda: abi.set_style('Numbered List'),
+ 'style-name', lambda abi, style: style == 'Numbered List')
+
+ self.append('list-lower-case', _('Lower Case List'),
+ lambda: abi.set_style('Lower Case List'),
+ 'style-name', lambda abi, style: style == 'Lower Case List')
+
+ self.append('list-upper-case', _('Upper Case List'),
+ lambda: abi.set_style('Upper Case List'),
+ 'style-name', lambda abi, style: style == 'Upper Case List')