diff options
author | Simon Poirier <simpoir@gmail.com> | 2009-03-22 00:26:04 (GMT) |
---|---|---|
committer | Simon Poirier <simpoir@gmail.com> | 2009-03-22 00:26:04 (GMT) |
commit | 625f9bb52eda3215d82d1b76ec6a806459f321ce (patch) | |
tree | 4e1d9a00a5d830fd9f5392980d606e2143d24a30 /tutorials |
Initial commit
Diffstat (limited to 'tutorials')
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 Binary files differnew file mode 100644 index 0000000..01a159e --- /dev/null +++ b/tutorials/Writus.activity/TAbiWordActivity.pyc 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 Binary files differnew file mode 100644 index 0000000..5d7d5b4 --- /dev/null +++ b/tutorials/Writus.activity/toolbar.pyc |