Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/tutorials/Writus.activity/TAbiWordActivity.py
diff options
context:
space:
mode:
Diffstat (limited to 'tutorials/Writus.activity/TAbiWordActivity.py')
-rw-r--r--tutorials/Writus.activity/TAbiWordActivity.py435
1 files changed, 435 insertions, 0 deletions
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