From f717b645409ce67c07aa2c534b8ca19c4570ec10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Sun, 2 Sep 2018 10:44:38 +0200 Subject: [PATCH] Use dropdown for attachments issue7659 review50551002 --- CHANGELOG | 1 + tryton/action/main.py | 14 ++---- tryton/common/common.py | 18 +++++++- tryton/gui/window/attachment.py | 43 +++++++++++++++++++ tryton/gui/window/form.py | 39 +++++++++++++++-- tryton/gui/window/tabcontent.py | 14 ++++-- .../window/view_form/view/form_gtk/binary.py | 25 +++++------ 7 files changed, 119 insertions(+), 35 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 963d634e94..3224ec3ade 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Use dropdown for attachment * Apply factor on domain parser * Use tab name in CSV Export/Import * Add bus management diff --git a/tryton/action/main.py b/tryton/action/main.py index 72ffc062e0..2ae4ba31fb 100644 --- a/tryton/action/main.py +++ b/tryton/action/main.py @@ -1,13 +1,11 @@ # This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. import gettext -import tempfile -import os import webbrowser import tryton.rpc as rpc -from tryton.common import RPCProgress, RPCExecute, RPCException, slugify -from tryton.common import message, selection, file_open, mailto +from tryton.common import RPCProgress, RPCExecute, RPCException +from tryton.common import message, selection, file_write, file_open, mailto from tryton.config import CONFIG from tryton.pyson import PYSONDecoder @@ -39,14 +37,8 @@ class Action(object): (type, data, print_p, name) = res if not print_p and direct_print: print_p = True - dtemp = tempfile.mkdtemp(prefix='tryton_') - fp_name = os.path.join(dtemp, - slugify(name) + os.extsep + slugify(type)) - if isinstance(data, str): - data = data.encode('utf-8') - with open(fp_name, 'wb') as file_d: - file_d.write(data) + fp_name = file_write((name, type), data) if email_print: mailto(to=email.get('to'), cc=email.get('cc'), subject=email.get('subject'), body=email.get('body'), diff --git a/tryton/common/common.py b/tryton/common/common.py index 142184bc18..98e06179a5 100644 --- a/tryton/common/common.py +++ b/tryton/common/common.py @@ -8,6 +8,7 @@ import pango import gettext import os import subprocess +import tempfile import re import logging import unicodedata @@ -426,7 +427,22 @@ def slugify(value): return _slugify_hyphenate_re.sub('-', value) -def file_open(filename, type, print_p=False): +def file_write(filename, data): + if isinstance(data, str): + data = data.encode('utf-8') + dtemp = tempfile.mkdtemp(prefix='tryton_') + if not isinstance(filename, str): + name, ext = filename + else: + name, ext = os.path.splitext(filename) + filename = ''.join([slugify(name), os.extsep, slugify(ext)]) + filepath = os.path.join(dtemp, filename) + with open(filepath, 'wb') as fp: + fp.write(data) + return filepath + + +def file_open(filename, type=None, print_p=False): def save(): save_name = file_selection(_('Save As...'), action=gtk.FILE_CHOOSER_ACTION_SAVE) diff --git a/tryton/gui/window/attachment.py b/tryton/gui/window/attachment.py index 291cf926b2..990cdde04c 100644 --- a/tryton/gui/window/attachment.py +++ b/tryton/gui/window/attachment.py @@ -5,7 +5,10 @@ import os from urllib.request import urlopen from urllib.parse import urlparse, unquote import gettext +import webbrowser +from functools import partial +from tryton.common import RPCExecute, RPCException, file_write, file_open from tryton.gui.window.view_form.screen import Screen from tryton.gui.window.win_form import WinForm @@ -46,3 +49,43 @@ class Attachment(WinForm): name_field.set_client(new_record, file_name) data_field.set_client(new_record, urlopen(uri).read()) self.screen.display() + + def add_file(self, filename): + self.add_uri('file:///' + filename) + + @staticmethod + def get_attachments(record): + attachments = [] + context = {} + if record and record.id >= 0: + context = record.get_context() + try: + attachments = RPCExecute('model', 'ir.attachment', + 'search_read', [ + ('resource', '=', '%s,%s' % ( + record.model_name, record.id)), + ], 0, 20, None, ['rec_name', 'name', 'type', 'link'], + context=context) + except RPCException: + pass + for attachment in attachments: + name = attachment['rec_name'] + callback = getattr( + Attachment, 'open_' + attachment['type'], Attachment.open_data) + yield name, partial( + callback, attachment=attachment, context=context) + + @staticmethod + def open_link(attachment, context): + if attachment['link']: + webbrowser.open(attachment['link'], new=2) + + @staticmethod + def open_data(attachment, context): + try: + value, = RPCExecute('model', 'ir.attachment', 'read', + [attachment['id']], ['data'], context=context) + except RPCException: + return + filepath = file_write(attachment['name'], value['data']) + file_open(filepath) diff --git a/tryton/gui/window/form.py b/tryton/gui/window/form.py index ffcee1feb9..9b71eceb72 100644 --- a/tryton/gui/window/form.py +++ b/tryton/gui/window/form.py @@ -106,11 +106,42 @@ class Form(SignalEvent, TabContent): self.screen.destroy() def sig_attach(self, widget=None): - record = self.screen.current_record - if not record or record.id < 0: + def window(widget): + return Attachment( + record, lambda: self.update_attachment_count(reload=True)) + + def add_file(widget): + filename = common.file_selection(_("Select")) + if filename: + window(widget).add_file(filename) + + def activate(widget, callback): + callback() + + button = self.buttons['attach'] + if widget != button: + if button.props.sensitive: + button.props.active = True return - Attachment(record, - lambda: self.update_attachment_count(reload=True)) + record = self.screen.current_record + menu = button._menu = Gtk.Menu() + for name, callback in Attachment.get_attachments(record): + item = Gtk.MenuItem() + item.set_label(name) + item.connect('activate', activate, callback) + menu.add(item) + menu.add(Gtk.SeparatorMenuItem()) + add_item = Gtk.MenuItem() + add_item.set_label(_("Add...")) + add_item.connect('activate', add_file) + menu.add(add_item) + manage_item = Gtk.MenuItem() + manage_item.set_label(_("Manage...")) + manage_item.connect('activate', window) + menu.add(manage_item) + menu.show_all() + menu.connect('deactivate', self._popup_menu_hide, button) + self.action_popup(button) def update_attachment_count(self, reload=False): record = self.screen.current_record diff --git a/tryton/gui/window/tabcontent.py b/tryton/gui/window/tabcontent.py index 3476bbe0d7..c9e9fc1d00 100644 --- a/tryton/gui/window/tabcontent.py +++ b/tryton/gui/window/tabcontent.py @@ -16,12 +16,13 @@ _ = gettext.gettext class ToolbarItem(object): def __init__(self, id, label, - tooltip=None, icon_name=None, accel_path=None): + tooltip=None, icon_name=None, accel_path=None, toggle=False): self.id = id self.label = label self.tooltip = tooltip self.icon_name = icon_name self.accel_path = accel_path + self.toggle = toggle @property def menu(self): @@ -104,7 +105,8 @@ class TabContent(InfoBar): label=_("A_ttachments..."), tooltip=_("Add an attachment to the record"), icon_name='tryton-attach', - accel_path='/Form/Attachments'), + accel_path='/Form/Attachments', + toggle=True), ToolbarItem( id='note', label=_("_Notes..."), @@ -248,13 +250,17 @@ class TabContent(InfoBar): callback = getattr(self, 'sig_%s' % item.id, None) if not callback: continue - toolitem = gtk.ToolButton() + if item.toggle: + toolitem = gtk.ToggleToolButton() + toolitem.connect('toggled', callback) + else: + toolitem = gtk.ToolButton() + toolitem.connect('clicked', callback) toolitem.set_icon_widget( common.IconFactory.get_image( item.icon_name, gtk.ICON_SIZE_LARGE_TOOLBAR)) toolitem.set_label(item.label) toolitem.set_use_underline(True) - toolitem.connect('clicked', callback) self.tooltips.set_tip(toolitem, item.tooltip) self.buttons[item.id] = toolitem elif not item and previous: diff --git a/tryton/gui/window/view_form/view/form_gtk/binary.py b/tryton/gui/window/view_form/view/form_gtk/binary.py index fd4d72e05b..b5a61176fb 100644 --- a/tryton/gui/window/view_form/view/form_gtk/binary.py +++ b/tryton/gui/window/view_form/view/form_gtk/binary.py @@ -3,9 +3,8 @@ import gtk import gettext import os -import tempfile from tryton.common import common -from tryton.common import file_selection, Tooltips, file_open, slugify +from tryton.common import file_selection, Tooltips, file_open, file_write from tryton.common.entry_position import reset_position from tryton.config import CONFIG from .widget import Widget @@ -89,21 +88,20 @@ class BinaryMixin(Widget): self.filename_field.set_client(self.record, os.path.basename(filename)) + def get_data(self): + if hasattr(self.field, 'get_data'): + data = self.field.get_data(self.record) + else: + data = self.field.get(self.record) + return data + def open_(self, widget=None): if not self.filename_field: return filename = self.filename_field.get(self.record) if not filename: return - dtemp = tempfile.mkdtemp(prefix='tryton_') - root, ext = os.path.splitext(filename) - filename = ''.join([slugify(root), os.extsep, slugify(ext)]) - file_path = os.path.join(dtemp, filename) - with open(file_path, 'wb') as fp: - if hasattr(self.field, 'get_data'): - fp.write(self.field.get_data(self.record)) - else: - fp.write(self.field.get(self.record)) + file_path = file_write(filename, self.get_data()) root, type_ = os.path.splitext(filename) if type_: type_ = type_[1:] @@ -117,10 +115,7 @@ class BinaryMixin(Widget): action=gtk.FILE_CHOOSER_ACTION_SAVE) if filename: with open(filename, 'wb') as fp: - if hasattr(self.field, 'get_data'): - fp.write(self.field.get_data(self.record)) - else: - fp.write(self.field.get(self.record)) + fp.write(self.get_data()) def clear(self, widget=None): if self.filename_field: