Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/tutorials
diff options
context:
space:
mode:
Diffstat (limited to 'tutorials')
-rw-r--r--tutorials/Writus.activity/NEWS172
-rw-r--r--tutorials/Writus.activity/TAbiWordActivity.py435
-rw-r--r--tutorials/Writus.activity/TAbiWordActivity.pycbin0 -> 14857 bytes
-rw-r--r--tutorials/Writus.activity/TAbiWordActivity.py~434
-rw-r--r--tutorials/Writus.activity/activity/activity-write.svg13
-rw-r--r--tutorials/Writus.activity/activity/activity.info9
-rw-r--r--tutorials/Writus.activity/keybindings.xml36
-rw-r--r--tutorials/Writus.activity/locale/af/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/am/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/ar/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/ay/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/bg/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/bn/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/bn_IN/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/ca/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/de/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/dz/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/el/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/en/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/es/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/fa/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/fa_AF/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/ff/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/fr/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/gu/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/ha/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/hi/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/ht/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/ig/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/is/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/it/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/ja/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/km/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/ko/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/mk/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/ml/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/mn/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/mr/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/mvo/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/nb/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/ne/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/nl/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/pa/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/pap/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/pis/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/pl/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/ps/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/pt/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/pt_BR/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/qu/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/ro/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/ru/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/rw/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/sd/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/si/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/sl/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/te/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/th/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/tpi/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/tr/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/ur/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/vi/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/yo/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/zh_CN/activity.linfo2
-rw-r--r--tutorials/Writus.activity/locale/zh_TW/activity.linfo2
-rwxr-xr-xtutorials/Writus.activity/setup.py22
-rw-r--r--tutorials/Writus.activity/toolbar.py780
-rw-r--r--tutorials/Writus.activity/toolbar.pycbin0 -> 29421 bytes
68 files changed, 2017 insertions, 0 deletions
diff --git a/tutorials/Writus.activity/NEWS b/tutorials/Writus.activity/NEWS
new file mode 100644
index 0000000..361d4d4
--- /dev/null
+++ b/tutorials/Writus.activity/NEWS
@@ -0,0 +1,172 @@
+58
+
+* Fix 6021: Write crash on buddy left
+* Add license field to activity.info
+
+57
+
+* Translation release
+
+56
+
+* Translation release
+
+55
+
+* Translation release
+
+54
+
+* Fix 4871: the write save as entries of the journal come up uncolored (rwh)
+* Fix 4861: Incremental find cycles through words while typing (uwog)
+
+53
+
+* Use 'instance' instead of 'data' as a path prefix; part of 4850 (uwog)
+
+52
+
+* Fix 4731: Find control should have embedded search/cancel icons (uwog)
+
+51
+
+* Fix 4614: Write should change the order of the zoom icons (uwog)
+* Fix 4022: Write should not change the format of an opened journal entry (uwog)
+* Remove tons of hacks to go with the new AbiWidget version (uwog)
+
+50
+
+* First implementation of 4165: Write must expose UI to
+ allow specifying output format (uwog)
+* Update .pot file with new strings (uwog)
+
+49
+
+* Enable/disable the search functions based on the input (uwog)
+* Support for searching text (foddex, tiny bit of uwog)
+* Support custom keybindings (foddex)
+
+48
+
+* Fix 2124: Fail to page in Write (foddex)
+* Fix 3899: do not use set_title (uwog)
+
+47
+
+* Advertise odt support (marco)
+
+45
+
+* Update spanish translation (beckerde)
+
+44
+
+* Update spanish translation (beckerde)
+
+43
+
+* Update spanish translation (beckerde)
+
+42
+
+* Initial german translation (fab)
+
+41
+
+* Fix #3319: default tab should be 'text' (uwog)
+
+40
+
+* Add first 3000 words indexing (tomeu)
+
+39
+
+* The part needed in write for viewing the source of browse #3261 (erikos)
+* Updated the translations for new strings (uwog)
+
+38
+
+* Updated activity icon (erikos)
+
+37
+
+* (Temporary) add styles used by loaded documents that are not available
+ by default to the style dropdown list (uwog)
+* (Temporary) add fonts used by loaded documents that are not available
+ on the system to the font dropdown list (uwog)
+* Set a tooltip on various menu items (#821) (uwog)
+* Hook up the abiword canvas 'style-name' signal to the Format toolbar (uwog)
+* Hook up the abiword canvas 'font-family' signal to the Text toolbar (uwog)
+* Add a format-text-size icon before the text size selection dropdown (uwog)
+* Don't forget to actually show the separators in the Text toolbar (uwog)
+* Add a Style label before the Style combobox (uwog)
+* Hook up the abiword canvas 'font-size' signal to the Text toolbar (uwog)
+
+36
+
+* Use a journal object picker when inserting images, instead of a normal
+ GTK+ Open File dialog (uwog)
+* Port to new tubes API (cassidy)
+* Add supported mimetypes for abiword, msword, xhtml, html and rtf (uwog)
+* Add a format toolbar (uwog)
+* Implement basic style support (uwog)
+* Make style names translatable (uwog)
+
+35
+
+* Update zoom icon names
+* Adapt icon names to the new API
+
+34
+
+* Add French translation. (Samuel Bizien)
+
+33
+
+* Add Greek translation. (simosx)
+
+32
+
+* #2370 Add spanish translation (xavi)
+
+31
+
+* Add macedonian translation.
+
+30
+
+* Update some icons (uwog)
+* Implement full justification (uwog)
+* Workaround for missing buddy removed in _on_dbus_names_changed (uwog)
+* Implement buddies leaving (uwog)
+* Workaround for wrong view margin size calculation (uwog)
+* Remove the border around the view (uwog)
+* Connect to the zoom signal to show the currect zoom level (uwog)
+
+29
+
+* Use a logger for the toolbar (uwog)
+
+28
+
+* add total page count label (uwog)
+* replace print's with proper logger calls (uwog)
+* Implement a color button, which respects the cursors context (uwog)
+
+27
+
+* Make sure the table icon shows up (uwog)
+* Use ToolComboBoxes for ComboBoxes om the toolbar (uwog)
+
+25
+
+* Add brazilian translation. (DiegoZacarao)
+
+24
+
+* Set the right mime type when saving to the journal. (tomeu)
+
+23
+
+* Arabian translation.
+* Adapt to combobox API change.
diff --git a/tutorials/Writus.activity/TAbiWordActivity.py b/tutorials/Writus.activity/TAbiWordActivity.py
new file mode 100644
index 0000000..adcf579
--- /dev/null
+++ b/tutorials/Writus.activity/TAbiWordActivity.py
@@ -0,0 +1,435 @@
+# Copyright (C) 2006 by Martin Sevior
+# Copyright (C) 2006-2007 Marc Maurer <uwog@uwog.net>
+# Copyright (C) 2007, One Laptop Per Child
+#
+# 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 gettext import gettext as _
+import logging
+import os
+import time
+import shutil
+
+import dbus
+import gtk
+import telepathy
+import telepathy.client
+import gobject
+
+from sugar.activity.activity import Activity, ActivityToolbox, EditToolbar
+from sugar.presence import presenceservice
+
+from abiword import Canvas
+import toolbar
+from toolbar import WriteActivityToolbarExtension, WriteEditToolbar, TextToolbar, ImageToolbar, TableToolbar, FormatToolbar, ViewToolbar
+from sugar.activity.activity import get_bundle_path
+
+logger = logging.getLogger('twrite-activity')
+
+from sugar.tutorius import gtkutils, overlayer
+from sugar.tutorius.core import Tutorial, State, FiniteStateMachine
+from sugar.tutorius.actions import DialogMessage, OnceWrapper, BubbleMessage
+from sugar.tutorius.filters import GtkWidgetEventFilter, TimerEvent
+
+NUM_TEST = {
+ "INIT":State("INIT",
+ action_list=[
+ OnceWrapper(BubbleMessage(message="Welcome to the text editor tutorial!\n\n Click on the canvas and type a letter.", pos=[100,100], tailpos=[-10,-20])),
+ ],
+ event_filter_list=[
+ GtkWidgetEventFilter("TEXT","0.0.0.1.0.0.0","key-press-event"),
+ TimerEvent("LOST",15),
+ ],
+ ),
+ "LOST":State("LOST",
+ action_list=[BubbleMessage("Click in the canvas and type on your keyboard", [400, 400]),],
+ event_filter_list=[
+ GtkWidgetEventFilter("TEXT","0.0.0.1.0.0.0","key-press-event"),
+ TimerEvent("INIT",5),
+ ],
+ ),
+ "TEXT":State("TEXT",
+ action_list=[OnceWrapper(BubbleMessage(" You can type more letters if you want!\n\n" +
+ "To proceed to the next step, select your text.\n\n Click and drag over the text!", [200,150])),],
+ event_filter_list=[
+ GtkWidgetEventFilter("SELECTED","0.0.0.1.0.0","text-selected"),
+ ],
+ ),
+ "SELECTED":State("SELECTED",
+ action_list=[
+ OnceWrapper(BubbleMessage("To copy-paste text, go to the ^^^ EDIT toolbar.", [50, 120], tailpos=[150,-20])),
+ OnceWrapper(BubbleMessage("You can also use one of the big letters\n<- there to change the style of the text", [300, 5], tailpos=[-15,5]))
+ ],
+ event_filter_list=[
+ GtkWidgetEventFilter("EDIT","0.0.0.0.0.1","focus"),
+ GtkWidgetEventFilter("BOLD","0.0.0.0.0.2.0.0.0","clicked"),
+ GtkWidgetEventFilter("ITALIC","0.0.0.0.0.2.0.0.1","clicked"),
+ GtkWidgetEventFilter("UNDER","0.0.0.0.0.2.0.0.2","clicked"),
+ ],
+ ),
+ "BOLD":State("BOLD",
+ action_list=[OnceWrapper(BubbleMessage("The text is now bold! Wow!", [50, 60], tailpos=[30,-20])),],
+ event_filter_list=[
+ GtkWidgetEventFilter("TEXT","0.0.0.0.0.2.0.0.0.0","leave-notify-event"),
+ ],
+ ),
+ "ITALIC":State("ITALIC",
+ action_list=[
+ OnceWrapper(BubbleMessage("The text is now slanted! Wow!", [100, 60], tailpos=[30,-20])),
+ ],
+ event_filter_list=[
+ GtkWidgetEventFilter("TEXT","0.0.0.0.0.2.0.0.1.0","leave-notify-event"),
+ ],
+ ),
+ "UNDER":State("UNDER",
+ action_list=[
+ OnceWrapper(BubbleMessage("There is a line under the text! Wow!", [150, 60], tailpos=[30,-20])),
+ ],
+ event_filter_list=[
+ GtkWidgetEventFilter("TEXT","0.0.0.0.0.2.0.0.2.0","leave-notify-event"),
+ ],
+ ),
+ "EDIT":State("EDIT",
+ action_list=[
+ OnceWrapper(BubbleMessage("Use ^^^ Copy to remember the text\nthat is selected", [250, 68]))
+ ],
+ event_filter_list=[
+ GtkWidgetEventFilter("COPY","0.0.0.0.0.1.0.0.3","clicked"),
+ ],
+ ),
+ "COPY":State("COPY",
+ action_list=[
+ OnceWrapper(BubbleMessage("Click ^^^ Paste to copy the remembered text where the cursor is", [315, 60]))
+ ],
+ event_filter_list=[
+ GtkWidgetEventFilter("PASTE","0.0.0.0.0.1.0.0.4","clicked"),
+ ],
+ ),
+ "PASTE":State("PASTE",
+ action_list=[BubbleMessage("The text was copied! Good job!", [315, 60]),],
+ event_filter_list=[
+ GtkWidgetEventFilter("TEXT","0.0.0.0.0.1.0.0.4.0","leave-notify-event"),
+ ],
+ ),
+
+}
+
+
+class TAbiWordActivity (Activity):
+
+ def __init__ (self, handle):
+ Activity.__init__ (self, handle)
+ # 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()
+ toolbox.set_name("Toolbox")
+
+ activity_toolbar_ext = WriteActivityToolbarExtension(self, toolbox, self.abiword_canvas)
+
+ text_toolbar = TextToolbar(toolbox, self.abiword_canvas)
+ text_toolbar.set_name("TextToolbar")
+
+ self._edit_toolbar = WriteEditToolbar(toolbox, self.abiword_canvas, text_toolbar)
+ toolbox.add_toolbar(_('Edit'), self._edit_toolbar)
+ self._edit_toolbar.show()
+ self._edit_toolbar.set_name("EditToolbar")
+
+ toolbox.add_toolbar(_('Text'), text_toolbar)
+ text_toolbar.show()
+
+ image_toolbar = ImageToolbar(toolbox, self.abiword_canvas, self)
+ toolbox.add_toolbar(_('Image'), image_toolbar)
+ image_toolbar.show()
+
+ table_toolbar = TableToolbar(toolbox, self.abiword_canvas)
+ toolbox.add_toolbar(_('Table'), table_toolbar)
+ table_toolbar.show()
+
+ format_toolbar = FormatToolbar(toolbox, self.abiword_canvas)
+ toolbox.add_toolbar(_('Format'), format_toolbar)
+ format_toolbar.show()
+
+ view_toolbar = ViewToolbar(self.abiword_canvas)
+ toolbox.add_toolbar(_('View'), view_toolbar)
+ view_toolbar.show()
+
+ # the text toolbar should be our default toolbar
+ toolbox.set_current_toolbar(toolbar.TOOLBAR_TEXT)
+
+ self.set_canvas(self.abiword_canvas)
+ self.abiword_canvas.connect_after('map-event', self._map_event_cb)
+ self.abiword_canvas.show()
+ self.abiword_canvas.set_name("abiword-canvas")
+
+ 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" )
+ self.abiword_canvas.invoke_cmd('com.abisource.abiword.loadbindings.fromURI', keybindings_file, 0, 0)
+
+ # no ugly borders please
+ self.abiword_canvas.set_property("shadow-type", gtk.SHADOW_NONE)
+
+ # we only do per-word selections (when using the mouse)
+ self.abiword_canvas.set_word_selections(True)
+
+ # we want a nice border so we can select paragraphs easily
+ self.abiword_canvas.set_show_margin(True)
+
+ # activity sharing
+ self.participants = {}
+ pservice = presenceservice.get_instance()
+
+ bus = dbus.Bus()
+ name, path = pservice.get_preferred_connection()
+ self.conn = telepathy.client.Connection(name, path)
+ self.initiating = None
+ self.joined = False
+
+ self.connect('shared', self._shared_cb)
+
+ if self._shared_activity:
+ # we are joining the activity
+ logger.debug("We are joining an activity")
+ self.connect('joined', self._joined_cb)
+ self._shared_activity.connect('buddy-joined', self._buddy_joined_cb)
+ self._shared_activity.connect('buddy-left', self._buddy_left_cb)
+ if self.get_shared():
+# # oh, OK, we've already joined
+ self._joined_cb()
+ else:
+ # we are creating the activity
+ logger.debug("We are creating an activity")
+
+ owner = pservice.get_owner()
+
+ def _shared_cb(self, activity):
+ logger.debug('My TWrite activity was shared')
+ self.initiating = True
+ self._setup()
+
+ self._shared_activity.connect('buddy-joined', self._buddy_joined_cb)
+ self._shared_activity.connect('buddy-left', self._buddy_left_cb)
+
+ logger.debug('This is my activity: offering a tube...')
+ id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(
+ "com.abisource.abiword.abicollab", {})
+ logger.debug('Tube address: %s', self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].GetDBusTubeAddress(id))
+
+
+ def _setup(self):
+ logger.debug("_setup()")
+
+ if self._shared_activity is None:
+ logger.error('Failed to share or join activity')
+ return
+
+ bus_name, conn_path, channel_paths = self._shared_activity.get_channels()
+
+ # Work out what our room is called and whether we have Tubes already
+ room = None
+ tubes_chan = None
+ text_chan = None
+ for channel_path in channel_paths:
+ channel = telepathy.client.Channel(bus_name, channel_path)
+ htype, handle = channel.GetHandle()
+ if htype == telepathy.HANDLE_TYPE_ROOM:
+ logger.debug('Found our room: it has handle#%d "%s"',
+ handle, self.conn.InspectHandles(htype, [handle])[0])
+ room = handle
+ ctype = channel.GetChannelType()
+ if ctype == telepathy.CHANNEL_TYPE_TUBES:
+ logger.debug('Found our Tubes channel at %s', channel_path)
+ tubes_chan = channel
+ elif ctype == telepathy.CHANNEL_TYPE_TEXT:
+ logger.debug('Found our Text channel at %s', channel_path)
+ text_chan = channel
+
+ if room is None:
+ logger.error("Presence service didn't create a room")
+ return
+ if text_chan is None:
+ logger.error("Presence service didn't create a text channel")
+ return
+
+ # Make sure we have a Tubes channel - PS doesn't yet provide one
+ if tubes_chan is None:
+ logger.debug("Didn't find our Tubes negotation channel, requesting one...")
+ tubes_chan = self.conn.request_channel(telepathy.CHANNEL_TYPE_TUBES,
+ telepathy.HANDLE_TYPE_ROOM, room, True)
+ logger.debug("Got our tubes negotiation channel")
+
+ self.tubes_chan = tubes_chan
+ self.text_chan = text_chan
+
+ tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('NewTube',
+ self._new_tube_cb)
+
+ def _list_tubes_reply_cb(self, tubes):
+ for tube_info in tubes:
+ self._new_tube_cb(*tube_info)
+
+ def _list_tubes_error_cb(self, e):
+ logger.error('ListTubes() failed: %s', e)
+
+ def _joined_cb(self, activity):
+ logger.debug("_joined_cb()")
+ if not self._shared_activity:
+ return
+
+ self.joined = True
+ logger.debug('Joined an existing TWrite session')
+ self._setup()
+
+ logger.debug('This is not my activity: waiting for a tube...')
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(
+ reply_handler=self._list_tubes_reply_cb,
+ error_handler=self._list_tubes_error_cb)
+
+ def _new_tube_cb(self, id, initiator, type, service, params, state):
+ logger.debug('New tube: ID=%d initiator=%d type=%d service=%s '
+ 'params=%r state=%d', id, initiator, type, service,
+ params, state)
+
+ if (type == telepathy.TUBE_TYPE_DBUS and
+ service == "com.abisource.abiword.abicollab"):
+ if state == telepathy.TUBE_STATE_LOCAL_PENDING:
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)
+
+ initiator_path = None;
+ contacts = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].GetDBusNames(id)
+ #print 'dbus contact mapping',self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].GetDBusNames(id)
+ for i, struct in enumerate(contacts):
+ #print 'mapping i',i
+ handle, path = struct
+ if handle == initiator:
+ logger.debug('found initiator dbus path: %s', path)
+ initiator_path = path
+ break;
+
+ if initiator_path is None:
+ logger.error('Unable to get the dbus path of the tube initiator')
+ else:
+ # pass this tube to abicollab
+ address = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].GetDBusTubeAddress(id)
+ if self.joined:
+ logger.debug('Passing tube address to abicollab (join): %s', address)
+ self.abiword_canvas.invoke_cmd('com.abisource.abiword.abicollab.olpc.joinTube', address, 0, 0)
+ if initiator_path is not None:
+ logger.debug('Adding the initiator to the session: %s', initiator_path)
+ self.abiword_canvas.invoke_cmd('com.abisource.abiword.abicollab.olpc.buddyJoined', initiator_path, 0, 0)
+ else:
+ logger.debug('Passing tube address to abicollab (offer): %s', address)
+ self.abiword_canvas.invoke_cmd('com.abisource.abiword.abicollab.olpc.offerTube', address, 0, 0)
+
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('DBusNamesChanged',
+ self._on_dbus_names_changed)
+
+ # HACK, as DBusNamesChanged doesn't fire on buddies leaving
+ self.tubes_chan[telepathy.CHANNEL_INTERFACE_GROUP].connect_to_signal('MembersChanged',
+ self._on_members_changed)
+
+ def _on_dbus_names_changed(self, tube_id, added, removed):
+ logger.debug('_on_dbus_names_changed')
+# if tube_id == self.tube_id:
+ for handle, bus_name in added:
+ logger.debug('added handle: %s, with dbus_name: %s', handle, bus_name)
+ self.abiword_canvas.invoke_cmd('com.abisource.abiword.abicollab.olpc.buddyJoined', bus_name, 0, 0)
+ self.participants[handle] = bus_name
+
+# if handle == self.self_handle:
+ # I've just joined - set my unique name
+# print 'i\'ve just joined'
+# self.set_unique_name(bus_name)
+# self.participants[handle] = bus_name
+# self.bus_name_to_handle[bus_name] = handle
+
+# HACK: doesn't work yet, bad morgs!
+# for handle in removed:
+# logger.debug('removed handle: %s, with dbus name: %s', handle, bus_name)
+# bus_name = self.participants.pop(handle, None)
+
+ def _on_members_changed(self, message, added, removed, local_pending, remote_pending, actor, reason):
+ logger.debug("_on_members_changed")
+ for handle in removed:
+ bus_name = self.participants.pop(handle, None)
+ if bus_name is None:
+ # FIXME: that shouldn't happen so probably hide another bug.
+ # Should be investigated
+ continue
+
+ logger.debug('removed handle: %d, with dbus name: %s', handle,
+ bus_name)
+ self.abiword_canvas.invoke_cmd('com.abisource.abiword.abicollab.olpc.buddyLeft', bus_name, 0, 0)
+
+ def _buddy_joined_cb (self, activity, buddy):
+ logger.debug('buddy joined with object path: %s', buddy.object_path())
+# self.abiword_canvas.invoke_cmd('com.abisource.abiword.abicollab.olpc.buddyJoined', buddy.object_path(), 0, 0)
+
+ def _buddy_left_cb (self, activity, buddy):
+ logger.debug('buddy left with object path: %s', buddy.object_path())
+ #self.abiword_canvas.invoke_cmd('com.abisource.abiword.abicollab.olpc.buddyLeft', self.participants[buddy.object_path()], 0, 0)
+
+ def read_file(self, file_path):
+ logging.debug('AbiWordActivity.read_file: %s, mimetype: %s', file_path, self.metadata['mime_type'])
+ if 'source' in self.metadata and self.metadata['source'] == '1':
+ logger.debug('Opening file in view source mode')
+ self.abiword_canvas.load_file('file://' + file_path, 'text/plain')
+ else:
+ self.abiword_canvas.load_file('file://' + file_path, '') # we pass no mime/file type, let libabiword autodetect it, so we can handle multiple file formats
+
+ def write_file(self, file_path):
+ logging.debug('AbiWordActivity.write_file')
+
+ # check if we have a default mimetype; if not, fall back to OpenDocument
+ # also fallback if we know we cannot export in that format
+ if 'mime_type' not in self.metadata or self.metadata['mime_type'] == '' or \
+ self.metadata['mime_type'] == 'application/msword':
+ self.metadata['mime_type'] = 'application/vnd.oasis.opendocument.text'
+
+ # if we were viewing the source of a file,
+ # then always save as plain text
+ actual_mimetype = self.metadata['mime_type'];
+ if 'source' in self.metadata and self.metadata['source'] == '1':
+ logger.debug('Writing file as type source (text/plain)')
+ actual_mimetype = 'text/plain'
+
+ 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)
+
+ def get_tutorials(self):
+ if getattr(self,"_tutorials",None) is None:
+ num_test_fsm = FiniteStateMachine("NUMS", state_dict=NUM_TEST)
+ self._tutorials = {"NUMS":Tutorial(_("Copy-paste and style"),num_test_fsm)}
+
+ return self._tutorials
diff --git a/tutorials/Writus.activity/TAbiWordActivity.pyc b/tutorials/Writus.activity/TAbiWordActivity.pyc
new file mode 100644
index 0000000..01a159e
--- /dev/null
+++ b/tutorials/Writus.activity/TAbiWordActivity.pyc
Binary files differ
diff --git a/tutorials/Writus.activity/TAbiWordActivity.py~ b/tutorials/Writus.activity/TAbiWordActivity.py~
new file mode 100644
index 0000000..8e38609
--- /dev/null
+++ b/tutorials/Writus.activity/TAbiWordActivity.py~
@@ -0,0 +1,434 @@
+# Copyright (C) 2006 by Martin Sevior
+# Copyright (C) 2006-2007 Marc Maurer <uwog@uwog.net>
+# Copyright (C) 2007, One Laptop Per Child
+#
+# 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 gettext import gettext as _
+import logging
+import os
+import time
+import shutil
+
+import dbus
+import gtk
+import telepathy
+import telepathy.client
+import gobject
+
+from sugar.activity.activity import Activity, ActivityToolbox, EditToolbar
+from sugar.presence import presenceservice
+
+from abiword import Canvas
+import toolbar
+from toolbar import WriteActivityToolbarExtension, WriteEditToolbar, TextToolbar, ImageToolbar, TableToolbar, FormatToolbar, ViewToolbar
+from sugar.activity.activity import get_bundle_path
+
+logger = logging.getLogger('twrite-activity')
+
+from sugar.tutorius.core import Tutorial, State, FiniteStateMachine
+from sugar.tutorius.actions import DialogMessage, OnceWrapper
+from sugar.tutorius.filters import GtkWidgetEventFilter, TimerEvent
+
+NUM_TEST = {
+ "INIT":State("INIT",
+ action_list=[
+ OnceWrapper(DialogMessage("Welcome to the text editor tutorial!\n\n Click on the canvas and type a letter.", [100,150])),
+ ],
+ event_filter_list=[
+ GtkWidgetEventFilter("TEXT","0.0.1.0.0.0","key-press-event"),
+ TimerEvent("LOST",15),
+ ],
+ ),
+ "LOST":State("LOST",
+ action_list=[DialogMessage("Click in the canvas and type on your keyboard", [400, 400]),],
+ event_filter_list=[
+ GtkWidgetEventFilter("TEXT","0.0.1.0.0.0","key-press-event"),
+ TimerEvent("INIT",5),
+ ],
+ ),
+ "TEXT":State("TEXT",
+ action_list=[OnceWrapper(DialogMessage(" You can type more letters if you want!\n\n" +
+ "To procede to the next step, select your text.\n\n Click and drag over the text!", [100,150])),],
+ event_filter_list=[
+ GtkWidgetEventFilter("SELECTED","0.0.1.0.0","text-selected"),
+ ],
+ ),
+ "SELECTED":State("SELECTED",
+ action_list=[
+ OnceWrapper(DialogMessage("To copy-paste text, go to the ^^^ EDIT toolbar.", [50, 100])),
+ OnceWrapper(DialogMessage("You can also use one of the big letters\n<- there to change the style of the text", [300, 5]))
+ ],
+ event_filter_list=[
+ GtkWidgetEventFilter("EDIT","0.0.0.0.1","focus"),
+ GtkWidgetEventFilter("BOLD","0.0.0.0.2.0.0.0","clicked"),
+ GtkWidgetEventFilter("ITALIC","0.0.0.0.2.0.0.1","clicked"),
+ GtkWidgetEventFilter("UNDER","0.0.0.0.2.0.0.2","clicked"),
+ ],
+ ),
+ "BOLD":State("BOLD",
+ action_list=[OnceWrapper(DialogMessage("The text is now bold! Wow!", [50, 60])),],
+ event_filter_list=[
+ GtkWidgetEventFilter("TEXT","0.0.0.0.2.0.0.0.0","leave-notify-event"),
+ ],
+ ),
+ "ITALIC":State("ITALIC",
+ action_list=[
+ OnceWrapper(DialogMessage("The text is now slanted! Wow!", [100, 60])),
+ ],
+ event_filter_list=[
+ GtkWidgetEventFilter("TEXT","0.0.0.0.2.0.0.1.0","leave-notify-event"),
+ ],
+ ),
+ "UNDER":State("UNDER",
+ action_list=[
+ OnceWrapper(DialogMessage("There is a line under the text! Wow!", [150, 60])),
+ ],
+ event_filter_list=[
+ GtkWidgetEventFilter("TEXT","0.0.0.0.2.0.0.2.0","leave-notify-event"),
+ ],
+ ),
+ "EDIT":State("EDIT",
+ action_list=[
+ OnceWrapper(DialogMessage("Use ^^^ Copy to remember the text\nthat is selected", [250, 68]))
+ ],
+ event_filter_list=[
+ GtkWidgetEventFilter("COPY","0.0.0.0.1.0.0.3","clicked"),
+ ],
+ ),
+ "COPY":State("COPY",
+ action_list=[
+ OnceWrapper(DialogMessage("Click ^^^ Paste to copy the remembered text where the cursor is", [315, 60]))
+ ],
+ event_filter_list=[
+ GtkWidgetEventFilter("PASTE","0.0.0.0.1.0.0.4","clicked"),
+ ],
+ ),
+ "PASTE":State("PASTE",
+ action_list=[DialogMessage("The text was copied! Good job!", [315, 60]),],
+ event_filter_list=[
+ GtkWidgetEventFilter("TEXT","0.0.0.0.1.0.0.4.0","leave-notify-event"),
+ ],
+ ),
+
+}
+
+
+class TAbiWordActivity (Activity):
+
+ def __init__ (self, handle):
+ Activity.__init__ (self, handle)
+ # 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()
+ toolbox.set_name("Toolbox")
+
+ activity_toolbar_ext = WriteActivityToolbarExtension(self, toolbox, self.abiword_canvas)
+
+ text_toolbar = TextToolbar(toolbox, self.abiword_canvas)
+ text_toolbar.set_name("TextToolbar")
+
+ self._edit_toolbar = WriteEditToolbar(toolbox, self.abiword_canvas, text_toolbar)
+ toolbox.add_toolbar(_('Edit'), self._edit_toolbar)
+ self._edit_toolbar.show()
+ self._edit_toolbar.set_name("EditToolbar")
+
+ toolbox.add_toolbar(_('Text'), text_toolbar)
+ text_toolbar.show()
+
+ image_toolbar = ImageToolbar(toolbox, self.abiword_canvas, self)
+ toolbox.add_toolbar(_('Image'), image_toolbar)
+ image_toolbar.show()
+
+ table_toolbar = TableToolbar(toolbox, self.abiword_canvas)
+ toolbox.add_toolbar(_('Table'), table_toolbar)
+ table_toolbar.show()
+
+ format_toolbar = FormatToolbar(toolbox, self.abiword_canvas)
+ toolbox.add_toolbar(_('Format'), format_toolbar)
+ format_toolbar.show()
+
+ view_toolbar = ViewToolbar(self.abiword_canvas)
+ toolbox.add_toolbar(_('View'), view_toolbar)
+ view_toolbar.show()
+
+ # the text toolbar should be our default toolbar
+ toolbox.set_current_toolbar(toolbar.TOOLBAR_TEXT)
+
+ self.set_canvas(self.abiword_canvas)
+ self.abiword_canvas.connect_after('map-event', self._map_event_cb)
+ self.abiword_canvas.show()
+ self.abiword_canvas.set_name("abiword-canvas")
+
+ 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" )
+ self.abiword_canvas.invoke_cmd('com.abisource.abiword.loadbindings.fromURI', keybindings_file, 0, 0)
+
+ # no ugly borders please
+ self.abiword_canvas.set_property("shadow-type", gtk.SHADOW_NONE)
+
+ # we only do per-word selections (when using the mouse)
+ self.abiword_canvas.set_word_selections(True)
+
+ # we want a nice border so we can select paragraphs easily
+ self.abiword_canvas.set_show_margin(True)
+
+ # activity sharing
+ self.participants = {}
+ pservice = presenceservice.get_instance()
+
+ bus = dbus.Bus()
+ name, path = pservice.get_preferred_connection()
+ self.conn = telepathy.client.Connection(name, path)
+ self.initiating = None
+ self.joined = False
+
+ self.connect('shared', self._shared_cb)
+
+ if self._shared_activity:
+ # we are joining the activity
+ logger.debug("We are joining an activity")
+ self.connect('joined', self._joined_cb)
+ self._shared_activity.connect('buddy-joined', self._buddy_joined_cb)
+ self._shared_activity.connect('buddy-left', self._buddy_left_cb)
+ if self.get_shared():
+# # oh, OK, we've already joined
+ self._joined_cb()
+ else:
+ # we are creating the activity
+ logger.debug("We are creating an activity")
+
+ owner = pservice.get_owner()
+
+ def _shared_cb(self, activity):
+ logger.debug('My TWrite activity was shared')
+ self.initiating = True
+ self._setup()
+
+ self._shared_activity.connect('buddy-joined', self._buddy_joined_cb)
+ self._shared_activity.connect('buddy-left', self._buddy_left_cb)
+
+ logger.debug('This is my activity: offering a tube...')
+ id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube(
+ "com.abisource.abiword.abicollab", {})
+ logger.debug('Tube address: %s', self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].GetDBusTubeAddress(id))
+
+
+ def _setup(self):
+ logger.debug("_setup()")
+
+ if self._shared_activity is None:
+ logger.error('Failed to share or join activity')
+ return
+
+ bus_name, conn_path, channel_paths = self._shared_activity.get_channels()
+
+ # Work out what our room is called and whether we have Tubes already
+ room = None
+ tubes_chan = None
+ text_chan = None
+ for channel_path in channel_paths:
+ channel = telepathy.client.Channel(bus_name, channel_path)
+ htype, handle = channel.GetHandle()
+ if htype == telepathy.HANDLE_TYPE_ROOM:
+ logger.debug('Found our room: it has handle#%d "%s"',
+ handle, self.conn.InspectHandles(htype, [handle])[0])
+ room = handle
+ ctype = channel.GetChannelType()
+ if ctype == telepathy.CHANNEL_TYPE_TUBES:
+ logger.debug('Found our Tubes channel at %s', channel_path)
+ tubes_chan = channel
+ elif ctype == telepathy.CHANNEL_TYPE_TEXT:
+ logger.debug('Found our Text channel at %s', channel_path)
+ text_chan = channel
+
+ if room is None:
+ logger.error("Presence service didn't create a room")
+ return
+ if text_chan is None:
+ logger.error("Presence service didn't create a text channel")
+ return
+
+ # Make sure we have a Tubes channel - PS doesn't yet provide one
+ if tubes_chan is None:
+ logger.debug("Didn't find our Tubes negotation channel, requesting one...")
+ tubes_chan = self.conn.request_channel(telepathy.CHANNEL_TYPE_TUBES,
+ telepathy.HANDLE_TYPE_ROOM, room, True)
+ logger.debug("Got our tubes negotiation channel")
+
+ self.tubes_chan = tubes_chan
+ self.text_chan = text_chan
+
+ tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('NewTube',
+ self._new_tube_cb)
+
+ def _list_tubes_reply_cb(self, tubes):
+ for tube_info in tubes:
+ self._new_tube_cb(*tube_info)
+
+ def _list_tubes_error_cb(self, e):
+ logger.error('ListTubes() failed: %s', e)
+
+ def _joined_cb(self, activity):
+ logger.debug("_joined_cb()")
+ if not self._shared_activity:
+ return
+
+ self.joined = True
+ logger.debug('Joined an existing TWrite session')
+ self._setup()
+
+ logger.debug('This is not my activity: waiting for a tube...')
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes(
+ reply_handler=self._list_tubes_reply_cb,
+ error_handler=self._list_tubes_error_cb)
+
+ def _new_tube_cb(self, id, initiator, type, service, params, state):
+ logger.debug('New tube: ID=%d initiator=%d type=%d service=%s '
+ 'params=%r state=%d', id, initiator, type, service,
+ params, state)
+
+ if (type == telepathy.TUBE_TYPE_DBUS and
+ service == "com.abisource.abiword.abicollab"):
+ if state == telepathy.TUBE_STATE_LOCAL_PENDING:
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id)
+
+ initiator_path = None;
+ contacts = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].GetDBusNames(id)
+ #print 'dbus contact mapping',self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].GetDBusNames(id)
+ for i, struct in enumerate(contacts):
+ #print 'mapping i',i
+ handle, path = struct
+ if handle == initiator:
+ logger.debug('found initiator dbus path: %s', path)
+ initiator_path = path
+ break;
+
+ if initiator_path is None:
+ logger.error('Unable to get the dbus path of the tube initiator')
+ else:
+ # pass this tube to abicollab
+ address = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].GetDBusTubeAddress(id)
+ if self.joined:
+ logger.debug('Passing tube address to abicollab (join): %s', address)
+ self.abiword_canvas.invoke_cmd('com.abisource.abiword.abicollab.olpc.joinTube', address, 0, 0)
+ if initiator_path is not None:
+ logger.debug('Adding the initiator to the session: %s', initiator_path)
+ self.abiword_canvas.invoke_cmd('com.abisource.abiword.abicollab.olpc.buddyJoined', initiator_path, 0, 0)
+ else:
+ logger.debug('Passing tube address to abicollab (offer): %s', address)
+ self.abiword_canvas.invoke_cmd('com.abisource.abiword.abicollab.olpc.offerTube', address, 0, 0)
+
+ self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal('DBusNamesChanged',
+ self._on_dbus_names_changed)
+
+ # HACK, as DBusNamesChanged doesn't fire on buddies leaving
+ self.tubes_chan[telepathy.CHANNEL_INTERFACE_GROUP].connect_to_signal('MembersChanged',
+ self._on_members_changed)
+
+ def _on_dbus_names_changed(self, tube_id, added, removed):
+ logger.debug('_on_dbus_names_changed')
+# if tube_id == self.tube_id:
+ for handle, bus_name in added:
+ logger.debug('added handle: %s, with dbus_name: %s', handle, bus_name)
+ self.abiword_canvas.invoke_cmd('com.abisource.abiword.abicollab.olpc.buddyJoined', bus_name, 0, 0)
+ self.participants[handle] = bus_name
+
+# if handle == self.self_handle:
+ # I've just joined - set my unique name
+# print 'i\'ve just joined'
+# self.set_unique_name(bus_name)
+# self.participants[handle] = bus_name
+# self.bus_name_to_handle[bus_name] = handle
+
+# HACK: doesn't work yet, bad morgs!
+# for handle in removed:
+# logger.debug('removed handle: %s, with dbus name: %s', handle, bus_name)
+# bus_name = self.participants.pop(handle, None)
+
+ def _on_members_changed(self, message, added, removed, local_pending, remote_pending, actor, reason):
+ logger.debug("_on_members_changed")
+ for handle in removed:
+ bus_name = self.participants.pop(handle, None)
+ if bus_name is None:
+ # FIXME: that shouldn't happen so probably hide another bug.
+ # Should be investigated
+ continue
+
+ logger.debug('removed handle: %d, with dbus name: %s', handle,
+ bus_name)
+ self.abiword_canvas.invoke_cmd('com.abisource.abiword.abicollab.olpc.buddyLeft', bus_name, 0, 0)
+
+ def _buddy_joined_cb (self, activity, buddy):
+ logger.debug('buddy joined with object path: %s', buddy.object_path())
+# self.abiword_canvas.invoke_cmd('com.abisource.abiword.abicollab.olpc.buddyJoined', buddy.object_path(), 0, 0)
+
+ def _buddy_left_cb (self, activity, buddy):
+ logger.debug('buddy left with object path: %s', buddy.object_path())
+ #self.abiword_canvas.invoke_cmd('com.abisource.abiword.abicollab.olpc.buddyLeft', self.participants[buddy.object_path()], 0, 0)
+
+ def read_file(self, file_path):
+ logging.debug('AbiWordActivity.read_file: %s, mimetype: %s', file_path, self.metadata['mime_type'])
+ if 'source' in self.metadata and self.metadata['source'] == '1':
+ logger.debug('Opening file in view source mode')
+ self.abiword_canvas.load_file('file://' + file_path, 'text/plain')
+ else:
+ self.abiword_canvas.load_file('file://' + file_path, '') # we pass no mime/file type, let libabiword autodetect it, so we can handle multiple file formats
+
+ def write_file(self, file_path):
+ logging.debug('AbiWordActivity.write_file')
+
+ # check if we have a default mimetype; if not, fall back to OpenDocument
+ # also fallback if we know we cannot export in that format
+ if 'mime_type' not in self.metadata or self.metadata['mime_type'] == '' or \
+ self.metadata['mime_type'] == 'application/msword':
+ self.metadata['mime_type'] = 'application/vnd.oasis.opendocument.text'
+
+ # if we were viewing the source of a file,
+ # then always save as plain text
+ actual_mimetype = self.metadata['mime_type'];
+ if 'source' in self.metadata and self.metadata['source'] == '1':
+ logger.debug('Writing file as type source (text/plain)')
+ actual_mimetype = 'text/plain'
+
+ 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)
+
+ def get_tutorials(self):
+ if getattr(self,"_tutorials",None) is None:
+ num_test_fsm = FiniteStateMachine("NUMS", state_dict=NUM_TEST)
+ self._tutorials = {"NUMS":Tutorial(_("Woot"),num_test_fsm)}
+
+ return self._tutorials
diff --git a/tutorials/Writus.activity/activity/activity-write.svg b/tutorials/Writus.activity/activity/activity-write.svg
new file mode 100644
index 0000000..45c7d48
--- /dev/null
+++ b/tutorials/Writus.activity/activity/activity-write.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ <!ENTITY stroke_color "#010101">
+ <!ENTITY fill_color "#FFFFFF">
+]><svg enable-background="new 0 0 55 55" height="55px" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="activity-write">
+ <g display="inline">
+ <g>
+ <path d="M36.519,37.127c0,0.008-0.031,0.047-0.034,0.049 c-0.055,0.07-0.131,0.113-0.196,0.162c-0.012,0.016-0.012,0.035-0.022,0.043l-7.826,5.037c-0.096,0.064-0.218,0.053-0.307-0.02 c-0.087-0.072-0.115-0.191-0.069-0.295l3.475-8.266l2.088-2.717l10.192-13.495V6.088H22.876L10.932,18.027v30.887h32.887V27.802 l-5.469,7.054L36.519,37.127z" fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5"/>
+ <path d="M43.818,17.626L33.626,31.121l-2.088,2.717l-3.475,8.266 c-0.046,0.104-0.018,0.223,0.069,0.295c0.089,0.072,0.211,0.084,0.307,0.02l7.826-5.037c0.011-0.008,0.011-0.027,0.022-0.043 c0.065-0.049,0.142-0.092,0.196-0.162c0.003-0.002,0.034-0.041,0.034-0.049l1.831-2.271l5.469-7.054l8.011-10.329 c0.022-0.023,0.149-0.203,0.149-0.203c0.096-0.161,0.148-0.345,0.148-0.556c0-0.061,0-0.118-0.012-0.176 c-0.087-0.763-0.715-1.682-1.639-2.413c-0.923-0.728-1.969-1.123-2.728-1.034c-0.326,0.042-0.789,0.375-0.789,0.375 L43.818,17.626z" fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5"/>
+ </g>
+ <polyline fill="none" points="10.932,18.027 22.876,18.027 22.876,6.088 " stroke="&stroke_color;" stroke-width="3.5"/>
+ </g>
+</g></svg>
+
diff --git a/tutorials/Writus.activity/activity/activity.info b/tutorials/Writus.activity/activity/activity.info
new file mode 100644
index 0000000..a05433a
--- /dev/null
+++ b/tutorials/Writus.activity/activity/activity.info
@@ -0,0 +1,9 @@
+[Activity]
+name = Writus
+service_name = org.laptop.TAbiWordActivity
+class = TAbiWordActivity.TAbiWordActivity
+icon = activity-write
+activity_version = 1
+show_launcher = 1
+mime_types = text/rtf;text/plain;application/x-abiword;text/x-xml-abiword;application/msword;application/rtf;application/xhtml+xml;text/html;application/vnd.oasis.opendocument.text
+license = GPLv2+
diff --git a/tutorials/Writus.activity/keybindings.xml b/tutorials/Writus.activity/keybindings.xml
new file mode 100644
index 0000000..275e975
--- /dev/null
+++ b/tutorials/Writus.activity/keybindings.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<editbindings name="default" mode="append" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://www.abisource.com/abiword-keybindings-1.0.xsd">
+ <unbind-mappings handler="contextEmbedLayout" />
+ <unbind-mappings handler="contextPosObject" />
+ <unbind-mappings handler="contextTOC" />
+ <unbind-mappings handler="contextText" />
+ <unbind-mappings handler="contextFrame" />
+ <unbind-mappings handler="contextRevision" />
+ <unbind-mappings handler="contextHyperlink" />
+ <unbind-mappings handler="contextImage" />
+ <unbind-mappings handler="contextMisspellText" />
+ <unbind-mappings handler="contextMenu" />
+ <unbind-mappings handler="dlgFmtPosImage" />
+ <unbind-mappings handler="dlgFmtImage" />
+ <unbind-mappings handler="dlgFont" />
+ <unbind-mappings handler="dlgSpell" />
+ <unbind-mappings handler="viewFullScreen" />
+ <unbind-mappings handler="querySaveAndExit" />
+ <unbind-mappings handler="findAgain" />
+ <unbind-mappings handler="helpContents" />
+ <unbind-mappings handler="find" />
+ <unbind-mappings handler="go" />
+ <unbind-mappings handler="replace" />
+ <unbind-mappings handler="insSymbol" />
+ <unbind-mappings handler="fileNewUsingTemplate" />
+ <unbind-mappings handler="fileOpen" />
+ <unbind-mappings handler="print" />
+ <unbind-mappings handler="fileSaveAs" />
+ <unbind-mappings handler="closeWindow" />
+ <unbind-mappings handler="fileNew" />
+ <unbind-mappings handler="fileSave" />
+ <unbind-mappings handler="cycleWindows" />
+ <unbind-mappings handler="togglePlain" />
+ <unbind-mappings handler="Test_Ftr" />
+ <unbind-mappings handler="Test_Dump" />
+</editbindings>
diff --git a/tutorials/Writus.activity/locale/af/activity.linfo b/tutorials/Writus.activity/locale/af/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/af/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/am/activity.linfo b/tutorials/Writus.activity/locale/am/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/am/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/ar/activity.linfo b/tutorials/Writus.activity/locale/ar/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/ar/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/ay/activity.linfo b/tutorials/Writus.activity/locale/ay/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/ay/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/bg/activity.linfo b/tutorials/Writus.activity/locale/bg/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/bg/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/bn/activity.linfo b/tutorials/Writus.activity/locale/bn/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/bn/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/bn_IN/activity.linfo b/tutorials/Writus.activity/locale/bn_IN/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/bn_IN/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/ca/activity.linfo b/tutorials/Writus.activity/locale/ca/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/ca/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/de/activity.linfo b/tutorials/Writus.activity/locale/de/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/de/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/dz/activity.linfo b/tutorials/Writus.activity/locale/dz/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/dz/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/el/activity.linfo b/tutorials/Writus.activity/locale/el/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/el/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/en/activity.linfo b/tutorials/Writus.activity/locale/en/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/en/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/es/activity.linfo b/tutorials/Writus.activity/locale/es/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/es/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/fa/activity.linfo b/tutorials/Writus.activity/locale/fa/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/fa/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/fa_AF/activity.linfo b/tutorials/Writus.activity/locale/fa_AF/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/fa_AF/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/ff/activity.linfo b/tutorials/Writus.activity/locale/ff/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/ff/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/fr/activity.linfo b/tutorials/Writus.activity/locale/fr/activity.linfo
new file mode 100644
index 0000000..631a440
--- /dev/null
+++ b/tutorials/Writus.activity/locale/fr/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Tutorius
diff --git a/tutorials/Writus.activity/locale/gu/activity.linfo b/tutorials/Writus.activity/locale/gu/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/gu/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/ha/activity.linfo b/tutorials/Writus.activity/locale/ha/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/ha/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/hi/activity.linfo b/tutorials/Writus.activity/locale/hi/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/hi/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/ht/activity.linfo b/tutorials/Writus.activity/locale/ht/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/ht/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/ig/activity.linfo b/tutorials/Writus.activity/locale/ig/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/ig/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/is/activity.linfo b/tutorials/Writus.activity/locale/is/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/is/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/it/activity.linfo b/tutorials/Writus.activity/locale/it/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/it/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/ja/activity.linfo b/tutorials/Writus.activity/locale/ja/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/ja/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/km/activity.linfo b/tutorials/Writus.activity/locale/km/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/km/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/ko/activity.linfo b/tutorials/Writus.activity/locale/ko/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/ko/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/mk/activity.linfo b/tutorials/Writus.activity/locale/mk/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/mk/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/ml/activity.linfo b/tutorials/Writus.activity/locale/ml/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/ml/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/mn/activity.linfo b/tutorials/Writus.activity/locale/mn/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/mn/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/mr/activity.linfo b/tutorials/Writus.activity/locale/mr/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/mr/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/mvo/activity.linfo b/tutorials/Writus.activity/locale/mvo/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/mvo/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/nb/activity.linfo b/tutorials/Writus.activity/locale/nb/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/nb/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/ne/activity.linfo b/tutorials/Writus.activity/locale/ne/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/ne/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/nl/activity.linfo b/tutorials/Writus.activity/locale/nl/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/nl/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/pa/activity.linfo b/tutorials/Writus.activity/locale/pa/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/pa/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/pap/activity.linfo b/tutorials/Writus.activity/locale/pap/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/pap/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/pis/activity.linfo b/tutorials/Writus.activity/locale/pis/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/pis/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/pl/activity.linfo b/tutorials/Writus.activity/locale/pl/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/pl/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/ps/activity.linfo b/tutorials/Writus.activity/locale/ps/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/ps/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/pt/activity.linfo b/tutorials/Writus.activity/locale/pt/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/pt/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/pt_BR/activity.linfo b/tutorials/Writus.activity/locale/pt_BR/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/pt_BR/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/qu/activity.linfo b/tutorials/Writus.activity/locale/qu/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/qu/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/ro/activity.linfo b/tutorials/Writus.activity/locale/ro/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/ro/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/ru/activity.linfo b/tutorials/Writus.activity/locale/ru/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/ru/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/rw/activity.linfo b/tutorials/Writus.activity/locale/rw/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/rw/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/sd/activity.linfo b/tutorials/Writus.activity/locale/sd/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/sd/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/si/activity.linfo b/tutorials/Writus.activity/locale/si/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/si/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/sl/activity.linfo b/tutorials/Writus.activity/locale/sl/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/sl/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/te/activity.linfo b/tutorials/Writus.activity/locale/te/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/te/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/th/activity.linfo b/tutorials/Writus.activity/locale/th/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/th/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/tpi/activity.linfo b/tutorials/Writus.activity/locale/tpi/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/tpi/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/tr/activity.linfo b/tutorials/Writus.activity/locale/tr/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/tr/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/ur/activity.linfo b/tutorials/Writus.activity/locale/ur/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/ur/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/vi/activity.linfo b/tutorials/Writus.activity/locale/vi/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/vi/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/yo/activity.linfo b/tutorials/Writus.activity/locale/yo/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/yo/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/zh_CN/activity.linfo b/tutorials/Writus.activity/locale/zh_CN/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/zh_CN/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/locale/zh_TW/activity.linfo b/tutorials/Writus.activity/locale/zh_TW/activity.linfo
new file mode 100644
index 0000000..ef58029
--- /dev/null
+++ b/tutorials/Writus.activity/locale/zh_TW/activity.linfo
@@ -0,0 +1,2 @@
+[Activity]
+name = Écrire
diff --git a/tutorials/Writus.activity/setup.py b/tutorials/Writus.activity/setup.py
new file mode 100755
index 0000000..fae74b8
--- /dev/null
+++ b/tutorials/Writus.activity/setup.py
@@ -0,0 +1,22 @@
+#!/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/tutorials/Writus.activity/toolbar.py b/tutorials/Writus.activity/toolbar.py
new file mode 100644
index 0000000..ad56e16
--- /dev/null
+++ b/tutorials/Writus.activity/toolbar.py
@@ -0,0 +1,780 @@
+# Copyright (C) 2006, Martin Sevior
+# Copyright (C) 2006-2007, Marc Maurer <uwog@uwog.net>
+# Copyright (C) 2007, One Laptop Per Child
+#
+# 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 gettext import gettext as _
+import logging
+import os
+import time
+
+import abiword
+import gtk
+
+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.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.graphics.menuitem import MenuItem
+from sugar.datastore import datastore
+
+import sugar.profile
+
+import dbus
+
+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
+ _EXPORT_FORMATS = [['application/rtf', _('Rich Text (RTF)'), _('RTF'), ""],
+ ['text/html', _('Hypertext (HTML)'), _('HTML'), "html4:yes; declare-xml:no; embed-css:yes; embed-images:yes;"],
+ ['text/plain', _('Plain Text (TXT)'), _('TXT'), ""]]
+
+ def __init__(self, activity, toolbox, abiword_canvas):
+
+ self._activity = activity
+ self._abiword_canvas = abiword_canvas
+ self._activity_toolbar = toolbox.get_activity_toolbar()
+ self._keep_palette = self._activity_toolbar.keep.get_palette()
+
+ # hook up the export formats to the Keep button
+ for i, f in enumerate(self._EXPORT_FORMATS):
+ menu_item = MenuItem(f[1])
+ menu_item.connect('activate', self._export_as_cb, f[0], f[2], f[3])
+ self._keep_palette.menu.append(menu_item)
+ menu_item.show()
+
+ def _export_as_cb(self, menu_item, mimetype, jpostfix, exp_props):
+ logger.debug('exporting file, mimetype: %s, exp_props: %s', mimetype, exp_props);
+
+ # special case HTML export to set the activity name as the HTML title
+ if mimetype == "text/html":
+ exp_props += " title:" + self._activity.metadata['title'] + ';';
+
+ # create a new journal item
+ fileObject = datastore.create()
+ act_meta = self._activity.metadata
+ fileObject.metadata['title'] = act_meta['title'] + ' (' + jpostfix + ')';
+ fileObject.metadata['title_set_by_user'] = act_meta['title_set_by_user']
+ fileObject.metadata['mime_type'] = mimetype
+ fileObject.metadata['fulltext'] = \
+ self._abiword_canvas.get_content(extension_or_mimetype=".txt")[:3000]
+
+ fileObject.metadata['icon-color'] = act_meta['icon-color']
+ fileObject.metadata['activity'] = act_meta['activity']
+ fileObject.metadata['keep'] = act_meta['keep']
+
+# TODO: Activity class should provide support for preview, see #5119
+# self._activity.take_screenshot()
+# if self._activity._preview:
+# preview = self._activity._get_preview()
+# fileObject.metadata['preview'] = dbus.ByteArray(preview)
+
+ fileObject.metadata['share-scope'] = act_meta['share-scope']
+
+ # write out the document contents in the requested format
+ fileObject.file_path = os.path.join(self._activity.get_activity_root(), 'instance', '%i' % time.time())
+ self._abiword_canvas.save('file://' + fileObject.file_path, mimetype, exp_props)
+
+ # store the journal item
+ datastore.write(fileObject, transfer_ownership=True)
+ fileObject.destroy()
+ del fileObject
+
+class WriteEditToolbar(EditToolbar):
+
+ def __init__(self, toolbox, abiword_canvas, text_toolbar):
+
+ EditToolbar.__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,
+ 'system-search')
+ self._search_entry.connect('activate', self._search_entry_activated_cb)
+ self._search_entry.connect('changed', self._search_entry_changed_cb)
+ self._search_entry.add_clear_button();
+ self._add_widget(self._search_entry, expand=True)
+
+ self._findprev = ToolButton('go-previous')
+ self._findprev.set_tooltip(_('Find previous'))
+ self.insert(self._findprev, -1)
+ self._findprev.show()
+ self._findprev.connect('clicked', self._findprev_cb);
+
+ self._findnext = ToolButton('go-next')
+ self._findnext.set_tooltip(_('Find next'))
+ self.insert(self._findnext, -1)
+ self._findnext.show()
+ self._findnext.connect('clicked', self._findnext_cb);
+
+ # set the initial state of the search controls
+ # note: we won't simple call self._search_entry_changed_cb
+ # here, as that will call into the abiword_canvas, which
+ # is not mapped on screen here, causing the set_find_string
+ # call to fail
+ 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)
+
+ if not self._search_entry.props.text:
+ self._search_entry.activate()
+ # set the button contexts
+ self._findprev.set_sensitive(False)
+ self._findnext.set_sensitive(False)
+ return
+
+ self._abiword_canvas.set_find_string(self._search_entry.props.text)
+
+ # set the button contexts
+ self._findprev.set_sensitive(True)
+ 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!')
+
+ # bad foddex! this function was copied from sugar's activity.py
+ 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()
+
+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 = gtk.ColorButton()
+ 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(newcolor.red // 256.0, newcolor.green // 256.0, 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)
+ 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):
+ 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)
+ tool_item.show_all()
+
+ self._table_rows_after = ToolButton('row-insert')
+ 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()
+
+ self._abiword_canvas.connect('table-state', self._isTable_cb)
+
+ def _table_cb(self, abi, rows, cols):
+ self._abiword_canvas.insert_table(rows,cols)
+
+ def _table_rows_after_cb(self, button):
+ self._abiword_canvas.invoke_cmd('insertRowsAfter', '', 0, 0)
+
+ def _table_delete_rows_cb(self, button):
+ self._abiword_canvas.invoke_cmd('deleteRows', '', 0, 0)
+
+ def _table_cols_after_cb(self, button):
+ self._abiword_canvas.invoke_cmd('insertColsAfter', '', 0, 0)
+
+ def _table_delete_cols_cb(self, button):
+ self._abiword_canvas.invoke_cmd('deleteColumns', '', 0, 0)
+
+ def _isTable_cb(self, abi, b):
+ self._table_rows_after.set_sensitive(b)
+ 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):
+ gtk.Toolbar.__init__(self)
+
+ self._abiword_canvas = abiword_canvas
+ self._zoom_percentage = 0;
+
+ self._zoom_out = ToolButton('zoom-out')
+ self._zoom_out.set_tooltip(_('Zoom Out'))
+ self._zoom_out_id = self._zoom_out.connect('clicked', self._zoom_out_cb)
+ self.insert(self._zoom_out, -1)
+ self._zoom_out.show()
+
+ self._zoom_in = ToolButton('zoom-in')
+ self._zoom_in.set_tooltip(_('Zoom In'))
+ self._zoom_in_id = self._zoom_in.connect('clicked', self._zoom_in_cb)
+ self.insert(self._zoom_in, -1)
+ self._zoom_in.show()
+
+ # TODO: fix the initial value
+ self._zoom_spin_adj = gtk.Adjustment(0, 25, 400, 25, 50, 0)
+ self._zoom_spin = gtk.SpinButton(self._zoom_spin_adj, 0, 0)
+ self._zoom_spin_id = self._zoom_spin.connect('value-changed', self._zoom_spin_cb)
+ self._zoom_spin.set_numeric(True)
+ self._zoom_spin.show()
+ tool_item_zoom = gtk.ToolItem()
+ tool_item_zoom.add(self._zoom_spin)
+ self.insert(tool_item_zoom, -1)
+ tool_item_zoom.show()
+
+ zoom_perc_label = gtk.Label(_("%"))
+ zoom_perc_label.show()
+ tool_item_zoom_perc_label = gtk.ToolItem()
+ tool_item_zoom_perc_label.add(zoom_perc_label)
+ self.insert(tool_item_zoom_perc_label, -1)
+ tool_item_zoom_perc_label.show()
+
+ separator = gtk.SeparatorToolItem()
+ separator.set_draw(True)
+ separator.show()
+ self.insert(separator, -1)
+
+ page_label = gtk.Label(_("Page: "))
+ page_label.show()
+ tool_item_page_label = gtk.ToolItem()
+ tool_item_page_label.add(page_label)
+ self.insert(tool_item_page_label, -1)
+ tool_item_page_label.show()
+
+ self._page_spin_adj = gtk.Adjustment(0, 1, 0, 1, 1, 0)
+ self._page_spin = gtk.SpinButton(self._page_spin_adj, 0, 0)
+ self._page_spin_id = self._page_spin.connect('value-changed', self._page_spin_cb)
+ self._page_spin.set_numeric(True)
+ self._page_spin.show()
+ tool_item_page = gtk.ToolItem()
+ tool_item_page.add(self._page_spin)
+ self.insert(tool_item_page, -1)
+ tool_item_page.show()
+
+ self._total_page_label = gtk.Label(" / 0")
+ self._total_page_label.show()
+ tool_item = gtk.ToolItem()
+ tool_item.add(self._total_page_label)
+ self.insert(tool_item, -1)
+ tool_item.show()
+
+ self._abiword_canvas.connect("page-count", self._page_count_cb)
+ self._abiword_canvas.connect("current-page", self._current_page_cb)
+ self._abiword_canvas.connect("zoom", self._zoom_cb)
+
+ 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:
+ self._zoom_spin.set_value(zoom)
+ finally:
+ self._zoom_spin.handler_unblock(self._zoom_spin_id)
+
+ def _zoom_out_cb(self, button):
+ if self._zoom_percentage == 0:
+ self._zoom_percentage = self._abiword_canvas.get_zoom_percentage()
+ if self._zoom_percentage >= 50:
+ self.set_zoom_percentage(self._zoom_percentage - 25)
+
+ def _zoom_in_cb(self, button):
+ if self._zoom_percentage == 0:
+ self._zoom_percentage = self._abiword_canvas.get_zoom_percentage()
+ if self._zoom_percentage <= 375:
+ self.set_zoom_percentage(self._zoom_percentage + 25)
+
+ def _zoom_spin_cb(self, button):
+ self._zoom_percentage = self._zoom_spin.get_value_as_int()
+ self._abiword_canvas.set_zoom_percentage(self._zoom_percentage)
+
+ def _page_spin_cb(self, button):
+ self._page_num = self._page_spin.get_value_as_int()
+ self._abiword_canvas.set_current_page(self._page_num)
+
+ def _page_count_cb(self, canvas, count):
+ current_page = canvas.get_current_page_num()
+ self._page_spin_adj.set_all(current_page, 1, count, 1, 1, 0)
+ self._total_page_label.props.label = \
+ ' / ' + str(count)
+
+ def _current_page_cb(self, canvas, num):
+ self._page_spin.handler_block(self._page_spin_id)
+ try:
+ self._page_spin.set_value(num)
+ finally:
+ self._page_spin.handler_unblock(self._page_spin_id)
+
diff --git a/tutorials/Writus.activity/toolbar.pyc b/tutorials/Writus.activity/toolbar.pyc
new file mode 100644
index 0000000..5d7d5b4
--- /dev/null
+++ b/tutorials/Writus.activity/toolbar.pyc
Binary files differ