def __init__(self, handle): activity.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 = DocumentView() self._new_instance = True toolbar_box = ToolbarBox() self.activity_button = ActivityToolbarButton(self) toolbar_box.toolbar.insert(self.activity_button, -1) separator = Gtk.SeparatorToolItem() separator.show() self.activity_button.props.page.insert(separator, 2) ExportButtonFactory(self, self.abiword_canvas) self.activity_button.show() edit_toolbar = ToolbarButton() edit_toolbar.props.page = EditToolbar(self, toolbar_box) edit_toolbar.props.icon_name = 'toolbar-edit' edit_toolbar.props.label = _('Edit') toolbar_box.toolbar.insert(edit_toolbar, -1) view_toolbar = ToolbarButton() view_toolbar.props.page = ViewToolbar(self.abiword_canvas) view_toolbar.props.icon_name = 'toolbar-view' view_toolbar.props.label = _('View') toolbar_box.toolbar.insert(view_toolbar, -1) # due to http://bugzilla.abisource.com/show_bug.cgi?id=13585 if self.abiword_canvas.get_version() != '3.0': self.speech_toolbar_button = ToolbarButton(icon_name='speak') toolbar_box.toolbar.insert(self.speech_toolbar_button, -1) self._init_speech() separator = Gtk.SeparatorToolItem() toolbar_box.toolbar.insert(separator, -1) text_toolbar = ToolbarButton() text_toolbar.props.page = TextToolbar(self.abiword_canvas) text_toolbar.props.icon_name = 'format-text' text_toolbar.props.label = _('Text') toolbar_box.toolbar.insert(text_toolbar, -1) para_toolbar = ToolbarButton() para_toolbar.props.page = ParagraphToolbar(self.abiword_canvas) para_toolbar.props.icon_name = 'paragraph-bar' para_toolbar.props.label = _('Paragraph') toolbar_box.toolbar.insert(para_toolbar, -1) insert_toolbar = ToolbarButton() insert_toolbar.props.page = InsertToolbar(self.abiword_canvas) insert_toolbar.props.icon_name = 'insert-table' insert_toolbar.props.label = _('Table') toolbar_box.toolbar.insert(insert_toolbar, -1) image = ToolButton('insert-picture') image.set_tooltip(_('Insert Image')) self._image_id = image.connect('clicked', self._image_cb) toolbar_box.toolbar.insert(image, -1) palette = image.get_palette() content_box = Gtk.VBox() palette.set_content(content_box) image_floating_checkbutton = Gtk.CheckButton(_('Floating')) image_floating_checkbutton.connect( 'toggled', self._image_floating_checkbutton_toggled_cb) content_box.pack_start(image_floating_checkbutton, True, True, 0) content_box.show_all() self.floating_image = False separator = Gtk.SeparatorToolItem() separator.props.draw = False separator.set_size_request(0, -1) separator.set_expand(True) separator.show() toolbar_box.toolbar.insert(separator, -1) stop = StopButton(self) toolbar_box.toolbar.insert(stop, -1) toolbar_box.show_all() self.set_toolbar_box(toolbar_box) # add a overlay to be able to show a icon while joining a shared doc overlay = Gtk.Overlay() overlay.add(self.abiword_canvas) overlay.show() self._connecting_box = ConnectingBox() overlay.add_overlay(self._connecting_box) self.set_canvas(overlay) # we want a nice border so we can select paragraphs easily self.abiword_canvas.set_show_margin(True) # Read default font face and size client = GConf.Client.get_default() self._default_font_face = client.get_string( '/desktop/sugar/activities/write/font_face') if not self._default_font_face: self._default_font_face = 'Sans' self._default_font_size = client.get_int( '/desktop/sugar/activities/write/font_size') if self._default_font_size == 0: self._default_font_size = 12 # activity sharing self.participants = {} self.joined = False self.connect('shared', self._shared_cb) if self.shared_activity: # we are joining the activity logger.error('We are joining an activity') # display a icon while joining self._connecting_box.show() # disable the abi widget self.abiword_canvas.set_sensitive(False) self._new_instance = False 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(): self._joined_cb(self) else: # we are creating the activity logger.error("We are creating an activity") self.abiword_canvas.zoom_width() self.abiword_canvas.show() self.connect_after('map-event', self.__map_activity_event_cb) self.abiword_canvas.connect('size-allocate', self.size_allocate_cb)
def __init__(self, handle): activity.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 = DocumentView() self._new_instance = True toolbar_box = ToolbarBox() self.activity_button = ActivityToolbarButton(self) toolbar_box.toolbar.insert(self.activity_button, -1) separator = Gtk.SeparatorToolItem() separator.show() self.activity_button.props.page.insert(separator, 2) ExportButtonFactory(self, self.abiword_canvas) self.activity_button.show() edit_toolbar = ToolbarButton() edit_toolbar.props.page = EditToolbar(self, toolbar_box) edit_toolbar.props.icon_name = 'toolbar-edit' edit_toolbar.props.label = _('Edit') toolbar_box.toolbar.insert(edit_toolbar, -1) view_toolbar = ToolbarButton() view_toolbar.props.page = ViewToolbar(self.abiword_canvas) view_toolbar.props.icon_name = 'toolbar-view' view_toolbar.props.label = _('View') toolbar_box.toolbar.insert(view_toolbar, -1) # due to http://bugzilla.abisource.com/show_bug.cgi?id=13585 if self.abiword_canvas.get_version() != '3.0': self.speech_toolbar_button = ToolbarButton(icon_name='speak') toolbar_box.toolbar.insert(self.speech_toolbar_button, -1) self._init_speech() separator = Gtk.SeparatorToolItem() toolbar_box.toolbar.insert(separator, -1) text_toolbar = ToolbarButton() text_toolbar.props.page = TextToolbar(self.abiword_canvas) text_toolbar.props.icon_name = 'format-text' text_toolbar.props.label = _('Text') toolbar_box.toolbar.insert(text_toolbar, -1) para_toolbar = ToolbarButton() para_toolbar.props.page = ParagraphToolbar(self.abiword_canvas) para_toolbar.props.icon_name = 'paragraph-bar' para_toolbar.props.label = _('Paragraph') toolbar_box.toolbar.insert(para_toolbar, -1) insert_toolbar = ToolbarButton() insert_toolbar.props.page = InsertToolbar(self.abiword_canvas) insert_toolbar.props.icon_name = 'insert-table' insert_toolbar.props.label = _('Table') toolbar_box.toolbar.insert(insert_toolbar, -1) image = ToolButton('insert-picture') image.set_tooltip(_('Insert Image')) self._image_id = image.connect('clicked', self.__image_cb) toolbar_box.toolbar.insert(image, -1) palette = image.get_palette() box = PaletteMenuBox() palette.set_content(box) box.show() menu_item = PaletteMenuItem(_('Floating')) menu_item.connect('activate', self.__image_cb, True) box.append_item(menu_item) menu_item.show() separator = Gtk.SeparatorToolItem() separator.props.draw = False separator.set_size_request(0, -1) separator.set_expand(True) separator.show() toolbar_box.toolbar.insert(separator, -1) stop = StopButton(self) toolbar_box.toolbar.insert(stop, -1) toolbar_box.show_all() self.set_toolbar_box(toolbar_box) # add a overlay to be able to show a icon while joining a shared doc overlay = Gtk.Overlay() overlay.add(self.abiword_canvas) overlay.show() self._connecting_box = ConnectingBox() overlay.add_overlay(self._connecting_box) self.set_canvas(overlay) # we want a nice border so we can select paragraphs easily self.abiword_canvas.set_show_margin(True) # Set default font face and size self._default_font_face = 'Sans' self._default_font_size = 12 # activity sharing self.participants = {} 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') # display a icon while joining self._connecting_box.show() # disable the abi widget self.abiword_canvas.set_sensitive(False) self._new_instance = False 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(): self._joined_cb(self) else: # we are creating the activity logger.debug("We are creating an activity") self.abiword_canvas.zoom_width() self.abiword_canvas.show() self.connect_after('map-event', self.__map_activity_event_cb) self.abiword_canvas.connect('size-allocate', self.size_allocate_cb)
class AbiWordActivity(activity.Activity): def __init__(self, handle): activity.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 = DocumentView() self._new_instance = True toolbar_box = ToolbarBox() self.activity_button = ActivityToolbarButton(self) toolbar_box.toolbar.insert(self.activity_button, -1) separator = Gtk.SeparatorToolItem() separator.show() self.activity_button.props.page.insert(separator, 2) ExportButtonFactory(self, self.abiword_canvas) self.activity_button.show() edit_toolbar = ToolbarButton() edit_toolbar.props.page = EditToolbar(self, toolbar_box) edit_toolbar.props.icon_name = 'toolbar-edit' edit_toolbar.props.label = _('Edit') toolbar_box.toolbar.insert(edit_toolbar, -1) view_toolbar = ToolbarButton() view_toolbar.props.page = ViewToolbar(self.abiword_canvas) view_toolbar.props.icon_name = 'toolbar-view' view_toolbar.props.label = _('View') toolbar_box.toolbar.insert(view_toolbar, -1) # due to http://bugzilla.abisource.com/show_bug.cgi?id=13585 if self.abiword_canvas.get_version() != '3.0': self.speech_toolbar_button = ToolbarButton(icon_name='speak') toolbar_box.toolbar.insert(self.speech_toolbar_button, -1) self._init_speech() separator = Gtk.SeparatorToolItem() toolbar_box.toolbar.insert(separator, -1) text_toolbar = ToolbarButton() text_toolbar.props.page = TextToolbar(self.abiword_canvas) text_toolbar.props.icon_name = 'format-text' text_toolbar.props.label = _('Text') toolbar_box.toolbar.insert(text_toolbar, -1) para_toolbar = ToolbarButton() para_toolbar.props.page = ParagraphToolbar(self.abiword_canvas) para_toolbar.props.icon_name = 'paragraph-bar' para_toolbar.props.label = _('Paragraph') toolbar_box.toolbar.insert(para_toolbar, -1) insert_toolbar = ToolbarButton() insert_toolbar.props.page = InsertToolbar(self.abiword_canvas) insert_toolbar.props.icon_name = 'insert-table' insert_toolbar.props.label = _('Table') toolbar_box.toolbar.insert(insert_toolbar, -1) image = ToolButton('insert-picture') image.set_tooltip(_('Insert Image')) self._image_id = image.connect('clicked', self._image_cb) toolbar_box.toolbar.insert(image, -1) palette = image.get_palette() content_box = Gtk.VBox() palette.set_content(content_box) image_floating_checkbutton = Gtk.CheckButton(_('Floating')) image_floating_checkbutton.connect( 'toggled', self._image_floating_checkbutton_toggled_cb) content_box.pack_start(image_floating_checkbutton, True, True, 0) content_box.show_all() self.floating_image = False separator = Gtk.SeparatorToolItem() separator.props.draw = False separator.set_size_request(0, -1) separator.set_expand(True) separator.show() toolbar_box.toolbar.insert(separator, -1) stop = StopButton(self) toolbar_box.toolbar.insert(stop, -1) toolbar_box.show_all() self.set_toolbar_box(toolbar_box) # add a overlay to be able to show a icon while joining a shared doc overlay = Gtk.Overlay() overlay.add(self.abiword_canvas) overlay.show() self._connecting_box = ConnectingBox() overlay.add_overlay(self._connecting_box) self.set_canvas(overlay) # we want a nice border so we can select paragraphs easily self.abiword_canvas.set_show_margin(True) # Read default font face and size client = GConf.Client.get_default() self._default_font_face = client.get_string( '/desktop/sugar/activities/write/font_face') if not self._default_font_face: self._default_font_face = 'Sans' self._default_font_size = client.get_int( '/desktop/sugar/activities/write/font_size') if self._default_font_size == 0: self._default_font_size = 12 # activity sharing self.participants = {} self.joined = False self.connect('shared', self._shared_cb) if self.shared_activity: # we are joining the activity logger.error('We are joining an activity') # display a icon while joining self._connecting_box.show() # disable the abi widget self.abiword_canvas.set_sensitive(False) self._new_instance = False 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(): self._joined_cb(self) else: # we are creating the activity logger.error("We are creating an activity") self.abiword_canvas.zoom_width() self.abiword_canvas.show() self.connect_after('map-event', self.__map_activity_event_cb) self.abiword_canvas.connect('size-allocate', self.size_allocate_cb) def _init_speech(self): import speech from speechtoolbar import SpeechToolbar if speech.supported: self.speech_toolbar = SpeechToolbar(self) self.speech_toolbar_button.set_page(self.speech_toolbar) self.speech_toolbar_button.show() def size_allocate_cb(self, abi, alloc): GObject.idle_add(abi.queue_draw) def __map_activity_event_cb(self, event, activity): # set custom keybindings for Write # we do it later because have problems if done before - OLPC #11049 logger.error('Loading keybindings') keybindings_file = os.path.join(get_bundle_path(), 'keybindings.xml') self.abiword_canvas.invoke_ex( 'com.abisource.abiword.loadbindings.fromURI', keybindings_file, 0, 0) # set default font if self._new_instance: self.abiword_canvas.select_all() logging.error('Setting default font to %s %d in new documents', self._default_font_face, self._default_font_size) self.abiword_canvas.set_font_name(self._default_font_face) self.abiword_canvas.set_font_size(str(self._default_font_size)) self.abiword_canvas.moveto_bod() self.abiword_canvas.select_bod() if hasattr(self.abiword_canvas, 'toggle_rulers'): # this is not available yet on upstream abiword self.abiword_canvas.view_print_layout() self.abiword_canvas.toggle_rulers(False) self.abiword_canvas.grab_focus() def get_preview(self): if not hasattr(self.abiword_canvas, 'render_page_to_image'): return activity.Activity.get_preview(self) from gi.repository import GdkPixbuf pixbuf = self.abiword_canvas.render_page_to_image(1) pixbuf = pixbuf.scale_simple(style.zoom(300), style.zoom(225), GdkPixbuf.InterpType.BILINEAR) preview_data = [] def save_func(buf, lenght, data): data.append(buf) return True pixbuf.save_to_callbackv(save_func, preview_data, 'png', [], []) preview_data = ''.join(preview_data) return preview_data def _shared_cb(self, activity): logger.error('My Write activity was shared') self._sharing_setup() self.shared_activity.connect('buddy-joined', self._buddy_joined_cb) self.shared_activity.connect('buddy-left', self._buddy_left_cb) channel = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES] logger.error('This is my activity: offering a tube...') id = channel.OfferDBusTube('com.abisource.abiword.abicollab', {}) logger.error('Tube address: %s', channel.GetDBusTubeAddress(id)) def _sharing_setup(self): logger.debug("_sharing_setup()") if self.shared_activity is None: logger.error('Failed to share or join activity') return self.conn = self.shared_activity.telepathy_conn self.tubes_chan = self.shared_activity.telepathy_tubes_chan self.text_chan = self.shared_activity.telepathy_text_chan self.tube_id = None self.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.error("_joined_cb()") if not self.shared_activity: self._enable_collaboration() return self.joined = True logger.error('Joined an existing Write session') self._sharing_setup() logger.error('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) self._enable_collaboration() def _enable_collaboration(self): """ when communication established, hide the download icon and enable the abi widget """ self.abiword_canvas.zoom_width() self.abiword_canvas.set_sensitive(True) self._connecting_box.hide() def _new_tube_cb(self, id, initiator, type, service, params, state): logger.error('New tube: ID=%d initiator=%d type=%d service=%s ' 'params=%r state=%d', id, initiator, type, service, params, state) if self.tube_id is not None: # We are already using a tube return if type != telepathy.TUBE_TYPE_DBUS or \ service != "com.abisource.abiword.abicollab": return channel = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES] if state == telepathy.TUBE_STATE_LOCAL_PENDING: channel.AcceptDBusTube(id) # look for the initiator's D-Bus unique name initiator_dbus_name = None dbus_names = channel.GetDBusNames(id) for handle, name in dbus_names: if handle == initiator: logger.error('found initiator D-Bus name: %s', name) initiator_dbus_name = name break if initiator_dbus_name is None: logger.error('Unable to get the D-Bus name of the tube initiator') return cmd_prefix = 'com.abisource.abiword.abicollab.olpc.' # pass this tube to abicollab address = channel.GetDBusTubeAddress(id) if self.joined: logger.error('Passing tube address to abicollab (join): %s', address) self.abiword_canvas.invoke_ex(cmd_prefix + 'joinTube', address, 0, 0) # The intiator of the session has to be the first passed # to the Abicollab backend. logger.error('Adding the initiator to the session: %s', initiator_dbus_name) self.abiword_canvas.invoke_ex(cmd_prefix + 'buddyJoined', initiator_dbus_name, 0, 0) else: logger.error('Passing tube address to abicollab (offer): %s', address) self.abiword_canvas.invoke_ex(cmd_prefix + 'offerTube', address, 0, 0) self.tube_id = id channel.connect_to_signal('DBusNamesChanged', self._on_dbus_names_changed) self._on_dbus_names_changed(id, dbus_names, []) def _on_dbus_names_changed(self, tube_id, added, removed): """ We call com.abisource.abiword.abicollab.olpc.buddy{Joined,Left} according members of the D-Bus tube. That's why we don't add/remove buddies in _buddy_{joined,left}_cb. """ logger.error('_on_dbus_names_changed') # if tube_id == self.tube_id: cmd_prefix = 'com.abisource.abiword.abicollab.olpc' for handle, bus_name in added: logger.error('added handle: %s, with dbus_name: %s', handle, bus_name) self.abiword_canvas.invoke_ex(cmd_prefix + '.buddyJoined', bus_name, 0, 0) self.participants[handle] = bus_name def _on_members_changed(self, message, added, removed, local_pending, remote_pending, actor, reason): logger.error("_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 cmd_prefix = 'com.abisource.abiword.abicollab.olpc' logger.error('removed handle: %d, with dbus name: %s', handle, bus_name) self.abiword_canvas.invoke_ex(cmd_prefix + '.buddyLeft', bus_name, 0, 0) def _buddy_joined_cb(self, activity, buddy): logger.error('buddy joined with object path: %s', buddy.object_path()) def _buddy_left_cb(self, activity, buddy): logger.error('buddy left with object path: %s', buddy.object_path()) def read_file(self, file_path): logging.debug('AbiWordActivity.read_file: %s, mimetype: %s', file_path, self.metadata['mime_type']) if self._is_plain_text(self.metadata['mime_type']): self.abiword_canvas.load_file('file://' + file_path, 'text/plain') else: # we pass no mime/file type, let libabiword autodetect it, # so we can handle multiple file formats self.abiword_canvas.load_file('file://' + file_path, '') self.abiword_canvas.zoom_width() self._new_instance = False def write_file(self, file_path): logging.debug('AbiWordActivity.write_file: %s, mimetype: %s', file_path, self.metadata['mime_type']) # if we were editing a text file save as plain text if self._is_plain_text(self.metadata['mime_type']): logger.debug('Writing file as type source (text/plain)') self.abiword_canvas.save('file://' + file_path, 'text/plain', '') else: #if the file is new, save in .odt format if self.metadata['mime_type'] == '': self.metadata['mime_type'] = 'application/rtf' # Abiword can't save in .doc format, save in .rtf instead if self.metadata['mime_type'] == 'application/msword': self.metadata['mime_type'] = 'application/rtf' self.abiword_canvas.save('file://' + file_path, self.metadata['mime_type'], '') # due to http://bugzilla.abisource.com/show_bug.cgi?id=13585 if self.abiword_canvas.get_version() != '3.0': self.metadata['fulltext'] = self.abiword_canvas.get_content( 'text/plain', None)[:3000] def _is_plain_text(self, mime_type): # These types have 'text/plain' in their mime_parents but we need # use it like rich text if mime_type in ['application/rtf', 'text/rtf', 'text/html']: return False from sugar3 import mime mime_parents = mime.get_mime_parents(self.metadata['mime_type']) return self.metadata['mime_type'] in ['text/plain', 'text/csv'] or \ 'text/plain' in mime_parents def _image_floating_checkbutton_toggled_cb(self, checkbutton): self.floating_image = checkbutton.get_active() def _image_cb(self, button): try: chooser = ObjectChooser(self, what_filter='Image', filter_type=FILTER_TYPE_GENERIC_MIME, show_preview=True) except: # for compatibility with older versions chooser = ObjectChooser(self, what_filter='Image') try: result = chooser.run() if result == Gtk.ResponseType.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, self.floating_image) finally: chooser.destroy() del chooser
class AbiWordActivity(activity.Activity): def __init__(self, handle): activity.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 = DocumentView() self._new_instance = True toolbar_box = ToolbarBox() self.activity_button = ActivityToolbarButton(self) toolbar_box.toolbar.insert(self.activity_button, -1) separator = Gtk.SeparatorToolItem() separator.show() self.activity_button.props.page.insert(separator, 2) ExportButtonFactory(self, self.abiword_canvas) self.activity_button.show() edit_toolbar = ToolbarButton() edit_toolbar.props.page = EditToolbar(self, toolbar_box) edit_toolbar.props.icon_name = 'toolbar-edit' edit_toolbar.props.label = _('Edit') toolbar_box.toolbar.insert(edit_toolbar, -1) view_toolbar = ToolbarButton() view_toolbar.props.page = ViewToolbar(self.abiword_canvas) view_toolbar.props.icon_name = 'toolbar-view' view_toolbar.props.label = _('View') toolbar_box.toolbar.insert(view_toolbar, -1) # due to http://bugzilla.abisource.com/show_bug.cgi?id=13585 if self.abiword_canvas.get_version() != '3.0': self.speech_toolbar_button = ToolbarButton(icon_name='speak') toolbar_box.toolbar.insert(self.speech_toolbar_button, -1) self._init_speech() separator = Gtk.SeparatorToolItem() toolbar_box.toolbar.insert(separator, -1) text_toolbar = ToolbarButton() text_toolbar.props.page = TextToolbar(self.abiword_canvas) text_toolbar.props.icon_name = 'format-text' text_toolbar.props.label = _('Text') toolbar_box.toolbar.insert(text_toolbar, -1) para_toolbar = ToolbarButton() para_toolbar.props.page = ParagraphToolbar(self.abiword_canvas) para_toolbar.props.icon_name = 'paragraph-bar' para_toolbar.props.label = _('Paragraph') toolbar_box.toolbar.insert(para_toolbar, -1) insert_toolbar = ToolbarButton() insert_toolbar.props.page = InsertToolbar(self.abiword_canvas) insert_toolbar.props.icon_name = 'insert-table' insert_toolbar.props.label = _('Table') toolbar_box.toolbar.insert(insert_toolbar, -1) image = ToolButton('insert-picture') image.set_tooltip(_('Insert Image')) self._image_id = image.connect('clicked', self.__image_cb) toolbar_box.toolbar.insert(image, -1) palette = image.get_palette() box = PaletteMenuBox() palette.set_content(box) box.show() menu_item = PaletteMenuItem(_('Floating')) menu_item.connect('activate', self.__image_cb, True) box.append_item(menu_item) menu_item.show() separator = Gtk.SeparatorToolItem() separator.props.draw = False separator.set_size_request(0, -1) separator.set_expand(True) separator.show() toolbar_box.toolbar.insert(separator, -1) stop = StopButton(self) toolbar_box.toolbar.insert(stop, -1) toolbar_box.show_all() self.set_toolbar_box(toolbar_box) # add a overlay to be able to show a icon while joining a shared doc overlay = Gtk.Overlay() overlay.add(self.abiword_canvas) overlay.show() self._connecting_box = ConnectingBox() overlay.add_overlay(self._connecting_box) self.set_canvas(overlay) # we want a nice border so we can select paragraphs easily self.abiword_canvas.set_show_margin(True) # Set default font face and size self._default_font_face = 'Sans' self._default_font_size = 12 # activity sharing self.participants = {} 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') # display a icon while joining self._connecting_box.show() # disable the abi widget self.abiword_canvas.set_sensitive(False) self._new_instance = False 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(): self._joined_cb(self) else: # we are creating the activity logger.debug("We are creating an activity") self.abiword_canvas.zoom_width() self.abiword_canvas.show() self.connect_after('map-event', self.__map_activity_event_cb) self.abiword_canvas.connect('size-allocate', self.size_allocate_cb) def _init_speech(self): import speech from speechtoolbar import SpeechToolbar if speech.supported: self.speech_toolbar = SpeechToolbar(self) self.speech_toolbar_button.set_page(self.speech_toolbar) self.speech_toolbar_button.show() def size_allocate_cb(self, abi, alloc): GObject.idle_add(abi.queue_draw) def __map_activity_event_cb(self, event, activity): # set custom keybindings for Write # we do it later because have problems if done before - OLPC #11049 logger.debug('Loading keybindings') keybindings_file = os.path.join(get_bundle_path(), 'keybindings.xml') self.abiword_canvas.invoke_ex( 'com.abisource.abiword.loadbindings.fromURI', keybindings_file, 0, 0) # set default font if self._new_instance: self.abiword_canvas.select_all() logging.debug('Setting default font to %s %d in new documents', self._default_font_face, self._default_font_size) self.abiword_canvas.set_font_name(self._default_font_face) self.abiword_canvas.set_font_size(str(self._default_font_size)) self.abiword_canvas.moveto_bod() self.abiword_canvas.select_bod() if hasattr(self.abiword_canvas, 'toggle_rulers'): # this is not available yet on upstream abiword self.abiword_canvas.view_print_layout() self.abiword_canvas.toggle_rulers(False) self.abiword_canvas.grab_focus() def get_preview(self): if not hasattr(self.abiword_canvas, 'render_page_to_image'): return activity.Activity.get_preview(self) from gi.repository import GdkPixbuf pixbuf = self.abiword_canvas.render_page_to_image(1) pixbuf = pixbuf.scale_simple(style.zoom(300), style.zoom(225), GdkPixbuf.InterpType.BILINEAR) preview_data = [] def save_func(buf, lenght, data): data.append(buf) return True pixbuf.save_to_callbackv(save_func, preview_data, 'png', [], []) preview_data = ''.join(preview_data) return preview_data def _shared_cb(self, activity): logger.debug('My Write activity was shared') self._sharing_setup() self.shared_activity.connect('buddy-joined', self._buddy_joined_cb) self.shared_activity.connect('buddy-left', self._buddy_left_cb) channel = self.tubes_chan[TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES] logger.debug('This is my activity: offering a tube...') id = channel.OfferDBusTube('com.abisource.abiword.abicollab', {}) logger.debug('Tube address: %s', channel.GetDBusTubeAddress(id)) def _sharing_setup(self): logger.debug("_sharing_setup()") if self.shared_activity is None: logger.error('Failed to share or join activity') return self.conn = self.shared_activity.telepathy_conn self.tubes_chan = self.shared_activity.telepathy_tubes_chan self.text_chan = self.shared_activity.telepathy_text_chan self.tube_id = None self.tubes_chan[ TelepathyGLib.IFACE_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: self._enable_collaboration() return self.joined = True logger.debug('Joined an existing Write session') self._sharing_setup() logger.debug('This is not my activity: waiting for a tube...') self.tubes_chan[TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES].ListTubes( reply_handler=self._list_tubes_reply_cb, error_handler=self._list_tubes_error_cb) self._enable_collaboration() def _enable_collaboration(self): """ when communication established, hide the download icon and enable the abi widget """ self.abiword_canvas.zoom_width() self.abiword_canvas.set_sensitive(True) self._connecting_box.hide() 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 self.tube_id is not None: # We are already using a tube return if type != TelepathyGLib.TubeType.DBUS or \ service != "com.abisource.abiword.abicollab": return channel = self.tubes_chan[TelepathyGLib.IFACE_CHANNEL_TYPE_TUBES] if state == TelepathyGLib.TubeState.LOCAL_PENDING: channel.AcceptDBusTube(id) # look for the initiator's D-Bus unique name initiator_dbus_name = None dbus_names = channel.GetDBusNames(id) for handle, name in dbus_names: if handle == initiator: logger.debug('found initiator D-Bus name: %s', name) initiator_dbus_name = name break if initiator_dbus_name is None: logger.error('Unable to get the D-Bus name of the tube initiator') return cmd_prefix = 'com.abisource.abiword.abicollab.olpc.' # pass this tube to abicollab address = channel.GetDBusTubeAddress(id) if self.joined: logger.debug('Passing tube address to abicollab (join): %s', address) self.abiword_canvas.invoke_ex(cmd_prefix + 'joinTube', address, 0, 0) # The intiator of the session has to be the first passed # to the Abicollab backend. logger.debug('Adding the initiator to the session: %s', initiator_dbus_name) self.abiword_canvas.invoke_ex(cmd_prefix + 'buddyJoined', initiator_dbus_name, 0, 0) else: logger.debug('Passing tube address to abicollab (offer): %s', address) self.abiword_canvas.invoke_ex(cmd_prefix + 'offerTube', address, 0, 0) self.tube_id = id channel.connect_to_signal('DBusNamesChanged', self._on_dbus_names_changed) self._on_dbus_names_changed(id, dbus_names, []) def _on_dbus_names_changed(self, tube_id, added, removed): """ We call com.abisource.abiword.abicollab.olpc.buddy{Joined,Left} according members of the D-Bus tube. That's why we don't add/remove buddies in _buddy_{joined,left}_cb. """ logger.debug('_on_dbus_names_changed') # if tube_id == self.tube_id: cmd_prefix = 'com.abisource.abiword.abicollab.olpc' for handle, bus_name in added: logger.debug('added handle: %s, with dbus_name: %s', handle, bus_name) self.abiword_canvas.invoke_ex(cmd_prefix + '.buddyJoined', bus_name, 0, 0) self.participants[handle] = bus_name 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 cmd_prefix = 'com.abisource.abiword.abicollab.olpc' logger.debug('removed handle: %d, with dbus name: %s', handle, bus_name) self.abiword_canvas.invoke_ex(cmd_prefix + '.buddyLeft', bus_name, 0, 0) def _buddy_joined_cb(self, activity, buddy): logger.debug('buddy joined with object path: %s', buddy.object_path()) def _buddy_left_cb(self, activity, buddy): logger.debug('buddy left with object path: %s', buddy.object_path()) def read_file(self, file_path): logging.debug('AbiWordActivity.read_file: %s, mimetype: %s', file_path, self.metadata['mime_type']) if self._is_plain_text(self.metadata['mime_type']): self.abiword_canvas.load_file('file://' + file_path, 'text/plain') else: # we pass no mime/file type, let libabiword autodetect it, # so we can handle multiple file formats self.abiword_canvas.load_file('file://' + file_path, '') self.abiword_canvas.zoom_width() self._new_instance = False def write_file(self, file_path): logging.debug('AbiWordActivity.write_file: %s, mimetype: %s', file_path, self.metadata['mime_type']) # if we were editing a text file save as plain text if self._is_plain_text(self.metadata['mime_type']): logger.debug('Writing file as type source (text/plain)') self.abiword_canvas.save('file://' + file_path, 'text/plain', '') else: #if the file is new, save in .odt format if self.metadata['mime_type'] == '': self.metadata['mime_type'] = 'application/rtf' # Abiword can't save in .doc format, save in .rtf instead if self.metadata['mime_type'] == 'application/msword': self.metadata['mime_type'] = 'application/rtf' self.abiword_canvas.save('file://' + file_path, self.metadata['mime_type'], '') # due to http://bugzilla.abisource.com/show_bug.cgi?id=13585 if self.abiword_canvas.get_version() != '3.0': self.metadata['fulltext'] = self.abiword_canvas.get_content( 'text/plain', None)[:3000] def _is_plain_text(self, mime_type): # These types have 'text/plain' in their mime_parents but we need # use it like rich text if mime_type in ['application/rtf', 'text/rtf', 'text/html']: return False from sugar3 import mime mime_parents = mime.get_mime_parents(self.metadata['mime_type']) return self.metadata['mime_type'] in ['text/plain', 'text/csv'] or \ 'text/plain' in mime_parents def __image_cb(self, button, floating=False): try: chooser = ObjectChooser(self, what_filter='Image', filter_type=FILTER_TYPE_GENERIC_MIME, show_preview=True) except: # for compatibility with older versions chooser = ObjectChooser(self, what_filter='Image') try: result = chooser.run() if result == Gtk.ResponseType.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, floating) finally: chooser.destroy() del chooser