Codebase list sugar-write-activity / debian/98-1 widgets.py
debian/98-1

Tree @debian/98-1 (Download .tar.gz)

widgets.py @debian/98-1raw · history · blame

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

import os
import dbus
import time
from gettext import gettext as _
import logging

from gi.repository import Abi
from gi.repository import GLib

from sugar3.graphics.radiotoolbutton import RadioToolButton
from sugar3.graphics.toolbutton import ToolButton
from sugar3.graphics.palettemenu import PaletteMenuItem
from sugar3.datastore import datastore

from sugar3.activity.activity import SCOPE_PRIVATE

logger = logging.getLogger('write-activity')


class AbiButton(RadioToolButton):

    def __init__(self, abi, abi_signal, do_abi_cb, on_abi_cb=None, **kwargs):
        RadioToolButton.__init__(self, **kwargs)

        self._abi_handler = abi.connect(abi_signal, self.__abi_cb,
                                        abi_signal, on_abi_cb)
        self._toggled_handler = self.connect('toggled', self.__toggled_cb,
                                             abi, do_abi_cb)

    def __toggled_cb(self, button, abi, do_abi_cb):
        if not button.props.active:
            return

        abi.handler_block(self._abi_handler)
        try:
            logging.debug('Do abi %s' % do_abi_cb)
            do_abi_cb()
        finally:
            abi.handler_unblock(self._abi_handler)

    def __abi_cb(self, abi, prop, abi_signal, on_abi_cb):
        if (on_abi_cb is None and not prop) or \
                (on_abi_cb is not None and not on_abi_cb(abi, prop)):
            return

        self.handler_block(self._toggled_handler)
        try:
            logging.debug('On abi %s prop=%r' % (abi_signal, prop))
            self.set_active(True)
        finally:
            self.handler_unblock(self._toggled_handler)


class AbiMenuItem(PaletteMenuItem):

    def __init__(self, abi, abi_signal, do_abi_cb, icon_name, label,
                 button, on_abi_cb=None, button_icon_name=None):
        self._icon_name = icon_name
        # _button_icon_name is used only in the first case of
        # the list menu
        self._button_icon_name = button_icon_name
        self._button = button
        PaletteMenuItem.__init__(self, icon_name=icon_name, text_label=label)

        self._abi_handler = abi.connect(abi_signal, self.__abi_cb,
                                        abi_signal, on_abi_cb)
        self.connect('activate', self.__activated_cb, abi, do_abi_cb)

    def __activated_cb(self, button, abi, do_abi_cb):

        if self._button_icon_name is not None:
            if self._button.get_icon_name() == self._button_icon_name:
                return
        else:
            if self._button.get_icon_name() == self._icon_name:
                return

        abi.handler_block(self._abi_handler)
        try:
            logging.debug('Do abi %s' % do_abi_cb)
            do_abi_cb()
            if self._button_icon_name is not None:
                self._button.set_icon_name(self._button_icon_name)
            else:
                self._button.set_icon_name(self._icon_name)
        finally:
            abi.handler_unblock(self._abi_handler)

    def __abi_cb(self, abi, prop, abi_signal, on_abi_cb):
        if (on_abi_cb is None and not prop) or \
                (on_abi_cb is not None and not on_abi_cb(abi, prop)):
            return

        logging.debug('On abi %s prop=%r' % (abi_signal, prop))
        if self._button_icon_name is not None:
            self._button.set_icon_name(self._button_icon_name)
        else:
            self._button.set_icon_name(self._icon_name)


