diff --git a/CHANGELOG b/CHANGELOG
index d35e52886e..a58c8f39de 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,7 @@
* Selection widget used for many2one dynamically change their content following
the domain specification
+* Add open button on binary and image widgets
+* Hide buttons on image widget if readonly
* Added a connection manager à la gajim
* Fix warning in wizards
* Added possibility to use server-side icons
diff --git a/COPYRIGHT b/COPYRIGHT
index 42539e2dbc..3a41a1760a 100644
--- a/COPYRIGHT
+++ b/COPYRIGHT
@@ -4,6 +4,7 @@ Copyright (C) 2007-2010 Bertrand Chenal.
Copyright (C) 2008-2010 B2CK SPRL.
Copyright (C) 2008-2010 Udo Spallek.
Copyright (C) 2008-2010 virtual things - Preisler & Spallek GbR.
+Copyright (C) 2007-2009 Lorenzo Gil Sanchez.
Copyright (C) 2004-2008 Tiny SPRL.
This program is free software: you can redistribute it and/or modify
diff --git a/tryton/common/common.py b/tryton/common/common.py
index 9190b73c02..5601976b24 100644
--- a/tryton/common/common.py
+++ b/tryton/common/common.py
@@ -58,7 +58,7 @@ class TrytonIconFactory(gtk.IconFactory):
continue
try:
pixbuf = gtk.gdk.pixbuf_new_from_file(
- os.path.join(PIXMAPS_DIR, fname))
+ os.path.join(PIXMAPS_DIR, fname).encode('utf-8'))
except Exception:
continue
icon_set = gtk.IconSet(pixbuf)
@@ -103,7 +103,7 @@ class TrytonIconFactory(gtk.IconFactory):
fileno, path = tempfile.mkstemp()
with os.fdopen(fileno, 'w') as svgfile:
svgfile.write(icon['icon'])
- pixbuf = gtk.gdk.pixbuf_new_from_file(path)
+ pixbuf = gtk.gdk.pixbuf_new_from_file(path.encode('utf-8'))
os.remove(path)
iconset = gtk.IconSet(pixbuf)
self.add(icon['name'], iconset)
@@ -309,7 +309,8 @@ def file_selection(title, filename='', parent=None,
def update_preview_cb(win, img):
filename = win.get_preview_filename()
try:
- pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(filename, 128, 128)
+ pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(filename.encode('utf-8'),
+ 128, 128)
img.set_from_pixbuf(pixbuf)
have_preview = True
except Exception:
@@ -519,7 +520,7 @@ def error(title, parent, details):
box = gtk.VBox()
label_error = gtk.Label()
- label_error.set_markup('' + _('Error: ') + '' + title)
+ label_error.set_markup('' + _('Error: ') + '' + to_xml(title))
label_error.set_alignment(0, 0.5)
label_error.set_padding(-1, 14)
label_error.modify_font(pango.FontDescription("monospace"))
@@ -1104,11 +1105,12 @@ class RPCProgress(object):
def run(self):
thread.start_new_thread(self.start, ())
+ watch = gtk.gdk.Cursor(gtk.gdk.WATCH)
+ self.parent.window.set_cursor(watch)
i = 0
win = None
progressbar = None
while (not self.res) and (not self.error):
- time.sleep(0.1)
i += 1
if i > 10:
if not win or not progressbar:
@@ -1145,11 +1147,14 @@ class RPCProgress(object):
win.set_transient_for(self.parent)
win.set_modal(True)
win.show_all()
+ win.window.set_cursor(watch)
with gtk.gdk.lock:
progressbar.pulse()
with gtk.gdk.lock:
while gtk.events_pending():
gtk.main_iteration()
+ time.sleep(0.1)
+ self.parent.window.set_cursor(None)
if win:
win.destroy()
while gtk.events_pending():
diff --git a/tryton/common/domain_inversion.py b/tryton/common/domain_inversion.py
index 05480c8624..b0222ce68e 100644
--- a/tryton/common/domain_inversion.py
+++ b/tryton/common/domain_inversion.py
@@ -351,7 +351,7 @@ def test_simplify():
domain = ['OR', [['x', '=', 3]], [['y', '=', 5]]]
assert simplify(domain) == ['OR', [['x', '=', 3]], [['y', '=', 5]]]
domain = ['OR', ['x', '=', 3], ['AND', ['y', '=', 5]]]
- assert simplify(domain) == ['OR', ['x', '=', 3], ['y', '=', 5]]
+ assert simplify(domain) == ['OR', ['x', '=', 3], [['y', '=', 5]]]
def test_merge():
domain = [['x', '=', 6], ['y', '=', 7]]
@@ -405,6 +405,17 @@ def test_evaldomain():
assert not eval_domain(domain, {'x': 5})
assert not eval_domain(domain, {'x': 11})
+def test_localize():
+ domain = [['x', '=', 5]]
+ assert localize_domain(domain) == [['x', '=', 5]]
+
+ domain = [['x', '=', 5], ['x.code', '=', 7]]
+ assert localize_domain(domain, 'x') == [['id', '=', 5], ['code', '=', 7]]
+
+ domain = ['OR', ['AND', ['x', '>', 7], ['x', '<', 15]], ['x.code', '=', 8]]
+ assert localize_domain(domain, 'x') == \
+ ['OR', ['AND', ['id', '>', 7], ['id', '<', 15]], ['code', '=', 8]]
+
if __name__ == '__main__':
test_simple_inversion()
test_and_inversion()
@@ -416,3 +427,4 @@ if __name__ == '__main__':
test_parse()
test_simplify()
test_evaldomain()
+ test_localize()
diff --git a/tryton/config.py b/tryton/config.py
index e0944aa718..d41373afed 100644
--- a/tryton/config.py
+++ b/tryton/config.py
@@ -187,7 +187,7 @@ if not os.path.isdir(PIXMAPS_DIR):
PIXMAPS_DIR = os.path.join(PREFIX, 'share', 'pixmaps', 'tryton')
TRYTON_ICON = gtk.gdk.pixbuf_new_from_file(
- os.path.join(PIXMAPS_DIR, 'tryton-icon.png'))
+ os.path.join(PIXMAPS_DIR, 'tryton-icon.png').encode('utf-8'))
def _data_dir():
data_dir = os.path.join(CURRENT_DIR, 'share', 'tryton')
diff --git a/tryton/gui/window/dblogin.py b/tryton/gui/window/dblogin.py
index 65abe999f4..bc39d07bae 100644
--- a/tryton/gui/window/dblogin.py
+++ b/tryton/gui/window/dblogin.py
@@ -389,6 +389,8 @@ class DBLogin(object):
self.combo_profile.add_attribute(cell, 'sensitive', 1)
self.combo_profile.set_model(self.profile_store)
self.combo_profile.connect('changed', self.profile_changed)
+ self.combo_profile.connect('move-active', self.profile_move_active)
+ self.move_active = False
self.profile_label = gtk.Label(_(u'Profile:'))
self.profile_label.set_justify(gtk.JUSTIFY_RIGHT)
self.profile_label.set_alignment(1, 0.5)
@@ -456,10 +458,16 @@ class DBLogin(object):
username = ''
if username:
self.entry_login.set_text(username)
- self.entry_password.grab_focus()
+ focus_widget = self.entry_password
else:
self.entry_login.set_text('')
- self.entry_login.grab_focus()
+ focus_widget = self.entry_login
+ if not self.move_active:
+ focus_widget.grab_focus()
+ self.move_active = False
+
+ def profile_move_active(self, combobox, scrolltype):
+ self.move_active = True
def run(self, profile_name, parent):
if not profile_name:
diff --git a/tryton/gui/window/view_board/action.py b/tryton/gui/window/view_board/action.py
index cfff94e34c..d41d408252 100644
--- a/tryton/gui/window/view_board/action.py
+++ b/tryton/gui/window/view_board/action.py
@@ -143,11 +143,10 @@ class Action(SignalEvent):
self.widget.show_all()
if self.action['view_type'] == 'form':
- self.screen = Screen(self.action['res_model'], self._window,
- view_type=self.action['view_mode'], context=self.context,
- view_ids=view_ids, domain=self.domain, readonly=True)
- self.screen.screen_container.alternate_view = True
- self.screen.switch_view(view_type=self.action['view_mode'])
+ self.screen = Screen(self.action['res_model'],
+ self._window, view_type=self.action['view_mode'],
+ context=self.context, view_ids=view_ids,
+ domain=self.domain, readonly=True, alternate_view=True)
alignment.add(self.screen.screen_container.alternate_viewport)
name = self.screen.current_view.title
self.screen.signal_connect(self, 'record-message', self._sig_label)
diff --git a/tryton/gui/window/view_form/model/group.py b/tryton/gui/window/view_form/model/group.py
index 5454e608cf..b6cee4c6fa 100644
--- a/tryton/gui/window/view_form/model/group.py
+++ b/tryton/gui/window/view_form/model/group.py
@@ -155,25 +155,16 @@ class Group(SignalEvent, list):
return list({}.fromkeys(res))
def load(self, ids, display=True, modified=False):
- if not ids:
- return True
- # Work with sets to compute intersection and set difference
- set_records = set(self.get(x) for x in ids)
- for record in set_records & set(self.record_removed):
- self.record_removed.remove(record)
- for record in set_records & set(self.record_deleted):
- self.record_deleted.remove(record)
-
- set_records -= set(x for x in self)
- # return on the list form in order to preserver ordering
- ids = [x for x in ids if self.get(x) in set_records]
if not ids:
return True
if len(ids) > 1:
self.lock_signal = True
+
new_records = []
for id in ids:
+ if self.get(id):
+ continue
new_record = Record(self.model_name, id, self.window,
parent=self.parent, parent_name=self.parent_name, group=self)
self.append(new_record)
@@ -182,6 +173,15 @@ class Group(SignalEvent, list):
self._record_changed)
new_record.signal_connect(self, 'record-modified',
self._record_modified)
+
+ # Remove previously removed or deleted records
+ for record in self.record_removed[:]:
+ if record.id in ids:
+ self.record_removed.remove(record)
+ for record in self.record_deleted[:]:
+ if record.id in ids:
+ self.record_deleted.remove(record)
+
if self.lock_signal:
self.lock_signal = False
self.signal('group-cleared')
@@ -234,7 +234,7 @@ class Group(SignalEvent, list):
if record[field]:
if index >= record[field].get(record):
index += 1
- record[field].set(record, index, modified=True)
+ record[field].set_client(record, index)
else:
index = record[field].get(record)
diff --git a/tryton/gui/window/view_form/screen/screen.py b/tryton/gui/window/view_form/screen/screen.py
index a9889be6f8..d3a0ea98e1 100644
--- a/tryton/gui/window/view_form/screen/screen.py
+++ b/tryton/gui/window/view_form/screen/screen.py
@@ -20,7 +20,7 @@ class Screen(SignalEvent):
def __init__(self, model_name, window, view_ids=None, view_type=None,
context=None, views_preload=None, domain=None, row_activate=None,
limit=None, readonly=False, exclude_field=None, sort=None,
- search_value=None):
+ search_value=None, alternate_view=False):
if view_ids is None:
view_ids = []
if view_type is None:
@@ -54,6 +54,7 @@ class Screen(SignalEvent):
self.__current_record = None
self.current_record = None
self.screen_container = ScreenContainer()
+ self.screen_container.alternate_view = alternate_view
self.filter_widget = None
self.widget = self.screen_container.widget_get()
self.__current_view = 0
diff --git a/tryton/gui/window/view_form/view/form.py b/tryton/gui/window/view_form/view/form.py
index 2c9e351b54..3a4599d9ca 100644
--- a/tryton/gui/window/view_form/view/form.py
+++ b/tryton/gui/window/view_form/view/form.py
@@ -121,8 +121,9 @@ class ViewForm(ParserView):
'to be able to use the action button!'), self.window)
return False
email = {}
- if 'email' in action:
- email = self.screen.current_record.expr_eval(action['email'])
+ if 'pyson_email' in action:
+ email = self.screen.current_record.expr_eval(
+ action['pyson_email'])
if not email:
email = {}
email['subject'] = action['name'].replace('_', '')
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 560e9ae312..d9d4c19a78 100644
--- a/tryton/gui/window/view_form/view/form_gtk/binary.py
+++ b/tryton/gui/window/view_form/view/form_gtk/binary.py
@@ -1,10 +1,12 @@
#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 __future__ import with_statement
import base64
import gtk
import gettext
import os
-from tryton.common import file_selection, message, warning, Tooltips
+import tempfile
+from tryton.common import file_selection, message, warning, Tooltips, file_open
from interface import WidgetInterface
_ = gettext.gettext
@@ -45,13 +47,25 @@ class Binary(WidgetInterface):
self.but_new = gtk.Button()
img_new = gtk.Image()
- img_new.set_from_stock('tryton-open', gtk.ICON_SIZE_SMALL_TOOLBAR)
+ img_new.set_from_stock('tryton-find', gtk.ICON_SIZE_SMALL_TOOLBAR)
self.but_new.set_image(img_new)
self.but_new.set_relief(gtk.RELIEF_NONE)
self.but_new.connect('clicked', self.sig_new)
- self.tooltips.set_tip(self.but_new, _('Select a File'))
+ self.tooltips.set_tip(self.but_new, _('Select a File...'))
self.widget.pack_start(self.but_new, expand=False, fill=False)
+ if self.filename:
+ self.but_open = gtk.Button()
+ img_open = gtk.Image()
+ img_open.set_from_stock('tryton-open', gtk.ICON_SIZE_SMALL_TOOLBAR)
+ self.but_open.set_image(img_open)
+ self.but_open.set_relief(gtk.RELIEF_NONE)
+ self.but_open.connect('clicked', self.sig_open)
+ self.tooltips.set_tip(self.but_open, _('Open...'))
+ self.widget.pack_start(self.but_open, expand=False, fill=False)
+ else:
+ self.but_open = None
+
self.but_save_as = gtk.Button()
img_save_as = gtk.Image()
img_save_as.set_from_stock('tryton-save-as',
@@ -73,6 +87,10 @@ class Binary(WidgetInterface):
self.tooltips.enable()
+ @property
+ def filename_field(self):
+ return self.record.group.fields.get(self.filename)
+
def _readonly_set(self, value):
if value:
self.but_new.hide()
@@ -83,6 +101,9 @@ class Binary(WidgetInterface):
self.but_remove.show()
if self.wid_text:
focus_chain = [self.wid_text]
+ elif self.filename:
+ focus_chain = [self.but_new, self.but_open, self.but_save_as,
+ self.but_remove]
else:
focus_chain = [self.but_new, self.but_save_as, self.but_remove]
self.widget.set_focus_chain(focus_chain)
@@ -94,36 +115,42 @@ class Binary(WidgetInterface):
return self.wid_size.grab_focus()
def sig_new(self, widget=None):
- try:
- filename = file_selection(_('Open...'),
- parent=self.window)
- if filename and self.field:
- self.field.set_client(self.record,
- base64.encodestring(open(filename, 'rb').read()))
- if self.filename and self.record:
- name_wid = self.record.group.fields[self.filename]
- name_wid.set_client(self.record, os.path.basename(filename))
- self.display(self.record, self.field)
- except Exception, exception:
- warning(_('Error reading the file.\nError message:\n%s') \
- % str(exception), self.window, _('Error'))
+ filename = file_selection(_('Open...'),
+ parent=self.window)
+ if filename and self.field:
+ self.field.set_client(self.record,
+ base64.encodestring(open(filename, 'rb').read()))
+ if self.filename_field:
+ self.filename_field.set_client(self.record,
+ os.path.basename(filename))
+ self.display(self.record, self.field)
+
+ def sig_open(self, widget=None):
+ if not self.filename_field:
+ return
+ dtemp = tempfile.mkdtemp(prefix='tryton_')
+ filename = self.filename_field.get(self.record).replace(
+ os.sep, '_').replace(os.altsep or os.sep, '_')
+ file_path = os.path.join(dtemp, filename)
+ with open(file_path, 'wb') as fp:
+ fp.write(base64.decodestring(self.field.get(self.record)))
+ root, type_ = os.path.splitext(filename)
+ if type_:
+ type_ = type_[1:]
+ file_open(file_path, type_, self.window)
def sig_save_as(self, widget=None):
- try:
- filename = file_selection(_('Save As...'),
- parent=self.window, action=gtk.FILE_CHOOSER_ACTION_SAVE)
- if filename and self.field:
- file_p = open(filename,'wb+')
- file_p.write(base64.decodestring(
- self.field.get(self.record)))
- file_p.close()
- except Exception, exception:
- warning(_('Error writing the file.\nError message:\n%s') \
- % str(exception), self.window, _('Error'))
+ filename = ''
+ if self.filename_field:
+ filename = self.filename_field.get(self.record)
+ filename = file_selection(_('Save As...'), filename=filename,
+ parent=self.window, action=gtk.FILE_CHOOSER_ACTION_SAVE)
+ if filename:
+ with open(filename,'wb') as fp:
+ fp.write(base64.decodestring(self.field.get(self.record)))
def sig_remove(self, widget=None):
- if self.field:
- self.field.set_client(self.record, False)
+ self.field.set_client(self.record, False)
self.display(self.record, self.field)
def sig_key_press(self, widget, event, *args):
@@ -132,7 +159,10 @@ class Binary(WidgetInterface):
self.sig_new(widget)
return True
elif event.keyval == gtk.keysyms.F2:
- self.sig_save_as(widget)
+ if self.filename:
+ self.sig_open(widget)
+ else:
+ self.sig_save_as(widget)
return True
return False
@@ -142,12 +172,15 @@ class Binary(WidgetInterface):
if self.wid_text:
self.wid_text.set_text('')
self.wid_size.set_text('')
+ if self.but_open:
+ self.but_open.set_sensitive(False)
self.but_save_as.set_sensitive(False)
return False
if self.wid_text:
- name_wid = record.group.fields[self.filename]
- self.wid_text.set_text(name_wid.get(record) or '')
+ self.wid_text.set_text(self.filename_field.get(record) or '')
self.wid_size.set_text(humanize(len(field.get(record) or [])))
+ if self.but_open:
+ self.but_open.set_sensitive(bool(field.get(record)))
self.but_save_as.set_sensitive(bool(field.get(record)))
return True
@@ -159,8 +192,8 @@ class Binary(WidgetInterface):
def set_value(self, record, field):
if self.wid_text:
- name_wid = self.record.group.fields[self.filename]
- name_wid.set_client(self.record, self.wid_text.get_text() or False)
+ self.filename_field.set_client(self.record,
+ self.wid_text.get_text() or False)
return
def _color_widget(self):
diff --git a/tryton/gui/window/view_form/view/form_gtk/char.py b/tryton/gui/window/view_form/view/form_gtk/char.py
index a99550a0e0..28fad0dde1 100644
--- a/tryton/gui/window/view_form/view/form_gtk/char.py
+++ b/tryton/gui/window/view_form/view/form_gtk/char.py
@@ -1,7 +1,12 @@
#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 gtk
from interface import WidgetInterface
+from tryton.common import Tooltips
+
+_ = gettext.gettext
class Char(WidgetInterface):
"Char"
@@ -50,4 +55,21 @@ class Sha(Char):
def __init__(self, field_name, model_name, window, attrs=None):
super(Sha, self).__init__(field_name, model_name, window, attrs=attrs)
- self.entry.set_visibility(False)
+ self.entry.props.visibility = False
+
+ self.visibility_checkbox = gtk.CheckButton()
+ self.visibility_checkbox.connect('toggled', self.toggle_visibility)
+ Tooltips().set_tip(self.visibility_checkbox, _('Show plain text'))
+ self.widget.pack_start(self.visibility_checkbox, expand=False)
+
+ def _readonly_set(self, value):
+ super(Char, self)._readonly_set(value)
+ self.entry.set_editable(not value)
+ self.visibility_checkbox.props.visible = not value
+ if value:
+ self.widget.set_focus_chain([])
+ else:
+ self.widget.set_focus_chain([self.entry, self.visibility_checkbox])
+
+ def toggle_visibility(self, button):
+ self.entry.props.visibility = not self.entry.props.visibility
diff --git a/tryton/gui/window/view_form/view/form_gtk/image.py b/tryton/gui/window/view_form/view/form_gtk/image.py
index 36c722b8f0..124149bf99 100644
--- a/tryton/gui/window/view_form/view/form_gtk/image.py
+++ b/tryton/gui/window/view_form/view/form_gtk/image.py
@@ -1,10 +1,12 @@
#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 __future__ import with_statement
import gtk
import gettext
import os
+import tempfile
from base64 import encodestring, decodestring
-from tryton.common import file_selection, Tooltips
+from tryton.common import file_selection, Tooltips, file_open
from tryton.config import PIXMAPS_DIR
from interface import WidgetInterface
import urllib
@@ -39,37 +41,55 @@ class Image(WidgetInterface):
self.event.add(self.image)
self.widget.pack_start(self.event, expand=True, fill=True)
- self.alignment = gtk.Alignment(xalign=0.5, yalign=0.5)
- self.hbox = gtk.HBox(spacing=0)
- self.but_add = gtk.Button()
- img_add = gtk.Image()
- img_add.set_from_stock('tryton-open', gtk.ICON_SIZE_SMALL_TOOLBAR)
- self.but_add.set_image(img_add)
- self.but_add.set_relief(gtk.RELIEF_NONE)
- self.but_add.connect('clicked', self.sig_add)
- self.tooltips.set_tip(self.but_add, _('Set Image'))
- self.hbox.pack_start(self.but_add, expand=False, fill=False)
+ if not attrs.get('readonly'):
+ alignment = gtk.Alignment(xalign=0.5, yalign=0.5)
+ hbox = gtk.HBox(spacing=0)
+ self.but_add = gtk.Button()
+ img_add = gtk.Image()
+ img_add.set_from_stock('tryton-find', gtk.ICON_SIZE_SMALL_TOOLBAR)
+ self.but_add.set_image(img_add)
+ self.but_add.set_relief(gtk.RELIEF_NONE)
+ self.but_add.connect('clicked', self.sig_add)
+ self.tooltips.set_tip(self.but_add, _('Select an Image...'))
+ hbox.pack_start(self.but_add, expand=False, fill=False)
- self.but_save_as = gtk.Button()
- img_save_as = gtk.Image()
- img_save_as.set_from_stock('tryton-save', gtk.ICON_SIZE_SMALL_TOOLBAR)
- self.but_save_as.set_image(img_save_as)
- self.but_save_as.set_relief(gtk.RELIEF_NONE)
- self.but_save_as.connect('clicked', self.sig_save_as)
- self.tooltips.set_tip(self.but_save_as, _('Save As'))
- self.hbox.pack_start(self.but_save_as, expand=False, fill=False)
+ if self.filename:
+ self.but_open = gtk.Button()
+ img_open = gtk.Image()
+ img_open.set_from_stock('tryton-open', gtk.ICON_SIZE_SMALL_TOOLBAR)
+ self.but_open.set_image(img_open)
+ self.but_open.set_relief(gtk.RELIEF_NONE)
+ self.but_open.connect('clicked', self.sig_open)
+ self.tooltips.set_tip(self.but_open, _('Open...'))
+ hbox.pack_start(self.but_open, expand=False, fill=False)
+ else:
+ self.but_open = None
- self.but_remove = gtk.Button()
- img_remove = gtk.Image()
- img_remove.set_from_stock('tryton-clear', gtk.ICON_SIZE_SMALL_TOOLBAR)
- self.but_remove.set_image(img_remove)
- self.but_remove.set_relief(gtk.RELIEF_NONE)
- self.but_remove.connect('clicked', self.sig_remove)
- self.tooltips.set_tip(self.but_remove, _('Clear'))
- self.hbox.pack_start(self.but_remove, expand=False, fill=False)
+ self.but_save_as = gtk.Button()
+ img_save_as = gtk.Image()
+ img_save_as.set_from_stock('tryton-save', gtk.ICON_SIZE_SMALL_TOOLBAR)
+ self.but_save_as.set_image(img_save_as)
+ self.but_save_as.set_relief(gtk.RELIEF_NONE)
+ self.but_save_as.connect('clicked', self.sig_save_as)
+ self.tooltips.set_tip(self.but_save_as, _('Save As...'))
+ hbox.pack_start(self.but_save_as, expand=False, fill=False)
- self.alignment.add(self.hbox)
- self.widget.pack_start(self.alignment, expand=False, fill=False)
+ self.but_remove = gtk.Button()
+ img_remove = gtk.Image()
+ img_remove.set_from_stock('tryton-clear', gtk.ICON_SIZE_SMALL_TOOLBAR)
+ self.but_remove.set_image(img_remove)
+ self.but_remove.set_relief(gtk.RELIEF_NONE)
+ self.but_remove.connect('clicked', self.sig_remove)
+ self.tooltips.set_tip(self.but_remove, _('Clear'))
+ hbox.pack_start(self.but_remove, expand=False, fill=False)
+
+ alignment.add(hbox)
+ self.widget.pack_start(alignment, expand=False, fill=False)
+ else:
+ self.but_add = None
+ self.but_open = None
+ self.but_save_as = None
+ self.but_remove = None
self.tooltips.enable()
@@ -77,14 +97,23 @@ class Image(WidgetInterface):
self.update_img()
+ @property
+ def filename_field(self):
+ return self.record.group.fields.get(self.filename)
+
def grab_focus(self):
return self.image.grab_focus()
def _readonly_set(self, value):
self._readonly = value
- self.but_add.set_sensitive(not value)
- self.but_save_as.set_sensitive(not value)
- self.but_remove.set_sensitive(not value)
+ if self.but_add:
+ self.but_add.set_sensitive(not value)
+ if self.but_open:
+ self.but_open.set_sensitive(not value)
+ if self.but_save_as:
+ self.but_save_as.set_sensitive(not value)
+ if self.but_remove:
+ self.but_remove.set_sensitive(not value)
def sig_add(self, widget):
filter_all = gtk.FileFilter()
@@ -103,17 +132,34 @@ class Image(WidgetInterface):
if filename:
self.field.set_client(self.record,
encodestring(open(filename, 'rb').read()))
- if self.filename and self.record:
- name_wid = self.record.group.fields[self.filename]
- name_wid.set_client(self.record, os.path.basename(filename))
+ if self.filename_field:
+ self.filename_field.set_client(self.record,
+ os.path.basename(filename))
self.update_img()
+ def sig_open(self, widget=None):
+ if not self.filename_field:
+ return
+ dtemp = tempfile.mkdtemp(prefix='tryton_')
+ filename = self.filename_field.get(self.record).replace(
+ os.sep, '_').replace(os.altsep or os.sep, '_')
+ file_path = os.path.join(dtemp, filename)
+ with open(file_path, 'wb') as fp:
+ fp.write(decodestring(self.field.get(self.record)))
+ root, type_ = os.path.splitext(filename)
+ if type_:
+ type_ = type_[1:]
+ file_open(file_path, type_, self.window)
+
def sig_save_as(self, widget):
- filename = file_selection(_('Save As...'), parent=self.window,
- action=gtk.FILE_CHOOSER_ACTION_SAVE)
+ filename = ''
+ if self.filename_field:
+ filename = self.filename_field.get(self.record)
+ filename = file_selection(_('Save As...'), filename=filename,
+ parent=self.window, action=gtk.FILE_CHOOSER_ACTION_SAVE)
if filename:
- open(filename, 'wb').write(decodestring(
- self.field.get_client(self.record)))
+ with open(filename, 'wb') as fp:
+ fp.write(decodestring(self.field.get(self.record)))
def sig_remove(self, widget):
self.field.set_client(self.record, False)
diff --git a/tryton/gui/window/view_form/view/list.py b/tryton/gui/window/view_form/view/list.py
index b2aeac8917..f0e04915b2 100644
--- a/tryton/gui/window/view_form/view/list.py
+++ b/tryton/gui/window/view_form/view/list.py
@@ -285,8 +285,8 @@ class ViewList(ParserView):
message(_('No record selected!'), self.window)
return False
email = {}
- if action.get('email'):
- email = self.screen.current_record.expr_eval(action['email'])
+ if action.get('pyson_email'):
+ email = self.screen.current_record.expr_eval(action['pyson_email'])
if not email:
email = {}
email['subject'] = action['name'].replace('_', '')
diff --git a/tryton/ipc.py b/tryton/ipc.py
index 3f28f198a2..62ce5e83aa 100644
--- a/tryton/ipc.py
+++ b/tryton/ipc.py
@@ -19,6 +19,8 @@ class IPCServer(object):
instance = None
def __init__(self, hostname, port, database):
+ if Server.instance:
+ Server.instance.stop()
self.hostname = hostname
self.port = port
self.database = database
diff --git a/tryton/wizard/main.py b/tryton/wizard/main.py
index 2a51421478..450ad15cfd 100644
--- a/tryton/wizard/main.py
+++ b/tryton/wizard/main.py
@@ -25,8 +25,9 @@ class Dialog(object):
self.dia = gtk.Dialog(_('Wizard'), parent,
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
self.dia.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
- if hasattr(self.dia, 'set_deletable') and os.name != 'nt':
+ if hasattr(self.dia, 'set_deletable'):
self.dia.set_deletable(False)
+ self.dia.connect('close', self.close)
self.accel_group = gtk.AccelGroup()
self.dia.add_accel_group(self.accel_group)
@@ -145,6 +146,10 @@ class Dialog(object):
self.parent.present()
return False
+ def close(self, widget, event=None):
+ widget.emit_stop_by_name('close')
+ return True
+
def destroy(self):
self.dia.destroy()