diff --git a/CHANGELOG b/CHANGELOG index f006e0503b..9c5cc46a3d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Support document widget * Support icon of type URL * Allow sending email with existing attachments * Add paths and model context to action data diff --git a/bin/tryton b/bin/tryton index 22f294e978..b6c708968e 100755 --- a/bin/tryton +++ b/bin/tryton @@ -17,6 +17,7 @@ if hasattr(sys, 'frozen'): prefix = os.path.dirname(sys.executable) os.environ['GTK_EXE_PREFIX'] = prefix os.environ['GTK_DATA_PREFIX'] = prefix + os.environ['EVINCE_BACKENDS_DIR'] = prefix os.environ['XDG_DATA_DIRS'] = os.path.join(prefix, 'share') etc = os.path.join(prefix, 'etc') os.environ['GDK_PIXBUF_MODULE_FILE'] = os.path.join( diff --git a/setup-freeze.py b/setup-freeze.py index 6401799db9..37bcb659fd 100755 --- a/setup-freeze.py +++ b/setup-freeze.py @@ -29,6 +29,8 @@ include_files = [ os.path.join('lib', 'gtk-3.0')), (os.path.join(sys.prefix, 'lib', 'gdk-pixbuf-2.0'), os.path.join('lib', 'gdk-pixbuf-2.0')), + (os.path.join(sys.prefix, 'lib', 'evince'), + os.path.join('lib', 'evince')), (os.path.join(sys.prefix, 'share', 'locale'), os.path.join('share', 'locale')), (os.path.join(sys.prefix, 'share', 'icons', 'Adwaita'), @@ -41,6 +43,8 @@ include_files = [ required_gi_namespaces = [ 'Atk-1.0', + 'EvinceDocument-3.0', + 'EvinceView-3.0', 'GLib-2.0', 'GModule-2.0', 'GObject-2.0', diff --git a/tryton/__init__.py b/tryton/__init__.py index c34229e7b3..9bd7ef2afe 100644 --- a/tryton/__init__.py +++ b/tryton/__init__.py @@ -11,6 +11,11 @@ try: gi.require_version('GtkSpell', '3.0') except ValueError: pass +try: + gi.require_version('EvinceDocument', '3.0') + gi.require_version('EvinceView', '3.0') +except ValueError: + pass try: # Import earlier otherwise there is a segmentation fault on MSYS2 diff --git a/tryton/gui/window/view_form/view/form.py b/tryton/gui/window/view_form/view/form.py index 0174523f85..246b327028 100644 --- a/tryton/gui/window/view_form/view/form.py +++ b/tryton/gui/window/view_form/view/form.py @@ -13,6 +13,7 @@ from tryton.common.underline import set_underline from tryton.common.button import Button from tryton.config import CONFIG from .form_gtk.calendar_ import Date, Time, DateTime +from .form_gtk.document import Document from .form_gtk.float import Float from .form_gtk.integer import Integer from .form_gtk.selection import Selection @@ -167,6 +168,7 @@ class FormXMLViewParser(XMLViewParser): 'date': Date, 'datetime': DateTime, 'dict': DictWidget, + 'document': Document, 'email': Email, 'float': Float, 'html': HTML, diff --git a/tryton/gui/window/view_form/view/form_gtk/document.py b/tryton/gui/window/view_form/view/form_gtk/document.py new file mode 100644 index 0000000000..e8b4703e39 --- /dev/null +++ b/tryton/gui/window/view_form/view/form_gtk/document.py @@ -0,0 +1,91 @@ +# This file is part of Tryton. The COPYRIGHT file at the top level of +# this repository contains the full copyright notices and license terms. +from pathlib import Path +from tempfile import NamedTemporaryFile + +from gi.repository import Gtk, Gdk, GLib +try: + from gi.repository import EvinceDocument, EvinceView + EvinceDocument.init() +except ImportError: + EvinceDocument = EvinceView = None + +from tryton.common import data2pixbuf, resize_pixbuf + +from .widget import Widget +from .binary import BinaryMixin + + +class Document(BinaryMixin, Widget): + expand = True + + def __init__(self, view, attrs): + super().__init__(view, attrs) + + self.widget = Gtk.VBox(spacing=3) + self.image = Gtk.DrawingArea() + self.widget.pack_start( + self.image, expand=True, fill=True, padding=0) + + if EvinceView: + self.evince_view = EvinceView.View() + self.evince_scroll = Gtk.ScrolledWindow() + self.evince_scroll.add(self.evince_view) + self.widget.pack_start( + self.evince_scroll, expand=True, fill=True, padding=0) + else: + self.evince_view = self.evince_scroll = None + + def _draw(self, drawing_area, cr, pixbuf): + width = drawing_area.get_allocated_width() + height = drawing_area.get_allocated_height() + pixbuf = resize_pixbuf(pixbuf, width, height) + width = (width - pixbuf.get_width()) / 2 + height = (height - pixbuf.get_height()) / 2 + Gdk.cairo_set_source_pixbuf(cr, pixbuf, width, height) + cr.paint() + + def display(self): + super().display() + data = self.field.get_data(self.record) + + if not data: + self.image.hide() + if self.evince_view: + self.evince_view.set_model(EvinceView.DocumentModel()) + self.evince_scroll.hide() + return + + pixbuf = data2pixbuf(data) + if pixbuf: + try: + self.image.disconnect_by_func(self._draw) + except TypeError: + pass + self.image.connect('draw', self._draw, pixbuf) + self.image.queue_draw() + self.image.show() + if self.evince_view: + self.evince_view.set_model(EvinceView.DocumentModel()) + self.evince_scroll.hide() + else: + self.image.hide() + if self.evince_view: + self.evince_scroll.show() + if self.filename_field: + suffix = self.filename_field.get(self.record) + else: + suffix = None + try: + with NamedTemporaryFile(suffix=suffix) as fp: + fp.write(data) + path = Path(fp.name) + document = ( + EvinceDocument.Document.factory_get_document( + path.as_uri())) + model = EvinceView.DocumentModel() + model.set_document(document) + self.evince_view.set_model(model) + except GLib.GError: + self.evince_view.set_model(EvinceView.DocumentModel()) + self.evince_scroll.hide()