Improve contextual menu

- Actions for all relation fields on list view
- Use icons
- Add attachments entry

review284003
This commit is contained in:
Cédric Krier committed 2012-03-20 19:35:20 +01:00
1 parent 82bc90b8ff
commit ac263a72fa
6 files changed
+164 -120

No files matched your search

+4
View File
@@ -1,3 +1,7 @@
* Improve contextual menu:
- Actions for all relation fields on list view
- Use icons
- Add attachments entry
* Use RPCProgress almost everywhere
* Add a simple search box
* New domain parser using shlex
+31
View File
@@ -8,6 +8,7 @@ import gettext
import tempfile
import os
import webbrowser
from tryton.pyson import PYSONEncoder
from tryton.common import RPCProgress, RPCExecute, RPCException
_ = gettext.gettext
@@ -172,3 +173,33 @@ class Action(object):
elif not len(keyact) and warning:
message(_('No action defined!'))
return False
@staticmethod
def evaluate(action, atype, record):
'''
Evaluate the action with the record.
'''
action = action.copy()
if atype in ('print', 'action'):
email = {}
if 'pyson_email' in action:
email = record.expr_eval( action['pyson_email'])
if not email:
email = {}
if 'subject' not in email:
email['subject'] = action['name'].replace('_', '')
action['email'] = email
elif atype == 'relate':
encoder = PYSONEncoder()
if 'pyson_domain' in action:
action['pyson_domain'] = encoder.encode(
record.expr_eval(action['pyson_domain'], check_load=False))
if 'pyson_context' in action:
action['pyson_context'] = encoder.encode(
record.expr_eval(action['pyson_context'],
check_load=False))
else:
raise NotImplementedError("Action type '%s' is not supported" %
atype)
return action
+92
View File
@@ -0,0 +1,92 @@
#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 gtk
import gettext
from tryton.common import RPCExecute, RPCException
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
_ = gettext.gettext
def populate(menu, model, record_id, title=''):
'''
Fill menu with the actions of model for the record id.
If title is filled, the actions will be put in a submenu.
'''
if record_id is None or record_id < 0:
return
try:
toolbar = RPCExecute('model', model, 'view_toolbar_get')
except RPCException:
return
def activate(menuitem, action, atype):
screen = Screen(model)
screen.load([record_id])
action = Action.evaluate(action, atype, screen.current_record)
data = {
'model': model,
'id': record_id,
'ids': [record_id],
}
event = gtk.get_current_event()
allow_similar = False
if (event.state & gtk.gdk.CONTROL_MASK
or event.state & gtk.gdk.MOD1_MASK):
allow_similar = True
with Window(hide_current=True, allow_similar=allow_similar):
Action._exec_action(action, data, {})
def attachment(menuitem):
Attachment(model, record_id, None)
if title:
if len(menu):
menu.append(gtk.SeparatorMenuItem())
title_item = gtk.MenuItem(title)
menu.append(title_item)
submenu = gtk.Menu()
title_item.set_submenu(submenu)
action_menu = submenu
else:
action_menu = menu
if len(action_menu):
action_menu.append(gtk.SeparatorMenuItem())
attachment_item = gtk.ImageMenuItem('tryton-attachment')
attachment_item.set_label(_('Attachments...'))
action_menu.append(attachment_item)
attachment_item.connect('activate', attachment)
for atype, icon, label, flavor in (
('action', 'tryton-executable', _('Actions...'), None),
('relate', 'tryton-go-jump', _('Relate...'), None),
('print', 'tryton-print-open', _('Report...'), 'open'),
('print', 'tryton-print-email', _('E-Mail...'), 'email'),
('print', 'tryton-print', _('Print...'), 'print'),
):
if len(action_menu):
action_menu.append(gtk.SeparatorMenuItem())
title_item = gtk.ImageMenuItem(icon)
title_item.set_label(label)
action_menu.append(title_item)
if not toolbar[atype]:
title_item.set_sensitive(False)
continue
submenu = gtk.Menu()
title_item.set_submenu(submenu)
for action in toolbar[atype]:
action = action.copy()
item = gtk.MenuItem(action['name'])
submenu.append(item)
if flavor == 'print':
action['direct_print'] = True
elif flavor == 'email':
action['email_print'] = True
item.connect('activate', activate, action, atype)
menu.show_all()
+1 -24
View File
@@ -499,30 +499,7 @@ class Form(SignalEvent, TabContent):
return
record_id = self.screen.id_get()
record_ids = self.screen.sel_ids_get()
if atype in ('print', 'action'):
email = {}
if 'pyson_email' in action:
email = self.screen.current_record.expr_eval(
action['pyson_email'])
if not email:
email = {}
if 'subject' not in email:
email['subject'] = action['name'].replace('_', '')
action['email'] = email
self.screen.display()
elif atype == 'relate':
encoder = PYSONEncoder()
if 'pyson_domain' in action:
action['pyson_domain'] = encoder.encode(
self.screen.current_record.expr_eval(
action['pyson_domain'], check_load=False))
if 'pyson_context' in action:
action['pyson_context'] = encoder.encode(
self.screen.current_record.expr_eval(
action['pyson_context'], check_load=False))
else:
raise NotImplementedError("Action type '%s' is not supported" %
atype)
action = Action.evaluate(action, atype, self.screen.current_record)
data = {
'model': self.screen.model_name,
'id': record_id,
@@ -8,9 +8,8 @@ import tryton.common as common
from tryton.gui.window.view_form.screen import Screen
from tryton.gui.window.win_search import WinSearch
from tryton.gui.window.win_form import WinForm
from tryton.action import Action
from tryton.config import CONFIG
from tryton.pyson import PYSONEncoder
from tryton.common.popup_menu import populate
from tryton.common import RPCExecute, RPCException
_ = gettext.gettext
@@ -310,61 +309,6 @@ class Many2One(WidgetInterface):
def _populate_popup(self, widget, menu):
value = self.field.get(self.record)
try:
relates = RPCExecute('model', 'ir.action.keyword', 'get_keyword',
'form_relate', (self.attrs['relation'], -1))
except RPCException:
return False
menu_entries = []
menu_entries.append((None, None, None))
menu_entries.append((None, None, None))
menu_entries.append((_('Actions'),
lambda x: self.click_and_action('form_action'), 0))
menu_entries.append((_('Reports'),
lambda x: self.click_and_action('form_print'), 0))
menu_entries.append((None, None, None))
for relate in relates:
relate['string'] = relate['name']
fct = lambda action: lambda x: self.click_and_relate(action)
menu_entries.append(
('... ' + relate['name'], fct(relate), 0))
for stock_id, callback, sensitivity in menu_entries:
if stock_id:
item = gtk.ImageMenuItem(stock_id)
if callback:
item.connect("activate", callback)
item.set_sensitive(bool(sensitivity or value))
else:
item = gtk.SeparatorMenuItem()
item.show()
menu.append(item)
if self.has_target(value):
populate(menu, self.get_model(), self.id_from_value(value))
return True
def click_and_relate(self, action):
data = {}
context = {}
act = action.copy()
obj_id = self.field.get(self.record)
if not obj_id:
common.message(_('You must select a record to use the relation!'))
return False
screen = Screen(self.attrs['relation'])
screen.load([obj_id])
encoder = PYSONEncoder()
act['domain'] = encoder.encode(screen.current_record.expr_eval(
act.get('domain', []), check_load=False))
act['context'] = encoder.encode(screen.current_record.expr_eval(
act.get('context', {}), check_load=False))
data['model'] = self.attrs['relation']
data['id'] = obj_id
data['ids'] = [obj_id]
return Action._exec_action(act, data, context)
def click_and_action(self, atype):
obj_id = self.field.get(self.record)
return Action.exec_keyword(atype, {
'model': self.attrs['relation'],
'id': obj_id or False,
'ids': [obj_id],
}, alwaysask=True)
+33 -37
View File
@@ -18,6 +18,7 @@ from tryton.common.cellrenderertoggle import CellRendererToggle
from tryton.common.cellrendererbinary import CellRendererBinary
from tryton.pyson import PYSONEncoder
from tryton.gui.window import Window
from tryton.common.popup_menu import populate
from tryton.common import RPCExecute, RPCException
_ = gettext.gettext
@@ -547,45 +548,40 @@ class ViewList(ParserView):
model = selection.get_selected_rows()[0]
if (not path) or not path[0]:
return False
record = model.group[path[0][0]]
group = model.group
record = group[path[0][0]]
menu_entries = []
menu_entries.append(('gtk-copy', lambda x: self.on_copy(), 1))
if hasattr(path[1], '_type') and path[1]._type == 'many2one':
value = record[path[1].name].get(record)
try:
relates = RPCExecute('model', 'ir.action.keyword',
'get_keyword', 'form_relate',
(self.screen.group.fields[
path[1].name].attrs['relation'], -1))
except RPCException:
relates = None
if relates:
menu_entries.append((None, None, None))
menu_entries.append((_('Actions'),
lambda x: self.click_and_action(
'form_action', value, path), 0))
menu_entries.append((_('Reports'),
lambda x: self.click_and_action(
'form_print', value, path), 0))
menu_entries.append((None, None, None))
for relate in relates:
relate['string'] = relate['name']
fct = lambda action: lambda x: \
self.click_and_relate(action, value, path)
menu_entries.append(
('... ' + relate['name'], fct(relate), 0))
menu = gtk.Menu()
for stock_id, callback, sensitivity in menu_entries:
if stock_id:
item = gtk.ImageMenuItem(stock_id)
if callback:
item.connect('activate', callback)
item.set_sensitive(bool(sensitivity or value))
else:
item = gtk.SeparatorMenuItem()
item.show()
menu.append(item)
copy_item = gtk.ImageMenuItem('gtk-copy')
copy_item.connect('activate', lambda x: self.on_copy())
menu.append(copy_item)
record_id = record.id
# Don't activate actions if parent is modified
parent = record.parent if record else None
while parent:
if parent.modified:
record_id = -1
break
parent = parent.parent
populate(menu, group.model_name, record_id)
for col in self.widget_tree.get_columns():
if not col.get_visible() or not col.name:
continue
field = group.fields[col.name]
model = None
if field.attrs['type'] == 'many2one':
model = field.attrs['relation']
record_id = field.get(record)
elif field.attrs['type'] == 'reference':
value = field.get(record)
if value:
model, record_id = value.split(',')
record_id = int(record_id)
if not model:
continue
label = field.attrs['string']
populate(menu, model, record_id, title=label)
menu.show_all()
menu.popup(None, None, None, event.button, event.time)
elif event.button == 2:
event.button = 1