From 29c5ee48c6c7dffae39b69ce89bf6bc26284222a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Tue, 2 Feb 2016 19:15:25 +0100 Subject: [PATCH] Add Note issue4669 review9171002 --- CHANGELOG | 1 + tryton/common/popup_menu.py | 8 ++++ tryton/gui/window/attachment.py | 5 ++- tryton/gui/window/form.py | 34 ++++++++++++++++ tryton/gui/window/note.py | 40 +++++++++++++++++++ tryton/gui/window/view_form/model/record.py | 16 ++++++++ tryton/gui/window/view_form/screen/screen.py | 13 ++++++ tryton/gui/window/view_form/view/list.py | 7 +++- .../window/view_form/view/list_gtk/widget.py | 3 ++ 9 files changed, 123 insertions(+), 4 deletions(-) create mode 100644 tryton/gui/window/note.py diff --git a/CHANGELOG b/CHANGELOG index c14396d97e..2586792a6a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Add Note * Manage view_ids on tree view * Use Apple key for copy/past of list diff --git a/tryton/common/popup_menu.py b/tryton/common/popup_menu.py index d577aea8e3..9cf201e2ae 100644 --- a/tryton/common/popup_menu.py +++ b/tryton/common/popup_menu.py @@ -7,6 +7,7 @@ from tryton.gui.window.view_form.screen import Screen from tryton.action import Action from tryton.gui.window import Window from tryton.gui.window.attachment import Attachment +from tryton.gui.window.note import Note _ = gettext.gettext @@ -55,6 +56,9 @@ def populate(menu, model, record, title='', field=None): def attachment(menuitem): Attachment(load(record), None) + def note(menuitem): + Note(load(record), None) + def edit(menuitem): with Window(hide_current=True, allow_similar=True): Window.create(field.attrs.get('view_ids'), model, id_(record), @@ -82,6 +86,10 @@ def populate(menu, model, record, title='', field=None): attachment_item.set_label(_('Attachments...')) action_menu.append(attachment_item) attachment_item.connect('activate', attachment) + note_item = gtk.ImageMenuItem('tryton-note') + note_item.set_label(_('Notes...')) + action_menu.append(note_item) + note_item.connect('activate', note) def set_toolbar(toolbar): try: diff --git a/tryton/gui/window/attachment.py b/tryton/gui/window/attachment.py index 72aa5788e8..f38ed034eb 100644 --- a/tryton/gui/window/attachment.py +++ b/tryton/gui/window/attachment.py @@ -21,10 +21,11 @@ class Attachment(WinForm): ('resource', '=', self.resource), ], mode=['tree', 'form'], context=context, exclude_field='resource') - screen.search_filter() - screen.parent = record super(Attachment, self).__init__(screen, self.callback, view_type='tree') + screen.search_filter() + # Set parent after to be allowed to call search_filter + screen.parent = record def destroy(self): self.prev_view.save_width_height() diff --git a/tryton/gui/window/form.py b/tryton/gui/window/form.py index 61b1863f9a..58b2cf1c03 100644 --- a/tryton/gui/window/form.py +++ b/tryton/gui/window/form.py @@ -11,6 +11,7 @@ from tryton.gui.window import Window from tryton.gui.window.win_export import WinExport from tryton.gui.window.win_import import WinImport from tryton.gui.window.attachment import Attachment +from tryton.gui.window.note import Note from tryton.gui.window.revision import Revision from tryton.signal_event import SignalEvent from tryton.common import message, sur, sur_3b, timezoned_date @@ -44,6 +45,8 @@ class Form(SignalEvent, TabContent): (None,) * 5, ('attach', 'tryton-attachment', _('Attachment(0)'), _('Add an attachment to the record'), 'sig_attach'), + ('note', 'tryton-note', _('Note(0)'), + _('Add a note to the record'), 'sig_note'), ] menu_def = [ (_('_New'), 'tryton-new', 'sig_new', '/Form/New'), @@ -69,6 +72,7 @@ class Form(SignalEvent, TabContent): (None,) * 4, (_('A_ttachments...'), 'tryton-attachment', 'sig_attach', '/Form/Attachments'), + (_('_Notes...'), 'tryton-note', 'sig_note', '/Form/Notes'), (_('_Actions...'), 'tryton-executable', 'sig_action', '/Form/Actions'), (_('_Relate...'), 'tryton-go-jump', 'sig_relate', @@ -143,6 +147,7 @@ class Form(SignalEvent, TabContent): self.screen.signal_connect(self, 'record-saved', self._record_saved) self.screen.signal_connect(self, 'attachment-count', self._attachment_count) + self.screen.signal_connect(self, 'unread-note', self._unread_note) if res_id not in (None, False): if isinstance(res_id, (int, long)): @@ -211,6 +216,35 @@ class Form(SignalEvent, TabContent): self.buttons['attach'].props.sensitive = bool( record.id >= 0 if record else False) + def sig_note(self, widget=None): + record = self.screen.current_record + if not record or record.id < 0: + return + Note(record, + lambda: self.update_unread_note(reload=True)) + + def update_unread_note(self, reload=False): + record = self.screen.current_record + if record: + unread = record.get_unread_note(reload=reload) + else: + unread = 0 + self._unread_note(None, unread) + + def _unread_note(self, widget, signal_data): + label = _('Note(%d)') % signal_data + self.buttons['note'].set_label(label) + if signal_data: + self.buttons['note'].set_stock_id('tryton-note-hi') + else: + self.buttons['note'].set_stock_id('tryton-note') + record = self.screen.current_record + if not record or record.id < 0: + sensitive = False + else: + sensitive = True + self.buttons['note'].props.sensitive = sensitive + def sig_switch(self, widget=None): if not self.modified_save(): return diff --git a/tryton/gui/window/note.py b/tryton/gui/window/note.py new file mode 100644 index 0000000000..e9a5469fd0 --- /dev/null +++ b/tryton/gui/window/note.py @@ -0,0 +1,40 @@ +# 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 tryton.gui.window.view_form.screen import Screen +from tryton.gui.window.win_form import WinForm + + +class Note(WinForm): + "Note window" + + def __init__(self, record, callback=None): + self.resource = '%s,%s' % (record.model_name, record.id) + self.note_callback = callback + context = record.context_get() + context['resource'] = self.resource + screen = Screen('ir.note', domain=[ + ('resource', '=', self.resource), + ], mode=['tree', 'form'], context=context, + exclude_field='resource') + super(Note, self).__init__(screen, self.callback, view_type='tree') + screen.search_filter() + # Set parent after to be allowed to call search_filter + screen.parent = record + + def destroy(self): + self.prev_view.save_width_height() + super(Note, self).destroy() + + def callback(self, result): + if result: + resource = self.screen.group.fields['resource'] + unread = self.screen.group.fields['unread'] + for record in self.screen.group: + if record.loaded: + resource.set_client(record, self.resource) + if 'unread' not in record.modified_fields: + unread.set_client(record, False) + self.screen.group.save() + if self.note_callback: + self.note_callback() diff --git a/tryton/gui/window/view_form/model/record.py b/tryton/gui/window/view_form/model/record.py index 906e46d20d..c183e27e1b 100644 --- a/tryton/gui/window/view_form/model/record.py +++ b/tryton/gui/window/view_form/model/record.py @@ -31,6 +31,7 @@ class Record(SignalEvent): self.modified_fields = {} self._timestamp = None self.attachment_count = -1 + self.unread_note = -1 self.next = {} # Used in Group list self.value = {} self.autocompletion = {} @@ -616,6 +617,21 @@ class Record(SignalEvent): return 0 return self.attachment_count + def get_unread_note(self, reload=False): + if self.id < 0: + return 0 + if self.unread_note < 0 or reload: + try: + self.unread_note = RPCExecute('model', 'ir.note', + 'search_count', [ + ('resource', '=', + '%s,%s' % (self.model_name, self.id)), + ('unread', '=', True), + ], context=self.context_get()) + except RPCException: + return 0 + return self.unread_note + def destroy(self): for v in self.value.itervalues(): if hasattr(v, 'destroy'): diff --git a/tryton/gui/window/view_form/screen/screen.py b/tryton/gui/window/view_form/screen/screen.py index 37bdb90bfe..b4b3d6badc 100644 --- a/tryton/gui/window/view_form/screen/screen.py +++ b/tryton/gui/window/view_form/screen/screen.py @@ -324,8 +324,13 @@ class Screen(SignalEvent): if record and record.attachment_count > 0: attachment_count = record.attachment_count self.signal('attachment-count', attachment_count) + unread_note = 0 + if record and record.unread_note > 0: + unread_note = record.unread_note + self.signal('unread-note', unread_note) # update attachment-count after 1 second gobject.timeout_add(1000, self.update_attachment, record) + gobject.timeout_add(1000, self.update_note, record) return True current_record = property(__get_current_record, __set_current_record) @@ -338,6 +343,14 @@ class Screen(SignalEvent): self.signal('attachment-count', attachment_count) return False + def update_note(self, record): + if record != self.current_record: + return False + if record and self.signal_connected('unread-note'): + unread_note = record.get_unread_note() + self.signal('unread-note', unread_note) + return False + def destroy(self): self.group.destroy() for view in self.views: diff --git a/tryton/gui/window/view_form/view/list.py b/tryton/gui/window/view_form/view/list.py index 7d41b23c86..7b661b2fed 100644 --- a/tryton/gui/window/view_form/view/list.py +++ b/tryton/gui/window/view_form/view/list.py @@ -289,7 +289,9 @@ class ViewTree(View): self.parse(xml) self.treeview.set_property('rules-hint', True) - self.treeview.set_fixed_height_mode(True) + self.treeview.set_fixed_height_mode( + all(c.get_sizing() == gtk.TREE_VIEW_COLUMN_FIXED + for c in self.treeview.get_columns())) self.treeview.connect('button-press-event', self.__button_press) self.treeview.connect('key-press-event', self.on_keypress) self.treeview.connect_after('row-activated', self.__sig_switch) @@ -520,7 +522,8 @@ class ViewTree(View): expand = attributes.get('expand', False) column.set_expand(expand) column.set_resizable(True) - column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) + if field and field.attrs['type'] != 'text': + column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) def get_column_widget(self, column): 'Return the widget of the column' diff --git a/tryton/gui/window/view_form/view/list_gtk/widget.py b/tryton/gui/window/view_form/view/list_gtk/widget.py index fc62e2bcb8..cdf3400a17 100644 --- a/tryton/gui/window/view_form/view/list_gtk/widget.py +++ b/tryton/gui/window/view_form/view/list_gtk/widget.py @@ -178,6 +178,7 @@ class GenericText(Cell): if not isinstance(self.renderer, CellRendererBinary): self.renderer.connect_after('editing-started', send_keys, view.treeview) + self.renderer.set_property('yalign', 0) self.view = view @property @@ -833,6 +834,7 @@ class ProgressBar(object): orientation = self.orientations.get(self.attrs.get('orientation', 'left_to_right'), gtk.PROGRESS_LEFT_TO_RIGHT) self.renderer.set_property('orientation', orientation) + self.renderer.set_property('yalign', 0) @realized @CellCache.cache @@ -872,6 +874,7 @@ class Button(object): self.view = view self.renderer.connect('clicked', self.button_clicked) + self.renderer.set_property('yalign', 0) @realized @CellCache.cache