class ExportButtonFactory():

    _EXPORT_FORMATS = [{'mime_type': 'application/rtf',
                        'title': _('Rich Text (RTF)'),
                        'icon': 'save-as-rtf',
                        'jpostfix': _('RTF'),
                        'exp_props': ''},

                       {'mime_type': 'text/html',
                        'title': _('Hypertext (HTML)'),
                        'icon': 'save-as-html',
                        'jpostfix': _('HTML'),
                        'exp_props': 'html4:yes; declare-xml:no; '
                                     'embed-css:yes; embed-images:yes;'},

                       {'mime_type': 'text/plain',
                        'title': _('Plain Text (TXT)'),
                        'icon': 'save-as-txt',
                        'jpostfix': _('TXT'),
                        'exp_props': ''},

                       {'mime_type': 'application/pdf',
                        'title': _('Portable Document Format (PDF)'),
                        'icon': 'save-as-pdf',
                        'jpostfix': _('PDF'),
                        'exp_props': ''}]

    def __init__(self, activity, abi):

        toolbar = activity.activity_button.props.page
        for i in self._EXPORT_FORMATS:
            if abi.get_version() == '3.0' and i['title'].find('PDF') > -1:
                # pdf export crashes on abiword 3.0
                continue
            button = ToolButton(i['icon'])
            button.set_tooltip(i['title'])
            button.connect('clicked', self.__clicked_cb, activity, abi, i)
            toolbar.insert(button, -1)
            button.show()

    def __clicked_cb(self, menu_item, activity, abi, format):
        logger.debug('exporting file: %r' % format)

        exp_props = format['exp_props']

        # special case HTML export to set the activity name as the HTML title
        if format['mime_type'] == "text/html":
            exp_props += " title:" + activity.metadata['title'] + ';'

        # create a new journal item
        fileObject = datastore.create()
        act_meta = activity.metadata
        fileObject.metadata['title'] = \
            act_meta['title'] + ' (' + format['jpostfix'] + ')'
        fileObject.metadata['title_set_by_user'] = \
            act_meta['title_set_by_user']
        fileObject.metadata['mime_type'] = format['mime_type']
        # due to http://bugzilla.abisource.com/show_bug.cgi?id=13585
        if abi.get_version() != '3.0':
            fileObject.metadata['fulltext'] = abi.get_content('text/plain',
                                                              None)[:3000]

        fileObject.metadata['icon-color'] = act_meta['icon-color']

        # don't set application if PDF because Write can't open PDF files
        if format['mime_type'] != 'application/pdf':
            fileObject.metadata['activity'] = act_meta['activity']

        fileObject.metadata['keep'] = act_meta.get('keep', '0')

        preview = activity.get_preview()
        if preview is not None:
            fileObject.metadata['preview'] = dbus.ByteArray(preview)

        fileObject.metadata['share-scope'] = act_meta.get('share-scope',
                                                          SCOPE_PRIVATE)

        # write out the document contents in the requested format
        fileObject.file_path = os.path.join(activity.get_activity_root(),
                                            'instance', '%i' % time.time())
        abi.save('file://' + fileObject.file_path,
                 format['mime_type'], exp_props)

        # store the journal item
        datastore.write(fileObject, transfer_ownership=True)
        fileObject.destroy()
        del fileObject


class DocumentView(Abi.Widget):

    def __init__(self):
        Abi.init([])
        Abi.Widget.__init__(self)
        self.connect('size-allocate', self.__size_allocate_cb)
        try:
            self.connect('request-clear-area', self.__request_clear_area_cb)
        except:
            logging.error('EXCEPTION: request-clear-area signal not available')

        try:
            self.connect('unset-clear-area', self.__unset_clear_area_cb)
        except:
            logging.error('EXCEPTION: unset-clear-area signal not available')

        self.osk_changed = False
        self.dy = 0

    def __shallow_move_cb(self):
        self.moveto_right()
        return False

    def __size_allocate_cb(self, widget, allocation):
        self.set_allocation(allocation)

        if self.get_child() is not None:
            child_allocation = allocation
            child_allocation.y = 0
            child_allocation.x = 0
            child_allocation.height -= self.dy
            self.get_child().size_allocate(allocation)

        if self.osk_changed is True:
            self.moveto_left()
            GLib.timeout_add(100, self.__shallow_move_cb)
            self.osk_changed = False

    def __request_clear_area_cb(self, widget, clear, cursor):
        allocation = widget.get_allocation()
        allocation.x = 0
        allocation.y = 0
        allocation.x, allocation.y = \
            widget.get_window().get_root_coords(allocation.x, allocation.y)

        if clear.y > allocation.y + allocation.height or \
                clear.y + clear.height < allocation.y:
            return False

        self.dy = allocation.y + allocation.height - clear.y

        # Ensure there's at least some room for the view
        if self.dy > allocation.height - 80:
            self.dy = 0
            return False

        self.osk_changed = True
        self.queue_resize()
        return True

    def __unset_clear_area_cb(self, widget, snap_back):
        self.dy = 0
        self.queue_resize()
        return True

    def get_version(self):
        version = Abi._version
        logging.error('Abiword version %s', version)
        return version