merge.
This commit is contained in:
commit
fc87b7ef6f
82 files changed
+1007
-927
No files matched your search
@@ -581,24 +581,6 @@ Tabs Position
|
||||
* Right
|
||||
* Bottom
|
||||
|
||||
.. _Menu-Options-File_Actions:
|
||||
|
||||
File Actions...:
|
||||
Opens a dialog to set up file types for print and open actions.
|
||||
Use ``"%s"`` as a placeholder for the document name.
|
||||
|
||||
* Provided file types:
|
||||
|
||||
- ODT file: Open Office Writer Document
|
||||
- PDF file: Adobes(TM) Portable Document Format
|
||||
- PNG file: Portable Network Graphics format
|
||||
- TXT file: Pure text file
|
||||
|
||||
* Provided actions
|
||||
|
||||
- Open: Setting up program system call which opens the specific file type.
|
||||
- Print: Setting up program system call printing the specific file type.
|
||||
|
||||
.. _Menu-Options-Email:
|
||||
|
||||
Email...:
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
#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 time
|
||||
import datetime
|
||||
import tryton.rpc as rpc
|
||||
from tryton.common import message, error, selection, file_open, mailto
|
||||
from tryton.common import message, selection, file_open, mailto
|
||||
from tryton.gui.window import Window
|
||||
from tryton.pyson import PYSONDecoder
|
||||
from tryton.exceptions import TrytonServerError
|
||||
@@ -15,6 +13,7 @@ import tryton.common as common
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
class Action(object):
|
||||
|
||||
@staticmethod
|
||||
@@ -71,7 +70,7 @@ class Action(object):
|
||||
common.process_exception(exception)
|
||||
return
|
||||
if not res:
|
||||
raise Exception, 'ActionNotFound'
|
||||
raise Exception('ActionNotFound')
|
||||
action_type = res['type']
|
||||
try:
|
||||
res = rpc.execute('model', action_type, 'search_read',
|
||||
@@ -117,7 +116,8 @@ class Action(object):
|
||||
domain_context = ctx.copy()
|
||||
domain_context['context'] = ctx
|
||||
domain_context['_user'] = rpc._USER
|
||||
domain = PYSONDecoder(domain_context).decode(action['pyson_domain'])
|
||||
domain = PYSONDecoder(domain_context).decode(
|
||||
action['pyson_domain'])
|
||||
|
||||
search_context = ctx.copy()
|
||||
search_context['context'] = ctx
|
||||
|
||||
+7
-7
@@ -22,21 +22,18 @@ import logging
|
||||
from urlparse import urlparse
|
||||
import threading
|
||||
|
||||
from tryton import version
|
||||
from tryton import config
|
||||
import tryton.common as common
|
||||
from tryton.config import CONFIG, CURRENT_DIR, PREFIX, PIXMAPS_DIR, \
|
||||
TRYTON_ICON, get_config_dir
|
||||
from tryton.config import CONFIG, get_config_dir
|
||||
from tryton import translate
|
||||
from tryton import gui
|
||||
from tryton.ipc import Client as IPCClient
|
||||
import traceback
|
||||
import time
|
||||
import signal
|
||||
|
||||
if not hasattr(gtk.gdk, 'lock'):
|
||||
class _Lock(object):
|
||||
__enter__ = gtk.gdk.threads_enter
|
||||
|
||||
def __exit__(*ignored):
|
||||
gtk.gdk.threads_leave()
|
||||
|
||||
@@ -44,11 +41,13 @@ if not hasattr(gtk.gdk, 'lock'):
|
||||
|
||||
if sys.platform == 'win32':
|
||||
class Dialog(gtk.Dialog):
|
||||
|
||||
def run(self):
|
||||
with gtk.gdk.lock:
|
||||
return super(Dialog, self).run()
|
||||
gtk.Dialog = Dialog
|
||||
|
||||
|
||||
class TrytonClient(object):
|
||||
"Tryton client"
|
||||
|
||||
@@ -103,7 +102,8 @@ class TrytonClient(object):
|
||||
signal.signal(signal.SIGINT, lambda signum, frame: main.sig_quit())
|
||||
signal.signal(signal.SIGTERM, lambda signum, frame: main.sig_quit())
|
||||
if hasattr(signal, 'SIGQUIT'):
|
||||
signal.signal(signal.SIGQUIT, lambda signum, frame: main.sig_quit())
|
||||
signal.signal(signal.SIGQUIT,
|
||||
lambda signum, frame: main.sig_quit())
|
||||
|
||||
def excepthook(exctyp, exception, tb):
|
||||
import common
|
||||
@@ -135,7 +135,7 @@ class TrytonClient(object):
|
||||
if sys.platform == 'win32':
|
||||
while not self.quit_client.isSet():
|
||||
with gtk.gdk.lock:
|
||||
running = gtk.main_iteration(True)
|
||||
gtk.main_iteration(True)
|
||||
else:
|
||||
gtk.main()
|
||||
except KeyboardInterrupt:
|
||||
|
||||
@@ -27,7 +27,8 @@ class Button(gtk.Button):
|
||||
|
||||
def state_set(self, record):
|
||||
if record:
|
||||
states = record.expr_eval(self.attrs.get('states', {}), check_load=False)
|
||||
states = record.expr_eval(self.attrs.get('states', {}),
|
||||
check_load=False)
|
||||
else:
|
||||
states = {}
|
||||
if states.get('invisible', False):
|
||||
|
||||
@@ -4,6 +4,9 @@ import gtk
|
||||
import gobject
|
||||
import pango
|
||||
|
||||
BUTTON_BORDER = 10
|
||||
BUTTON_SPACING = 2
|
||||
|
||||
|
||||
class CellRendererBinary(gtk.GenericCellRenderer):
|
||||
__gproperties__ = {
|
||||
@@ -48,7 +51,8 @@ class CellRendererBinary(gtk.GenericCellRenderer):
|
||||
img_insensitive = img_sensitive.copy()
|
||||
img_sensitive.saturate_and_pixelate(img_insensitive, 0, False)
|
||||
width = img_sensitive.get_width()
|
||||
self.images[key] = (img_sensitive, img_insensitive, width)
|
||||
height = img_sensitive.get_height()
|
||||
self.images[key] = (img_sensitive, img_insensitive, width, height)
|
||||
|
||||
def do_set_property(self, pspec, value):
|
||||
setattr(self, pspec.name, value)
|
||||
@@ -56,24 +60,31 @@ class CellRendererBinary(gtk.GenericCellRenderer):
|
||||
def do_get_property(self, pspec):
|
||||
return getattr(self, pspec.name)
|
||||
|
||||
def button_width(self, cell_area):
|
||||
return ((cell_area.width / 2 - (len(self.buttons) + 1) * 2)
|
||||
/ len(self.buttons))
|
||||
def button_width(self):
|
||||
return (sum(width for _, _, width, _ in self.images.itervalues())
|
||||
+ (2 * (BUTTON_BORDER + BUTTON_SPACING) * len(self.buttons))
|
||||
- 2 * BUTTON_SPACING)
|
||||
|
||||
def on_get_size(self, widget, cell_area=None):
|
||||
if cell_area is None:
|
||||
return (0, 0, 30, 18)
|
||||
else:
|
||||
return (cell_area.x, cell_area.y, cell_area.width, cell_area.height)
|
||||
return (cell_area.x, cell_area.y,
|
||||
cell_area.width, cell_area.height)
|
||||
|
||||
def on_start_editing(self, event, widget, path, background_area,
|
||||
cell_area, flags):
|
||||
button_width = self.button_width(cell_area)
|
||||
if event is None:
|
||||
return
|
||||
button_width = self.button_width()
|
||||
|
||||
for index, button_name in enumerate(self.buttons):
|
||||
x_offset = button_width * index + 2 * (index + 1)
|
||||
x_button = cell_area.x + cell_area.width / 2 + 5 + x_offset
|
||||
if x_button < event.x < x_button + button_width:
|
||||
_, _, pxbf_width, _ = self.images[button_name]
|
||||
x_offset = (cell_area.width - button_width
|
||||
+ (pxbf_width + (2 * BUTTON_BORDER) + BUTTON_SPACING) * index)
|
||||
x_button = cell_area.x + x_offset
|
||||
if x_button < event.x < (x_button + pxbf_width
|
||||
+ (2 * BUTTON_BORDER)):
|
||||
break
|
||||
else:
|
||||
button_name = None
|
||||
@@ -83,9 +94,10 @@ class CellRendererBinary(gtk.GenericCellRenderer):
|
||||
return
|
||||
if not self.size and button_name == 'save':
|
||||
return
|
||||
if event is None or event.type == gtk.gdk.BUTTON_PRESS:
|
||||
if event.type == gtk.gdk.BUTTON_PRESS:
|
||||
self.clicking = button_name
|
||||
self.emit(button_name, path)
|
||||
|
||||
def timeout(self, widget):
|
||||
self.clicking = ''
|
||||
widget.queue_draw()
|
||||
@@ -99,24 +111,31 @@ class CellRendererBinary(gtk.GenericCellRenderer):
|
||||
if type(window) == gtk.gdk.Pixmap:
|
||||
return
|
||||
|
||||
button_width = self.button_width()
|
||||
|
||||
# display size
|
||||
layout = widget.create_pango_layout(self.size)
|
||||
layout.set_font_description(widget.style.font_desc)
|
||||
w, h = layout.get_size()
|
||||
x = int(cell_area.x + (cell_area.width - w / pango.SCALE) * 0)
|
||||
x = int(cell_area.x + cell_area.width - button_width - w / pango.SCALE
|
||||
- BUTTON_SPACING)
|
||||
y = int(cell_area.y + (cell_area.height - h / pango.SCALE) / 2)
|
||||
layout.set_width(((cell_area.width / 2) - 2) * pango.SCALE)
|
||||
state = gtk.STATE_NORMAL
|
||||
if flags & gtk.CELL_RENDERER_SELECTED:
|
||||
state = gtk.STATE_ACTIVE
|
||||
window.draw_layout(widget.style.text_gc[state], x, y, layout)
|
||||
if x >= cell_area.x:
|
||||
widget.style.paint_layout(window, state, True, expose_area,
|
||||
widget, "cellrendererbinary", x, y, layout)
|
||||
|
||||
# display buttons
|
||||
button_width = self.button_width(cell_area)
|
||||
for index, button_name in enumerate(self.buttons):
|
||||
state = gtk.STATE_NORMAL
|
||||
shadow = gtk.SHADOW_OUT
|
||||
pxbf_sens, pxbf_insens, pxbf_width = self.images[button_name]
|
||||
if self.clicking and flags & gtk.CELL_RENDERER_SELECTED:
|
||||
pxbf_sens, pxbf_insens, pxbf_width, pxbf_height = \
|
||||
self.images[button_name]
|
||||
if (self.clicking == button_name
|
||||
and flags & gtk.CELL_RENDERER_SELECTED):
|
||||
state = gtk.STATE_ACTIVE
|
||||
shadow = gtk.SHADOW_IN
|
||||
if (not self.editable and button_name in ('new', 'clear')
|
||||
@@ -125,14 +144,16 @@ class CellRendererBinary(gtk.GenericCellRenderer):
|
||||
pixbuf = pxbf_insens
|
||||
else:
|
||||
pixbuf = pxbf_sens
|
||||
x_offset = (cell_area.width / 2 + 5
|
||||
+ button_width * index + 2 * (index + 1)) - 1
|
||||
x_offset = (cell_area.width - button_width
|
||||
+ (pxbf_width + (2 * BUTTON_BORDER) + BUTTON_SPACING) * index)
|
||||
if x_offset < 0:
|
||||
continue
|
||||
widget.style.paint_box(window, state, shadow,
|
||||
None, widget, "button", cell_area.x + x_offset, cell_area.y,
|
||||
button_width, cell_area.height)
|
||||
pxbf_width + (2 * BUTTON_BORDER), cell_area.height)
|
||||
window.draw_pixbuf(widget.style.black_gc,
|
||||
pixbuf, 0, 0,
|
||||
cell_area.x + x_offset + button_width / 2 - pxbf_width / 2,
|
||||
cell_area.y)
|
||||
cell_area.x + x_offset + BUTTON_BORDER,
|
||||
cell_area.y + (cell_area.height - pxbf_height) / 2)
|
||||
|
||||
gobject.type_register(CellRendererBinary)
|
||||
@@ -54,19 +54,29 @@ class CellRendererButton(gtk.GenericCellRenderer):
|
||||
x = cell_area.x
|
||||
y = int(cell_area.y + (cell_area.height - h / pango.SCALE) / 2)
|
||||
window.draw_layout(widget.style.text_gc[0], x, y, layout)
|
||||
widget.style.paint_layout(window, state, True, expose_area, widget,
|
||||
"cellrendererbutton", x, y, layout)
|
||||
|
||||
layout = widget.create_pango_layout(self.text)
|
||||
layout.set_font_description(widget.style.font_desc)
|
||||
layout.set_ellipsize(pango.ELLIPSIZE_END)
|
||||
w, h = layout.get_size()
|
||||
x = int(cell_area.x + (cell_area.width - w / pango.SCALE) / 2)
|
||||
if cell_area.width < w / pango.SCALE:
|
||||
x = cell_area.x + 2 / pango.SCALE
|
||||
else:
|
||||
x = int(cell_area.x + (cell_area.width - w / pango.SCALE) / 2)
|
||||
y = int(cell_area.y + (cell_area.height - h / pango.SCALE) / 2)
|
||||
window.draw_layout(widget.style.text_gc[0], x, y, layout)
|
||||
layout.set_width((cell_area.width - 4) * pango.SCALE)
|
||||
layout.set_wrap(pango.WRAP_CHAR)
|
||||
widget.style.paint_layout(window, state, True, expose_area, widget,
|
||||
"cellrendererbutton", x, y, layout)
|
||||
|
||||
def on_get_size(self, widget, cell_area=None):
|
||||
if cell_area is None:
|
||||
return (0, 0, 30, 18)
|
||||
else:
|
||||
return (cell_area.x, cell_area.y, cell_area.width, cell_area.height)
|
||||
return (cell_area.x, cell_area.y,
|
||||
cell_area.width, cell_area.height)
|
||||
|
||||
def on_start_editing(self, event, widget, path, background_area,
|
||||
cell_area, flags):
|
||||
@@ -80,6 +90,7 @@ class CellRendererButton(gtk.GenericCellRenderer):
|
||||
while gtk.events_pending():
|
||||
gtk.main_iteration()
|
||||
self.emit("clicked", path)
|
||||
|
||||
def timeout(self, widget):
|
||||
self.clicking = False
|
||||
widget.queue_draw()
|
||||
|
||||
@@ -106,10 +106,13 @@ class CellRendererCombo(gtk.GenericCellRenderer):
|
||||
editable.modify_text(gtk.STATE_INSENSITIVE, fg_color)
|
||||
else:
|
||||
editable.modify_bg(gtk.STATE_ACTIVE, style.bg[gtk.STATE_ACTIVE])
|
||||
editable.modify_base(gtk.STATE_NORMAL, style.base[gtk.STATE_NORMAL])
|
||||
editable.modify_base(gtk.STATE_NORMAL,
|
||||
style.base[gtk.STATE_NORMAL])
|
||||
editable.modify_fg(gtk.STATE_NORMAL, style.fg[gtk.STATE_NORMAL])
|
||||
editable.modify_text(gtk.STATE_NORMAL, style.text[gtk.STATE_NORMAL])
|
||||
editable.modify_text(gtk.STATE_INSENSITIVE, style.text[gtk.STATE_INSENSITIVE])
|
||||
editable.modify_text(gtk.STATE_NORMAL,
|
||||
style.text[gtk.STATE_NORMAL])
|
||||
editable.modify_text(gtk.STATE_INSENSITIVE,
|
||||
style.text[gtk.STATE_INSENSITIVE])
|
||||
return editable
|
||||
|
||||
gobject.type_register(CellRendererCombo)
|
||||
@@ -3,7 +3,7 @@
|
||||
import gtk
|
||||
import gobject
|
||||
import pango
|
||||
from date_widget import mapping, DateEntry, compute_date
|
||||
from date_widget import DateEntry, compute_date
|
||||
|
||||
|
||||
class CellRendererDate(gtk.GenericCellRenderer):
|
||||
@@ -100,10 +100,13 @@ class CellRendererDate(gtk.GenericCellRenderer):
|
||||
editable.modify_text(gtk.STATE_INSENSITIVE, fg_color)
|
||||
else:
|
||||
editable.modify_bg(gtk.STATE_ACTIVE, style.bg[gtk.STATE_ACTIVE])
|
||||
editable.modify_base(gtk.STATE_NORMAL, style.base[gtk.STATE_NORMAL])
|
||||
editable.modify_base(gtk.STATE_NORMAL,
|
||||
style.base[gtk.STATE_NORMAL])
|
||||
editable.modify_fg(gtk.STATE_NORMAL, style.fg[gtk.STATE_NORMAL])
|
||||
editable.modify_text(gtk.STATE_NORMAL, style.text[gtk.STATE_NORMAL])
|
||||
editable.modify_text(gtk.STATE_INSENSITIVE, style.text[gtk.STATE_INSENSITIVE])
|
||||
editable.modify_text(gtk.STATE_NORMAL,
|
||||
style.text[gtk.STATE_NORMAL])
|
||||
editable.modify_text(gtk.STATE_INSENSITIVE,
|
||||
style.text[gtk.STATE_INSENSITIVE])
|
||||
|
||||
if self.text:
|
||||
editable.set_text(self.text)
|
||||
@@ -126,9 +129,10 @@ class CellRendererDate(gtk.GenericCellRenderer):
|
||||
if ok:
|
||||
self._date_cb(event)
|
||||
else:
|
||||
if hasattr(event, 'keyval') and not event.keyval == gtk.keysyms.Escape:
|
||||
if (hasattr(event, 'keyval')
|
||||
and not event.keyval == gtk.keysyms.Escape):
|
||||
dt = widget.date_get()
|
||||
res= compute_date(self.cmd, dt, widget.format)
|
||||
res = compute_date(self.cmd, dt, widget.format)
|
||||
if res:
|
||||
widget.date_set(res)
|
||||
self.cmd = ''
|
||||
|
||||
@@ -7,7 +7,6 @@ import locale
|
||||
|
||||
class CellRendererInteger(CellRendererText):
|
||||
|
||||
|
||||
def on_start_editing(self, event, widget, path, background_area,
|
||||
cell_area, flags):
|
||||
editable = super(CellRendererInteger, self).on_start_editing(event,
|
||||
|
||||
@@ -100,10 +100,13 @@ class CellRendererText(gtk.GenericCellRenderer):
|
||||
editable.modify_text(gtk.STATE_INSENSITIVE, fg_color)
|
||||
else:
|
||||
editable.modify_bg(gtk.STATE_ACTIVE, style.bg[gtk.STATE_ACTIVE])
|
||||
editable.modify_base(gtk.STATE_NORMAL, style.base[gtk.STATE_NORMAL])
|
||||
editable.modify_base(gtk.STATE_NORMAL,
|
||||
style.base[gtk.STATE_NORMAL])
|
||||
editable.modify_fg(gtk.STATE_NORMAL, style.fg[gtk.STATE_NORMAL])
|
||||
editable.modify_text(gtk.STATE_NORMAL, style.text[gtk.STATE_NORMAL])
|
||||
editable.modify_text(gtk.STATE_INSENSITIVE, style.text[gtk.STATE_INSENSITIVE])
|
||||
editable.modify_text(gtk.STATE_NORMAL,
|
||||
style.text[gtk.STATE_NORMAL])
|
||||
editable.modify_text(gtk.STATE_INSENSITIVE,
|
||||
style.text[gtk.STATE_INSENSITIVE])
|
||||
|
||||
editable.set_text(self.text)
|
||||
editable.grab_focus()
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
import gtk
|
||||
import gobject
|
||||
import pango
|
||||
|
||||
|
||||
class CellRendererToggle(gtk.GenericCellRenderer):
|
||||
|
||||
+120
-119
@@ -11,7 +11,7 @@ import subprocess
|
||||
import re
|
||||
import logging
|
||||
from tryton.config import CONFIG
|
||||
from tryton.config import TRYTON_ICON, PIXMAPS_DIR, DATA_DIR
|
||||
from tryton.config import TRYTON_ICON, PIXMAPS_DIR
|
||||
import time
|
||||
import sys
|
||||
import xmlrpclib
|
||||
@@ -37,7 +37,7 @@ try:
|
||||
except ImportError:
|
||||
ssl = None
|
||||
import dis
|
||||
from threading import Lock, Semaphore
|
||||
from threading import Lock
|
||||
try:
|
||||
import pytz
|
||||
except ImportError:
|
||||
@@ -128,6 +128,7 @@ class TrytonIconFactory(gtk.IconFactory):
|
||||
ICONFACTORY = TrytonIconFactory()
|
||||
ICONFACTORY.add_default()
|
||||
|
||||
|
||||
def find_in_path(name):
|
||||
if os.name == "nt":
|
||||
sep = ';'
|
||||
@@ -141,12 +142,14 @@ def find_in_path(name):
|
||||
return val
|
||||
return name
|
||||
|
||||
|
||||
def test_server_version(host, port):
|
||||
version = rpc.server_version(host, port)
|
||||
if not version:
|
||||
return False
|
||||
return version.split('.')[:2] == VERSION.split('.')[:2]
|
||||
|
||||
|
||||
def refresh_dblist(host, port):
|
||||
'''
|
||||
Return the number of database available
|
||||
@@ -158,6 +161,7 @@ def refresh_dblist(host, port):
|
||||
return -1
|
||||
return rpc.db_list(host, port)
|
||||
|
||||
|
||||
def refresh_langlist(lang_widget, host, port):
|
||||
liststore = lang_widget.get_model()
|
||||
liststore.clear()
|
||||
@@ -174,17 +178,18 @@ def refresh_langlist(lang_widget, host, port):
|
||||
liststore.insert(i, (val, key))
|
||||
if key == lang:
|
||||
index = i
|
||||
if key == 'en_US' and index < 0 :
|
||||
if key == 'en_US' and index < 0:
|
||||
index = i
|
||||
i += 1
|
||||
lang_widget.set_active(index)
|
||||
return lang_list
|
||||
|
||||
|
||||
def request_server(server_widget):
|
||||
result = False
|
||||
parent = get_toplevel_window()
|
||||
dialog = gtk.Dialog(
|
||||
title= _('Tryton Connection'),
|
||||
title=_('Tryton Connection'),
|
||||
parent=parent,
|
||||
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT |
|
||||
gtk.WIN_POS_CENTER_ON_PARENT |
|
||||
@@ -211,7 +216,7 @@ def request_server(server_widget):
|
||||
entry_server.set_text("localhost")
|
||||
entry_server.set_activates_default(True)
|
||||
entry_server.set_width_chars(16)
|
||||
table.attach(entry_server, 1, 2, 0, 1,yoptions=False,
|
||||
table.attach(entry_server, 1, 2, 0, 1, yoptions=False,
|
||||
xoptions=gtk.FILL | gtk.EXPAND)
|
||||
label_port = gtk.Label(_("Port:"))
|
||||
label_port.set_alignment(1, 0.5)
|
||||
@@ -242,6 +247,7 @@ def request_server(server_widget):
|
||||
dialog.destroy()
|
||||
return result
|
||||
|
||||
|
||||
def get_toplevel_window():
|
||||
windows = [x for x in gtk.window_list_toplevels()
|
||||
if x.window and x.props.visible]
|
||||
@@ -250,6 +256,7 @@ def get_toplevel_window():
|
||||
return window
|
||||
return trans2windows[None]
|
||||
|
||||
|
||||
def get_sensible_widget(window):
|
||||
from tryton.gui.main import Main
|
||||
main = Main.get_main()
|
||||
@@ -260,6 +267,7 @@ def get_sensible_widget(window):
|
||||
return page.widget
|
||||
return window
|
||||
|
||||
|
||||
def center_window(window, parent, sensible):
|
||||
parent_x, parent_y = parent.window.get_origin()
|
||||
window_allocation = window.get_allocation()
|
||||
@@ -270,10 +278,11 @@ def center_window(window, parent, sensible):
|
||||
int((sensible_allocation.height - window_allocation.height) / 2))
|
||||
window.move(x, y)
|
||||
|
||||
|
||||
def selection(title, values, alwaysask=False):
|
||||
if not values or len(values)==0:
|
||||
if not values or len(values) == 0:
|
||||
return None
|
||||
elif len(values)==1 and (not alwaysask):
|
||||
elif len(values) == 1 and (not alwaysask):
|
||||
key = values.keys()[0]
|
||||
return (key, values[key])
|
||||
|
||||
@@ -331,13 +340,14 @@ def selection(title, values, alwaysask=False):
|
||||
dialog.destroy()
|
||||
return res
|
||||
|
||||
|
||||
def file_selection(title, filename='',
|
||||
action=gtk.FILE_CHOOSER_ACTION_OPEN, preview=True, multi=False,
|
||||
filters=None):
|
||||
parent = get_toplevel_window()
|
||||
if action == gtk.FILE_CHOOSER_ACTION_OPEN:
|
||||
buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_OPEN,gtk.RESPONSE_OK)
|
||||
gtk.STOCK_OPEN, gtk.RESPONSE_OK)
|
||||
else:
|
||||
buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_SAVE, gtk.RESPONSE_OK)
|
||||
@@ -403,48 +413,9 @@ def file_selection(title, filename='',
|
||||
win.destroy()
|
||||
return filenames
|
||||
|
||||
|
||||
def file_open(filename, type, print_p=False):
|
||||
if os.name == 'nt':
|
||||
operation = 'open'
|
||||
if print_p:
|
||||
operation = 'print'
|
||||
try:
|
||||
os.startfile(os.path.normpath(filename), operation)
|
||||
except WindowsError:
|
||||
save_name = file_selection(_('Save As...'),
|
||||
action=gtk.FILE_CHOOSER_ACTION_SAVE)
|
||||
if save_name:
|
||||
file_p = open(filename, 'rb')
|
||||
save_p = open(save_name, 'wb+')
|
||||
save_p.write(file_p.read())
|
||||
save_p.close()
|
||||
file_p.close()
|
||||
return
|
||||
elif sys.platform == 'darwin':
|
||||
pid = os.fork()
|
||||
if not pid:
|
||||
pid = os.fork()
|
||||
if not pid:
|
||||
try:
|
||||
os.execv('/usr/bin/open', ['/usr/bin/open', filename])
|
||||
except OSError:
|
||||
sys.exit(0)
|
||||
time.sleep(0.1)
|
||||
sys.exit(0)
|
||||
os.waitpid(pid, 0)
|
||||
return
|
||||
cmd = ''
|
||||
if isinstance(CONFIG['client.actions'], basestring):
|
||||
CONFIG['client.actions'] = safe_eval(CONFIG['client.actions'])
|
||||
if type in CONFIG['client.actions']:
|
||||
if print_p:
|
||||
cmd = CONFIG['client.actions'][type][1]
|
||||
else:
|
||||
cmd = CONFIG['client.actions'][type][0]
|
||||
if not cmd:
|
||||
#TODO add dialog box
|
||||
pass
|
||||
if not cmd:
|
||||
def save():
|
||||
save_name = file_selection(_('Save As...'),
|
||||
action=gtk.FILE_CHOOSER_ACTION_SAVE)
|
||||
if save_name:
|
||||
@@ -453,25 +424,26 @@ def file_open(filename, type, print_p=False):
|
||||
save_p.write(file_p.read())
|
||||
save_p.close()
|
||||
file_p.close()
|
||||
return
|
||||
cmd = cmd % filename
|
||||
args = shlex.split(str(cmd))
|
||||
prog = find_in_path(args[0])
|
||||
args[0] = os.path.basename(args[0])
|
||||
if print_p:
|
||||
os.spawnv(os.P_WAIT, prog, args)
|
||||
return
|
||||
pid = os.fork()
|
||||
if not pid:
|
||||
pid = os.fork()
|
||||
if not pid:
|
||||
try:
|
||||
os.execv(prog, args)
|
||||
except OSError:
|
||||
sys.exit(0)
|
||||
time.sleep(0.1)
|
||||
sys.exit(0)
|
||||
os.waitpid(pid, 0)
|
||||
|
||||
if os.name == 'nt':
|
||||
operation = 'open'
|
||||
if print_p:
|
||||
operation = 'print'
|
||||
try:
|
||||
os.startfile(os.path.normpath(filename), operation)
|
||||
except WindowsError:
|
||||
save()
|
||||
elif sys.platform == 'darwin':
|
||||
try:
|
||||
subprocess.Popen(['/usr/bin/open', filename])
|
||||
except OSError:
|
||||
save()
|
||||
else:
|
||||
try:
|
||||
subprocess.Popen(['xdg-open', filename])
|
||||
except OSError:
|
||||
save()
|
||||
|
||||
|
||||
def mailto(to=None, cc=None, subject=None, body=None, attachment=None):
|
||||
if CONFIG['client.email']:
|
||||
@@ -485,6 +457,23 @@ def mailto(to=None, cc=None, subject=None, body=None, attachment=None):
|
||||
args = shlex.split(str(cmd))
|
||||
subprocess.Popen(args)
|
||||
return
|
||||
if os.name != 'nt' and sys.platform != 'darwin':
|
||||
args = ['xdg-email', '--utf8']
|
||||
if cc:
|
||||
args.extend(['--cc', cc])
|
||||
if subject:
|
||||
args.extend(['--subject', subject])
|
||||
if body:
|
||||
args.extend(['--body', body])
|
||||
if attachment:
|
||||
args.extend(['--attach', attachment])
|
||||
if to:
|
||||
args.append(to)
|
||||
try:
|
||||
subprocess.Popen(args)
|
||||
return
|
||||
except OSError:
|
||||
pass
|
||||
#http://www.faqs.org/rfcs/rfc2368.html
|
||||
url = "mailto:"
|
||||
if to:
|
||||
@@ -540,7 +529,7 @@ class MessageDialog(UniqueDialog):
|
||||
|
||||
def build_dialog(self, parent, message, msg_type):
|
||||
dialog = gtk.MessageDialog(parent,
|
||||
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT, msg_type,
|
||||
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, msg_type,
|
||||
gtk.BUTTONS_OK, message)
|
||||
return dialog
|
||||
|
||||
@@ -559,7 +548,8 @@ class WarningDialog(UniqueDialog):
|
||||
dialog.set_markup('<b>%s</b>' % (to_xml(title)))
|
||||
dialog.format_secondary_markup(to_xml(message))
|
||||
else:
|
||||
dialog.set_markup('<b>%s</b>\n%s' % (to_xml(title), to_xml(message)))
|
||||
dialog.set_markup(
|
||||
'<b>%s</b>\n%s' % (to_xml(title), to_xml(message)))
|
||||
return dialog
|
||||
|
||||
warning = WarningDialog()
|
||||
@@ -659,7 +649,7 @@ class AskDialog(UniqueDialog):
|
||||
|
||||
def build_dialog(self, parent, question, visibility):
|
||||
win = gtk.Dialog('Tryton', parent,
|
||||
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_OK, gtk.RESPONSE_OK))
|
||||
win.set_has_separator(True)
|
||||
@@ -704,9 +694,9 @@ ask = AskDialog()
|
||||
class ConcurrencyDialog(UniqueDialog):
|
||||
|
||||
def build_dialog(self, parent, resource, obj_id, context):
|
||||
dialog = gtk.Dialog(_('Concurrency Exception'), parent, gtk.DIALOG_MODAL
|
||||
| gtk.DIALOG_DESTROY_WITH_PARENT | gtk.WIN_POS_CENTER_ON_PARENT
|
||||
| gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
|
||||
dialog = gtk.Dialog(_('Concurrency Exception'), parent,
|
||||
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT |
|
||||
gtk.WIN_POS_CENTER_ON_PARENT | gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
|
||||
dialog.set_has_separator(True)
|
||||
dialog.set_default_response(gtk.RESPONSE_CANCEL)
|
||||
hbox = gtk.HBox()
|
||||
@@ -830,15 +820,16 @@ class ErrorDialog(UniqueDialog):
|
||||
if title == details:
|
||||
title = ''
|
||||
log = logging.getLogger('common.message')
|
||||
log.error(details)
|
||||
log.error(details + '\n' + title)
|
||||
|
||||
response = super(ErrorDialog, self).__call__(title, details)
|
||||
if response == gtk.RESPONSE_OK:
|
||||
send_bugtracker(details)
|
||||
send_bugtracker(title, details)
|
||||
|
||||
error = ErrorDialog()
|
||||
|
||||
def send_bugtracker(msg):
|
||||
|
||||
def send_bugtracker(title, msg):
|
||||
from tryton import rpc
|
||||
parent = get_toplevel_window()
|
||||
win = gtk.Dialog(_('Bug Tracker'), parent,
|
||||
@@ -897,21 +888,15 @@ def send_bugtracker(msg):
|
||||
protocol = 'http'
|
||||
if ssl or hasattr(socket, 'ssl'):
|
||||
protocol = 'https'
|
||||
server = xmlrpclib.Server(('%s://%s:%s@' + CONFIG['roundup.xmlrpc'])
|
||||
% (protocol, user, password), allow_none=True)
|
||||
server = xmlrpclib.Server(
|
||||
('%s://%s:%s@' + CONFIG['roundup.xmlrpc'])
|
||||
% (protocol, user, password), allow_none=True)
|
||||
if hashlib:
|
||||
msg_md5 = hashlib.md5(msg).hexdigest()
|
||||
msg_md5 = hashlib.md5(msg + '\n' + title).hexdigest()
|
||||
else:
|
||||
msg_md5 = md5.new(msg).hexdigest()
|
||||
# use the last line of the message as title
|
||||
title = '[no title]'
|
||||
for line in msg.splitlines():
|
||||
#don't use empty line nor ^ from sql error
|
||||
if line and '^' != line.strip():
|
||||
if len(line) > 128:
|
||||
title = line[:128] + '...'
|
||||
else:
|
||||
title = line
|
||||
msg_md5 = md5.new(msg + '\n' + title).hexdigest()
|
||||
if not title:
|
||||
title = '[no title]'
|
||||
issue_id = None
|
||||
msg_ids = server.filter('msg', None, {'summary': str(msg_md5)})
|
||||
if msg_ids:
|
||||
@@ -921,9 +906,10 @@ def send_bugtracker(msg):
|
||||
if issue_id:
|
||||
# issue to same message already exists, add user to nosy-list
|
||||
server.set('issue' + str(issue_id), *['nosy=+' + user])
|
||||
message(_('The same bug was already reported by another user.\n' \
|
||||
'To keep you informed your username is added to the nosy-list of this issue') + \
|
||||
'%s' % issue_id)
|
||||
message(
|
||||
_('The same bug was already reported by another user.\n'
|
||||
'To keep you informed your username is added '
|
||||
'to the nosy-list of this issue') + '%s' % issue_id)
|
||||
else:
|
||||
# create a new issue for this error-message
|
||||
# first create message
|
||||
@@ -934,23 +920,27 @@ def send_bugtracker(msg):
|
||||
'nosy=' + user, 'title=' + title, 'priority=bug'])
|
||||
message(_('Created new bug with ID ') + \
|
||||
'issue%s' % issue_id)
|
||||
webbrowser.open(CONFIG['roundup.url'] + 'issue%s' % issue_id, new=2)
|
||||
webbrowser.open(CONFIG['roundup.url'] + 'issue%s' % issue_id,
|
||||
new=2)
|
||||
except (socket.error, xmlrpclib.Fault), exception:
|
||||
if (isinstance(exception, xmlrpclib.Fault)
|
||||
and 'roundup.cgi.exceptions.Unauthorised' in
|
||||
exception.faultString):
|
||||
message(_('Connection error!\nBad username or password!'))
|
||||
return send_bugtracker(msg, parent)
|
||||
return send_bugtracker(title, msg)
|
||||
tb_s = reduce(lambda x, y: x + y,
|
||||
traceback.format_exception(sys.exc_type,
|
||||
sys.exc_value, sys.exc_traceback))
|
||||
message(_('Exception:') + '\n' + tb_s, msg_type=gtk.MESSAGE_ERROR)
|
||||
|
||||
|
||||
def to_xml(string):
|
||||
return string.replace('&','&').replace('<','<').replace('>','>')
|
||||
return string.replace('&', '&'
|
||||
).replace('<', '<').replace('>', '>')
|
||||
|
||||
PLOCK = Lock()
|
||||
|
||||
|
||||
def process_exception(exception, *args, **kwargs):
|
||||
|
||||
if isinstance(exception, TrytonError):
|
||||
@@ -1049,6 +1039,7 @@ def process_exception(exception, *args, **kwargs):
|
||||
error(error_title, error_detail)
|
||||
return False
|
||||
|
||||
|
||||
def node_attributes(node):
|
||||
result = {}
|
||||
attrs = node.attributes
|
||||
@@ -1058,6 +1049,7 @@ def node_attributes(node):
|
||||
result[str(attrs.item(i).localName)] = str(attrs.item(i).nodeValue)
|
||||
return result
|
||||
|
||||
|
||||
def hex2rgb(hexstring, digits=2):
|
||||
"""
|
||||
Converts a hexstring color to a rgb tuple.
|
||||
@@ -1068,11 +1060,12 @@ def hex2rgb(hexstring, digits=2):
|
||||
if isinstance(hexstring, (tuple, list)):
|
||||
return hexstring
|
||||
top = float(int(digits * 'f', 16))
|
||||
r = int(hexstring[1:digits+1], 16)
|
||||
g = int(hexstring[digits+1:digits*2+1], 16)
|
||||
b = int(hexstring[digits*2+1:digits*3+1], 16)
|
||||
r = int(hexstring[1:digits + 1], 16)
|
||||
g = int(hexstring[digits + 1:digits * 2 + 1], 16)
|
||||
b = int(hexstring[digits * 2 + 1:digits * 3 + 1], 16)
|
||||
return r / top, g / top, b / top
|
||||
|
||||
|
||||
def clamp(minValue, maxValue, value):
|
||||
"""Make sure value is between minValue and maxValue"""
|
||||
if value < minValue:
|
||||
@@ -1081,12 +1074,14 @@ def clamp(minValue, maxValue, value):
|
||||
return maxValue
|
||||
return value
|
||||
|
||||
|
||||
def lighten(r, g, b, amount):
|
||||
"""Return a lighter version of the color (r, g, b)"""
|
||||
return (clamp(0.0, 1.0, r + amount),
|
||||
clamp(0.0, 1.0, g + amount),
|
||||
clamp(0.0, 1.0, b + amount))
|
||||
|
||||
|
||||
def generateColorscheme(masterColor, keys, light=0.06):
|
||||
"""
|
||||
Generates a dictionary where the keys match the keys argument and
|
||||
@@ -1121,7 +1116,7 @@ class DBProgress(object):
|
||||
|
||||
def update(self, combo, progressbar, callback, dbname=''):
|
||||
self.db_info = None
|
||||
thread = threading.Thread(target=self.start).start()
|
||||
threading.Thread(target=self.start).start()
|
||||
gobject.timeout_add(100, self.end, combo, progressbar, callback,
|
||||
dbname)
|
||||
|
||||
@@ -1206,7 +1201,8 @@ class RPCProgress(object):
|
||||
hbox.pack_start(img, expand=True, fill=False)
|
||||
vbox2 = gtk.VBox(False, 0)
|
||||
label = gtk.Label()
|
||||
label.set_markup('<b>'+_('Operation in progress')+'</b>')
|
||||
label.set_markup(
|
||||
'<b>' + _('Operation in progress') + '</b>')
|
||||
label.set_alignment(0.0, 0.5)
|
||||
vbox2.pack_start(label, expand=True, fill=False)
|
||||
vbox2.pack_start(gtk.HSeparator(), expand=True, fill=True)
|
||||
@@ -1272,8 +1268,8 @@ COLOR_SCHEMES = {
|
||||
}
|
||||
|
||||
COLORS = {
|
||||
'invalid':'#ff6969',
|
||||
'required':'#d2d2ff',
|
||||
'invalid': '#ff6969',
|
||||
'required': '#d2d2ff',
|
||||
}
|
||||
|
||||
DT_FORMAT = '%Y-%m-%d'
|
||||
@@ -1286,7 +1282,7 @@ FLOAT_TIME_CONV = {
|
||||
'w': 168,
|
||||
'd': 24,
|
||||
'h': 1,
|
||||
'm': 1.0/60,
|
||||
'm': 1.0 / 60,
|
||||
}
|
||||
|
||||
FLOAT_TIME_SEPS = {
|
||||
@@ -1298,6 +1294,7 @@ FLOAT_TIME_SEPS = {
|
||||
'm': _('m'),
|
||||
}
|
||||
|
||||
|
||||
def text_to_float_time(text, conv=None):
|
||||
try:
|
||||
try:
|
||||
@@ -1340,6 +1337,7 @@ def text_to_float_time(text, conv=None):
|
||||
except ValueError:
|
||||
return 0.0
|
||||
|
||||
|
||||
def float_time_to_text(val, conv=None):
|
||||
if conv:
|
||||
tmp_conv = FLOAT_TIME_CONV.copy()
|
||||
@@ -1362,7 +1360,7 @@ def float_time_to_text(val, conv=None):
|
||||
val = val - days * conv['d']
|
||||
hours = int(val)
|
||||
val = val - hours
|
||||
mins = int((val% 1 + 0.01) / conv['m'])
|
||||
mins = int((val % 1 + 0.01) / conv['m'])
|
||||
if years:
|
||||
value += ' ' + locale.format('%d', years, True) + FLOAT_TIME_SEPS['Y']
|
||||
if months:
|
||||
@@ -1376,6 +1374,7 @@ def float_time_to_text(val, conv=None):
|
||||
value = value.strip()
|
||||
return value
|
||||
|
||||
|
||||
def filter_domain(domain):
|
||||
'''
|
||||
Return the biggest subset of domain with only AND operator
|
||||
@@ -1394,23 +1393,23 @@ def filter_domain(domain):
|
||||
return res
|
||||
|
||||
_ALLOWED_CODES = set(dis.opmap[x] for x in [
|
||||
'POP_TOP','ROT_TWO','ROT_THREE','ROT_FOUR','DUP_TOP',
|
||||
'BUILD_LIST','BUILD_MAP','BUILD_TUPLE',
|
||||
'LOAD_CONST','RETURN_VALUE','STORE_SUBSCR',
|
||||
'UNARY_POSITIVE','UNARY_NEGATIVE','UNARY_NOT',
|
||||
'UNARY_INVERT','BINARY_POWER','BINARY_MULTIPLY',
|
||||
'BINARY_DIVIDE','BINARY_FLOOR_DIVIDE','BINARY_TRUE_DIVIDE',
|
||||
'BINARY_MODULO','BINARY_ADD','BINARY_SUBTRACT',
|
||||
'BINARY_LSHIFT','BINARY_RSHIFT','BINARY_AND','BINARY_XOR', 'BINARY_OR',
|
||||
'STORE_MAP', 'LOAD_NAME', 'CALL_FUNCTION', 'COMPARE_OP', 'LOAD_ATTR',
|
||||
'STORE_NAME', 'GET_ITER', 'FOR_ITER', 'LIST_APPEND', 'JUMP_ABSOLUTE',
|
||||
'DELETE_NAME', 'JUMP_IF_TRUE', 'JUMP_IF_FALSE', 'JUMP_IF_FALSE_OR_POP',
|
||||
'JUMP_IF_TRUE_OR_POP', 'POP_JUMP_IF_FALSE', 'POP_JUMP_IF_TRUE',
|
||||
'BINARY_SUBSCR',
|
||||
] if x in dis.opmap)
|
||||
'POP_TOP', 'ROT_TWO', 'ROT_THREE', 'ROT_FOUR', 'DUP_TOP', 'BUILD_LIST',
|
||||
'BUILD_MAP', 'BUILD_TUPLE', 'LOAD_CONST', 'RETURN_VALUE',
|
||||
'STORE_SUBSCR', 'UNARY_POSITIVE', 'UNARY_NEGATIVE', 'UNARY_NOT',
|
||||
'UNARY_INVERT', 'BINARY_POWER', 'BINARY_MULTIPLY', 'BINARY_DIVIDE',
|
||||
'BINARY_FLOOR_DIVIDE', 'BINARY_TRUE_DIVIDE', 'BINARY_MODULO',
|
||||
'BINARY_ADD', 'BINARY_SUBTRACT', 'BINARY_LSHIFT', 'BINARY_RSHIFT',
|
||||
'BINARY_AND', 'BINARY_XOR', 'BINARY_OR', 'STORE_MAP', 'LOAD_NAME',
|
||||
'CALL_FUNCTION', 'COMPARE_OP', 'LOAD_ATTR', 'STORE_NAME', 'GET_ITER',
|
||||
'FOR_ITER', 'LIST_APPEND', 'JUMP_ABSOLUTE', 'DELETE_NAME',
|
||||
'JUMP_IF_TRUE', 'JUMP_IF_FALSE', 'JUMP_IF_FALSE_OR_POP',
|
||||
'JUMP_IF_TRUE_OR_POP', 'POP_JUMP_IF_FALSE', 'POP_JUMP_IF_TRUE',
|
||||
'BINARY_SUBSCR',
|
||||
] if x in dis.opmap)
|
||||
|
||||
_SAFE_EVAL_CACHE = {}
|
||||
|
||||
|
||||
def safe_eval(source, data=None):
|
||||
if '__subclasses__' in source:
|
||||
raise ValueError('__subclasses__ not allowed')
|
||||
@@ -1447,6 +1446,7 @@ def safe_eval(source, data=None):
|
||||
'dict': dict,
|
||||
}}, data)
|
||||
|
||||
|
||||
def timezoned_date(date):
|
||||
if pytz and rpc.CONTEXT.get('timezone'):
|
||||
lzone = pytz.timezone(rpc.CONTEXT['timezone'])
|
||||
@@ -1456,6 +1456,7 @@ def timezoned_date(date):
|
||||
date = ldt
|
||||
return date
|
||||
|
||||
|
||||
def humanize(size):
|
||||
for x in ('bytes', 'KB', 'MB', 'GB', 'TB', 'PB'):
|
||||
if size < 1000:
|
||||
|
||||
@@ -24,6 +24,7 @@ mapping = {
|
||||
'%p': ('__', '([_ AP][_ M])'),
|
||||
}
|
||||
|
||||
|
||||
class DateEntry(gtk.Entry):
|
||||
|
||||
def __init__(self, format, callback=None, callback_process=None):
|
||||
@@ -68,7 +69,8 @@ class DateEntry(gtk.Entry):
|
||||
return
|
||||
|
||||
if self.mode_cmd:
|
||||
if self.callback: self.callback(value)
|
||||
if self.callback:
|
||||
self.callback(value)
|
||||
self.stop_emission('insert-text')
|
||||
return
|
||||
|
||||
@@ -110,7 +112,8 @@ class DateEntry(gtk.Entry):
|
||||
end = len(self.initial_value)
|
||||
if end < 0:
|
||||
end = 0
|
||||
while (start > 0) and (self.initial_value[start] not in ['_', ' ','0','X']):
|
||||
while (start > 0
|
||||
and self.initial_value[start] not in ['_', ' ', '0', 'X']):
|
||||
start -= 1
|
||||
text = self.get_text()
|
||||
text = text[:start] + self.initial_value[start:end] + text[end:]
|
||||
@@ -179,15 +182,16 @@ class DateEntry(gtk.Entry):
|
||||
|
||||
for a in range(len(self.initial_value)):
|
||||
if self.initial_value[a] == text[a]:
|
||||
text = text[:a] + default[a] + text[a+1:]
|
||||
text = text[:a] + default[a] + text[a + 1:]
|
||||
return text
|
||||
|
||||
def test_date(self, text):
|
||||
default = datetime_strftime(datetime.datetime(2000, 1, 1),
|
||||
self.format)
|
||||
try:
|
||||
time.strptime(self.compute_date(text, default=default, year='2000'),
|
||||
self.format)
|
||||
time.strptime(
|
||||
self.compute_date(text, default=default, year='2000'),
|
||||
self.format)
|
||||
except ValueError:
|
||||
return False
|
||||
return True
|
||||
@@ -287,7 +291,8 @@ class ComplexEntry(gtk.HBox):
|
||||
self.widget_cmd.show()
|
||||
self._date_cb(event)
|
||||
else:
|
||||
if hasattr(event, 'keyval') and not event.keyval == gtk.keysyms.Escape:
|
||||
if (hasattr(event, 'keyval')
|
||||
and not event.keyval == gtk.keysyms.Escape):
|
||||
cmd = self.widget_cmd.get_text()
|
||||
dt = self.widget.date_get() or datetime.datetime.now()
|
||||
res = compute_date(cmd, dt, self.widget.format)
|
||||
@@ -296,6 +301,7 @@ class ComplexEntry(gtk.HBox):
|
||||
self.widget_cmd.set_text('')
|
||||
self.widget_cmd.hide()
|
||||
|
||||
|
||||
def compute_date(cmd, dt, format):
|
||||
lst = {
|
||||
'^=(\d+)d$': lambda dt, r: dt + \
|
||||
@@ -320,8 +326,8 @@ def compute_date(cmd, dt, format):
|
||||
relativedelta(months=int(r.group(1))),
|
||||
'^([\\+-]\d+)y$': lambda dt, r: dt + \
|
||||
relativedelta(years=int(r.group(1))),
|
||||
'^=$': lambda dt,r: datetime.datetime.now(),
|
||||
'^-$': lambda dt,r: False
|
||||
'^=$': lambda dt, r: datetime.datetime.now(),
|
||||
'^-$': lambda dt, r: False
|
||||
}
|
||||
for r, f in lst.items():
|
||||
groups = re.match(r, cmd)
|
||||
@@ -336,6 +342,7 @@ def compute_date(cmd, dt, format):
|
||||
if __name__ == '__main__':
|
||||
win = gtk.Window()
|
||||
win.set_title('gtk.Entry subclass')
|
||||
|
||||
def cb(window, event):
|
||||
gtk.main_quit()
|
||||
win.connect('delete-event', cb)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#Copyright (c) 2002-2007 John D. Hunter; All Rights Reserved
|
||||
import time
|
||||
|
||||
|
||||
def datetime_strftime(dt, fmt):
|
||||
'''
|
||||
Allow datetime strftime formatting for years before 1900.
|
||||
@@ -46,5 +47,5 @@ def datetime_strftime(dt, fmt):
|
||||
s = s1
|
||||
syear = "%04d" % (dt.year,)
|
||||
for site in sites:
|
||||
s = s[:site] + syear + s[site+4:]
|
||||
s = s[:site] + syear + s[site + 4:]
|
||||
return s
|
||||
@@ -4,6 +4,7 @@
|
||||
import operator
|
||||
import types
|
||||
|
||||
|
||||
def in_(a, b):
|
||||
if isinstance(a, (list, tuple)):
|
||||
return any(operator.contains(b, x) for x in a)
|
||||
@@ -28,6 +29,7 @@ OPERATORS = {
|
||||
'not child_of': lambda a, b: True,
|
||||
}
|
||||
|
||||
|
||||
def locale_part(expression, field_name):
|
||||
if expression == field_name:
|
||||
return 'id'
|
||||
@@ -36,12 +38,14 @@ def locale_part(expression, field_name):
|
||||
return local
|
||||
return expression
|
||||
|
||||
|
||||
def is_leaf(expression):
|
||||
return (isinstance(expression, (list, tuple))
|
||||
and len(expression) > 2
|
||||
and isinstance(expression[1], basestring)
|
||||
and expression[1] in OPERATORS)
|
||||
|
||||
|
||||
def eval_leaf(part, context, boolop=operator.and_):
|
||||
field, operand, value = part[:3]
|
||||
if '.' in field:
|
||||
@@ -54,6 +58,7 @@ def eval_leaf(part, context, boolop=operator.and_):
|
||||
return True
|
||||
return OPERATORS[operand](context[field], value)
|
||||
|
||||
|
||||
def inverse_leaf(domain):
|
||||
if domain in ('AND', 'OR'):
|
||||
return domain
|
||||
@@ -67,6 +72,7 @@ def inverse_leaf(domain):
|
||||
else:
|
||||
return map(inverse_leaf, domain)
|
||||
|
||||
|
||||
def eval_domain(domain, context, boolop=operator.and_):
|
||||
"compute domain boolean value according to the context"
|
||||
if is_leaf(domain):
|
||||
@@ -83,6 +89,7 @@ def eval_domain(domain, context, boolop=operator.and_):
|
||||
return boolop(eval_domain(domain[0], context),
|
||||
eval_domain(domain[1:], context, boolop))
|
||||
|
||||
|
||||
def localize_domain(domain, field_name=None):
|
||||
"returns only locale part of domain. eg: langage.code -> code"
|
||||
if domain in ('AND', 'OR', True, False):
|
||||
@@ -97,6 +104,7 @@ def localize_domain(domain, field_name=None):
|
||||
else:
|
||||
return [localize_domain(part, field_name) for part in domain]
|
||||
|
||||
|
||||
def unlocalize_domain(domain, fieldname):
|
||||
if domain in ('AND', 'OR', True, False):
|
||||
return domain
|
||||
@@ -105,6 +113,7 @@ def unlocalize_domain(domain, fieldname):
|
||||
else:
|
||||
return [unlocalize_domain(part, fieldname) for part in domain]
|
||||
|
||||
|
||||
def simplify(domain):
|
||||
"remove unused domain delimiter"
|
||||
if is_leaf(domain):
|
||||
@@ -120,6 +129,7 @@ def simplify(domain):
|
||||
else:
|
||||
return [simplify(branch) for branch in domain]
|
||||
|
||||
|
||||
def merge(domain, domoperator=None):
|
||||
if not domain or domain in ('AND', 'OR'):
|
||||
return []
|
||||
@@ -146,6 +156,7 @@ def parse(domain):
|
||||
else:
|
||||
return And(domain[1:] if domain[0] == 'AND' else domain)
|
||||
|
||||
|
||||
def domain_inversion(domain, symbol, context=None):
|
||||
"""compute an inversion of the domain eventually the context is used to
|
||||
simplify the expression"""
|
||||
@@ -169,7 +180,10 @@ class And(object):
|
||||
self.variables |= expression.variables
|
||||
|
||||
def base(self, expression):
|
||||
return expression if '.' not in expression else expression.split('.')[0]
|
||||
if '.' not in expression:
|
||||
return expression
|
||||
else:
|
||||
return expression.split('.')[0]
|
||||
|
||||
def inverse(self, symbol, context):
|
||||
result = []
|
||||
@@ -209,8 +223,8 @@ class Or(And):
|
||||
if (symbol not in self.variables
|
||||
and not known_variables >= self.variables):
|
||||
# In this case we don't know anything about this OR part, we
|
||||
# consider it to be True (because people will have the constraint on
|
||||
# this part later).
|
||||
# consider it to be True (because people will have the constraint
|
||||
# on this part later).
|
||||
return True
|
||||
for part in self.branches:
|
||||
if isinstance(part, And):
|
||||
@@ -244,6 +258,7 @@ class Or(And):
|
||||
else:
|
||||
return simplify(['OR'] + result)
|
||||
|
||||
|
||||
# Test stuffs
|
||||
def test_simple_inversion():
|
||||
domain = [['x', '=', 3]]
|
||||
@@ -258,6 +273,7 @@ def test_simple_inversion():
|
||||
domain = [['x.id', '>', 5]]
|
||||
assert domain_inversion(domain, 'x') == [['x.id', '>', 5]]
|
||||
|
||||
|
||||
def test_and_inversion():
|
||||
domain = [['x', '=', 3], ['y', '>', 5]]
|
||||
assert domain_inversion(domain, 'x') == [['x', '=', 3]]
|
||||
@@ -274,11 +290,13 @@ def test_and_inversion():
|
||||
assert domain_inversion(domain, 'y', {'x': 3}) == [['y', '<', 3]]
|
||||
assert domain_inversion(domain, 'x') == [['x.id', '>', 5]]
|
||||
|
||||
|
||||
def test_or_inversion():
|
||||
domain = ['OR', ['x', '=', 3], ['y', '>', 5], ['z', '=', 'abc']]
|
||||
assert domain_inversion(domain, 'x') == [['x', '=', 3]]
|
||||
assert domain_inversion(domain, 'x', {'y': 4}) == [['x', '=', 3]]
|
||||
assert domain_inversion(domain, 'x', {'y': 4, 'z': 'ab'}) == [['x', '=', 3]]
|
||||
assert domain_inversion(domain, 'x', {'y': 4, 'z': 'ab'}) ==\
|
||||
[['x', '=', 3]]
|
||||
assert domain_inversion(domain, 'x', {'y': 7}) == True
|
||||
assert domain_inversion(domain, 'x', {'y': 7, 'z': 'b'}) == True
|
||||
assert domain_inversion(domain, 'x', {'z': 'abc'}) == True
|
||||
@@ -299,6 +317,7 @@ def test_or_inversion():
|
||||
assert domain_inversion(domain, 'length', {'length': 0, 'name': 'n'}) ==\
|
||||
[['length', '>', 5]]
|
||||
|
||||
|
||||
def test_orand_inversion():
|
||||
domain = ['OR', [['x', '=', 3], ['y', '>', 5], ['z', '=', 'abc']],
|
||||
[['x', '=', 4]], [['y', '>', 6]]]
|
||||
@@ -308,6 +327,7 @@ def test_orand_inversion():
|
||||
assert domain_inversion(domain, 'x', {'y': 7}) == True
|
||||
assert domain_inversion(domain, 'x', {'z': 'ab'}) == True
|
||||
|
||||
|
||||
def test_andor_inversion():
|
||||
domain = [['OR', ['x', '=', 4], ['y', '>', 6]], ['z', '=', 3]]
|
||||
assert domain_inversion(domain, 'z') == [['z', '=', 3]]
|
||||
@@ -315,6 +335,7 @@ def test_andor_inversion():
|
||||
assert domain_inversion(domain, 'z', {'x': 5, 'y': 5}) == False
|
||||
assert domain_inversion(domain, 'z', {'x': 5, 'y': 7}) == [['z', '=', 3]]
|
||||
|
||||
|
||||
def test_andand_inversion():
|
||||
domain = [[['x', '=', 4], ['y', '>', 6]], ['z', '=', 3]]
|
||||
assert domain_inversion(domain, 'z') == [['z', '=', 3]]
|
||||
@@ -325,6 +346,7 @@ def test_andand_inversion():
|
||||
domain = [[['x', '=', 4], ['y', '>', 6], ['z', '=', 2]], [['w', '=', 2]]]
|
||||
assert domain_inversion(domain, 'z', {'x': 4}) == [['z', '=', 2]]
|
||||
|
||||
|
||||
def test_oror_inversion():
|
||||
domain = ['OR', ['OR', ['x', '=', 3], ['y', '>', 5]],
|
||||
['OR', ['x', '=', 2], ['z', '=', 'abc']],
|
||||
@@ -342,6 +364,7 @@ def test_oror_inversion():
|
||||
assert domain_inversion(domain, 'x', {'y': 4, 'z': 'b'}) == \
|
||||
['OR', [['x', '=', 3]], [['x', '=', 2]]]
|
||||
|
||||
|
||||
def test_parse():
|
||||
domain = parse([['x', '=', 5]])
|
||||
assert domain.variables == set('x')
|
||||
@@ -352,6 +375,7 @@ def test_parse():
|
||||
domain = parse([[['x', '=', 4], ['y', '>', 6]], ['z', '=', 3]])
|
||||
assert domain.variables == set('xyz')
|
||||
|
||||
|
||||
def test_simplify():
|
||||
domain = [['x', '=', 3]]
|
||||
assert simplify(domain) == [['x', '=', 3]]
|
||||
@@ -364,6 +388,7 @@ def test_simplify():
|
||||
domain = ['OR', ['x', '=', 3], ['AND', ['y', '=', 5]]]
|
||||
assert simplify(domain) == ['OR', ['x', '=', 3], [['y', '=', 5]]]
|
||||
|
||||
|
||||
def test_merge():
|
||||
domain = [['x', '=', 6], ['y', '=', 7]]
|
||||
assert merge(domain) == ['AND', ['x', '=', 6], ['y', '=', 7]]
|
||||
@@ -391,6 +416,7 @@ def test_merge():
|
||||
['OR', ['c', '=', 3], ['AND', ['d', '=', 4], ['d2', '=', 6]]],
|
||||
['d', '=', 5], ['e', '=', 6], ['f', '=', 7]]
|
||||
|
||||
|
||||
def test_evaldomain():
|
||||
domain = [['x', '>', 5]]
|
||||
assert eval_domain(domain, {'x': 6})
|
||||
@@ -423,6 +449,7 @@ 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]]
|
||||
|
||||
@@ -20,8 +20,8 @@ class EvalEnvironment(dict):
|
||||
if self.eval_type == 'eval':
|
||||
return self.parent.get_eval(check_load=self.check_load)[item]
|
||||
else:
|
||||
return self.parent.group.fields[item].get_on_change_value(self.parent,
|
||||
check_load=self.check_load)
|
||||
return self.parent.group.fields[item].get_on_change_value(
|
||||
self.parent, check_load=self.check_load)
|
||||
|
||||
def __getattr__(self, item):
|
||||
try:
|
||||
|
||||
+82
-31
@@ -32,6 +32,7 @@ OPERATORS = set((
|
||||
'ilike',
|
||||
'not ilike'))
|
||||
|
||||
|
||||
def operator(field):
|
||||
type_ = field['type'] if field else ''
|
||||
if type_ in ('char', 'text', 'many2one') or not field:
|
||||
@@ -68,7 +69,7 @@ def cast(field, value):
|
||||
return key
|
||||
return value
|
||||
elif type_ == 'datetime':
|
||||
value = value.replace(' : ', ':') # Parser add spaces arround :
|
||||
value = value.replace(' : ', ':') # Parser add spaces arround :
|
||||
try:
|
||||
value = datetime.datetime(*time.strptime(value,
|
||||
date_format() + ' ' + HM_FORMAT)[:6])
|
||||
@@ -87,6 +88,7 @@ def cast(field, value):
|
||||
else:
|
||||
return value
|
||||
|
||||
|
||||
def quote(value):
|
||||
for test in (':', ' '):
|
||||
if test in value:
|
||||
@@ -127,15 +129,16 @@ class Base(object):
|
||||
right = property(_get_right, _set_right)
|
||||
|
||||
def domain(self, parent_field=None):
|
||||
raise NotImplementedError("domain method is missing on %s" % \
|
||||
self.__class__)
|
||||
raise NotImplementedError("domain method is missing on %s"
|
||||
% self.__class__)
|
||||
|
||||
def complete_fmt(self, left, value, right):
|
||||
return (self.fmt % (left, value, right)).strip()
|
||||
|
||||
def complete(self, parent_field=None):
|
||||
left = self.left and tuple(self.left.complete()) or ('',)
|
||||
right = self.right and tuple(self.right.complete(parent_field)) or ('',)
|
||||
right = (self.right and tuple(self.right.complete(parent_field))
|
||||
or ('',))
|
||||
|
||||
for lvalue in left:
|
||||
for rvalue in right:
|
||||
@@ -144,15 +147,15 @@ class Base(object):
|
||||
def split(self, words=""):
|
||||
field, value = None, ' '.join(words)
|
||||
for i in xrange(len(words)):
|
||||
candidate = ' '.join(words[-i-1:])
|
||||
candidate = ' '.join(words[-i - 1:])
|
||||
if candidate.lower() in self.parser.dom_fields:
|
||||
field = self.parser.dom_fields[candidate.lower()]
|
||||
value = ' '.join(words[:-i-1])
|
||||
value = ' '.join(words[:-i - 1])
|
||||
|
||||
return field, value
|
||||
|
||||
def __str__(self, prefix=''):
|
||||
res = '%s[%s] %s'% (prefix, self.pos or 'root', self.value)
|
||||
res = '%s[%s] %s' % (prefix, self.pos or 'root', self.value)
|
||||
res = res.ljust(30) + str(type(self))
|
||||
if self.left:
|
||||
res += "\n" + self.left.__str__(' ' + prefix)
|
||||
@@ -202,8 +205,8 @@ class Literal(Base):
|
||||
candidate = word + candidate
|
||||
for field in suggestions:
|
||||
if field.lower().startswith(candidate.lower()):
|
||||
if words[pos+1:]:
|
||||
new_item = ' '.join(reversed(words[pos+1:]))
|
||||
if words[pos + 1:]:
|
||||
new_item = ' '.join(reversed(words[pos + 1:]))
|
||||
new_item += ' ' + field
|
||||
else:
|
||||
new_item = field
|
||||
@@ -221,7 +224,7 @@ class Literal(Base):
|
||||
|
||||
if field.lower().startswith(candidate.lower()):
|
||||
new_item = quote(field)
|
||||
if (i-len(words) < 0) and words[i-len(words):]:
|
||||
if (i - len(words) < 0) and words[i - len(words):]:
|
||||
find = False
|
||||
for item in self.suggest(words[i - len(words):],
|
||||
self.parser.sugg_fields):
|
||||
@@ -230,7 +233,7 @@ class Literal(Base):
|
||||
find = True
|
||||
if find:
|
||||
break
|
||||
new_item += ' ' + ' '.join(words[i-len(words):])
|
||||
new_item += ' ' + ' '.join(words[i - len(words):])
|
||||
yield new_item
|
||||
break
|
||||
|
||||
@@ -246,7 +249,8 @@ class Literal(Base):
|
||||
if parent_field:
|
||||
if parent_field['type'] in ('selection', 'reference', 'boolean'):
|
||||
if parent_field['type'] in ('selection', 'reference'):
|
||||
suggestions = tuple(x[1] for x in parent_field['selection'])
|
||||
suggestions = tuple(x[1]
|
||||
for x in parent_field['selection'])
|
||||
else:
|
||||
suggestions = (_('Y'), _('Yes'), _('True'), '1', _('N'),
|
||||
_('No'), _('False'), '0')
|
||||
@@ -369,12 +373,13 @@ class Colon(Base, InfixMixin):
|
||||
if field['type'] in ('char', 'text', 'many2one'):
|
||||
value += '%'
|
||||
|
||||
domain.append((field['name'] , operator(field), value))
|
||||
domain.append((field['name'], operator(field), value))
|
||||
else:
|
||||
domain.extend(self.right.domain(field))
|
||||
|
||||
return domain
|
||||
|
||||
|
||||
class DoubleDot(Base, InfixMixin):
|
||||
value = '..'
|
||||
lbp = 70
|
||||
@@ -436,7 +441,7 @@ class Comma(Base, InfixMixin):
|
||||
node = node.left
|
||||
walk(self.left, values)
|
||||
|
||||
return [(field_name, 'in' , list(reversed(values)))]
|
||||
return [(field_name, 'in', list(reversed(values)))]
|
||||
|
||||
|
||||
class Comparator(Base, InfixMixin):
|
||||
@@ -464,15 +469,19 @@ class Comparator(Base, InfixMixin):
|
||||
res.append((field_name, self.value, value))
|
||||
return res
|
||||
|
||||
|
||||
class LessThan(Comparator):
|
||||
value = '<'
|
||||
|
||||
|
||||
class LessThanOrEqual(Comparator):
|
||||
value = '<='
|
||||
|
||||
|
||||
class BiggerThan(Comparator):
|
||||
value = '>'
|
||||
|
||||
|
||||
class BiggerThanOrEqual(Comparator):
|
||||
value = '>='
|
||||
|
||||
@@ -519,6 +528,7 @@ class Not(Base, InfixMixin):
|
||||
res.append((field_name, oper, value))
|
||||
return res
|
||||
|
||||
|
||||
class NotEqual(Equal):
|
||||
value = '!='
|
||||
fmt = '%s%s%s'
|
||||
@@ -574,6 +584,7 @@ class CloseParenthesis(Base):
|
||||
return self.left.domain()
|
||||
return []
|
||||
|
||||
|
||||
class OpenParenthesis(Base):
|
||||
value = '('
|
||||
lbp = 10
|
||||
@@ -701,7 +712,7 @@ class DomainParser:
|
||||
return left
|
||||
|
||||
def next_token(self):
|
||||
try :
|
||||
try:
|
||||
new_tk = self.token_flow.next()
|
||||
except StopIteration:
|
||||
return End(self)
|
||||
@@ -808,6 +819,7 @@ class DomainParser:
|
||||
domain = domain[1:]
|
||||
else:
|
||||
nary = ' '
|
||||
|
||||
def format(expression):
|
||||
if (isinstance(expression, (list, tuple))
|
||||
and len(expression) > 2
|
||||
@@ -892,6 +904,7 @@ def test_parser():
|
||||
}
|
||||
return DomainParser(fields)
|
||||
|
||||
|
||||
def test_selection_complete():
|
||||
parser = test_parser()
|
||||
assert list(parser.parse('S').complete()) == ['Selection:']
|
||||
@@ -912,6 +925,7 @@ def test_selection_complete():
|
||||
assert list(parser.parse('Selection: Ham foo').complete()) == [
|
||||
'Selection: Ham foo']
|
||||
|
||||
|
||||
def test_selection_domain():
|
||||
parser = test_parser()
|
||||
assert parser.parse('S').domain() == [('rec_name', 'ilike', 'S%')]
|
||||
@@ -923,6 +937,7 @@ def test_selection_domain():
|
||||
assert parser.parse('Selection: Ham; Spam').domain() == [
|
||||
('selection', 'in', ['ham', 'spam'])]
|
||||
|
||||
|
||||
def test_selection_string():
|
||||
parser = test_parser()
|
||||
assert parser.string([]) == ''
|
||||
@@ -933,6 +948,7 @@ def test_selection_string():
|
||||
assert parser.string([('selection', 'in', ['ham', 'spam'])]) == \
|
||||
'Selection: Ham; Spam'
|
||||
|
||||
|
||||
def test_boolean_complete():
|
||||
parser = test_parser()
|
||||
assert list(parser.parse('b').complete()) == ['Boolean:']
|
||||
@@ -943,13 +959,15 @@ def test_boolean_complete():
|
||||
assert list(parser.parse('boolean: y').complete()) == ['Boolean: Y',
|
||||
'Boolean: Yes']
|
||||
assert list(parser.parse('boolean: 1').complete()) == ['Boolean: 1']
|
||||
assert list(parser.parse('boolean: false').complete()) == ['Boolean: False']
|
||||
assert list(parser.parse('boolean: false').complete()) ==\
|
||||
['Boolean: False']
|
||||
assert list(parser.parse('boolean: f').complete()) == ['Boolean: False']
|
||||
assert list(parser.parse('boolean: n').complete()) == ['Boolean: N',
|
||||
'Boolean: No']
|
||||
assert list(parser.parse('boolean: 0').complete()) == ['Boolean: 0']
|
||||
assert list(parser.parse('boolean: =1').complete()) == ['Boolean: =1']
|
||||
|
||||
|
||||
def test_boolean_domain():
|
||||
parser = test_parser()
|
||||
assert parser.parse('B').domain() == [('rec_name', 'ilike', 'B%')]
|
||||
@@ -961,6 +979,7 @@ def test_boolean_domain():
|
||||
assert parser.parse('Boolean: true; false').domain() == [
|
||||
('boolean', 'in', [True, False])]
|
||||
|
||||
|
||||
def test_boolean_string():
|
||||
parser = test_parser()
|
||||
assert parser.string([('boolean', '=', True)]) == 'Boolean: True'
|
||||
@@ -968,6 +987,7 @@ def test_boolean_string():
|
||||
assert parser.string([('boolean', 'in', [True, False])]) == \
|
||||
'Boolean: True; False'
|
||||
|
||||
|
||||
def test_char_complete():
|
||||
parser = test_parser()
|
||||
assert list(parser.parse('c').complete()) == ['Char:']
|
||||
@@ -977,6 +997,7 @@ def test_char_complete():
|
||||
'Char: "foo bar"']
|
||||
assert list(parser.parse('char: =foo').complete()) == ['Char: =foo']
|
||||
|
||||
|
||||
def test_char_domain():
|
||||
parser = test_parser()
|
||||
assert parser.parse('c').domain() == [('rec_name', 'ilike', 'c%')]
|
||||
@@ -989,7 +1010,9 @@ def test_char_domain():
|
||||
assert parser.parse('Char: !=foo').domain() == [('char', '!=', 'foo')]
|
||||
assert parser.parse('Char: != foo').domain() == [('char', '!=', 'foo')]
|
||||
assert parser.parse('Char: 2; 3').domain() == [('char', 'in', ['2', '3'])]
|
||||
assert parser.parse('Char:! 2; 3').domain() == [('char', 'not in', ['2', '3'])]
|
||||
assert parser.parse('Char:! 2; 3').domain() == [
|
||||
('char', 'not in', ['2', '3'])]
|
||||
|
||||
|
||||
def test_char_string():
|
||||
parser = test_parser()
|
||||
@@ -1007,6 +1030,7 @@ def test_numeric_complete():
|
||||
assert list(parser.parse('numeric: 100').complete()) == [
|
||||
'Numeric: 100']
|
||||
|
||||
|
||||
def test_numeric_domain():
|
||||
parser = test_parser()
|
||||
assert parser.parse('Numeric: >100').domain() == [
|
||||
@@ -1022,17 +1046,20 @@ def test_numeric_domain():
|
||||
assert parser.parse('Numeric: 2; 3').domain() == [
|
||||
('numeric', 'in', [Decimal(2), Decimal(3)])]
|
||||
|
||||
|
||||
def test_numeric_string():
|
||||
parser = test_parser()
|
||||
assert parser.string([('numeric', '=', Decimal(100))]) == 'Numeric: 100.00'
|
||||
assert parser.string([('numeric', 'in', [Decimal(2), Decimal(3)])]) == \
|
||||
'Numeric: 2.00; 3.00'
|
||||
|
||||
|
||||
def test_integer_complete():
|
||||
parser = test_parser()
|
||||
assert list(parser.parse('integer: 42').complete()) == ['Integer: 42']
|
||||
assert list(parser.parse('integer: 2;3').complete()) == ['Integer: 2; 3']
|
||||
|
||||
|
||||
def test_integer_domain():
|
||||
parser = test_parser()
|
||||
assert parser.parse('Integer: >42').domain() == [
|
||||
@@ -1050,6 +1077,7 @@ def test_integer_domain():
|
||||
assert parser.parse('Integer: 2; 3').domain() == [
|
||||
('integer', 'in', [2, 3])]
|
||||
|
||||
|
||||
def test_integer_string():
|
||||
parser = test_parser()
|
||||
assert parser.string([('integer', '=', 42)]) == 'Integer: 42'
|
||||
@@ -1061,6 +1089,7 @@ def test_float_complete():
|
||||
parser = test_parser()
|
||||
assert list(parser.parse('float: 3.14').complete()) == ['Float: 3.14']
|
||||
|
||||
|
||||
def test_float_domain():
|
||||
parser = test_parser()
|
||||
assert parser.parse('Float: >3.14').domain() == [
|
||||
@@ -1076,6 +1105,7 @@ def test_float_domain():
|
||||
assert parser.parse('Float: foo').domain() == [
|
||||
('float', '=', False)]
|
||||
|
||||
|
||||
def test_float_string():
|
||||
parser = test_parser()
|
||||
assert parser.string([('float', '=', 3.14)]) == 'Float: 3.14'
|
||||
@@ -1084,6 +1114,7 @@ def test_float_string():
|
||||
assert parser.string([('float', 'not in', [3.14, 42])]) == \
|
||||
'Float: !3.14; 42.00'
|
||||
|
||||
|
||||
def test_date_complete():
|
||||
today = datetime.date.today()
|
||||
today_str = datetime_strftime(today, date_format())
|
||||
@@ -1113,6 +1144,7 @@ def test_date_complete():
|
||||
'Date: 12/04/2002',
|
||||
]
|
||||
|
||||
|
||||
def test_date_domain():
|
||||
today = datetime.date.today()
|
||||
today_str = datetime_strftime(today, date_format())
|
||||
@@ -1127,7 +1159,9 @@ def test_date_domain():
|
||||
assert parser.parse('Date: 12/4/2002').domain() == [
|
||||
('date', '=', datetime.date(2002, 12, 4))]
|
||||
assert parser.parse('Date: 12/4/2002; 1/1/1970').domain() == [
|
||||
('date', 'in', [datetime.date(2002, 12, 4), datetime.date(1970, 1, 1)])]
|
||||
('date', 'in', [datetime.date(2002, 12, 4),
|
||||
datetime.date(1970, 1, 1)])]
|
||||
|
||||
|
||||
def test_date_string():
|
||||
parser = test_parser()
|
||||
@@ -1138,6 +1172,7 @@ def test_date_string():
|
||||
datetime.date(1970, 1, 1)])]) == \
|
||||
'Date: 12/04/2002; 01/01/1970'
|
||||
|
||||
|
||||
def test_datetime_complete():
|
||||
today = datetime.date.today()
|
||||
today_str = datetime_strftime(today, date_format())
|
||||
@@ -1149,6 +1184,7 @@ def test_datetime_complete():
|
||||
assert list(parser.parse('Date Time: ' + today_str + ' 12:30:00'
|
||||
).complete()) == ['Date Time: "' + today_str + ' 12:30:00"']
|
||||
|
||||
|
||||
def test_datetime_domain():
|
||||
today = datetime.date.today()
|
||||
today_str = datetime_strftime(today, date_format())
|
||||
@@ -1170,6 +1206,7 @@ def test_datetime_domain():
|
||||
datetime.datetime.combine(datetime.date(1970, 1, 1),
|
||||
datetime.time.min)])]
|
||||
|
||||
|
||||
def test_datetime_string():
|
||||
parser = test_parser()
|
||||
assert parser.string([('datetime', '=', False)]) == 'Date Time: ='
|
||||
@@ -1184,6 +1221,7 @@ def test_datetime_string():
|
||||
datetime.time.min)])]) == \
|
||||
'Date Time: 12/04/2002; 01/01/1970'
|
||||
|
||||
|
||||
def test_composite_complete():
|
||||
today = datetime.date.today()
|
||||
today_str = datetime_strftime(today, date_format())
|
||||
@@ -1230,6 +1268,7 @@ def test_composite_complete():
|
||||
'Selection: Ham or Selection: Spamham',
|
||||
]
|
||||
|
||||
|
||||
def test_composite_domain():
|
||||
parser = test_parser()
|
||||
assert parser.parse('char: foo boolean: false').domain() == [
|
||||
@@ -1250,9 +1289,9 @@ def test_composite_domain():
|
||||
assert parser.parse('char: foo or char: bar or boolean: 0').domain() == [
|
||||
'OR', ('char', 'ilike', 'foo%'), ('char', 'ilike', 'bar%'),
|
||||
('boolean', '=', False)]
|
||||
assert parser.parse('(char: foo or char: bar) and boolean: 1').domain() == [
|
||||
['OR', ('char', 'ilike', 'foo%'), ('char', 'ilike', 'bar%')],
|
||||
('boolean', '=', True)]
|
||||
assert parser.parse('(char: foo or char: bar) and boolean: 1').domain() ==\
|
||||
[['OR', ('char', 'ilike', 'foo%'), ('char', 'ilike', 'bar%')],
|
||||
('boolean', '=', True)]
|
||||
assert parser.parse('(char: foo or char: bar) boolean: 1').domain() == [
|
||||
['OR', ('char', 'ilike', 'foo%'), ('char', 'ilike', 'bar%')],
|
||||
('boolean', '=', True)]
|
||||
@@ -1260,15 +1299,18 @@ def test_composite_domain():
|
||||
).domain() == [
|
||||
['OR', ('char', 'ilike', 'foo%'), ('char', 'ilike', 'bar%')],
|
||||
('datetime', '=', datetime.datetime(2002, 12, 4, 0, 0))]
|
||||
assert parser.parse('(char: foo or char: bar) Date Time: 12/4/2002').domain() == [
|
||||
['OR', ('char', 'ilike', 'foo%'), ('char', 'ilike', 'bar%')],
|
||||
('datetime', '=', datetime.datetime(2002, 12, 4, 0, 0))]
|
||||
assert parser.parse('char: foo or (char: bar and boolean: 1)').domain() == [
|
||||
'OR', ('char', 'ilike', 'foo%'), [
|
||||
('char', 'ilike', 'bar%'), ('boolean', '=', True)]]
|
||||
assert parser.parse('char: foo and (char: bar or boolean: 0)').domain() == [
|
||||
('char', 'ilike', 'foo%'),
|
||||
['OR', ('char', 'ilike', 'bar%'), ('boolean', '=', False)]]
|
||||
assert parser.parse(
|
||||
'(char: foo or char: bar) Date Time: 12/4/2002').domain() == [
|
||||
['OR', ('char', 'ilike', 'foo%'), ('char', 'ilike', 'bar%')],
|
||||
('datetime', '=', datetime.datetime(2002, 12, 4, 0, 0))]
|
||||
assert parser.parse(
|
||||
'char: foo or (char: bar and boolean: 1)').domain() == [
|
||||
'OR', ('char', 'ilike', 'foo%'), [
|
||||
('char', 'ilike', 'bar%'), ('boolean', '=', True)]]
|
||||
assert parser.parse(
|
||||
'char: foo and (char: bar or boolean: 0)').domain() == [
|
||||
('char', 'ilike', 'foo%'),
|
||||
['OR', ('char', 'ilike', 'bar%'), ('boolean', '=', False)]]
|
||||
assert parser.parse('char: foo (char: bar or boolean: 0)').domain() == [
|
||||
('char', 'ilike', 'foo%'),
|
||||
['OR', ('char', 'ilike', 'bar%'), ('boolean', '=', False)]]
|
||||
@@ -1338,6 +1380,7 @@ def test_composite_string():
|
||||
['OR', ('char', 'ilike', 'bar%'), ('boolean', '=', False)]]) == \
|
||||
'Char: foo (Char: bar or Boolean: False)'
|
||||
|
||||
|
||||
def test_quote_complete():
|
||||
parser = test_parser()
|
||||
assert list(parser.parse('"char"').complete()) == ['Char:']
|
||||
@@ -1348,12 +1391,14 @@ def test_quote_complete():
|
||||
assert list(parser.parse('Char: "Boolean: false"').complete()) == [
|
||||
'Char: "Boolean: false"']
|
||||
|
||||
|
||||
def test_quote_domain():
|
||||
parser = test_parser()
|
||||
assert parser.parse('"char"').domain() == [('rec_name', 'ilike', 'char%')]
|
||||
assert parser.parse('char: "foo bar"').domain() == [
|
||||
('char', 'ilike', 'foo bar%')]
|
||||
|
||||
|
||||
def test_quote_string():
|
||||
parser = test_parser()
|
||||
assert parser.string([('rec_name', 'ilike', 'char:%')]) == '"char:"'
|
||||
@@ -1361,16 +1406,19 @@ def test_quote_string():
|
||||
assert parser.string([('char', 'ilike', 'Boolean: false%')]) == \
|
||||
'Char: "Boolean: false"'
|
||||
|
||||
|
||||
def test_column():
|
||||
parser = test_parser()
|
||||
assert parser.parse(':').domain() == []
|
||||
assert parser.parse('::').domain() == []
|
||||
assert parser.parse(': foo').domain() == [('rec_name', 'ilike', 'foo%')]
|
||||
assert parser.parse('foo :').domain() == [('rec_name', 'ilike', 'foo%')]
|
||||
assert parser.parse(': foo b').domain() == [('rec_name', 'ilike', 'foo b%')]
|
||||
assert parser.parse(': foo b').domain() == [
|
||||
('rec_name', 'ilike', 'foo b%')]
|
||||
assert parser.parse('": foo b"').domain() == [
|
||||
('rec_name', 'ilike', ': foo b%')]
|
||||
|
||||
|
||||
def test_double_dot():
|
||||
parser = test_parser()
|
||||
assert parser.parse('Integer: 0..42').domain() == [
|
||||
@@ -1384,6 +1432,7 @@ def test_double_dot():
|
||||
assert parser.parse('Integer: 0..42 Char: foo').domain() == [
|
||||
('integer', '>=', 0), ('integer', '<', 42), ('char', 'ilike', 'foo%')]
|
||||
|
||||
|
||||
def test_comma():
|
||||
parser = test_parser()
|
||||
assert parser.parse('Char: foo; bar').domain() == [
|
||||
@@ -1409,6 +1458,7 @@ def test_comma():
|
||||
assert parser.parse('Integer: 0; 1;; 2; 3').domain() == [
|
||||
('integer', 'in', [0, 1, 0, 2, 3])]
|
||||
|
||||
|
||||
def test_comparator():
|
||||
parser = test_parser()
|
||||
assert parser.parse('< foo').domain() == [
|
||||
@@ -1430,6 +1480,7 @@ def test_comparator():
|
||||
assert parser.parse('foo <').domain() == [
|
||||
('rec_name', 'ilike', 'foo%'), ('rec_name', '<', '')]
|
||||
|
||||
|
||||
def test_str():
|
||||
parser = test_parser()
|
||||
assert str(parser.parse('foo bar spam ham =')) == '''\
|
||||
|
||||
+7
-28
@@ -15,11 +15,13 @@ from tryton.exceptions import TrytonError
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
def get_home_dir():
|
||||
if os.name == 'nt':
|
||||
return os.path.join(os.environ['HOMEDRIVE'], os.environ['HOMEPATH'])
|
||||
return os.environ['HOME']
|
||||
|
||||
|
||||
def get_config_dir():
|
||||
if os.name == 'nt':
|
||||
return os.path.join(os.environ['APPDATA'], '.config', 'tryton',
|
||||
@@ -29,20 +31,6 @@ def get_config_dir():
|
||||
if not os.path.isdir(get_config_dir()):
|
||||
os.makedirs(get_config_dir(), 0700)
|
||||
|
||||
def find_path(progs, args):
|
||||
if os.name == 'nt':
|
||||
return ''
|
||||
if sys.platform == 'darwin':
|
||||
return ''
|
||||
paths = [x for x in os.environ['PATH'].split(':')
|
||||
if os.path.isdir(x)]
|
||||
for dir in paths:
|
||||
for prog in progs:
|
||||
val = os.path.join(dir, prog)
|
||||
if os.path.isfile(val) or os.path.islink(val):
|
||||
return val + ' ' + args
|
||||
return ''
|
||||
|
||||
|
||||
class ConfigManager(object):
|
||||
"Config manager"
|
||||
@@ -78,17 +66,6 @@ class ConfigManager(object):
|
||||
'client.default_path': get_home_dir(),
|
||||
'client.lang': locale.getdefaultlocale()[0],
|
||||
'client.language_direction': 'ltr',
|
||||
'client.actions': {
|
||||
'odt': {0: find_path(['ooffice', 'ooffice2', 'libreoffice'], '"%s"'),
|
||||
1: find_path(['ooffice', 'ooffice2', 'libreoffice'], '-p "%s"')},
|
||||
'txt': {0: find_path(['ooffice', 'ooffice2', 'libreoffice'], '"%s"'),
|
||||
1: find_path(['ooffice', 'ooffice2', 'libreoffice'], '-p "%s"')},
|
||||
'pdf': {0: find_path(['evince', 'xpdf', 'gpdf',
|
||||
'kpdf', 'epdfview', 'acroread'], '"%s"'), 1: ''},
|
||||
'png': {0: find_path(['feh', 'display', 'qiv', 'eye'], '"%s"'), 1: ''},
|
||||
'csv': {0: find_path(['ooffice', 'ooffice2', 'libreoffice'], '"%s"'),
|
||||
1: find_path(['ooffice', 'ooffice2', 'libreoffice'], '-p "%s"')},
|
||||
},
|
||||
'client.email': '',
|
||||
'client.can_change_accelerators': False,
|
||||
'client.limit': 1000,
|
||||
@@ -128,18 +105,19 @@ class ConfigManager(object):
|
||||
|
||||
if opt.config and not os.path.isfile(opt.config):
|
||||
raise TrytonError(_('File "%s" not found') % (opt.config,))
|
||||
self.rcfile = opt.config or os.path.join(get_config_dir(), 'tryton.conf')
|
||||
self.rcfile = opt.config or os.path.join(
|
||||
get_config_dir(), 'tryton.conf')
|
||||
self.load()
|
||||
|
||||
for arg in ('log_level', 'log_logger'):
|
||||
if getattr(opt, arg):
|
||||
self.options['logging.'+arg[4:]] = getattr(opt, arg)
|
||||
self.options['logging.' + arg[4:]] = getattr(opt, arg)
|
||||
if opt.verbose:
|
||||
self.options['logging.default'] = 'INFO'
|
||||
|
||||
for arg in ('login', 'port', 'server'):
|
||||
if getattr(opt, arg):
|
||||
self.options['login.'+arg] = getattr(opt, arg)
|
||||
self.options['login.' + arg] = getattr(opt, arg)
|
||||
|
||||
def save(self):
|
||||
try:
|
||||
@@ -204,6 +182,7 @@ if not os.path.isdir(PIXMAPS_DIR):
|
||||
TRYTON_ICON = gtk.gdk.pixbuf_new_from_file(
|
||||
os.path.join(PIXMAPS_DIR, 'tryton-icon.png').encode('utf-8'))
|
||||
|
||||
|
||||
def _data_dir():
|
||||
data_dir = os.path.join(CURRENT_DIR, 'share', 'tryton')
|
||||
if not os.path.isdir(data_dir):
|
||||
|
||||
@@ -25,7 +25,6 @@ class Fingerprints(dict):
|
||||
self[host] = sha1
|
||||
|
||||
def save(self):
|
||||
lines = []
|
||||
with open(KNOWN_HOSTS_PATH, 'w') as known_hosts:
|
||||
known_hosts.writelines('%s %s' % (host, sha1)
|
||||
+ os.linesep for host, sha1 in self.iteritems())
|
||||
@@ -33,7 +32,7 @@ class Fingerprints(dict):
|
||||
def __setitem__(self, key, value):
|
||||
assert isinstance(key, basestring)
|
||||
if value:
|
||||
assert len(value) == 59 # len of formated sha1
|
||||
assert len(value) == 59 # len of formated sha1
|
||||
else:
|
||||
value = ''
|
||||
super(Fingerprints, self).__setitem__(key, value)
|
||||
|
||||
+71
-67
@@ -15,15 +15,13 @@ except ImportError:
|
||||
import json
|
||||
import webbrowser
|
||||
import tryton.rpc as rpc
|
||||
from tryton.config import CONFIG, TRYTON_ICON, PIXMAPS_DIR, DATA_DIR, \
|
||||
get_config_dir
|
||||
from tryton.config import CONFIG, TRYTON_ICON, get_config_dir
|
||||
import tryton.common as common
|
||||
from tryton.action import Action
|
||||
from tryton.exceptions import TrytonServerError, TrytonError
|
||||
from tryton.gui.window import Window
|
||||
from tryton.gui.window.preference import Preference
|
||||
from tryton.gui.window import Limit
|
||||
from tryton.gui.window import FileActions
|
||||
from tryton.gui.window import Email
|
||||
from tryton.gui.window.dblogin import DBLogin
|
||||
from tryton.gui.window.dbcreate import DBCreate
|
||||
@@ -32,12 +30,10 @@ from tryton.gui.window.tips import Tips
|
||||
from tryton.gui.window.about import About
|
||||
from tryton.gui.window.shortcuts import Shortcuts
|
||||
from tryton.gui.window.dbrestore import DBRestore
|
||||
import re
|
||||
import tryton.translate as translate
|
||||
import tryton.plugins
|
||||
import pango
|
||||
import time
|
||||
import threading
|
||||
try:
|
||||
import gtk_osxapplication
|
||||
except ImportError:
|
||||
@@ -82,7 +78,8 @@ class Main(object):
|
||||
self.macapp = None
|
||||
if gtk_osxapplication is not None:
|
||||
self.macapp = gtk_osxapplication.OSXApplication()
|
||||
self.macapp.connect("NSApplicationBlockTermination", self.sig_close)
|
||||
self.macapp.connect("NSApplicationBlockTermination",
|
||||
self.sig_close)
|
||||
|
||||
gtk.accel_map_add_entry('<tryton>/File/Connect', gtk.keysyms.O,
|
||||
gtk.gdk.CONTROL_MASK)
|
||||
@@ -101,7 +98,7 @@ class Main(object):
|
||||
gtk.accel_map_add_entry('<tryton>/Form/Save', gtk.keysyms.S,
|
||||
gtk.gdk.CONTROL_MASK)
|
||||
gtk.accel_map_add_entry('<tryton>/Form/Duplicate', gtk.keysyms.D,
|
||||
gtk.gdk.CONTROL_MASK|gtk.gdk.SHIFT_MASK)
|
||||
gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK)
|
||||
gtk.accel_map_add_entry('<tryton>/Form/Delete', gtk.keysyms.D,
|
||||
gtk.gdk.CONTROL_MASK)
|
||||
gtk.accel_map_add_entry('<tryton>/Form/Next', gtk.keysyms.Page_Down,
|
||||
@@ -112,10 +109,10 @@ class Main(object):
|
||||
gtk.gdk.CONTROL_MASK)
|
||||
gtk.accel_map_add_entry('<tryton>/Form/Close', gtk.keysyms.W,
|
||||
gtk.gdk.CONTROL_MASK)
|
||||
gtk.accel_map_add_entry('<tryton>/Form/Previous Tab', gtk.keysyms.Page_Up,
|
||||
gtk.gdk.CONTROL_MASK)
|
||||
gtk.accel_map_add_entry('<tryton>/Form/Next Tab', gtk.keysyms.Page_Down,
|
||||
gtk.gdk.CONTROL_MASK)
|
||||
gtk.accel_map_add_entry('<tryton>/Form/Previous Tab',
|
||||
gtk.keysyms.Page_Up, gtk.gdk.CONTROL_MASK)
|
||||
gtk.accel_map_add_entry('<tryton>/Form/Next Tab',
|
||||
gtk.keysyms.Page_Down, gtk.gdk.CONTROL_MASK)
|
||||
gtk.accel_map_add_entry('<tryton>/Form/Goto', gtk.keysyms.G,
|
||||
gtk.gdk.CONTROL_MASK)
|
||||
gtk.accel_map_add_entry('<tryton>/Form/Reload', gtk.keysyms.R,
|
||||
@@ -189,11 +186,6 @@ class Main(object):
|
||||
|
||||
self.sig_statusbar_show()
|
||||
|
||||
if os.name == 'nt' or sys.platform == 'darwin':
|
||||
# Disable actions, on win32 we use os.startfile
|
||||
# and on mac we use /usr/bin/open
|
||||
self.menuitem_actions.set_sensitive(False)
|
||||
|
||||
# Adding a timer the check to requests
|
||||
gobject.timeout_add(5 * 60 * 1000, self.request_set)
|
||||
_MAIN.append(self)
|
||||
@@ -259,6 +251,7 @@ class Main(object):
|
||||
self.menuitem_shortcut = menuitem_shortcut
|
||||
menubar.add(menuitem_shortcut)
|
||||
menuitem_shortcut.set_accel_path('<tryton>/Shortcuts')
|
||||
|
||||
def shortcut_activate(widget):
|
||||
if (not menuitem_shortcut.get_submenu()
|
||||
or not menuitem_shortcut.get_submenu().get_children()):
|
||||
@@ -276,7 +269,7 @@ class Main(object):
|
||||
if self.macapp is not None:
|
||||
self.menubar.set_no_show_all(True)
|
||||
self.macapp.set_menu_bar(self.menubar)
|
||||
self.macapp.insert_app_menu_item(self.aboutitem,0)
|
||||
self.macapp.insert_app_menu_item(self.aboutitem, 0)
|
||||
menuitem_file.show_all()
|
||||
menuitem_user.show_all()
|
||||
menuitem_options.show_all()
|
||||
@@ -296,7 +289,8 @@ class Main(object):
|
||||
if not update:
|
||||
self.sb_username = gtk.Label()
|
||||
self.sb_username.set_alignment(0.0, 0.5)
|
||||
self.status_hbox.pack_start(self.sb_username, True, True, padding=5)
|
||||
self.status_hbox.pack_start(self.sb_username, True, True,
|
||||
padding=5)
|
||||
|
||||
self.sb_requests = gtk.Label()
|
||||
self.sb_requests.set_alignment(0.5, 0.5)
|
||||
@@ -310,14 +304,16 @@ class Main(object):
|
||||
|
||||
self.secure_img = gtk.Image()
|
||||
self.secure_img.set_from_stock('tryton-lock', gtk.ICON_SIZE_MENU)
|
||||
self.status_hbox.pack_start(self.secure_img, False, True, padding=2)
|
||||
self.status_hbox.pack_start(self.secure_img, False, True,
|
||||
padding=2)
|
||||
|
||||
self.status_hbox.show_all()
|
||||
|
||||
def _set_menu_file(self):
|
||||
menu_file = gtk.Menu()
|
||||
|
||||
imagemenuitem_connect = gtk.ImageMenuItem(_('_Connect...'), self.accel_group)
|
||||
imagemenuitem_connect = gtk.ImageMenuItem(_('_Connect...'),
|
||||
self.accel_group)
|
||||
image = gtk.Image()
|
||||
image.set_from_stock('tryton-connect', gtk.ICON_SIZE_MENU)
|
||||
imagemenuitem_connect.set_image(image)
|
||||
@@ -351,7 +347,8 @@ class Main(object):
|
||||
image.set_from_stock('tryton-folder-new', gtk.ICON_SIZE_MENU)
|
||||
imagemenuitem_db_new.set_image(image)
|
||||
imagemenuitem_db_new.connect('activate', self.sig_db_new)
|
||||
imagemenuitem_db_new.set_accel_path('<tryton>/File/Database/New Database')
|
||||
imagemenuitem_db_new.set_accel_path(
|
||||
'<tryton>/File/Database/New Database')
|
||||
menu_database.add(imagemenuitem_db_new)
|
||||
|
||||
imagemenuitem_db_restore = gtk.ImageMenuItem(_('_Restore Database...'))
|
||||
@@ -359,7 +356,8 @@ class Main(object):
|
||||
image.set_from_stock('tryton-folder-saved-search', gtk.ICON_SIZE_MENU)
|
||||
imagemenuitem_db_restore.set_image(image)
|
||||
imagemenuitem_db_restore.connect('activate', self.sig_db_restore)
|
||||
imagemenuitem_db_restore.set_accel_path('<tryton>/File/Database/Restore Database')
|
||||
imagemenuitem_db_restore.set_accel_path(
|
||||
'<tryton>/File/Database/Restore Database')
|
||||
menu_database.add(imagemenuitem_db_restore)
|
||||
|
||||
imagemenuitem_db_dump = gtk.ImageMenuItem(_('_Backup Database...'))
|
||||
@@ -367,7 +365,8 @@ class Main(object):
|
||||
image.set_from_stock('tryton-save-as', gtk.ICON_SIZE_MENU)
|
||||
imagemenuitem_db_dump.set_image(image)
|
||||
imagemenuitem_db_dump.connect('activate', self.sig_db_dump)
|
||||
imagemenuitem_db_dump.set_accel_path('<tryton>/File/Database/Backup Database')
|
||||
imagemenuitem_db_dump.set_accel_path(
|
||||
'<tryton>/File/Database/Backup Database')
|
||||
menu_database.add(imagemenuitem_db_dump)
|
||||
|
||||
imagemenuitem_db_drop = gtk.ImageMenuItem(_('Dro_p Database...'))
|
||||
@@ -375,11 +374,12 @@ class Main(object):
|
||||
image.set_from_stock('tryton-delete', gtk.ICON_SIZE_MENU)
|
||||
imagemenuitem_db_drop.set_image(image)
|
||||
imagemenuitem_db_drop.connect('activate', self.sig_db_drop)
|
||||
imagemenuitem_db_drop.set_accel_path('<tryton>/File/Database/Drop Database')
|
||||
imagemenuitem_db_drop.set_accel_path(
|
||||
'<tryton>/File/Database/Drop Database')
|
||||
menu_database.add(imagemenuitem_db_drop)
|
||||
|
||||
|
||||
imagemenuitem_close = gtk.ImageMenuItem(_('_Quit...'), self.accel_group)
|
||||
imagemenuitem_close = gtk.ImageMenuItem(_('_Quit...'),
|
||||
self.accel_group)
|
||||
image = gtk.Image()
|
||||
image.set_from_stock('tryton-log-out', gtk.ICON_SIZE_MENU)
|
||||
imagemenuitem_close.set_image(image)
|
||||
@@ -404,7 +404,8 @@ class Main(object):
|
||||
|
||||
menu_user.add(gtk.SeparatorMenuItem())
|
||||
|
||||
imagemenuitem_menu = gtk.ImageMenuItem(_('_Menu Reload'), self.accel_group)
|
||||
imagemenuitem_menu = gtk.ImageMenuItem(_('_Menu Reload'),
|
||||
self.accel_group)
|
||||
image = gtk.Image()
|
||||
image.set_from_stock('tryton-start-here', gtk.ICON_SIZE_MENU)
|
||||
imagemenuitem_menu.set_image(image)
|
||||
@@ -414,11 +415,11 @@ class Main(object):
|
||||
|
||||
imagemenuitem_menu_toggle = gtk.ImageMenuItem(_('_Menu Toggle'),
|
||||
self.accel_group)
|
||||
imagemenuitem_menu_toggle.connect('activate', lambda *a: self.menu_toggle())
|
||||
imagemenuitem_menu_toggle.connect('activate',
|
||||
lambda *a: self.menu_toggle())
|
||||
imagemenuitem_menu_toggle.set_accel_path('<tryton>/User/Menu Toggle')
|
||||
menu_user.add(imagemenuitem_menu_toggle)
|
||||
|
||||
|
||||
imagemenuitem_home = gtk.ImageMenuItem(_('_Home'), self.accel_group)
|
||||
image = gtk.Image()
|
||||
image.set_from_stock('tryton-go-home', gtk.ICON_SIZE_MENU)
|
||||
@@ -434,7 +435,8 @@ class Main(object):
|
||||
image.set_from_stock('tryton-mail-message-new', gtk.ICON_SIZE_MENU)
|
||||
imagemenuitem_send_request.set_image(image)
|
||||
imagemenuitem_send_request.connect('activate', self.sig_request_new)
|
||||
imagemenuitem_send_request.set_accel_path('<tryton>/User/Send a Request')
|
||||
imagemenuitem_send_request.set_accel_path(
|
||||
'<tryton>/User/Send a Request')
|
||||
menu_user.add(imagemenuitem_send_request)
|
||||
|
||||
imagemenuitem_open_request = gtk.ImageMenuItem(_('_Read my Requests'))
|
||||
@@ -442,7 +444,8 @@ class Main(object):
|
||||
image.set_from_stock('tryton-mail-message', gtk.ICON_SIZE_MENU)
|
||||
imagemenuitem_open_request.set_image(image)
|
||||
imagemenuitem_open_request.connect('activate', self.sig_request_open)
|
||||
imagemenuitem_open_request.set_accel_path('<tryton>/User/Read my Requests')
|
||||
imagemenuitem_open_request.set_accel_path(
|
||||
'<tryton>/User/Read my Requests')
|
||||
menu_user.add(imagemenuitem_open_request)
|
||||
return menu_user
|
||||
|
||||
@@ -460,7 +463,8 @@ class Main(object):
|
||||
radiomenuitem_default = gtk.RadioMenuItem(label=_('_Default'))
|
||||
radiomenuitem_default.connect('activate',
|
||||
lambda x: self.sig_toolbar('default'))
|
||||
radiomenuitem_default.set_accel_path('<tryton>/Options/Toolbar/Default')
|
||||
radiomenuitem_default.set_accel_path(
|
||||
'<tryton>/Options/Toolbar/Default')
|
||||
menu_toolbar.add(radiomenuitem_default)
|
||||
if (CONFIG['client.toolbar'] or 'both') == 'default':
|
||||
radiomenuitem_default.set_active(True)
|
||||
@@ -543,18 +547,20 @@ class Main(object):
|
||||
|
||||
checkmenuitem_statusbar = gtk.CheckMenuItem(_('Statusbar'))
|
||||
checkmenuitem_statusbar.connect('activate',
|
||||
lambda menuitem: self.sig_statusbar_change(menuitem.get_active()))
|
||||
checkmenuitem_statusbar.set_accel_path('<tryton>/Options/Form/Statusbar')
|
||||
lambda menuitem: self.sig_statusbar_change(menuitem.get_active()))
|
||||
checkmenuitem_statusbar.set_accel_path(
|
||||
'<tryton>/Options/Form/Statusbar')
|
||||
menu_form.add(checkmenuitem_statusbar)
|
||||
if CONFIG['form.statusbar']:
|
||||
checkmenuitem_statusbar.set_active(True)
|
||||
|
||||
checkmenuitem_save_width_height = gtk.CheckMenuItem(_('Save Width/Height'))
|
||||
checkmenuitem_save_width_height = gtk.CheckMenuItem(
|
||||
_('Save Width/Height'))
|
||||
checkmenuitem_save_width_height.connect('activate',
|
||||
lambda menuitem: CONFIG.__setitem__('client.save_width_height',
|
||||
menuitem.get_active()))
|
||||
lambda menuitem: CONFIG.__setitem__('client.save_width_height',
|
||||
menuitem.get_active()))
|
||||
checkmenuitem_save_width_height.set_accel_path(
|
||||
'<tryton>/Options/Form/Save Width Height')
|
||||
'<tryton>/Options/Form/Save Width Height')
|
||||
menu_form.add(checkmenuitem_save_width_height)
|
||||
if CONFIG['client.save_width_height']:
|
||||
checkmenuitem_save_width_height.set_active(True)
|
||||
@@ -602,7 +608,8 @@ class Main(object):
|
||||
label=_('Left'))
|
||||
radiomenuitem_left.connect('activate',
|
||||
lambda x: CONFIG.__setitem__('client.form_tab', 'left'))
|
||||
radiomenuitem_left.set_accel_path('<tryton>/Options/Tabs Position/Left')
|
||||
radiomenuitem_left.set_accel_path(
|
||||
'<tryton>/Options/Tabs Position/Left')
|
||||
menu_tab.add(radiomenuitem_left)
|
||||
if (CONFIG['client.form_tab'] or 'left') == 'left':
|
||||
radiomenuitem_left.set_active(True)
|
||||
@@ -611,7 +618,8 @@ class Main(object):
|
||||
label=_('Right'))
|
||||
radiomenuitem_right.connect('activate',
|
||||
lambda x: CONFIG.__setitem__('client.form_tab', 'right'))
|
||||
radiomenuitem_right.set_accel_path('<tryton>/Options/Tabs Position/Right')
|
||||
radiomenuitem_right.set_accel_path(
|
||||
'<tryton>/Options/Tabs Position/Right')
|
||||
menu_tab.add(radiomenuitem_right)
|
||||
if (CONFIG['client.form_tab'] or 'left') == 'right':
|
||||
radiomenuitem_right.set_active(True)
|
||||
@@ -620,7 +628,8 @@ class Main(object):
|
||||
label=_('Bottom'))
|
||||
radiomenuitem_bottom.connect('activate',
|
||||
lambda x: CONFIG.__setitem__('client.form_tab', 'bottom'))
|
||||
radiomenuitem_bottom.set_accel_path('<tryton>/Options/Tabs Position/Bottom')
|
||||
radiomenuitem_bottom.set_accel_path(
|
||||
'<tryton>/Options/Tabs Position/Bottom')
|
||||
menu_tab.add(radiomenuitem_bottom)
|
||||
if (CONFIG['client.form_tab'] or 'left') == 'bottom':
|
||||
radiomenuitem_bottom.set_active(True)
|
||||
@@ -631,12 +640,6 @@ class Main(object):
|
||||
menuitem_limit.set_accel_path('<tryton>/Options/Search Limit')
|
||||
menu_options.add(menuitem_limit)
|
||||
|
||||
menuitem_actions = gtk.MenuItem(_('File _Actions...'))
|
||||
self.menuitem_actions = menuitem_actions
|
||||
menuitem_actions.connect('activate', self.sig_file_actions)
|
||||
menuitem_actions.set_accel_path('<tryton>/Options/File Actions')
|
||||
menu_options.add(menuitem_actions)
|
||||
|
||||
menuitem_email = gtk.MenuItem(_('_Email...'))
|
||||
self.menuitem_email = menuitem_email
|
||||
menuitem_email.connect('activate', self.sig_email)
|
||||
@@ -657,13 +660,15 @@ class Main(object):
|
||||
def _set_menu_plugins(self):
|
||||
menu_plugins = gtk.Menu()
|
||||
|
||||
imagemenuitem_plugin_execute = gtk.ImageMenuItem(_('_Execute a Plugin'))
|
||||
imagemenuitem_plugin_execute = gtk.ImageMenuItem(
|
||||
_('_Execute a Plugin'))
|
||||
image = gtk.Image()
|
||||
image.set_from_stock('tryton-executable', gtk.ICON_SIZE_MENU)
|
||||
imagemenuitem_plugin_execute.set_image(image)
|
||||
imagemenuitem_plugin_execute.connect('activate', self.sig_plugin_execute)
|
||||
imagemenuitem_plugin_execute.connect('activate',
|
||||
self.sig_plugin_execute)
|
||||
imagemenuitem_plugin_execute.set_accel_path(
|
||||
'<tryton>/Plugins/Execute a Plugin')
|
||||
'<tryton>/Plugins/Execute a Plugin')
|
||||
menu_plugins.add(imagemenuitem_plugin_execute)
|
||||
return menu_plugins
|
||||
|
||||
@@ -678,15 +683,16 @@ class Main(object):
|
||||
imagemenuitem_tips.set_accel_path('<tryton>/Help/Tips')
|
||||
menu_help.add(imagemenuitem_tips)
|
||||
|
||||
imagemenuitem_shortcuts = gtk.ImageMenuItem(_('_Keyboard Shortcuts...'))
|
||||
imagemenuitem_shortcuts = gtk.ImageMenuItem(
|
||||
_('_Keyboard Shortcuts...'))
|
||||
image = gtk.Image()
|
||||
image.set_from_stock('tryton-help', gtk.ICON_SIZE_MENU)
|
||||
imagemenuitem_shortcuts.set_image(image)
|
||||
imagemenuitem_shortcuts.connect('activate', self.sig_shortcuts)
|
||||
imagemenuitem_shortcuts.set_accel_path('<tryton>/Help/Keyboard Shortcuts')
|
||||
imagemenuitem_shortcuts.set_accel_path(
|
||||
'<tryton>/Help/Keyboard Shortcuts')
|
||||
menu_help.add(imagemenuitem_shortcuts)
|
||||
|
||||
|
||||
imagemenuitem_about = gtk.ImageMenuItem(_('_About...'))
|
||||
image = gtk.Image()
|
||||
image.set_from_stock('gtk-about', gtk.ICON_SIZE_MENU)
|
||||
@@ -817,9 +823,6 @@ class Main(object):
|
||||
def sig_limit(self, widget):
|
||||
Limit().run()
|
||||
|
||||
def sig_file_actions(self, widget):
|
||||
FileActions().run()
|
||||
|
||||
def sig_email(self, widget):
|
||||
Email().run()
|
||||
|
||||
@@ -843,7 +846,8 @@ class Main(object):
|
||||
'get_preferences', False, rpc.CONTEXT)
|
||||
if prefs and 'language_direction' in prefs:
|
||||
translate.set_language_direction(prefs['language_direction'])
|
||||
CONFIG['client.language_direction'] = prefs['language_direction']
|
||||
CONFIG['client.language_direction'] = \
|
||||
prefs['language_direction']
|
||||
self.sb_username.set_text(prefs.get('status_bar', ''))
|
||||
if prefs and 'language' in prefs:
|
||||
translate.setlang(prefs['language'], prefs.get('locale'))
|
||||
@@ -866,8 +870,8 @@ class Main(object):
|
||||
ctx = {}
|
||||
ctx.update(rpc.CONTEXT)
|
||||
ctx['active_test'] = False
|
||||
return Window.create(None, 'res.request', False, [ ],
|
||||
mode=['form', 'tree'], context=ctx)
|
||||
return Window.create(None, 'res.request', False, [],
|
||||
mode=['form', 'tree'], context=ctx)
|
||||
|
||||
def sig_request_open(self, widget):
|
||||
ctx = {}
|
||||
@@ -881,8 +885,8 @@ class Main(object):
|
||||
else:
|
||||
raise
|
||||
ids = ids1 + ids2
|
||||
return Window.create(False, 'res.request', ids, [ ],
|
||||
mode=['tree', 'form'], context=ctx)
|
||||
return Window.create(False, 'res.request', ids, [],
|
||||
mode=['tree', 'form'], context=ctx)
|
||||
|
||||
def request_set(self, exception=False):
|
||||
try:
|
||||
@@ -897,7 +901,6 @@ class Main(object):
|
||||
else:
|
||||
ids, ids2 = rpc.execute('model', 'res.request', 'request_get',
|
||||
rpc.CONTEXT)
|
||||
label = _('Requests (%s/%s)') % (len(ids), len(ids2))
|
||||
message = _('Waiting requests: %s received - %s sent') % (len(ids),
|
||||
len(ids2))
|
||||
self.sb_requests.set_text(message)
|
||||
@@ -935,7 +938,8 @@ class Main(object):
|
||||
common.ICONFACTORY.load_icons()
|
||||
if prefs and 'language_direction' in prefs:
|
||||
translate.set_language_direction(prefs['language_direction'])
|
||||
CONFIG['client.language_direction'] = prefs['language_direction']
|
||||
CONFIG['client.language_direction'] = \
|
||||
prefs['language_direction']
|
||||
menu_id = self.sig_win_menu(quiet=False, prefs=prefs)
|
||||
if menu_id:
|
||||
self.sig_home_new(quiet=True, except_id=menu_id, prefs=prefs)
|
||||
@@ -1026,7 +1030,8 @@ class Main(object):
|
||||
if (self.menu_screen
|
||||
and self.menu_screen.current_view.view_type == 'tree'):
|
||||
try:
|
||||
has_focus = self.menu_screen.current_view.widget_tree.has_focus()
|
||||
has_focus = \
|
||||
self.menu_screen.current_view.widget_tree.has_focus()
|
||||
except AttributeError:
|
||||
has_focus = (self.menu_screen.current_view.widget_tree.flags()
|
||||
& gtk.HAS_FOCUS)
|
||||
@@ -1114,7 +1119,7 @@ class Main(object):
|
||||
def sig_configure(self, widget, event):
|
||||
if hasattr(event, 'width') \
|
||||
and hasattr(event, 'height'):
|
||||
self._width = int(event.width)
|
||||
self._width = int(event.width)
|
||||
self._height = int(event.height)
|
||||
return False
|
||||
|
||||
@@ -1336,7 +1341,7 @@ class Main(object):
|
||||
if dbname:
|
||||
with open(filename, 'rb') as file_p:
|
||||
data = file_p.read()
|
||||
host, port = url.rsplit(':' , 1)
|
||||
host, port = url.rsplit(':', 1)
|
||||
rpcprogress = common.RPCProgress('db_exec', (host, int(port),
|
||||
'restore', dbname, passwd, buffer(data), update))
|
||||
try:
|
||||
@@ -1497,7 +1502,6 @@ class Main(object):
|
||||
direct_print = json.loads(params.get('direct_print', 'false'))
|
||||
email_print = json.loads(params.get('email_print', 'false'))
|
||||
email = json.loads(params.get('email', 'null'))
|
||||
name = json.loads(params.get('name', 'false'))
|
||||
context = json.loads(params.get('context', '{}'))
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#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 window import *
|
||||
from fileactions import *
|
||||
from email import *
|
||||
from limit import *
|
||||
@@ -3,9 +3,8 @@
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
import gtk
|
||||
import gettext
|
||||
import os
|
||||
import webbrowser
|
||||
from tryton.config import TRYTON_ICON, PIXMAPS_DIR, DATA_DIR
|
||||
from tryton.config import TRYTON_ICON
|
||||
from tryton.version import VERSION, WEBSITE
|
||||
from tryton.common import get_toplevel_window
|
||||
|
||||
|
||||
@@ -5,20 +5,27 @@ from tryton.gui.window.view_form.screen import Screen
|
||||
from tryton.gui.window.win_form import WinForm
|
||||
|
||||
|
||||
class Attachment(object):
|
||||
class Attachment(WinForm):
|
||||
"Attachment window"
|
||||
|
||||
def __init__(self, model_name, record_id):
|
||||
def __init__(self, model_name, record_id, callback=None):
|
||||
self.resource = '%s,%s' % (model_name, record_id)
|
||||
|
||||
def run(self):
|
||||
self.attachment_callback = callback
|
||||
screen = Screen('ir.attachment', domain=[
|
||||
('resource', '=', self.resource),
|
||||
], mode=['tree', 'form'], context={
|
||||
'resource': self.resource,
|
||||
}, exclude_field='resource')
|
||||
screen.search_filter()
|
||||
def callback(result):
|
||||
if result:
|
||||
screen.group.save()
|
||||
WinForm(screen, callback, view_type='tree')
|
||||
super(Attachment, self).__init__(screen, self.callback,
|
||||
view_type='tree')
|
||||
|
||||
def destroy(self):
|
||||
self.prev_view.save_width_height()
|
||||
super(Attachment, self).destroy()
|
||||
|
||||
def callback(self, result):
|
||||
if result:
|
||||
self.screen.group.save()
|
||||
if self.attachment_callback:
|
||||
self.attachment_callback()
|
||||
@@ -2,8 +2,6 @@
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
"Board"
|
||||
import gettext
|
||||
import gtk
|
||||
from tryton.config import CONFIG
|
||||
from tryton.signal_event import SignalEvent
|
||||
from tryton.gui import Main
|
||||
import tryton.rpc as rpc
|
||||
|
||||
@@ -5,7 +5,7 @@ import gobject
|
||||
import gettext
|
||||
import re
|
||||
import tryton.common as common
|
||||
from tryton.config import CONFIG, TRYTON_ICON, PIXMAPS_DIR
|
||||
from tryton.config import CONFIG, TRYTON_ICON
|
||||
from tryton.exceptions import TrytonServerError
|
||||
import tryton.rpc as rpc
|
||||
|
||||
@@ -28,10 +28,11 @@ class DBCreate(object):
|
||||
self.entry_adminpasswd2.set_sensitive(True)
|
||||
self.entry_server_connection.modify_text(gtk.STATE_INSENSITIVE, \
|
||||
gtk.gdk.color_parse(common.COLOR_SCHEMES["black"]))
|
||||
self.tooltips.set_tip(self.entry_server_connection,_("This is the URL of " \
|
||||
"the Tryton server. Use server 'localhost' and port '8000' if " \
|
||||
"the server is installed on this computer. Click on 'Change' to " \
|
||||
"change the address."))
|
||||
self.tooltips.set_tip(self.entry_server_connection,
|
||||
_("This is the URL of the Tryton server. Use server "
|
||||
"'localhost' and port '8000' if the server is installed "
|
||||
"on this computer. Click on 'Change' to change the "
|
||||
"address."))
|
||||
else:
|
||||
self.entry_serverpasswd.set_sensitive(False)
|
||||
self.entry_dbname.set_sensitive(False)
|
||||
@@ -89,12 +90,12 @@ class DBCreate(object):
|
||||
must be filled, then the Create button is set to sensitive. This
|
||||
event method doesn't check the valid of single entrys.
|
||||
"""
|
||||
if self.entry_server_connection.get_text() != "" \
|
||||
and self.entry_serverpasswd.get_text() != "" \
|
||||
and self.entry_dbname.get_text() != "" \
|
||||
and self.combo_language.get_active() != -1 \
|
||||
and self.entry_adminpasswd.get_text() != "" \
|
||||
and self.entry_adminpasswd2.get_text() != "":
|
||||
if (self.entry_server_connection.get_text() != ""
|
||||
and self.entry_serverpasswd.get_text() != ""
|
||||
and self.entry_dbname.get_text() != ""
|
||||
and self.combo_language.get_active() != -1
|
||||
and self.entry_adminpasswd.get_text() != ""
|
||||
and self.entry_adminpasswd2.get_text() != ""):
|
||||
widget.unset_flags(gtk.HAS_DEFAULT)
|
||||
self.button_create.set_sensitive(True)
|
||||
self.button_create.set_flags(gtk.CAN_DEFAULT)
|
||||
@@ -114,7 +115,7 @@ class DBCreate(object):
|
||||
- First character must be a letter
|
||||
"""
|
||||
|
||||
if (new_text.isalnum() or new_text == '_' ):
|
||||
if new_text.isalnum() or new_text == '_':
|
||||
_hid = entry.get_data('handlerid')
|
||||
entry.handler_block(_hid)
|
||||
_pos = entry.get_position()
|
||||
@@ -133,11 +134,9 @@ class DBCreate(object):
|
||||
self.port = port
|
||||
|
||||
# GTK Stuffs
|
||||
self.dialog = gtk.Dialog(
|
||||
title= _("Create new database"),
|
||||
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT
|
||||
| gtk.WIN_POS_CENTER_ON_PARENT,
|
||||
)
|
||||
self.dialog = gtk.Dialog(title=_("Create new database"),
|
||||
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT |
|
||||
gtk.WIN_POS_CENTER_ON_PARENT)
|
||||
self.dialog.set_has_separator(True)
|
||||
self.dialog.set_icon(TRYTON_ICON)
|
||||
# This event is needed for controlling the button_create
|
||||
@@ -152,7 +151,8 @@ class DBCreate(object):
|
||||
img_connect = gtk.Image()
|
||||
img_connect.set_from_stock('tryton-new', gtk.ICON_SIZE_BUTTON)
|
||||
self.button_create.set_image(img_connect)
|
||||
self.tooltips.set_tip(self.button_create, _('Create the new database.'))
|
||||
self.tooltips.set_tip(self.button_create,
|
||||
_('Create the new database.'))
|
||||
self.dialog.add_action_widget(self.button_create, gtk.RESPONSE_OK)
|
||||
self.dialog.set_default_response(gtk.RESPONSE_OK)
|
||||
|
||||
@@ -163,10 +163,10 @@ class DBCreate(object):
|
||||
table.set_col_spacings(3)
|
||||
|
||||
self.label_server_setup = gtk.Label()
|
||||
self.label_server_setup.set_markup("<b>"+ _("Server Setup:")+ "</b>")
|
||||
self.label_server_setup.set_markup("<b>" + _("Server Setup:") + "</b>")
|
||||
self.label_server_setup.set_justify(gtk.JUSTIFY_LEFT)
|
||||
self.label_server_setup.set_alignment(0, 1)
|
||||
self.label_server_setup.set_padding( 9, 5)
|
||||
self.label_server_setup.set_padding(9, 5)
|
||||
table.attach(self.label_server_setup, 0, 3, 0, 1, xoptions=gtk.FILL)
|
||||
self.label_server = gtk.Label(_("Server connection:"))
|
||||
self.label_server.set_alignment(1, 0.5)
|
||||
@@ -176,25 +176,26 @@ class DBCreate(object):
|
||||
self.entry_server_connection.set_sensitive(False)
|
||||
self.entry_server_connection.unset_flags(gtk.CAN_FOCUS)
|
||||
self.entry_server_connection.set_editable(False)
|
||||
self.tooltips.set_tip(self.entry_server_connection, _("This is the URL of " \
|
||||
"the server. Use server 'localhost' and port '8000' if " \
|
||||
"the server is installed on this computer. Click on 'Change' to " \
|
||||
"change the address."))
|
||||
self.tooltips.set_tip(self.entry_server_connection,
|
||||
_("This is the URL of the server. Use server 'localhost' and port "
|
||||
"'8000' if the server is installed on this computer. Click on "
|
||||
"'Change' to change the address."))
|
||||
self.button_server_change = gtk.Button(_("C_hange"), stock=None,
|
||||
use_underline=True)
|
||||
img_button_server_change = gtk.Image()
|
||||
img_button_server_change.set_from_stock('tryton-preferences-system', \
|
||||
img_button_server_change.set_from_stock('tryton-preferences-system',
|
||||
gtk.ICON_SIZE_BUTTON)
|
||||
self.button_server_change.set_image(img_button_server_change)
|
||||
table.attach(self.button_server_change, 2, 3, 1, 2, yoptions=False, xoptions=gtk.FILL)
|
||||
self.tooltips.set_tip(self.button_server_change, _("Setup the " \
|
||||
"server connection..."))
|
||||
table.attach(self.button_server_change, 2, 3, 1, 2, yoptions=False,
|
||||
xoptions=gtk.FILL)
|
||||
self.tooltips.set_tip(self.button_server_change,
|
||||
_("Setup the server connection..."))
|
||||
|
||||
table.attach(self.entry_server_connection, 1, 2, 1, 2)
|
||||
self.label_serverpasswd = gtk.Label(_("Tryton Server Password:"))
|
||||
self.label_serverpasswd.set_justify(gtk.JUSTIFY_RIGHT)
|
||||
self.label_serverpasswd.set_alignment(1, 0.5)
|
||||
self.label_serverpasswd.set_padding( 3, 3)
|
||||
self.label_serverpasswd.set_padding(3, 3)
|
||||
table.attach(self.label_serverpasswd, 0, 1, 2, 3, xoptions=gtk.FILL,
|
||||
yoptions=gtk.FILL)
|
||||
self.entry_serverpasswd = gtk.Entry()
|
||||
@@ -212,15 +213,15 @@ class DBCreate(object):
|
||||
table.attach(self.hseparator, 0, 3, 3, 4, yoptions=gtk.FILL)
|
||||
|
||||
label_dbname = gtk.Label()
|
||||
label_dbname.set_markup("<b>" + _("New database setup:") + "</b>")
|
||||
label_dbname.set_markup("<b>" + _("New database setup:") + "</b>")
|
||||
label_dbname.set_justify(gtk.JUSTIFY_LEFT)
|
||||
label_dbname.set_alignment(0, 1)
|
||||
label_dbname.set_padding( 9, 5)
|
||||
label_dbname.set_padding(9, 5)
|
||||
table.attach(label_dbname, 0, 3, 4, 5, xoptions=gtk.FILL,
|
||||
yoptions=gtk.FILL)
|
||||
label_dbname = gtk.Label(_("Database name:"))
|
||||
label_dbname.set_justify(gtk.JUSTIFY_RIGHT)
|
||||
label_dbname.set_padding( 3, 3)
|
||||
label_dbname.set_padding(3, 3)
|
||||
label_dbname.set_alignment(1, 0.5)
|
||||
table.attach(label_dbname, 0, 1, 5, 6, xoptions=gtk.FILL,
|
||||
yoptions=gtk.FILL)
|
||||
@@ -229,18 +230,18 @@ class DBCreate(object):
|
||||
self.entry_dbname.set_width_chars(16)
|
||||
self.entry_dbname.set_activates_default(True)
|
||||
table.attach(self.entry_dbname, 1, 3, 5, 6, yoptions=gtk.FILL)
|
||||
self.tooltips.set_tip(self.entry_dbname, _("Choose the name of the new " \
|
||||
"database.\n" \
|
||||
"Allowed characters are alphanumerical or _ (underscore)\n" \
|
||||
"You need to avoid all accents, space or special characters! " \
|
||||
"Example: tryton"))
|
||||
handlerid = self.entry_dbname.connect("insert-text", \
|
||||
self.tooltips.set_tip(self.entry_dbname,
|
||||
_("Choose the name of the new database.\n"
|
||||
"Allowed characters are alphanumerical or _ (underscore)\n"
|
||||
"You need to avoid all accents, space or special characters! "
|
||||
"Example: tryton"))
|
||||
handlerid = self.entry_dbname.connect("insert-text",
|
||||
self.entry_insert_text)
|
||||
self.entry_dbname.set_data('handlerid', handlerid)
|
||||
label_language = gtk.Label(_("Default language:"))
|
||||
label_language.set_justify(gtk.JUSTIFY_RIGHT)
|
||||
label_language.set_alignment(1, 0.5)
|
||||
label_language.set_padding( 3, 3)
|
||||
label_language.set_padding(3, 3)
|
||||
table.attach(label_language, 0, 1, 6, 7, xoptions=gtk.FILL,
|
||||
yoptions=gtk.FILL)
|
||||
eventbox_language = gtk.EventBox()
|
||||
@@ -253,24 +254,25 @@ class DBCreate(object):
|
||||
"the administration menu."))
|
||||
label_adminpasswd = gtk.Label(_("Admin password:"))
|
||||
label_adminpasswd.set_justify(gtk.JUSTIFY_RIGHT)
|
||||
label_adminpasswd.set_padding( 3, 3)
|
||||
label_adminpasswd.set_padding(3, 3)
|
||||
label_adminpasswd.set_alignment(1, 0.5)
|
||||
table.attach(label_adminpasswd, 0, 1, 7, 8, xoptions=gtk.FILL,
|
||||
yoptions=gtk.FILL)
|
||||
self.entry_adminpasswd = gtk.Entry()
|
||||
self.entry_adminpasswd.set_visibility(False)
|
||||
self.entry_adminpasswd.set_activates_default(True)
|
||||
self.tooltips.set_tip(self.entry_adminpasswd, _("Choose a password for " \
|
||||
"the admin user of the new database. With these credentials you " \
|
||||
"will be later able to login into the database:\n" \
|
||||
"User name: admin\n" \
|
||||
"Password: <The password you set here>"))
|
||||
self.tooltips.set_tip(self.entry_adminpasswd,
|
||||
_("Choose a password for the admin user of the new database. "
|
||||
"With these credentials you will be later able to login into "
|
||||
"the database:\n"
|
||||
"User name: admin\n"
|
||||
"Password: <The password you set here>"))
|
||||
table.attach(self.entry_adminpasswd, 1, 3, 7, 8, yoptions=gtk.FILL)
|
||||
self.entry_adminpasswd.connect("key-press-event", \
|
||||
self.event_passwd_clear)
|
||||
label_adminpasswd2 = gtk.Label(_("Confirm admin password:"))
|
||||
label_adminpasswd2.set_justify(gtk.JUSTIFY_RIGHT)
|
||||
label_adminpasswd2.set_padding( 3, 3)
|
||||
label_adminpasswd2.set_padding(3, 3)
|
||||
label_adminpasswd2.set_alignment(1, 0.5)
|
||||
table.attach(label_adminpasswd2, 0, 1, 8, 9, xoptions=gtk.FILL,
|
||||
yoptions=gtk.FILL)
|
||||
@@ -355,14 +357,14 @@ class DBCreate(object):
|
||||
common.process_exception(exception)
|
||||
continue
|
||||
if exist:
|
||||
common.warning(_("A database with the same name " \
|
||||
"already exists.\n" \
|
||||
"Try another database name."),
|
||||
_("This database name already exist!"))
|
||||
self.entry_dbname.set_text("")
|
||||
self.entry_dbname.grab_focus()
|
||||
continue
|
||||
else: # Everything runs fine, break the block here
|
||||
common.warning(_("A database with the same name "
|
||||
"already exists.\n"
|
||||
"Try another database name."),
|
||||
_("This database name already exist!"))
|
||||
self.entry_dbname.set_text("")
|
||||
self.entry_dbname.grab_focus()
|
||||
continue
|
||||
else: # Everything runs fine, break the block here
|
||||
host = url_m.group(1)
|
||||
port = url_m.group(2)
|
||||
try:
|
||||
@@ -378,13 +380,13 @@ class DBCreate(object):
|
||||
self.entry_serverpasswd.set_text("")
|
||||
self.entry_serverpasswd.grab_focus()
|
||||
continue
|
||||
else: # Unclassified error
|
||||
common.warning(_("Can't create the " \
|
||||
"database, caused by an unknown reason.\n" \
|
||||
"If there is a database created, it could " \
|
||||
"be broken. Maybe drop this database! " \
|
||||
"Please check the error message for " \
|
||||
"possible informations.\n" \
|
||||
else: # Unclassified error
|
||||
common.warning(_("Can't create the "
|
||||
"database, caused by an unknown reason.\n"
|
||||
"If there is a database created, it could "
|
||||
"be broken. Maybe drop this database! "
|
||||
"Please check the error message for "
|
||||
"possible informations.\n"
|
||||
"Error message:\n")
|
||||
+ str(exception.faultCode),
|
||||
_("Error creating database!"))
|
||||
@@ -410,4 +412,3 @@ class DBCreate(object):
|
||||
parent.present()
|
||||
self.dialog.destroy()
|
||||
return dbname
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
# 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 threading
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
import gettext
|
||||
@@ -11,6 +9,7 @@ import tryton.rpc as rpc
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
class DBBackupDrop(object):
|
||||
"""
|
||||
Widget for database backup and drop.
|
||||
@@ -20,14 +19,16 @@ class DBBackupDrop(object):
|
||||
db_widget.hide()
|
||||
label.hide()
|
||||
dbprogress = common.DBProgress(host, port)
|
||||
|
||||
def callback(dbs, createdb):
|
||||
if dbs is None or dbs == -1:
|
||||
if dbs is None:
|
||||
label.set_label('<b>' + \
|
||||
_('Could not connect to server!') + '</b>')
|
||||
label.set_label('<b>' +
|
||||
_('Could not connect to server!') + '</b>')
|
||||
else:
|
||||
label.set_label('<b>' + \
|
||||
_('This client version is not compatible with the server!')
|
||||
label.set_label('<b>' +
|
||||
_('This client version is not compatible '
|
||||
'with the server!')
|
||||
+ '</b>')
|
||||
db_widget.hide()
|
||||
label.show()
|
||||
@@ -58,9 +59,9 @@ class DBBackupDrop(object):
|
||||
must be filled, then the Create button is set to sensitive. This
|
||||
event method doesn't check the valid of single entrys.
|
||||
"""
|
||||
if self.entry_server_connection.get_text() != "" \
|
||||
and self.combo_database.get_active_text() != "" \
|
||||
and self.entry_serverpasswd.get_text() != "":
|
||||
if (self.entry_server_connection.get_text() != ""
|
||||
and self.combo_database.get_active_text() != ""
|
||||
and self.entry_serverpasswd.get_text() != ""):
|
||||
widget.unset_flags(gtk.HAS_DEFAULT)
|
||||
self.button_ok.set_sensitive(True)
|
||||
self.button_ok.set_flags(gtk.CAN_DEFAULT)
|
||||
@@ -73,7 +74,7 @@ class DBBackupDrop(object):
|
||||
|
||||
def __init__(self, function=None):
|
||||
# This widget is used for creating and droping a database!
|
||||
if function =="backup":
|
||||
if function == "backup":
|
||||
dialog_title = _("Backup a database")
|
||||
button_ok_text = _("Backup")
|
||||
button_ok_tooltip = _("Backup the choosen database.")
|
||||
@@ -114,14 +115,15 @@ class DBBackupDrop(object):
|
||||
table.set_border_width(10)
|
||||
table.set_row_spacings(3)
|
||||
table.set_col_spacings(3)
|
||||
self.dialog_vbox.pack_start(table, True, False,0)
|
||||
self.dialog_vbox.pack_start(table, True, False, 0)
|
||||
|
||||
label_subtitle = gtk.Label()
|
||||
label_subtitle.set_markup("<b>"+ label_subtitle_text + "</b>")
|
||||
label_subtitle.set_markup("<b>" + label_subtitle_text + "</b>")
|
||||
label_subtitle.set_justify(gtk.JUSTIFY_LEFT)
|
||||
label_subtitle.set_alignment(0, 1)
|
||||
label_subtitle.set_padding( 9, 5)
|
||||
table.attach(label_subtitle, 0, 3, 0, 1, yoptions=False, xoptions=gtk.FILL)
|
||||
label_subtitle.set_padding(9, 5)
|
||||
table.attach(label_subtitle, 0, 3, 0, 1, yoptions=False,
|
||||
xoptions=gtk.FILL)
|
||||
|
||||
hseparator = gtk.HSeparator()
|
||||
table.attach(hseparator, 0, 3, 1, 2, yoptions=False)
|
||||
@@ -183,7 +185,7 @@ class DBBackupDrop(object):
|
||||
self.label_serverpasswd = gtk.Label(_("Tryton Server Password:"))
|
||||
self.label_serverpasswd.set_justify(gtk.JUSTIFY_RIGHT)
|
||||
self.label_serverpasswd.set_alignment(1, 0.5)
|
||||
self.label_serverpasswd.set_padding( 3, 3)
|
||||
self.label_serverpasswd.set_padding(3, 3)
|
||||
table.attach(self.label_serverpasswd, 0, 1, 4, 5, yoptions=False,
|
||||
xoptions=gtk.FILL)
|
||||
|
||||
@@ -234,4 +236,3 @@ class DBBackupDrop(object):
|
||||
self.dialog.destroy()
|
||||
|
||||
return (url, database, passwd)
|
||||
|
||||
@@ -5,10 +5,7 @@ import ConfigParser
|
||||
import gtk
|
||||
import gobject
|
||||
import os
|
||||
import re
|
||||
import gettext
|
||||
import threading
|
||||
import time
|
||||
|
||||
from tryton.version import VERSION
|
||||
import tryton.common as common
|
||||
@@ -31,8 +28,9 @@ class DBListEditor(object):
|
||||
# GTK Stuffs
|
||||
self.parent = parent
|
||||
self.dialog = gtk.Dialog(title=_(u'Profile Editor'), parent=parent,
|
||||
flags=gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT)
|
||||
self.ok_button = self.dialog.add_button(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT)
|
||||
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
|
||||
self.ok_button = self.dialog.add_button(gtk.STOCK_OK,
|
||||
gtk.RESPONSE_ACCEPT)
|
||||
self.dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
|
||||
self.dialog.set_has_separator(True)
|
||||
self.dialog.set_icon(TRYTON_ICON)
|
||||
@@ -94,7 +92,8 @@ class DBListEditor(object):
|
||||
database.set_padding(3, 3)
|
||||
self.database_entry = gtk.Entry()
|
||||
self.database_entry.connect('changed', self.dbentry_changed)
|
||||
self.database_entry.connect('changed', self.update_profiles, 'database')
|
||||
self.database_entry.connect('changed', self.update_profiles,
|
||||
'database')
|
||||
self.database_entry.set_activates_default(True)
|
||||
self.database_label = gtk.Label()
|
||||
self.database_label.set_use_markup(True)
|
||||
@@ -108,7 +107,7 @@ class DBListEditor(object):
|
||||
self.database_combo.connect('changed', self.dbcombo_changed)
|
||||
self.database_button = gtk.Button(_(u'Create'))
|
||||
self.database_button.connect('clicked', self.db_create)
|
||||
self.database_progressbar= gtk.ProgressBar()
|
||||
self.database_progressbar = gtk.ProgressBar()
|
||||
self.database_progressbar.set_text(_(u'Fetching databases list'))
|
||||
image = gtk.Image()
|
||||
image.set_from_stock('tryton-new', gtk.ICON_SIZE_BUTTON)
|
||||
@@ -131,7 +130,8 @@ class DBListEditor(object):
|
||||
username.set_alignment(1, 0.5)
|
||||
username.set_padding(3, 3)
|
||||
self.username_entry = gtk.Entry()
|
||||
self.username_entry.connect('changed', self.update_profiles, 'username')
|
||||
self.username_entry.connect('changed', self.update_profiles,
|
||||
'username')
|
||||
self.username_entry.set_activates_default(True)
|
||||
table.attach(username, 0, 1, 4, 5, yoptions=False, xoptions=gtk.FILL)
|
||||
table.attach(self.username_entry, 1, 2, 4, 5, yoptions=False)
|
||||
@@ -193,10 +193,10 @@ class DBListEditor(object):
|
||||
def profile_create(self, button):
|
||||
self.clear_entries()
|
||||
model = self.profile_tree.get_model()
|
||||
selection = self.profile_tree.get_selection()
|
||||
model.append(['', False])
|
||||
column = self.profile_tree.get_column(0)
|
||||
self.profile_tree.set_cursor(len(model)-1, column, start_editing=True)
|
||||
self.profile_tree.set_cursor(len(model) - 1, column,
|
||||
start_editing=True)
|
||||
|
||||
def profile_delete(self, button):
|
||||
self.clear_entries()
|
||||
@@ -375,7 +375,7 @@ class DBLogin(object):
|
||||
# GTK Stuffs
|
||||
self.parent = common.get_toplevel_window()
|
||||
self.dialog = gtk.Dialog(title=_('Login'), parent=self.parent,
|
||||
flags=gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT)
|
||||
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
|
||||
self.dialog.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
|
||||
self.dialog.set_has_separator(True)
|
||||
self.dialog.set_icon(TRYTON_ICON)
|
||||
@@ -444,7 +444,8 @@ class DBLogin(object):
|
||||
self.label_host.set_alignment(1, 0.5)
|
||||
self.label_host.set_padding(3, 3)
|
||||
self.entry_host = gtk.Entry()
|
||||
self.entry_host.connect_after('focus-out-event', self.clear_profile_combo)
|
||||
self.entry_host.connect_after('focus-out-event',
|
||||
self.clear_profile_combo)
|
||||
self.entry_host.set_activates_default(True)
|
||||
self.table_main.attach(self.label_host, 0, 1, 4, 5, xoptions=gtk.FILL)
|
||||
self.table_main.attach(self.entry_host, 1, 3, 4, 5)
|
||||
@@ -453,7 +454,8 @@ class DBLogin(object):
|
||||
self.label_database.set_alignment(1, 0.5)
|
||||
self.label_database.set_padding(3, 3)
|
||||
self.entry_database = gtk.Entry()
|
||||
self.entry_database.connect_after('focus-out-event', self.clear_profile_combo)
|
||||
self.entry_database.connect_after('focus-out-event',
|
||||
self.clear_profile_combo)
|
||||
self.entry_database.set_activates_default(True)
|
||||
self.table_main.attach(self.label_database, 0, 1, 5, 6,
|
||||
xoptions=gtk.FILL)
|
||||
@@ -465,12 +467,12 @@ class DBLogin(object):
|
||||
self.entry_login = gtk.Entry()
|
||||
self.entry_login.set_activates_default(True)
|
||||
self.table_main.attach(self.entry_login, 1, 3, 6, 7)
|
||||
label_password = gtk.Label(str = _("Password:"))
|
||||
label_password = gtk.Label(str=_("Password:"))
|
||||
label_password.set_justify(gtk.JUSTIFY_RIGHT)
|
||||
label_password.set_alignment(1, 0.5)
|
||||
label_password.set_padding(3, 3)
|
||||
self.table_main.attach(label_password, 0, 1, 7, 8, xoptions=gtk.FILL)
|
||||
label_username = gtk.Label(str = _("User name:"))
|
||||
label_username = gtk.Label(str=_("User name:"))
|
||||
label_username.set_alignment(1, 0.5)
|
||||
label_username.set_padding(3, 3)
|
||||
self.table_main.attach(label_username, 0, 1, 6, 7, xoptions=gtk.FILL)
|
||||
@@ -631,13 +633,10 @@ class DBLogin(object):
|
||||
port = self.get_port(netloc)
|
||||
except ValueError:
|
||||
continue
|
||||
if ':' in host:
|
||||
hostname = '[%s]' % host
|
||||
else:
|
||||
hostname = host
|
||||
try:
|
||||
if not common.test_server_version(host, port):
|
||||
common.warning('', _(u'Incompatible version of the server'))
|
||||
common.warning('',
|
||||
_(u'Incompatible version of the server'))
|
||||
continue
|
||||
except Exception, exception:
|
||||
common.process_exception(exception)
|
||||
@@ -660,4 +659,3 @@ class DBLogin(object):
|
||||
Main.get_main().refresh_ssl()
|
||||
raise TrytonError('QueryCanceled')
|
||||
return result
|
||||
|
||||
@@ -5,7 +5,7 @@ import gtk
|
||||
import gobject
|
||||
import gettext
|
||||
import tryton.common as common
|
||||
from tryton.config import CONFIG, TRYTON_ICON
|
||||
from tryton.config import CONFIG
|
||||
import tryton.rpc as rpc
|
||||
|
||||
_ = gettext.gettext
|
||||
@@ -19,10 +19,10 @@ class DBRestore(object):
|
||||
must be filled, then the restore button is set to sensitive. This
|
||||
event method doesn't check the valid of single entrys.
|
||||
"""
|
||||
if self.entry_server_url.get_text() != "" \
|
||||
and self.entry_server_password.get_text() != "" \
|
||||
and self.entry_server_url.get_text() != "" \
|
||||
and self.entry_db_name.get_text() != "":
|
||||
if (self.entry_server_url.get_text() != ""
|
||||
and self.entry_server_password.get_text() != ""
|
||||
and self.entry_server_url.get_text() != ""
|
||||
and self.entry_db_name.get_text() != ""):
|
||||
widget.unset_flags(gtk.HAS_DEFAULT)
|
||||
self.button_restore.set_sensitive(True)
|
||||
self.button_restore.set_flags(gtk.CAN_DEFAULT)
|
||||
@@ -40,7 +40,7 @@ class DBRestore(object):
|
||||
- Allowed characters are alpha-nummeric [A-Za-z0-9] and underscore (_)
|
||||
- First character must be a letter
|
||||
"""
|
||||
if (new_text.isalnum() or new_text == '_' ):
|
||||
if new_text.isalnum() or new_text == '_':
|
||||
_hid = entry.get_data('handlerid')
|
||||
entry.handler_block(_hid)
|
||||
_pos = entry.get_position()
|
||||
@@ -48,6 +48,7 @@ class DBRestore(object):
|
||||
new_text = ""
|
||||
_pos = entry.insert_text(new_text, _pos)
|
||||
entry.handler_unblock(_hid)
|
||||
|
||||
def _move_cursor():
|
||||
with gtk.gdk.lock:
|
||||
entry.set_position(_pos)
|
||||
@@ -158,7 +159,8 @@ class DBRestore(object):
|
||||
img_restore.set_from_stock('tryton-folder-saved-search', \
|
||||
gtk.ICON_SIZE_BUTTON)
|
||||
self.button_restore.set_image(img_restore)
|
||||
self.tooltips.set_tip(self.button_restore, _('Restore the database from file.'))
|
||||
self.tooltips.set_tip(self.button_restore,
|
||||
_('Restore the database from file.'))
|
||||
self.dialog.add_action_widget(self.button_restore, gtk.RESPONSE_OK)
|
||||
self.dialog.set_default_response(gtk.RESPONSE_OK)
|
||||
|
||||
|
||||
@@ -14,9 +14,9 @@ class Email(object):
|
||||
def __init__(self):
|
||||
self.parent = get_toplevel_window()
|
||||
self.win = gtk.Dialog(_('Email'), self.parent,
|
||||
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_OK, gtk.RESPONSE_OK))
|
||||
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK,
|
||||
gtk.RESPONSE_OK))
|
||||
self.win.set_default_response(gtk.RESPONSE_OK)
|
||||
self.win.set_icon(TRYTON_ICON)
|
||||
self.win.set_has_separator(True)
|
||||
@@ -84,7 +84,6 @@ class Email(object):
|
||||
|
||||
self.win.vbox.pack_start(hbox, expand=True, fill=True)
|
||||
|
||||
|
||||
self.win.show_all()
|
||||
|
||||
def run(self):
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
"File actions"
|
||||
import gtk
|
||||
import gettext
|
||||
from tryton.config import TRYTON_ICON, CONFIG
|
||||
from tryton.common import safe_eval, get_toplevel_window
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
class FileActions(object):
|
||||
"File actions window"
|
||||
|
||||
def __init__(self):
|
||||
self.parent= get_toplevel_window()
|
||||
self.win = gtk.Dialog(_('File Actions'), self.parent,
|
||||
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_OK, gtk.RESPONSE_OK))
|
||||
self.win.set_default_response(gtk.RESPONSE_OK)
|
||||
self.win.set_icon(TRYTON_ICON)
|
||||
self.win.set_has_separator(True)
|
||||
self.win.vbox.pack_start(gtk.Label(
|
||||
_('Edit Files Actions')), expand=False, fill=True)
|
||||
self.win.vbox.pack_start(gtk.HSeparator())
|
||||
self.treeview = gtk.TreeView()
|
||||
|
||||
self.model = gtk.ListStore(str, str, str, str)
|
||||
self.treeview.set_model(self.model)
|
||||
|
||||
for index, text in enumerate((_('File Type'), _('Open'), _('Print'))):
|
||||
renderer = gtk.CellRendererText()
|
||||
if index != 0:
|
||||
renderer.set_property('editable', True)
|
||||
renderer.connect('edited', self._sig_edited)
|
||||
column = gtk.TreeViewColumn(text, renderer, text=index + 1)
|
||||
column.set_resizable(True)
|
||||
self.treeview.append_column(column)
|
||||
|
||||
i = 1
|
||||
if isinstance(CONFIG['client.actions'], basestring):
|
||||
CONFIG['client.actions'] = safe_eval(CONFIG['client.actions'])
|
||||
extensions = CONFIG['client.actions'].keys()
|
||||
extensions.sort()
|
||||
for extension in extensions:
|
||||
iter = self.model.append()
|
||||
self.model.set(iter, 0, extension,
|
||||
1, _('%s file') % extension.upper(),
|
||||
2, CONFIG['client.actions'][extension][0],
|
||||
3, CONFIG['client.actions'][extension][1])
|
||||
|
||||
scroll = gtk.ScrolledWindow()
|
||||
scroll.add(self.treeview)
|
||||
scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
|
||||
viewport= gtk.Viewport()
|
||||
viewport.set_shadow_type(gtk.SHADOW_ETCHED_IN)
|
||||
viewport.set_size_request(400, 200)
|
||||
viewport.add(scroll)
|
||||
self.win.vbox.pack_start(viewport, expand=True, fill=True)
|
||||
|
||||
label = gtk.Label(_('Use "%s" as a placeholder for the file name'))
|
||||
label.set_alignment(0.0, 0.5)
|
||||
label.set_padding(0, 10)
|
||||
self.win.vbox.pack_start(label, expand=False, fill=True)
|
||||
self.win.show_all()
|
||||
|
||||
def run(self):
|
||||
"Run the window"
|
||||
res = self.win.run()
|
||||
if res == gtk.RESPONSE_OK:
|
||||
config = {}
|
||||
for extension, _, cmd_open, cmd_print in self.model:
|
||||
config[extension] = {
|
||||
0: cmd_open,
|
||||
1: cmd_print,
|
||||
}
|
||||
CONFIG['client.actions'] = config
|
||||
CONFIG.save()
|
||||
self.parent.present()
|
||||
self.win.destroy()
|
||||
return res
|
||||
|
||||
def _sig_edited(self, cell, path, new_text):
|
||||
iter = self.model.get_iter_from_string(path)
|
||||
(path, column) = self.treeview.get_cursor()
|
||||
column_id = self.treeview.get_columns().index(column)
|
||||
self.model.set(iter, column_id + 1, new_text)
|
||||
@@ -8,10 +8,8 @@ import locale
|
||||
import tryton.rpc as rpc
|
||||
from tryton.gui.window.view_form.screen import Screen
|
||||
from tryton.action import Action
|
||||
from tryton.config import CONFIG
|
||||
from tryton.gui import Main
|
||||
from tryton.gui.window import Window
|
||||
from tryton.gui.window.preference import Preference
|
||||
from tryton.gui.window.win_export import WinExport
|
||||
from tryton.gui.window.win_import import WinImport
|
||||
from tryton.gui.window.attachment import Attachment
|
||||
@@ -21,7 +19,7 @@ from tryton.common import (TRYTON_ICON, message, sur, sur_3b, COLOR_SCHEMES,
|
||||
from tryton.exceptions import TrytonServerError
|
||||
import tryton.common as common
|
||||
from tryton.translate import date_format
|
||||
from tryton.common import DT_FORMAT, DHM_FORMAT, HM_FORMAT
|
||||
from tryton.common import HM_FORMAT
|
||||
from tryton.pyson import PYSONEncoder
|
||||
|
||||
from tabcontent import TabContent
|
||||
@@ -183,9 +181,9 @@ class Form(SignalEvent, TabContent):
|
||||
return
|
||||
parent = common.get_toplevel_window()
|
||||
win = gtk.Dialog(_('Go to ID'), parent,
|
||||
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_OK, gtk.RESPONSE_OK))
|
||||
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK,
|
||||
gtk.RESPONSE_OK))
|
||||
win.set_icon(TRYTON_ICON)
|
||||
win.set_has_separator(True)
|
||||
win.set_default_response(gtk.RESPONSE_OK)
|
||||
@@ -200,6 +198,7 @@ class Form(SignalEvent, TabContent):
|
||||
entry.set_property('activates_default', True)
|
||||
entry.set_max_length(0)
|
||||
entry.set_alignment(1.0)
|
||||
|
||||
def sig_insert_text(widget, new_text, new_text_length, position):
|
||||
value = widget.get_text()
|
||||
position = widget.get_position()
|
||||
@@ -240,7 +239,9 @@ class Form(SignalEvent, TabContent):
|
||||
def sig_attach(self, widget=None):
|
||||
obj_id = self.id_get()
|
||||
if obj_id >= 0 and obj_id is not False:
|
||||
Attachment(self.model, obj_id).run()
|
||||
def callback():
|
||||
self.update_attachment_count(reload=True)
|
||||
Attachment(self.model, obj_id, callback)
|
||||
else:
|
||||
self.message_info(_('No record selected!'))
|
||||
self.update_attachment_count(reload=True)
|
||||
@@ -300,7 +301,7 @@ class Form(SignalEvent, TabContent):
|
||||
display_format = date_format() + ' ' + HM_FORMAT
|
||||
date = timezoned_date(line[key])
|
||||
value = common.datetime_strftime(date, display_format)
|
||||
message_str += val + ' ' + value +'\n'
|
||||
message_str += val + ' ' + value + '\n'
|
||||
message_str += _('Model:') + ' ' + self.model
|
||||
message(message_str)
|
||||
return True
|
||||
@@ -482,6 +483,7 @@ class Form(SignalEvent, TabContent):
|
||||
self.activate_save()
|
||||
|
||||
def modified_save(self, reload=True):
|
||||
self.screen.current_view.set_value()
|
||||
if self.screen.modified():
|
||||
value = sur_3b(
|
||||
_('This record has been modified\n' \
|
||||
|
||||
@@ -15,9 +15,9 @@ class Limit(object):
|
||||
def __init__(self):
|
||||
self.parent = get_toplevel_window()
|
||||
self.win = gtk.Dialog(_('Limit'), self.parent,
|
||||
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_OK, gtk.RESPONSE_OK))
|
||||
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK,
|
||||
gtk.RESPONSE_OK))
|
||||
self.win.set_default_response(gtk.RESPONSE_OK)
|
||||
self.win.set_icon(TRYTON_ICON)
|
||||
self.win.set_has_separator(True)
|
||||
@@ -29,8 +29,9 @@ class Limit(object):
|
||||
hbox.pack_start(gtk.Label(_('Limit:')), expand=True, fill=True)
|
||||
self.spin_limit = gtk.SpinButton(climb_rate=1, digits=0)
|
||||
self.spin_limit.set_numeric(False)
|
||||
self.spin_limit.set_adjustment(gtk.Adjustment(value=CONFIG['client.limit'],
|
||||
lower=1, upper=sys.maxint, step_incr=10, page_incr=100))
|
||||
self.spin_limit.set_adjustment(gtk.Adjustment(
|
||||
value=CONFIG['client.limit'], lower=1, upper=sys.maxint,
|
||||
step_incr=10, page_incr=100))
|
||||
hbox.pack_start(self.spin_limit, expand=True, fill=True)
|
||||
self.win.vbox.pack_start(hbox, expand=True, fill=True)
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class Preference(object):
|
||||
def __init__(self, user):
|
||||
self.parent = common.get_toplevel_window()
|
||||
self.win = gtk.Dialog(_('Preferences'), self.parent,
|
||||
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT)
|
||||
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
|
||||
self.win.set_position(gtk.WIN_POS_CENTER_ON_PARENT)
|
||||
self.win.set_has_separator(False)
|
||||
self.win.set_icon(TRYTON_ICON)
|
||||
|
||||
@@ -91,7 +91,7 @@ Examples:
|
||||
label.set_alignment(0, 0.5)
|
||||
table.attach(label, 1, 2, i, i + 1,
|
||||
yoptions=False, xoptions=gtk.FILL)
|
||||
i +=1
|
||||
i += 1
|
||||
return table
|
||||
|
||||
def run(self):
|
||||
|
||||
@@ -18,7 +18,8 @@ class Tips(object):
|
||||
|
||||
|
||||
'''),
|
||||
_(u'''<b>Do you know Triton, one of the namesakes for our project?</b>
|
||||
_(
|
||||
u'''<b>Do you know Triton, one of the namesakes for our project?</b>
|
||||
|
||||
Triton (pronounced /ˈtraɪtən/ TRYE-tən, or as in Greek Τρίτων) is the
|
||||
largest moon of the planet Neptune, discovered on October 10, 1846
|
||||
@@ -41,7 +42,6 @@ You can save any graphs in PNG file with right-click on it.
|
||||
'''),
|
||||
]
|
||||
|
||||
|
||||
parent = get_toplevel_window()
|
||||
self.win = gtk.Dialog(_('Tips'), parent,
|
||||
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
|
||||
@@ -118,5 +118,3 @@ You can save any graphs in PNG file with right-click on it.
|
||||
def tip_previous(self, widget):
|
||||
self.number -= 1
|
||||
self.tip_set()
|
||||
|
||||
|
||||
@@ -4,14 +4,9 @@
|
||||
import gtk
|
||||
from tryton.gui.window.view_form.screen import Screen
|
||||
import tryton.rpc as rpc
|
||||
import time
|
||||
import datetime
|
||||
from tryton.gui.window.win_search import WinSearch
|
||||
from tryton.action import Action as Action2
|
||||
import tryton.common as common
|
||||
from tryton.pyson import PYSONDecoder
|
||||
import gettext
|
||||
from tryton.config import CONFIG
|
||||
from tryton.signal_event import SignalEvent
|
||||
from tryton.exceptions import TrytonServerError
|
||||
from tryton.gui.window.win_form import WinForm
|
||||
@@ -89,7 +84,7 @@ class Action(SignalEvent):
|
||||
else:
|
||||
self.title.set_text(name)
|
||||
|
||||
self.widget.set_size_request(int(attrs.get('width',-1)),
|
||||
self.widget.set_size_request(int(attrs.get('width', -1)),
|
||||
int(attrs.get('height', -1)))
|
||||
|
||||
self.screen.search_filter()
|
||||
@@ -97,6 +92,7 @@ class Action(SignalEvent):
|
||||
def row_activate(self):
|
||||
if not self.screen.current_record:
|
||||
pass
|
||||
|
||||
def callback(result):
|
||||
if result:
|
||||
self.screen.current_record.save()
|
||||
@@ -133,5 +129,5 @@ class Action(SignalEvent):
|
||||
return
|
||||
del self.domain[:]
|
||||
self.domain.extend(new_domain)
|
||||
if hasattr(self, 'screen'): # Catch early update
|
||||
if hasattr(self, 'screen'): # Catch early update
|
||||
self.display()
|
||||
@@ -2,10 +2,13 @@
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
'Parser'
|
||||
import gtk
|
||||
from tryton.gui.window.view_form.view.form_gtk.parser import _container
|
||||
import gettext
|
||||
from tryton.gui.window.view_form.view.form_gtk.parser import _container, VBox
|
||||
import tryton.common as common
|
||||
from action import Action
|
||||
from tryton.config import CONFIG, TRYTON_ICON
|
||||
from tryton.config import CONFIG
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
class ParserBoard(object):
|
||||
@@ -47,13 +50,6 @@ class ParserBoard(object):
|
||||
xexpand=xexpand, xfill=xfill)
|
||||
elif node.localName == 'separator':
|
||||
text = attrs.get('string', '')
|
||||
if 'string' in attrs or 'name' in attrs:
|
||||
if not text:
|
||||
if 'name' in attrs and attrs['name'] in fields:
|
||||
if 'states' in fields[attrs['name']]:
|
||||
attrs['states'] = \
|
||||
fields[attrs['name']]['states']
|
||||
text = fields[attrs['name']]['string']
|
||||
vbox = VBox(attrs=attrs)
|
||||
if text:
|
||||
label = gtk.Label(text)
|
||||
@@ -68,22 +64,6 @@ class ParserBoard(object):
|
||||
xexpand=xexpand, xfill=xfill)
|
||||
elif node.localName == 'label':
|
||||
text = attrs.get('string', '')
|
||||
if not text:
|
||||
if 'name' in attrs and attrs['name'] in fields:
|
||||
if 'states' in fields[attrs['name']]:
|
||||
attrs['states'] = fields[attrs['name']]['states']
|
||||
if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL:
|
||||
text = _(':') + fields[attrs['name']]['string']
|
||||
else:
|
||||
text = fields[attrs['name']]['string'] + _(':')
|
||||
if 'align' not in attrs:
|
||||
attrs['align'] = 1.0
|
||||
else:
|
||||
for node in node.childNodes:
|
||||
if node.nodeType == node.TEXT_NODE:
|
||||
text += node.data
|
||||
else:
|
||||
text += node.toxml()
|
||||
if not text:
|
||||
container.empty_add(int(attrs.get('colspan', 1)))
|
||||
continue
|
||||
@@ -115,7 +95,8 @@ class ParserBoard(object):
|
||||
container.wid_add(notebook,
|
||||
colspan=int(attrs.get('colspan', 4)),
|
||||
yexpand=True, yfill=True)
|
||||
widget, new_widgets = self.parse(node, notebook, tooltips=tooltips)
|
||||
widget, new_widgets = self.parse(node, notebook,
|
||||
tooltips=tooltips)
|
||||
widgets += new_widgets
|
||||
elif node.localName == 'page':
|
||||
if CONFIG['client.form_tab'] == 'left':
|
||||
@@ -124,9 +105,10 @@ class ParserBoard(object):
|
||||
angle = -90
|
||||
else:
|
||||
angle = 0
|
||||
label = gtk.Label(attrs.get('string','No String Attr.'))
|
||||
label = gtk.Label(attrs.get('string', _('No String Attr.')))
|
||||
label.set_angle(angle)
|
||||
widget, new_widgets = self.parse(node, notebook, tooltips=tooltips)
|
||||
widget, new_widgets = self.parse(node, notebook,
|
||||
tooltips=tooltips)
|
||||
widgets += new_widgets
|
||||
notebook.append_page(widget, label)
|
||||
elif node.localName == 'group':
|
||||
@@ -145,7 +127,8 @@ class ParserBoard(object):
|
||||
hpaned = gtk.HPaned()
|
||||
container.wid_add(hpaned, colspan=int(attrs.get('colspan', 4)),
|
||||
yexpand=True, yfill=True)
|
||||
widget, new_widgets = self.parse(node, paned=hpaned, tooltips=tooltips)
|
||||
widget, new_widgets = self.parse(node, paned=hpaned,
|
||||
tooltips=tooltips)
|
||||
widgets += new_widgets
|
||||
if 'position' in attrs:
|
||||
hpaned.set_position(int(attrs['position']))
|
||||
@@ -153,19 +136,20 @@ class ParserBoard(object):
|
||||
vpaned = gtk.VPaned()
|
||||
container.wid_add(vpaned, colspan=int(attrs.get('colspan', 4)),
|
||||
yexpand=True, yfill=True)
|
||||
widget, new_widgets = self.parse(node, paned=vpaned, tooltips=tooltips)
|
||||
widget, new_widgets = self.parse(node, paned=vpaned,
|
||||
tooltips=tooltips)
|
||||
widgets += new_widgets
|
||||
if 'position' in attrs:
|
||||
vpaned.set_position(int(attrs['position']))
|
||||
elif node.localName == 'child':
|
||||
widget, new_widgets = self.parse(node, paned=paned, tooltips=tooltips)
|
||||
widget, new_widgets = self.parse(node, paned=paned,
|
||||
tooltips=tooltips)
|
||||
widgets += new_widgets
|
||||
if not paned.get_child1():
|
||||
paned.pack1(widget, resize=True, shrink=True)
|
||||
elif not paned.get_child2():
|
||||
paned.pack2(widget, resize=True, shrink=True)
|
||||
elif node.localName == 'action':
|
||||
name = str(attrs['name'])
|
||||
widget_act = Action(attrs, self.context)
|
||||
widgets.append(widget_act)
|
||||
yexpand = bool(attrs.get('yexpand', 1))
|
||||
|
||||
@@ -6,6 +6,7 @@ import xml.dom.minidom
|
||||
from parser import ParserBoard
|
||||
from tryton.gui.window.view_board.action import Action
|
||||
|
||||
|
||||
class ViewBoard(object):
|
||||
'View board'
|
||||
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
import os
|
||||
import tempfile
|
||||
import tryton.rpc as rpc
|
||||
from tryton.common import DT_FORMAT, DHM_FORMAT, HM_FORMAT, datetime_strftime, \
|
||||
from tryton.common import DT_FORMAT, DHM_FORMAT, datetime_strftime, \
|
||||
domain_inversion, eval_domain, localize_domain, unlocalize_domain, \
|
||||
merge, inverse_leaf, EvalEnvironment, RPCProgress
|
||||
import tryton.common as common
|
||||
import time
|
||||
import datetime
|
||||
from decimal import Decimal
|
||||
import logging
|
||||
from tryton.exceptions import TrytonServerError
|
||||
|
||||
|
||||
@@ -60,8 +59,8 @@ class CharField(object):
|
||||
def validation_domains(self, record):
|
||||
screen_domain, attr_domain = self.domains_get(record)
|
||||
if attr_domain:
|
||||
return screen_domain, screen_domain + unlocalize_domain(attr_domain,
|
||||
self.name)
|
||||
return (screen_domain, screen_domain +
|
||||
unlocalize_domain(attr_domain, self.name))
|
||||
else:
|
||||
return screen_domain, screen_domain
|
||||
|
||||
@@ -91,12 +90,13 @@ class CharField(object):
|
||||
elif domain == [('id', '=', False)]:
|
||||
res = False
|
||||
else:
|
||||
if (isinstance(inverted_domain, list) \
|
||||
and len(inverted_domain) == 1 and inverted_domain[0][1] == '='):
|
||||
# If the inverted domain is so constraint that only one value is
|
||||
# possible we should use it. But we must also pay attention to
|
||||
# the fact that the original domain might be a 'OR' domain and
|
||||
# thus not preventing the modification of fields.
|
||||
if (isinstance(inverted_domain, list)
|
||||
and len(inverted_domain) == 1
|
||||
and inverted_domain[0][1] == '='):
|
||||
# If the inverted domain is so constraint that only one value
|
||||
# is possible we should use it. But we must also pay attention
|
||||
# to the fact that the original domain might be a 'OR' domain
|
||||
# and thus not preventing the modification of fields.
|
||||
leftpart, _, value = inverted_domain[0][:3]
|
||||
setdefault = True
|
||||
if '.' in leftpart:
|
||||
@@ -135,9 +135,9 @@ class CharField(object):
|
||||
|
||||
def set_client(self, record, value, force_change=False):
|
||||
internal = record.value.get(self.name, False)
|
||||
prev_modified_fields = record.modified_fields.copy()
|
||||
self.set(record, value)
|
||||
if (internal or False) != (record.value.get(self.name, False) or False):
|
||||
if ((internal or False)
|
||||
!= (record.value.get(self.name, False) or False)):
|
||||
record.modified_fields.setdefault(self.name)
|
||||
record.signal('record-modified')
|
||||
self.sig_changed(record)
|
||||
@@ -230,11 +230,10 @@ class FloatField(CharField):
|
||||
|
||||
def set_client(self, record, value, force_change=False):
|
||||
internal = record.value.get(self.name)
|
||||
prev_modified_fields = record.modified_fields.copy()
|
||||
self.set(record, value)
|
||||
digits = record.expr_eval(self.attrs.get('digits', (16, 2)))
|
||||
if abs(float(internal or 0.0) - float(record.value[self.name] or 0.0)) \
|
||||
>= (10.0**(-int(digits[1]))):
|
||||
if (abs(float(internal or 0.0) - float(record.value[self.name] or 0.0))
|
||||
>= (10.0 ** (-int(digits[1])))):
|
||||
if not self.get_state_attrs(record).get('readonly', False):
|
||||
record.modified_fields.setdefault(self.name)
|
||||
record.signal('record-modified')
|
||||
@@ -248,12 +247,11 @@ class NumericField(CharField):
|
||||
def set_client(self, record, value, force_change=False):
|
||||
value = Decimal(str(value))
|
||||
internal = record.value.get(self.name)
|
||||
prev_modified_fields = record.modified_fields.copy()
|
||||
self.set(record, value)
|
||||
digits = record.expr_eval(self.attrs.get('digits', (16, 2)))
|
||||
if abs((internal or Decimal('0.0')) - \
|
||||
(record.value[self.name] or Decimal('0.0'))) \
|
||||
>= Decimal(str(10.0**(-int(digits[1])))):
|
||||
if (abs((internal or Decimal('0.0'))
|
||||
- (record.value[self.name] or Decimal('0.0')))
|
||||
>= Decimal(str(10.0 ** (-int(digits[1]))))):
|
||||
if not self.get_state_attrs(record).get('readonly', False):
|
||||
record.modified_fields.setdefault(self.name)
|
||||
record.signal('record-modified')
|
||||
@@ -270,12 +268,12 @@ class IntegerField(CharField):
|
||||
def get_client(self, record):
|
||||
return record.value.get(self.name) or 0
|
||||
|
||||
|
||||
class BooleanField(CharField):
|
||||
|
||||
def set_client(self, record, value, force_change=False):
|
||||
value = bool(value)
|
||||
internal = bool(record.value.get(self.name, False))
|
||||
prev_modified_fields = record.modified_fields.copy()
|
||||
self.set(record, value)
|
||||
if internal != bool(record.value.get(self.name, False)):
|
||||
record.modified_fields.setdefault(self.name)
|
||||
@@ -367,7 +365,6 @@ class M2OField(CharField):
|
||||
|
||||
def set_client(self, record, value, force_change=False):
|
||||
internal = record.value.get(self.name) or (False, '')
|
||||
prev_modified_fields = record.modified_fields.copy()
|
||||
self.set(record, value)
|
||||
if (internal[0] or False) != (record.value[self.name][0] or False):
|
||||
record.modified_fields.setdefault(self.name)
|
||||
@@ -394,7 +391,8 @@ class M2OField(CharField):
|
||||
|
||||
def domain_get(self, record):
|
||||
screen_domain, attr_domain = self.domains_get(record)
|
||||
return localize_domain(inverse_leaf(screen_domain), self.name) + attr_domain
|
||||
return (localize_domain(inverse_leaf(screen_domain), self.name)
|
||||
+ attr_domain)
|
||||
|
||||
def get_state_attrs(self, record):
|
||||
result = super(M2OField, self).get_state_attrs(record)
|
||||
@@ -404,6 +402,7 @@ class M2OField(CharField):
|
||||
result['readonly'] = True
|
||||
return result
|
||||
|
||||
|
||||
class O2OField(M2OField):
|
||||
pass
|
||||
|
||||
@@ -453,7 +452,8 @@ class O2MField(CharField):
|
||||
if record.model_name == self.attrs['relation']:
|
||||
group.fields = record.group.fields
|
||||
group.signal_connect(group, 'group-changed', self._group_changed)
|
||||
group.signal_connect(group, 'group-list-changed', self._group_list_changed)
|
||||
group.signal_connect(group, 'group-list-changed',
|
||||
self._group_list_changed)
|
||||
group.signal_connect(group, 'group-cleared', self._group_cleared)
|
||||
record.value[self.name] = group
|
||||
|
||||
@@ -493,10 +493,10 @@ class O2MField(CharField):
|
||||
if record.value.get(self.name) is None:
|
||||
return {}
|
||||
result = {}
|
||||
for record2 in (record.value[self.name] \
|
||||
+ record.value[self.name].record_removed \
|
||||
for record2 in (record.value[self.name]
|
||||
+ record.value[self.name].record_removed
|
||||
+ record.value[self.name].record_deleted):
|
||||
result.update(record2.get_timestamp())
|
||||
result.update(record2.get_timestamp())
|
||||
return result
|
||||
|
||||
def get_eval(self, record, check_load=True):
|
||||
@@ -530,7 +530,8 @@ class O2MField(CharField):
|
||||
record.value[self.name] = group
|
||||
group.load(value, display=False)
|
||||
group.signal_connect(group, 'group-changed', self._group_changed)
|
||||
group.signal_connect(group, 'group-list-changed', self._group_list_changed)
|
||||
group.signal_connect(group, 'group-list-changed',
|
||||
self._group_list_changed)
|
||||
group.signal_connect(group, 'group-cleared', self._group_cleared)
|
||||
if modified:
|
||||
record.modified_fields.setdefault(self.name)
|
||||
@@ -591,7 +592,8 @@ class O2MField(CharField):
|
||||
new_record.set_default(vals, modified=modified)
|
||||
group.add(new_record)
|
||||
group.signal_connect(group, 'group-changed', self._group_changed)
|
||||
group.signal_connect(group, 'group-list-changed', self._group_list_changed)
|
||||
group.signal_connect(group, 'group-list-changed',
|
||||
self._group_list_changed)
|
||||
group.signal_connect(group, 'group-cleared', self._group_cleared)
|
||||
return True
|
||||
|
||||
@@ -717,7 +719,8 @@ class M2MField(O2MField):
|
||||
group.fields = fields
|
||||
group.load(value, display=False)
|
||||
group.signal_connect(group, 'group-changed', self._group_changed)
|
||||
group.signal_connect(group, 'group-list-changed', self._group_list_changed)
|
||||
group.signal_connect(group, 'group-list-changed',
|
||||
self._group_list_changed)
|
||||
group.signal_connect(group, 'group-cleared', self._group_cleared)
|
||||
if modified:
|
||||
record.modified_fields.setdefault(self.name)
|
||||
@@ -744,7 +747,6 @@ class ReferenceField(CharField):
|
||||
|
||||
def set_client(self, record, value, force_change=False):
|
||||
internal = record.value.get(self.name)
|
||||
prev_modified_fields = record.modified_fields.copy()
|
||||
self.set(record, value)
|
||||
if (internal or False) != (record.value[self.name] or False):
|
||||
record.modified_fields.setdefault(self.name)
|
||||
@@ -792,6 +794,7 @@ class ReferenceField(CharField):
|
||||
record.modified_fields.setdefault(self.name)
|
||||
record.signal('record-modified')
|
||||
|
||||
|
||||
class BinaryField(CharField):
|
||||
|
||||
def get(self, record, check_load=True, readonly=True, modified=False):
|
||||
@@ -849,14 +852,14 @@ TYPES = {
|
||||
'char': CharField,
|
||||
'sha': CharField,
|
||||
'float_time': FloatField,
|
||||
'integer' : IntegerField,
|
||||
'biginteger' : IntegerField,
|
||||
'float' : FloatField,
|
||||
'numeric' : NumericField,
|
||||
'many2one' : M2OField,
|
||||
'many2many' : M2MField,
|
||||
'one2many' : O2MField,
|
||||
'reference' : ReferenceField,
|
||||
'integer': IntegerField,
|
||||
'biginteger': IntegerField,
|
||||
'float': FloatField,
|
||||
'numeric': NumericField,
|
||||
'many2one': M2OField,
|
||||
'many2many': M2MField,
|
||||
'one2many': O2MField,
|
||||
'reference': ReferenceField,
|
||||
'selection': SelectionField,
|
||||
'boolean': BooleanField,
|
||||
'datetime': DateTimeField,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#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 copy
|
||||
import tryton.rpc as rpc
|
||||
from record import Record
|
||||
from field import Field, O2MField
|
||||
@@ -236,8 +235,8 @@ class Group(SignalEvent, list):
|
||||
ctx = rpc.CONTEXT.copy()
|
||||
ctx.update(self._context)
|
||||
if self.parent_datetime_field:
|
||||
ctx['_datetime'] = self.parent.get_eval(check_load=False)\
|
||||
[self.parent_datetime_field]
|
||||
ctx['_datetime'] = self.parent.get_eval(check_load=False
|
||||
)[self.parent_datetime_field]
|
||||
return ctx
|
||||
|
||||
context = property(_get_context)
|
||||
@@ -247,7 +246,8 @@ class Group(SignalEvent, list):
|
||||
record.signal_unconnect(record.group)
|
||||
record.group = self
|
||||
record.signal_connect(self, 'record-changed', self._record_changed)
|
||||
record.signal_connect(self, 'record-modified', self._record_modified)
|
||||
record.signal_connect(self, 'record-modified',
|
||||
self._record_modified)
|
||||
if position == -1:
|
||||
self.append(record)
|
||||
else:
|
||||
@@ -373,7 +373,8 @@ class Group(SignalEvent, list):
|
||||
|
||||
if len(new) and len(to_add):
|
||||
ctx.update(self.context)
|
||||
args = ('model', self.model_name, 'default_get', to_add.keys(), ctx)
|
||||
args = ('model', self.model_name, 'default_get', to_add.keys(),
|
||||
ctx)
|
||||
try:
|
||||
values = rpc.execute(*args)
|
||||
except TrytonServerError, exception:
|
||||
|
||||
@@ -4,10 +4,7 @@ import tryton.rpc as rpc
|
||||
from tryton.signal_event import SignalEvent
|
||||
import tryton.common as common
|
||||
from tryton.pyson import PYSONDecoder
|
||||
import field
|
||||
import datetime
|
||||
import logging
|
||||
import time
|
||||
import field as fields
|
||||
from functools import reduce
|
||||
from tryton.exceptions import TrytonServerError
|
||||
|
||||
@@ -33,14 +30,14 @@ class Record(SignalEvent):
|
||||
self.modified_fields = {}
|
||||
self._timestamp = None
|
||||
self.attachment_count = -1
|
||||
self.next = {} # Used in Group list
|
||||
self.next = {} # Used in Group list
|
||||
self.value = {}
|
||||
self.autocompletion = {}
|
||||
self.exception = False
|
||||
|
||||
def __getitem__(self, name, raise_exception=False):
|
||||
if name not in self._loaded and self.id >= 0:
|
||||
ids = [self.id]
|
||||
ids = [self.id]
|
||||
if name == '*':
|
||||
loading = reduce(
|
||||
lambda x, y: 'eager' if x == y == 'eager' else 'lazy',
|
||||
@@ -65,21 +62,22 @@ class Record(SignalEvent):
|
||||
ids.append(record.id)
|
||||
n += 1
|
||||
if loading == 'eager':
|
||||
fields = [fname for fname, field in self.group.fields.iteritems()
|
||||
if field.attrs.get('loading', 'eager') == 'eager']
|
||||
fnames = [fname
|
||||
for fname, field in self.group.fields.iteritems()
|
||||
if field.attrs.get('loading', 'eager') == 'eager']
|
||||
else:
|
||||
fields = self.group.fields.keys()
|
||||
fields = [fname for fname in fields if fname not in self._loaded]
|
||||
fields.extend(('%s.rec_name' % fname for fname in fields[:]
|
||||
fnames = self.group.fields.keys()
|
||||
fnames = [fname for fname in fnames if fname not in self._loaded]
|
||||
fnames.extend(('%s.rec_name' % fname for fname in fnames[:]
|
||||
if self.group.fields[fname].attrs['type']
|
||||
in ('many2one', 'one2one', 'reference')))
|
||||
fields.append('_timestamp')
|
||||
fnames.append('_timestamp')
|
||||
ctx = rpc.CONTEXT.copy()
|
||||
ctx.update(self.context_get())
|
||||
ctx.update(dict(('%s.%s' % (self.model_name, fname), 'size')
|
||||
for fname, field in self.group.fields.iteritems()
|
||||
if field.attrs['type'] == 'binary'))
|
||||
args = ('model', self.model_name, 'read', ids, fields, ctx)
|
||||
args = ('model', self.model_name, 'read', ids, fnames, ctx)
|
||||
exception = None
|
||||
try:
|
||||
values = rpc.execute(*args)
|
||||
@@ -89,7 +87,7 @@ class Record(SignalEvent):
|
||||
values = common.process_exception(exception, *args)
|
||||
if not values:
|
||||
values = [{'id': x} for x in ids]
|
||||
default_values = dict((f, False) for f in fields)
|
||||
default_values = dict((f, False) for f in fnames)
|
||||
for value in values:
|
||||
value.update(default_values)
|
||||
self.exception = True
|
||||
@@ -391,10 +389,11 @@ class Record(SignalEvent):
|
||||
for fieldname, value in val.items():
|
||||
if fieldname not in self.group.fields:
|
||||
continue
|
||||
if isinstance(self.group.fields[fieldname], field.M2OField):
|
||||
if isinstance(self.group.fields[fieldname], fields.M2OField):
|
||||
if fieldname + '.rec_name' in val:
|
||||
value = (value, val[fieldname + '.rec_name'])
|
||||
elif isinstance(self.group.fields[fieldname], field.ReferenceField):
|
||||
elif isinstance(self.group.fields[fieldname],
|
||||
fields.ReferenceField):
|
||||
if value:
|
||||
ref_model, ref_id = value.split(',', 1)
|
||||
if fieldname + '.rec_name' in val:
|
||||
@@ -417,13 +416,14 @@ class Record(SignalEvent):
|
||||
continue
|
||||
if fieldname not in self.group.fields:
|
||||
continue
|
||||
if isinstance(self.group.fields[fieldname], field.O2MField):
|
||||
if isinstance(self.group.fields[fieldname], fields.O2MField):
|
||||
later[fieldname] = value
|
||||
continue
|
||||
if isinstance(self.group.fields[fieldname], field.M2OField):
|
||||
if isinstance(self.group.fields[fieldname], fields.M2OField):
|
||||
if fieldname + '.rec_name' in val:
|
||||
value = (value, val[fieldname + '.rec_name'])
|
||||
elif isinstance(self.group.fields[fieldname], field.ReferenceField):
|
||||
elif isinstance(self.group.fields[fieldname],
|
||||
fields.ReferenceField):
|
||||
if value:
|
||||
ref_model, ref_id = value.split(',', 1)
|
||||
if fieldname + '.rec_name' in val:
|
||||
@@ -501,14 +501,14 @@ class Record(SignalEvent):
|
||||
for fieldname, value in res.items():
|
||||
if fieldname not in self.group.fields:
|
||||
continue
|
||||
if isinstance(self.group.fields[fieldname], field.O2MField):
|
||||
if isinstance(self.group.fields[fieldname], fields.O2MField):
|
||||
later[fieldname] = value
|
||||
continue
|
||||
if isinstance(self.group.fields[fieldname], field.M2OField):
|
||||
if isinstance(self.group.fields[fieldname], fields.M2OField):
|
||||
if fieldname + '.rec_name' in res:
|
||||
value = (value, res[fieldname + '.rec_name'])
|
||||
elif isinstance(self.group.fields[fieldname],
|
||||
field.ReferenceField):
|
||||
fields.ReferenceField):
|
||||
if value:
|
||||
ref_model, ref_id = value.split(',', 1)
|
||||
if fieldname + '.rec_name' in res:
|
||||
@@ -602,5 +602,8 @@ class Record(SignalEvent):
|
||||
def destroy(self):
|
||||
super(Record, self).destroy()
|
||||
self.group = None
|
||||
for v in self.value.itervalues():
|
||||
if hasattr(v, 'destroy'):
|
||||
v.destroy()
|
||||
self.value = None
|
||||
self.next = None
|
||||
@@ -6,7 +6,6 @@ try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
import json
|
||||
import copy
|
||||
import collections
|
||||
import xml.dom.minidom
|
||||
import tryton.rpc as rpc
|
||||
@@ -14,7 +13,6 @@ from tryton.gui.window.view_form.model.group import Group
|
||||
from tryton.gui.window.view_form.model.record import Record
|
||||
from tryton.gui.window.view_form.view.screen_container import ScreenContainer
|
||||
from tryton.signal_event import SignalEvent
|
||||
from tryton.common import node_attributes
|
||||
from tryton.config import CONFIG
|
||||
import tryton.common as common
|
||||
from tryton.exceptions import TrytonServerError, TrytonServerUnavailable
|
||||
@@ -368,13 +366,18 @@ class Screen(SignalEvent):
|
||||
self.fields_view_tree = view
|
||||
break
|
||||
|
||||
# Ensure that loading is always eager for fields on tree view
|
||||
# and always lazy for fields only on form view
|
||||
if node.localName == 'tree':
|
||||
loading = 'eager'
|
||||
else:
|
||||
loading = 'lazy'
|
||||
for field in fields:
|
||||
if field not in self.group.fields:
|
||||
if field not in self.group.fields or loading == 'eager':
|
||||
fields[field]['loading'] = loading
|
||||
else:
|
||||
fields[field]['loading'] = \
|
||||
self.group.fields[field].attrs['loading']
|
||||
|
||||
children_field = view.get('field_childs')
|
||||
|
||||
@@ -436,9 +439,6 @@ class Screen(SignalEvent):
|
||||
record = group.new(default, self.domain, ctx)
|
||||
group.add(record, self.new_model_position())
|
||||
self.current_record = record
|
||||
fields = None
|
||||
if self.current_view:
|
||||
fields = self.current_view.get_fields()
|
||||
self.display()
|
||||
self.set_cursor(new=True)
|
||||
self.request_set()
|
||||
@@ -505,7 +505,8 @@ class Screen(SignalEvent):
|
||||
elif current_view.view_type == 'tree' and not self.current_record:
|
||||
# The widget might have been destroyed
|
||||
if self.screen_container:
|
||||
self.screen_container.set_cursor(new=new, reset_view=reset_view)
|
||||
self.screen_container.set_cursor(new=new,
|
||||
reset_view=reset_view)
|
||||
elif current_view.view_type in ('tree', 'form'):
|
||||
current_view.set_cursor(new=new, reset_view=reset_view)
|
||||
|
||||
@@ -528,7 +529,8 @@ class Screen(SignalEvent):
|
||||
res = False
|
||||
if self.current_view.view_type != 'tree':
|
||||
if self.current_record:
|
||||
res = self.current_record.modified or self.current_record.id < 0
|
||||
res = (self.current_record.modified
|
||||
or self.current_record.id < 0)
|
||||
else:
|
||||
for record in self.group:
|
||||
if record.modified or record.id < 0:
|
||||
|
||||
@@ -4,11 +4,9 @@ import operator
|
||||
from functools import reduce
|
||||
import gtk
|
||||
import gettext
|
||||
from tryton.common import message, TRYTON_ICON
|
||||
import tryton.rpc as rpc
|
||||
import tryton.common as common
|
||||
from interface import ParserView
|
||||
from tryton.config import CONFIG
|
||||
from tryton.exceptions import TrytonServerError
|
||||
from tryton.action import Action
|
||||
|
||||
@@ -196,4 +194,3 @@ class ViewForm(ParserView):
|
||||
self.screen.reload(written=True)
|
||||
else:
|
||||
self.screen.display()
|
||||
|
||||
@@ -5,7 +5,7 @@ import gettext
|
||||
import os
|
||||
import tempfile
|
||||
from tryton.common import common
|
||||
from tryton.common import file_selection, message, warning, Tooltips, file_open
|
||||
from tryton.common import file_selection, Tooltips, file_open
|
||||
from interface import WidgetInterface
|
||||
|
||||
_ = gettext.gettext
|
||||
@@ -24,12 +24,15 @@ class Binary(WidgetInterface):
|
||||
self.widget = gtk.HBox(spacing=0)
|
||||
self.wid_size = gtk.Entry()
|
||||
self.wid_size.set_width_chars(10)
|
||||
self.wid_size.set_alignment(1.0)
|
||||
self.wid_size.props.sensitive = False
|
||||
if self.filename and attrs.get('filename_visible'):
|
||||
self.wid_text = gtk.Entry()
|
||||
self.wid_text.set_property('activates_default', True)
|
||||
self.wid_text.connect('focus-in-event', lambda x, y: self._focus_in())
|
||||
self.wid_text.connect('focus-out-event', lambda x, y: self._focus_out())
|
||||
self.wid_text.connect('focus-in-event',
|
||||
lambda x, y: self._focus_in())
|
||||
self.wid_text.connect('focus-out-event',
|
||||
lambda x, y: self._focus_out())
|
||||
self.wid_text.connect_after('key_press_event', self.sig_key_press)
|
||||
self.widget.pack_start(self.wid_text, expand=True, fill=True)
|
||||
else:
|
||||
@@ -136,7 +139,7 @@ class Binary(WidgetInterface):
|
||||
filename = file_selection(_('Save As...'), filename=filename,
|
||||
action=gtk.FILE_CHOOSER_ACTION_SAVE)
|
||||
if filename:
|
||||
with open(filename,'wb') as fp:
|
||||
with open(filename, 'wb') as fp:
|
||||
fp.write(self.field.get_data(self.record))
|
||||
|
||||
def sig_remove(self, widget=None):
|
||||
|
||||
@@ -4,10 +4,8 @@ import time
|
||||
import datetime
|
||||
import gtk
|
||||
import gettext
|
||||
import locale
|
||||
from interface import WidgetInterface
|
||||
import tryton.rpc as rpc
|
||||
from tryton.common import DT_FORMAT, DHM_FORMAT, HM_FORMAT, message, \
|
||||
from tryton.common import DT_FORMAT, DHM_FORMAT, HM_FORMAT, \
|
||||
TRYTON_ICON, timezoned_date
|
||||
from tryton.common import date_widget, Tooltips, datetime_strftime, \
|
||||
get_toplevel_window
|
||||
@@ -89,7 +87,7 @@ class Calendar(WidgetInterface):
|
||||
if not value:
|
||||
self.entry.clear()
|
||||
else:
|
||||
if len(value)>10:
|
||||
if len(value) > 10:
|
||||
value = value[:10]
|
||||
date = datetime.date(*time.strptime(value, DT_FORMAT)[:3])
|
||||
value = datetime_strftime(date, self.format)
|
||||
@@ -101,9 +99,9 @@ class Calendar(WidgetInterface):
|
||||
def cal_open(self, widget):
|
||||
parent = get_toplevel_window()
|
||||
win = gtk.Dialog(_('Date Selection'), parent,
|
||||
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_OK, gtk.RESPONSE_OK))
|
||||
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK,
|
||||
gtk.RESPONSE_OK))
|
||||
win.set_has_separator(True)
|
||||
win.set_icon(TRYTON_ICON)
|
||||
|
||||
@@ -120,7 +118,7 @@ class Calendar(WidgetInterface):
|
||||
try:
|
||||
val = self.get_value(self.record)
|
||||
if val:
|
||||
cal.select_month(int(val[5:7])-1, int(val[0:4]))
|
||||
cal.select_month(int(val[5:7]) - 1, int(val[0:4]))
|
||||
cal.select_day(int(val[8:10]))
|
||||
except ValueError:
|
||||
pass
|
||||
@@ -220,9 +218,9 @@ class DateTime(WidgetInterface):
|
||||
def cal_open(self, widget):
|
||||
parent = get_toplevel_window()
|
||||
win = gtk.Dialog(_('Date Time Selection'), parent,
|
||||
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_OK, gtk.RESPONSE_OK))
|
||||
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK,
|
||||
gtk.RESPONSE_OK))
|
||||
win.set_has_separator(True)
|
||||
win.set_icon(TRYTON_ICON)
|
||||
win.vbox.set_spacing(2)
|
||||
@@ -252,7 +250,7 @@ class DateTime(WidgetInterface):
|
||||
if val:
|
||||
widget_hour.set_value(int(val[11:13]))
|
||||
widget_minute.set_value(int(val[-5:-3]))
|
||||
cal.select_month(int(val[5:7])-1, int(val[0:4]))
|
||||
cal.select_month(int(val[5:7]) - 1, int(val[0:4]))
|
||||
cal.select_day(int(val[8:10]))
|
||||
else:
|
||||
widget_hour.set_value(time.localtime()[3])
|
||||
|
||||
@@ -6,6 +6,7 @@ import gettext
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
class CheckBox(WidgetInterface):
|
||||
|
||||
def __init__(self, field_name, model_name, attrs=None):
|
||||
|
||||
@@ -67,4 +67,3 @@ class Float(Integer):
|
||||
if len(new_int) > digits[0] \
|
||||
or len(new_decimal) > digits[1]:
|
||||
entry.stop_emission('insert-text')
|
||||
|
||||
@@ -35,8 +35,9 @@ class FloatTime(WidgetInterface):
|
||||
value = self.entry.get_text()
|
||||
if not value:
|
||||
return field.set_client(record, 0.0)
|
||||
digits = record.expr_eval(field.attrs.get('digits', (16, 2)))
|
||||
return field.set_client(record,
|
||||
common.text_to_float_time(value, self.conv))
|
||||
round(common.text_to_float_time(value, self.conv), digits[1]))
|
||||
|
||||
def display(self, record, field):
|
||||
super(FloatTime, self).display(record, field)
|
||||
|
||||
@@ -54,7 +54,8 @@ class Image(WidgetInterface):
|
||||
if self.filename:
|
||||
self.but_open = gtk.Button()
|
||||
img_open = gtk.Image()
|
||||
img_open.set_from_stock('tryton-open', gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
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)
|
||||
@@ -65,7 +66,8 @@ class Image(WidgetInterface):
|
||||
|
||||
self.but_save_as = gtk.Button()
|
||||
img_save_as = gtk.Image()
|
||||
img_save_as.set_from_stock('tryton-save', gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
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)
|
||||
@@ -74,7 +76,8 @@ class Image(WidgetInterface):
|
||||
|
||||
self.but_remove = gtk.Button()
|
||||
img_remove = gtk.Image()
|
||||
img_remove.set_from_stock('tryton-clear', gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
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)
|
||||
@@ -193,7 +196,7 @@ class Image(WidgetInterface):
|
||||
if self.field:
|
||||
value = self.field.get_client(self.record)
|
||||
if isinstance(value, (int, long)):
|
||||
if value > 10**6:
|
||||
if value > 10 ** 6:
|
||||
value = False
|
||||
else:
|
||||
value = self.field.get_data(self.record)
|
||||
|
||||
@@ -9,7 +9,7 @@ class WidgetInterface(object):
|
||||
def __init__(self, field_name, model_name, attrs=None):
|
||||
self.field_name = field_name
|
||||
self.model_name = model_name
|
||||
self.view = None # Filled by ViewForm
|
||||
self.view = None # Filled by ViewForm
|
||||
self.attrs = attrs or {}
|
||||
for attr_name in ('readonly', 'invisible'):
|
||||
if attr_name in self.attrs:
|
||||
|
||||
@@ -50,7 +50,6 @@ class Many2Many(WidgetInterface):
|
||||
self.but_add.set_relief(gtk.RELIEF_NONE)
|
||||
hbox.pack_start(self.but_add, expand=False, fill=False)
|
||||
|
||||
|
||||
self.but_remove = gtk.Button()
|
||||
tooltips.set_tip(self.but_remove, _('Remove'))
|
||||
self.but_remove.connect('clicked', self._sig_remove)
|
||||
@@ -130,7 +129,7 @@ class Many2Many(WidgetInterface):
|
||||
else:
|
||||
dom = domain
|
||||
ids = rpc.execute('model', self.attrs['relation'], 'search',
|
||||
dom , 0, CONFIG['client.limit'], None, context)
|
||||
dom, 0, CONFIG['client.limit'], None, context)
|
||||
except TrytonServerError, exception:
|
||||
common.process_exception(exception)
|
||||
return False
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#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 gobject
|
||||
import gtk
|
||||
import gettext
|
||||
from interface import WidgetInterface
|
||||
from tryton.common import TRYTON_ICON, COLOR_SCHEMES
|
||||
import tryton.common as common
|
||||
from tryton.gui.window.view_form.screen import Screen
|
||||
from tryton.gui.window.win_search import WinSearch
|
||||
@@ -14,7 +12,6 @@ from tryton.action import Action
|
||||
from tryton.config import CONFIG
|
||||
from tryton.pyson import PYSONEncoder
|
||||
from tryton.exceptions import TrytonServerError
|
||||
import pango
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
@@ -33,7 +30,8 @@ class Many2One(WidgetInterface):
|
||||
self.wid_text.connect_after('key_press_event', self.sig_key_press)
|
||||
self.wid_text.connect('populate-popup', self._populate_popup)
|
||||
self.wid_text.connect('focus-in-event', lambda x, y: self._focus_in())
|
||||
self.wid_text.connect('focus-out-event', lambda x, y: self._focus_out())
|
||||
self.wid_text.connect('focus-out-event',
|
||||
lambda x, y: self._focus_out())
|
||||
self.wid_text.connect_after('changed', self.sig_changed)
|
||||
self.changed = True
|
||||
self.wid_text.connect('activate', self.sig_activate)
|
||||
@@ -118,12 +116,13 @@ class Many2One(WidgetInterface):
|
||||
common.process_exception(exception)
|
||||
self.changed = True
|
||||
return
|
||||
if len(ids)==1:
|
||||
if len(ids) == 1:
|
||||
self.field.set_client(self.record, ids[0],
|
||||
force_change=True)
|
||||
self.focus_out = True
|
||||
self.display(self.record, self.field)
|
||||
return
|
||||
|
||||
def callback(ids):
|
||||
if ids:
|
||||
self.field.set_client(self.record, ids[0],
|
||||
@@ -150,6 +149,7 @@ class Many2One(WidgetInterface):
|
||||
def sig_new(self, *args):
|
||||
self.focus_out = False
|
||||
screen = self.get_screen()
|
||||
|
||||
def callback(result):
|
||||
if result and screen.save_current():
|
||||
value = (screen.current_record.id,
|
||||
@@ -165,6 +165,7 @@ class Many2One(WidgetInterface):
|
||||
if value:
|
||||
screen = self.get_screen()
|
||||
screen.load([self.field.get(self.record)])
|
||||
|
||||
def callback(result):
|
||||
if result and screen.save_current():
|
||||
value = (screen.current_record.id,
|
||||
@@ -200,7 +201,7 @@ class Many2One(WidgetInterface):
|
||||
common.process_exception(exception)
|
||||
self.changed = True
|
||||
return False
|
||||
if ids and len(ids)==1:
|
||||
if ids and len(ids) == 1:
|
||||
self.field.set_client(self.record, ids[0],
|
||||
force_change=True)
|
||||
self.focus_out = True
|
||||
@@ -230,7 +231,8 @@ class Many2One(WidgetInterface):
|
||||
elif event.keyval == gtk.keysyms.F2:
|
||||
self.sig_edit(widget)
|
||||
return True
|
||||
elif event.keyval in (gtk.keysyms.Tab, gtk.keysyms.Return) and editable:
|
||||
elif (event.keyval in (gtk.keysyms.Tab, gtk.keysyms.Return)
|
||||
and editable):
|
||||
self.sig_activate(widget, event, key_press=True)
|
||||
return False
|
||||
|
||||
@@ -243,7 +245,7 @@ class Many2One(WidgetInterface):
|
||||
return False
|
||||
|
||||
def set_value(self, record, field):
|
||||
pass # No update of the model, the model is updated in real time !
|
||||
pass # No update of the model, the model is updated in real time !
|
||||
|
||||
def display(self, record, field):
|
||||
self.changed = False
|
||||
@@ -280,9 +282,9 @@ class Many2One(WidgetInterface):
|
||||
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))
|
||||
lambda x: self.click_and_action('form_action'), 0))
|
||||
menu_entries.append((_('Reports'),
|
||||
lambda x: self.click_and_action('form_print'),0))
|
||||
lambda x: self.click_and_action('form_print'), 0))
|
||||
menu_entries.append((None, None, None))
|
||||
for relate in relates:
|
||||
relate['string'] = relate['name']
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
import gtk
|
||||
import gettext
|
||||
from tryton.common import TRYTON_ICON, COLOR_SCHEMES
|
||||
from interface import WidgetInterface
|
||||
from tryton.gui.window.view_form.screen import Screen
|
||||
from tryton.gui.window.win_search import WinSearch
|
||||
@@ -11,7 +10,6 @@ from tryton.config import CONFIG
|
||||
from tryton.exceptions import TrytonServerError
|
||||
import tryton.common as common
|
||||
import tryton.rpc as rpc
|
||||
import pango
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
@@ -47,7 +45,8 @@ class One2Many(WidgetInterface):
|
||||
tooltips.set_tip(self.but_add, _('Add'))
|
||||
self.but_add.connect('clicked', self._sig_add)
|
||||
img_add = gtk.Image()
|
||||
img_add.set_from_stock('tryton-list-add', gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_add.set_from_stock('tryton-list-add',
|
||||
gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_add.set_alignment(0.5, 0.5)
|
||||
self.but_add.add(img_add)
|
||||
self.but_add.set_relief(gtk.RELIEF_NONE)
|
||||
@@ -57,7 +56,8 @@ class One2Many(WidgetInterface):
|
||||
tooltips.set_tip(self.but_remove, _('Remove'))
|
||||
self.but_remove.connect('clicked', self._sig_remove, True)
|
||||
img_remove = gtk.Image()
|
||||
img_remove.set_from_stock('tryton-list-remove', gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_remove.set_from_stock('tryton-list-remove',
|
||||
gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_remove.set_alignment(0.5, 0.5)
|
||||
self.but_remove.add(img_remove)
|
||||
self.but_remove.set_relief(gtk.RELIEF_NONE)
|
||||
@@ -111,7 +111,8 @@ class One2Many(WidgetInterface):
|
||||
tooltips.set_tip(self.but_pre, _('Previous'))
|
||||
self.but_pre.connect('clicked', self._sig_previous)
|
||||
img_pre = gtk.Image()
|
||||
img_pre.set_from_stock('tryton-go-previous', gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_pre.set_from_stock('tryton-go-previous',
|
||||
gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_pre.set_alignment(0.5, 0.5)
|
||||
self.but_pre.add(img_pre)
|
||||
self.but_pre.set_relief(gtk.RELIEF_NONE)
|
||||
@@ -136,7 +137,8 @@ class One2Many(WidgetInterface):
|
||||
tooltips.set_tip(but_switch, _('Switch'))
|
||||
but_switch.connect('clicked', self.switch_view)
|
||||
img_switch = gtk.Image()
|
||||
img_switch.set_from_stock('tryton-fullscreen', gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_switch.set_from_stock('tryton-fullscreen',
|
||||
gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_switch.set_alignment(0.5, 0.5)
|
||||
but_switch.add(img_switch)
|
||||
but_switch.set_relief(gtk.RELIEF_NONE)
|
||||
@@ -304,7 +306,8 @@ class One2Many(WidgetInterface):
|
||||
|
||||
try:
|
||||
if self.wid_text.get_text():
|
||||
dom = [('rec_name', 'ilike', '%' + self.wid_text.get_text() + '%'),
|
||||
dom = [('rec_name', 'ilike',
|
||||
'%' + self.wid_text.get_text() + '%'),
|
||||
['OR', domain, ('id', 'in', removed_ids)]]
|
||||
else:
|
||||
dom = ['OR', domain, ('id', 'in', removed_ids)]
|
||||
@@ -313,6 +316,7 @@ class One2Many(WidgetInterface):
|
||||
except TrytonServerError, exception:
|
||||
common.process_exception(exception)
|
||||
return False
|
||||
|
||||
def callback(ids):
|
||||
res_id = None
|
||||
if ids:
|
||||
@@ -328,7 +332,6 @@ class One2Many(WidgetInterface):
|
||||
else:
|
||||
callback(ids)
|
||||
|
||||
|
||||
def _sig_label(self, screen, signal_data):
|
||||
name = '_'
|
||||
if signal_data[0] >= 1:
|
||||
@@ -386,7 +389,7 @@ class One2Many(WidgetInterface):
|
||||
def set_value(self, record, field):
|
||||
self.screen.save_tree_state()
|
||||
self.screen.current_view.set_value()
|
||||
if self.screen.modified(): # TODO check if required
|
||||
if self.screen.modified(): # TODO check if required
|
||||
record.modified_fields.setdefault(field.name)
|
||||
record.signal('record-modified')
|
||||
return True
|
||||
@@ -72,7 +72,7 @@ class Image(gtk.Image):
|
||||
class Frame(gtk.Frame):
|
||||
|
||||
def __init__(self, label=None, attrs=None):
|
||||
if not label: # label must be None to have no label widget
|
||||
if not label: # label must be None to have no label widget
|
||||
label = None
|
||||
super(Frame, self).__init__(label=label)
|
||||
self.attrs = attrs or {}
|
||||
@@ -137,8 +137,8 @@ class _container(object):
|
||||
table.set_col_spacings(0)
|
||||
table.set_row_spacings(0)
|
||||
table.set_border_width(0)
|
||||
self.cont.append( (table, 0, 0) )
|
||||
self.col.append( col )
|
||||
self.cont.append((table, 0, 0))
|
||||
self.col.append(col)
|
||||
|
||||
def get(self):
|
||||
return self.cont[-1][0]
|
||||
@@ -155,8 +155,8 @@ class _container(object):
|
||||
table.resize(height + 1, self.col[-1])
|
||||
|
||||
def wid_add(self, widget, name='', yexpand=False, ypadding=2, rowspan=1,
|
||||
colspan=1, translate=False, fname=None, help_tip=False, yfill=False,
|
||||
xexpand=True, xfill=True, xpadding=3):
|
||||
colspan=1, translate=False, fname=None, help_tip=False,
|
||||
yfill=False, xexpand=True, xfill=True, xpadding=3):
|
||||
(table, width, height) = self.cont[-1]
|
||||
if colspan > self.col[-1]:
|
||||
colspan = self.col[-1]
|
||||
@@ -182,7 +182,8 @@ class _container(object):
|
||||
img.set_from_stock('tryton-locale', gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
button.set_image(img)
|
||||
button.set_relief(gtk.RELIEF_NONE)
|
||||
self.trans_box.append((button, name, fname, widget.get_children()[0]))
|
||||
self.trans_box.append((button, name, fname,
|
||||
widget.get_children()[0]))
|
||||
widget.get_child().pack_start(button, fill=False, expand=False)
|
||||
widget.show_all()
|
||||
table.attach(widget, width, width + colspan,
|
||||
@@ -355,9 +356,9 @@ class ParserForm(ParserInterface):
|
||||
colspan=colspan,
|
||||
yexpand=yexpand, yfill=yfill,
|
||||
xexpand=xexpand, xfill=xfill)
|
||||
widget, widgets, buttons, spam, notebook_list2, cursor_widget2 = \
|
||||
self.parse(model_name, node, fields, notebook,
|
||||
tooltips=tooltips)
|
||||
widget, widgets, buttons, spam, notebook_list2, cursor_widget2\
|
||||
= self.parse(model_name, node, fields, notebook,
|
||||
tooltips=tooltips)
|
||||
if not cursor_widget:
|
||||
cursor_widget = cursor_widget2
|
||||
notebook_list.extend(notebook_list2)
|
||||
@@ -369,7 +370,7 @@ class ParserForm(ParserInterface):
|
||||
elif node.localName == 'page':
|
||||
if CONFIG['client.form_tab'] == 'left':
|
||||
angle = 90
|
||||
tab_box = gtk.VBox(spacing=3)
|
||||
tab_box = gtk.VBox(spacing=3)
|
||||
image_pos, image_rotate = ('end',
|
||||
gtk.gdk.PIXBUF_ROTATE_COUNTERCLOCKWISE)
|
||||
elif CONFIG['client.form_tab'] == 'right':
|
||||
@@ -412,9 +413,9 @@ class ParserForm(ParserInterface):
|
||||
else:
|
||||
tab_box.pack_start(icon)
|
||||
tab_box.show_all()
|
||||
widget, widgets, buttons, spam, notebook_list2, cursor_widget2 = \
|
||||
self.parse(model_name, node, fields, notebook,
|
||||
tooltips=tooltips)
|
||||
widget, widgets, buttons, spam, notebook_list2, cursor_widget2\
|
||||
= self.parse(model_name, node, fields, notebook,
|
||||
tooltips=tooltips)
|
||||
if not cursor_widget:
|
||||
cursor_widget = cursor_widget2
|
||||
notebook_list.extend(notebook_list2)
|
||||
@@ -482,8 +483,8 @@ class ParserForm(ParserInterface):
|
||||
xexpand=xexpand, xfill=xfill)
|
||||
|
||||
elif node.localName == 'group':
|
||||
widget, widgets, buttons, spam, notebook_list2, cursor_widget2 = \
|
||||
self.parse(model_name, node, fields, tooltips=tooltips)
|
||||
widget, widgets, buttons, spam, notebook_list2, cursor_widget2\
|
||||
= self.parse(model_name, node, fields, tooltips=tooltips)
|
||||
if not cursor_widget:
|
||||
cursor_widget = cursor_widget2
|
||||
notebook_list.extend(notebook_list2)
|
||||
@@ -515,9 +516,9 @@ class ParserForm(ParserInterface):
|
||||
hpaned = gtk.HPaned()
|
||||
container.wid_add(hpaned, colspan=int(attrs.get('colspan', 4)),
|
||||
yexpand=True, yfill=True)
|
||||
widget, widgets, buttons, spam, notebook_list2, cursor_widget2 = \
|
||||
self.parse(model_name, node, fields, paned=hpaned,
|
||||
tooltips=tooltips)
|
||||
widget, widgets, buttons, spam, notebook_list2, cursor_widget2\
|
||||
= self.parse(model_name, node, fields, paned=hpaned,
|
||||
tooltips=tooltips)
|
||||
if not cursor_widget:
|
||||
cursor_widget = cursor_widget2
|
||||
notebook_list.extend(notebook_list2)
|
||||
@@ -531,9 +532,9 @@ class ParserForm(ParserInterface):
|
||||
vpaned = gtk.VPaned()
|
||||
container.wid_add(vpaned, colspan=int(attrs.get('colspan', 4)),
|
||||
yexpand=True, yfill=True)
|
||||
widget, widgets, buttons, spam, notebook_list, cursor_widget2 = \
|
||||
self.parse(model_name, node, fields, paned=vpaned,
|
||||
tooltips=tooltips)
|
||||
widget, widgets, buttons, spam, notebook_list, cursor_widget2\
|
||||
= self.parse(model_name, node, fields, paned=vpaned,
|
||||
tooltips=tooltips)
|
||||
if not cursor_widget:
|
||||
cursor_widget = cursor_widget2
|
||||
notebook_list.extend(notebook_list2)
|
||||
@@ -544,9 +545,9 @@ class ParserForm(ParserInterface):
|
||||
if 'position' in attrs:
|
||||
vpaned.set_position(int(attrs['position']))
|
||||
elif node.localName == 'child':
|
||||
widget, widgets, buttons, spam, notebook_list2, cursor_widget2 = \
|
||||
self.parse(model_name, node, fields, paned=paned,
|
||||
tooltips=tooltips)
|
||||
widget, widgets, buttons, spam, notebook_list2, cursor_widget2\
|
||||
= self.parse(model_name, node, fields, paned=paned,
|
||||
tooltips=tooltips)
|
||||
if not cursor_widget:
|
||||
cursor_widget = cursor_widget2
|
||||
notebook_list.extend(notebook_list2)
|
||||
@@ -568,7 +569,7 @@ class ParserForm(ParserInterface):
|
||||
obj_id = self.screen.current_record.id
|
||||
if obj_id < 0:
|
||||
common.message(
|
||||
_('You need to save the record before adding translations!'))
|
||||
_('You need to save the record before adding translations!'))
|
||||
return False
|
||||
|
||||
obj_id = self.screen.current_record.save(force_reload=False)
|
||||
@@ -599,6 +600,7 @@ class ParserForm(ParserInterface):
|
||||
widget_entry = widget_entry.get_children()[0]
|
||||
if isinstance(widget_entry, gtk.ScrolledWindow):
|
||||
widget_entry = widget_entry.get_child()
|
||||
|
||||
#widget accessor functions
|
||||
def value_get(widget):
|
||||
if isinstance(widget, gtk.Entry):
|
||||
@@ -643,10 +645,9 @@ class ParserForm(ParserInterface):
|
||||
else:
|
||||
return None, False
|
||||
|
||||
|
||||
parent = common.get_toplevel_window()
|
||||
win = gtk.Dialog(_('Add Translation'),parent,
|
||||
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
|
||||
win = gtk.Dialog(_('Add Translation'), parent,
|
||||
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
|
||||
win.set_has_separator(True)
|
||||
win.vbox.set_spacing(5)
|
||||
win.set_property('default-width', 600)
|
||||
@@ -657,7 +658,7 @@ class ParserForm(ParserInterface):
|
||||
accel_group = gtk.AccelGroup()
|
||||
win.add_accel_group(accel_group)
|
||||
|
||||
but_cancel = win.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
|
||||
win.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
|
||||
|
||||
but_ok = win.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
|
||||
but_ok.add_accelerator('clicked', accel_group, gtk.keysyms.Return,
|
||||
@@ -689,17 +690,16 @@ class ParserForm(ParserInterface):
|
||||
label.set_alignment(1.0, 0.5)
|
||||
(entry, yoptions) = widget_duplicate(widget_entry)
|
||||
|
||||
hbox = gtk.HBox(homogeneous=False)
|
||||
if code == lang['code']:
|
||||
value_set(entry, value_get(widget_entry))
|
||||
else:
|
||||
value_set(entry, val[name])
|
||||
|
||||
entries_list.append((val['id'], lang['code'], entry))
|
||||
table.attach(label, 0, 1, i, i+1, yoptions=False, xoptions=gtk.FILL,
|
||||
ypadding=2, xpadding=3)
|
||||
table.attach(entry, 1, 2, i, i+1, yoptions=yoptions,
|
||||
ypadding=2, xpadding=3)
|
||||
table.attach(label, 0, 1, i, i + 1, yoptions=False,
|
||||
xoptions=gtk.FILL, ypadding=2, xpadding=3)
|
||||
table.attach(entry, 1, 2, i, i + 1, yoptions=yoptions,
|
||||
ypadding=2, xpadding=3)
|
||||
i += 1
|
||||
|
||||
vbox.pack_start(table)
|
||||
@@ -714,7 +714,6 @@ class ParserForm(ParserInterface):
|
||||
win.vbox.add(scrolledwindow)
|
||||
win.show_all()
|
||||
|
||||
data = []
|
||||
response = win.run()
|
||||
if response == gtk.RESPONSE_OK:
|
||||
to_save = [(x[0], x[1], value_get(x[2])) for x in entries_list]
|
||||
@@ -776,11 +775,11 @@ WIDGETS_TYPE = {
|
||||
'one2many': (One2Many, 1, True, True),
|
||||
'many2many': (Many2Many, 1, True, True),
|
||||
'many2one': (Many2One, 1, False, False),
|
||||
'email' : (Email, 1, False, False),
|
||||
'url' : (URL, 1, False, False),
|
||||
'callto' : (CallTo, 1, False, False),
|
||||
'sip' : (SIP, 1, False, False),
|
||||
'image' : (Image2, 1, False, False),
|
||||
'email': (Email, 1, False, False),
|
||||
'url': (URL, 1, False, False),
|
||||
'callto': (CallTo, 1, False, False),
|
||||
'sip': (SIP, 1, False, False),
|
||||
'image': (Image2, 1, False, False),
|
||||
'progressbar': (ProgressBar, 1, False, False),
|
||||
'one2one': (One2One, 1, False, False),
|
||||
}
|
||||
@@ -88,7 +88,7 @@ class Reference(WidgetInterface):
|
||||
if selection:
|
||||
pop = sorted((len(x) for x in selection), reverse=True)
|
||||
average = sum(pop) / len(pop)
|
||||
deviation = int(math.sqrt(sum((x - average)**2 for x in pop) /
|
||||
deviation = int(math.sqrt(sum((x - average) ** 2 for x in pop) /
|
||||
len(pop)))
|
||||
width = max(next((x for x in pop if x < (deviation * 4)), 10), 10)
|
||||
else:
|
||||
@@ -149,7 +149,6 @@ class Reference(WidgetInterface):
|
||||
def sig_focus_out(self, widget, event, leave=False):
|
||||
if not self.focus_out:
|
||||
return
|
||||
child = self.widget_combo.get_child()
|
||||
self.changed = False
|
||||
value = self.field.get_client(self.record)
|
||||
|
||||
@@ -166,6 +165,7 @@ class Reference(WidgetInterface):
|
||||
if not leave:
|
||||
screen = Screen(model, mode=['form'])
|
||||
screen.load([obj_id])
|
||||
|
||||
def callback(result):
|
||||
if result and screen.save_current():
|
||||
value = (screen.current_record.id,
|
||||
@@ -178,7 +178,7 @@ class Reference(WidgetInterface):
|
||||
WinForm(screen, callback)
|
||||
return
|
||||
elif model:
|
||||
if not self._readonly and ( self.wid_text.get_text() or not leave):
|
||||
if not self._readonly and (self.wid_text.get_text() or not leave):
|
||||
domain = self.field.domain_get(self.record)
|
||||
context = self.field.context_get(self.record)
|
||||
|
||||
@@ -205,7 +205,8 @@ class Reference(WidgetInterface):
|
||||
|
||||
def callback(ids):
|
||||
if ids:
|
||||
self.field.set_client(self.record, (model, (ids[0], '')))
|
||||
self.field.set_client(self.record,
|
||||
(model, (ids[0], '')))
|
||||
self.focus_out = True
|
||||
self.changed = True
|
||||
self.display(self.record, self.field)
|
||||
@@ -223,6 +224,7 @@ class Reference(WidgetInterface):
|
||||
if not model:
|
||||
return
|
||||
screen = Screen(model, mode=['form'])
|
||||
|
||||
def callback(result):
|
||||
if result and screen.save_current():
|
||||
value = (screen.current_record.id,
|
||||
@@ -238,7 +240,8 @@ class Reference(WidgetInterface):
|
||||
elif event.keyval == gtk.keysyms.F2:
|
||||
self.sig_focus_out(widget, event)
|
||||
return True
|
||||
elif event.keyval in (gtk.keysyms.Tab, gtk.keysyms.Return) and editable:
|
||||
elif (event.keyval in (gtk.keysyms.Tab, gtk.keysyms.Return)
|
||||
and editable):
|
||||
if self.field.get(self.record) or \
|
||||
not self.wid_text.get_text():
|
||||
return False
|
||||
@@ -272,7 +275,7 @@ class Reference(WidgetInterface):
|
||||
if not field:
|
||||
child.set_text('')
|
||||
child.set_position(0)
|
||||
self.changed =True
|
||||
self.changed = True
|
||||
return False
|
||||
super(Reference, self).display(record, field)
|
||||
value = field.get_client(record)
|
||||
@@ -306,8 +309,8 @@ class Reference(WidgetInterface):
|
||||
if self.last_key[0] == key:
|
||||
self.last_key[1] += 1
|
||||
else:
|
||||
self.last_key = [ key, 1 ]
|
||||
if not self.key_catalog.has_key(key):
|
||||
self.last_key = [key, 1]
|
||||
if key not in self.key_catalog:
|
||||
return
|
||||
self.widget_combo.set_active_iter(
|
||||
self.key_catalog[key][self.last_key[1] \
|
||||
|
||||
@@ -7,7 +7,6 @@ import math
|
||||
from interface import WidgetInterface
|
||||
import tryton.rpc as rpc
|
||||
import tryton.common as common
|
||||
from tryton.pyson import PYSONDecoder
|
||||
from tryton.exceptions import TrytonServerError
|
||||
|
||||
|
||||
@@ -61,8 +60,8 @@ class Selection(WidgetInterface):
|
||||
if domain == self._last_domain:
|
||||
return
|
||||
|
||||
args = ('model', self.attrs['relation'], 'search_read', domain, 0, None,
|
||||
None, ['rec_name'], rpc.CONTEXT)
|
||||
args = ('model', self.attrs['relation'], 'search_read', domain, 0,
|
||||
None, None, ['rec_name'], rpc.CONTEXT)
|
||||
try:
|
||||
result = rpc.execute(*args)
|
||||
except TrytonServerError, exception:
|
||||
@@ -101,14 +100,15 @@ class Selection(WidgetInterface):
|
||||
if self._selection:
|
||||
pop = sorted((len(x) for x in self._selection), reverse=True)
|
||||
average = sum(pop) / len(pop)
|
||||
deviation = int(math.sqrt(sum((x - average)**2 for x in pop) /
|
||||
deviation = int(math.sqrt(sum((x - average) ** 2 for x in pop) /
|
||||
len(pop)))
|
||||
width = max(next((x for x in pop if x < (deviation * 4)), 10), 10)
|
||||
else:
|
||||
width = 10
|
||||
self.entry.child.set_width_chars(width)
|
||||
if self._selection:
|
||||
self.entry.child.set_max_length(max(len(x) for x in self._selection))
|
||||
self.entry.child.set_max_length(
|
||||
max(len(x) for x in self._selection))
|
||||
completion.set_text_column(0)
|
||||
return lst
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
import gtk
|
||||
from interface import WidgetInterface
|
||||
import tryton.rpc as rpc
|
||||
from tryton.config import CONFIG
|
||||
|
||||
try:
|
||||
@@ -28,7 +27,8 @@ class TextBox(WidgetInterface):
|
||||
#TODO better tab solution
|
||||
self.textview.set_accepts_tab(False)
|
||||
self.textview.connect('focus-in-event', lambda x, y: self._focus_in())
|
||||
self.textview.connect('focus-out-event', lambda x, y: self._focus_out())
|
||||
self.textview.connect('focus-out-event',
|
||||
lambda x, y: self._focus_out())
|
||||
self.scrolledwindow.add(self.textview)
|
||||
self.scrolledwindow.show_all()
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#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 char import Char
|
||||
import webbrowser
|
||||
@@ -104,4 +103,3 @@ class SIP(URL):
|
||||
else:
|
||||
self.tooltips.set_tip(self.button, '')
|
||||
self.tooltips.disable()
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
#This code is inspired by the pycha project (http://www.lorenzogil.com/projects/pycha/)
|
||||
#This code is inspired by the pycha project
|
||||
#(http://www.lorenzogil.com/projects/pycha/)
|
||||
from graph import Graph
|
||||
from tryton.common import hex2rgb, lighten, float_time_to_text
|
||||
from tryton.common import float_time_to_text
|
||||
import locale
|
||||
import math
|
||||
import cairo
|
||||
@@ -21,8 +22,8 @@ class Bar(Graph):
|
||||
w = self.area.w * bar.w
|
||||
h = self.area.h * bar.h
|
||||
|
||||
if w < 1 or h <1:
|
||||
return # don't draw too small
|
||||
if w < 1 or h < 1:
|
||||
return # don't draw too small
|
||||
|
||||
cr.set_source_rgba(0, 0, 0, 0.15)
|
||||
rectangle = self._getShadowRectangle(x, y, w, h)
|
||||
@@ -52,7 +53,8 @@ class Bar(Graph):
|
||||
|
||||
def sourceRectangle(self, x, y, w, h, r, g, b):
|
||||
linear = cairo.LinearGradient((x + w) / 2, y, (x + w) / 2, y + h)
|
||||
linear.add_color_stop_rgb(0, 3.5 * r / 5.0, 3.5 * g / 5.0, 3.5 * b / 5.0)
|
||||
linear.add_color_stop_rgb(0, 3.5 * r / 5.0, 3.5 * g / 5.0,
|
||||
3.5 * b / 5.0)
|
||||
linear.add_color_stop_rgb(1, r, g, b)
|
||||
return linear
|
||||
|
||||
@@ -71,8 +73,9 @@ class Bar(Graph):
|
||||
|
||||
highlight = False
|
||||
draw_bars = []
|
||||
yfields_float_time = dict([(x.get('key', x['name']), x.get('float_time'))
|
||||
for x in self.yfields if x.get('widget')])
|
||||
yfields_float_time = dict(
|
||||
(x.get('key', x['name']), x.get('float_time'))
|
||||
for x in self.yfields if x.get('widget'))
|
||||
for bar in self.bars:
|
||||
if intersect(bar, event):
|
||||
if not bar.highlight:
|
||||
@@ -179,7 +182,7 @@ class VerticalBar(Bar):
|
||||
return ylabels
|
||||
|
||||
def _getShadowRectangle(self, x, y, w, h):
|
||||
return (x-2, y-2, w+4, h+2)
|
||||
return (x - 2, y - 2, w + 4, h + 2)
|
||||
|
||||
|
||||
class HorizontalBar(Bar):
|
||||
@@ -236,7 +239,7 @@ class HorizontalBar(Bar):
|
||||
return [(x[0], x[1]) for x in ylabels]
|
||||
|
||||
def _getShadowRectangle(self, x, y, w, h):
|
||||
return (x, y-2, w+2, h+4)
|
||||
return (x, y - 2, w + 2, h + 4)
|
||||
|
||||
def _getLegendPosition(self, width, height):
|
||||
return self.area.x + self.area.w * 0.95 - width, \
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
#This code is inspired by the pycha project (http://www.lorenzogil.com/projects/pycha/)
|
||||
#This code is inspired by the pycha project
|
||||
#(http://www.lorenzogil.com/projects/pycha/)
|
||||
import gtk
|
||||
from functools import reduce
|
||||
from tryton.common import hex2rgb, generateColorscheme, DT_FORMAT, \
|
||||
DHM_FORMAT, COLOR_SCHEMES, datetime_strftime
|
||||
COLOR_SCHEMES, datetime_strftime
|
||||
from tryton.pyson import PYSONDecoder
|
||||
import locale
|
||||
import math
|
||||
import datetime
|
||||
import time
|
||||
from dateutil.relativedelta import relativedelta
|
||||
import tryton.rpc as rpc
|
||||
import cairo
|
||||
from tryton.action import Action
|
||||
from tryton.translate import date_format
|
||||
from tryton.gui.window import Window
|
||||
|
||||
|
||||
@@ -65,10 +64,11 @@ class Popup(object):
|
||||
def enter(self, widget, event):
|
||||
self.win.hide()
|
||||
|
||||
|
||||
class Graph(gtk.DrawingArea):
|
||||
'Graph'
|
||||
|
||||
__gsignals__ = { "expose-event": "override" }
|
||||
__gsignals__ = {"expose-event": "override"}
|
||||
|
||||
def __init__(self, xfield, yfields, attrs, model):
|
||||
super(Graph, self).__init__()
|
||||
@@ -167,13 +167,12 @@ class Graph(gtk.DrawingArea):
|
||||
def drawGraph(self, cr, width, height):
|
||||
pass
|
||||
|
||||
|
||||
def YLabels(self):
|
||||
ylabels = []
|
||||
if self.yrange == 0.0:
|
||||
base = 1
|
||||
else:
|
||||
base = 10**int(math.log(self.yrange, 10))
|
||||
base = 10 ** int(math.log(self.yrange, 10))
|
||||
for i in xrange(int(self.yrange / base) + 1):
|
||||
val = int(self.minyval / base) * base + i * base
|
||||
h = (val - self.minyval) * self.yscale
|
||||
@@ -356,7 +355,6 @@ class Graph(gtk.DrawingArea):
|
||||
self.ids[x].append(model.id)
|
||||
self.datas.setdefault(x, {})
|
||||
for yfield in self.yfields:
|
||||
name = yfield['name']
|
||||
key = yfield.get('key', yfield['name'])
|
||||
self.datas[x].setdefault(key, 0.0)
|
||||
if yfield.get('domain'):
|
||||
@@ -411,7 +409,8 @@ class Graph(gtk.DrawingArea):
|
||||
yLabelWidth = 0
|
||||
width = width - self.leftPadding - yLabelWidth - self.rightPadding
|
||||
height = height - self.topPadding - self.bottomPadding - xLabelHeight
|
||||
self.area = Area(self.leftPadding + yLabelWidth, self.topPadding, width, height)
|
||||
self.area = Area(self.leftPadding + yLabelWidth, self.topPadding,
|
||||
width, height)
|
||||
|
||||
def updateXY(self):
|
||||
self.maxxval = len(self.datas)
|
||||
@@ -458,6 +457,7 @@ class Graph(gtk.DrawingArea):
|
||||
def _getDatasKeys(self):
|
||||
return [x.get('key', x['name']) for x in self.yfields]
|
||||
|
||||
|
||||
class Area(object):
|
||||
|
||||
def __init__(self, x, y, w, h):
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
#This code is inspired by the pycha project (http://www.lorenzogil.com/projects/pycha/)
|
||||
#This code is inspired by the pycha project
|
||||
#(http://www.lorenzogil.com/projects/pycha/)
|
||||
from graph import Graph
|
||||
from tryton.common import hex2rgb, float_time_to_text, safe_eval
|
||||
import locale
|
||||
@@ -33,8 +34,8 @@ class Line(Graph):
|
||||
if self.xrange == 0:
|
||||
x = 1.0
|
||||
|
||||
if not bool(safe_eval(yfield2attrs[yfield].get('empty', '1'))) \
|
||||
and yval == 0:
|
||||
if (not bool(safe_eval(yfield2attrs[yfield].get('empty', '1')))
|
||||
and yval == 0):
|
||||
continue
|
||||
|
||||
point = Point(x, y, xval, yval, xfield, yfield)
|
||||
@@ -140,8 +141,9 @@ class Line(Graph):
|
||||
|
||||
highlight = False
|
||||
draw_points = []
|
||||
yfields_float_time = dict([(x.get('key', x['name']), x.get('float_time'))
|
||||
for x in self.yfields if x.get('widget')])
|
||||
yfields_float_time = dict(
|
||||
(x.get('key', x['name']), x.get('float_time'))
|
||||
for x in self.yfields if x.get('widget'))
|
||||
for point in self.points:
|
||||
if point == nearest[0] and nearest[1] < dia / 100:
|
||||
if not point.highlight:
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
from tryton.gui.window.view_form.view.interface import ParserInterface
|
||||
import tryton.common as common
|
||||
import gtk
|
||||
from graph import Graph
|
||||
from bar import VerticalBar, HorizontalBar
|
||||
from line import Line
|
||||
from pie import Pie
|
||||
from tryton.config import TRYTON_ICON, CONFIG
|
||||
import sys, os
|
||||
import sys
|
||||
import os
|
||||
import gettext
|
||||
|
||||
_ = gettext.gettext
|
||||
@@ -20,12 +20,12 @@ GRAPH_TYPE = {
|
||||
'pie': Pie,
|
||||
}
|
||||
|
||||
|
||||
def save(widget, graph):
|
||||
parent = common.get_toplevel_window()
|
||||
dia = gtk.Dialog(_('Save As'), parent,
|
||||
gtk.DIALOG_MODAL|gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
|
||||
gtk.STOCK_OK, gtk.RESPONSE_OK))
|
||||
gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
|
||||
(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK))
|
||||
dia.set_icon(TRYTON_ICON)
|
||||
dia.set_has_separator(True)
|
||||
dia.set_default_response(gtk.RESPONSE_OK)
|
||||
@@ -98,6 +98,7 @@ def save(widget, graph):
|
||||
dia.destroy()
|
||||
return
|
||||
|
||||
|
||||
def button_press(widget, event, graph):
|
||||
if event.button == 3:
|
||||
menu = gtk.Menu()
|
||||
@@ -132,7 +133,8 @@ class ParserGraph(ParserInterface):
|
||||
continue
|
||||
xfield = common.node_attributes(child)
|
||||
if not xfield.get('string'):
|
||||
xfield['string'] = fields[xfield['name']].attrs['string']
|
||||
xfield['string'] = fields[xfield['name']
|
||||
].attrs['string']
|
||||
break
|
||||
elif node.localName == 'y':
|
||||
for child in node.childNodes:
|
||||
@@ -140,10 +142,12 @@ class ParserGraph(ParserInterface):
|
||||
continue
|
||||
yattrs = common.node_attributes(child)
|
||||
if not yattrs.get('string') and yattrs['name'] != '#':
|
||||
yattrs['string'] = fields[yattrs['name']].attrs['string']
|
||||
yattrs['string'] = fields[yattrs['name']
|
||||
].attrs['string']
|
||||
yfields.append(yattrs)
|
||||
|
||||
widget = GRAPH_TYPE[attrs.get('type', 'vbar')](xfield, yfields, attrs, model)
|
||||
widget = GRAPH_TYPE[attrs.get('type', 'vbar')
|
||||
](xfield, yfields, attrs, model)
|
||||
event = gtk.EventBox()
|
||||
event.add(widget)
|
||||
event.connect('button-press-event', button_press, widget)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
#this repository contains the full copyright notices and license terms.
|
||||
#This code is inspired by the pycha project (http://www.lorenzogil.com/projects/pycha/)
|
||||
#This code is inspired by the pycha project
|
||||
#(http://www.lorenzogil.com/projects/pycha/)
|
||||
from graph import Graph, Area
|
||||
import math
|
||||
import cairo
|
||||
@@ -97,7 +98,8 @@ class Pie(Graph):
|
||||
cr.set_source_rgba(*color)
|
||||
slice.draw(cr, self.centerx, self.centery, self.radius)
|
||||
cr.fill()
|
||||
cr.set_source_rgb(*hex2rgb(self.attrs.get('background', '#f5f5f5')))
|
||||
cr.set_source_rgb(*hex2rgb(
|
||||
self.attrs.get('background', '#f5f5f5')))
|
||||
slice.draw(cr, self.centerx, self.centery, self.radius)
|
||||
cr.set_line_width(2)
|
||||
cr.stroke()
|
||||
@@ -120,7 +122,8 @@ class Pie(Graph):
|
||||
if event.y == self.centery:
|
||||
angle = math.pi / 2
|
||||
else:
|
||||
angle = math.atan((event.x - self.centerx)/(self.centery - event.y))
|
||||
angle = math.atan((event.x - self.centerx)
|
||||
/ (self.centery - event.y))
|
||||
if event.x >= self.centerx:
|
||||
if event.y <= self.centery:
|
||||
pass
|
||||
@@ -139,12 +142,14 @@ class Pie(Graph):
|
||||
if self.yfields[0].get('widget') == 'float_time':
|
||||
conv = None
|
||||
if self.yfields[0].get('float_time'):
|
||||
conv = rpc.CONTEXT.get(self.yfields[0]['float_time'])
|
||||
conv = rpc.CONTEXT.get(
|
||||
self.yfields[0]['float_time'])
|
||||
value = float_time_to_text(slice.fraction * self.sum,
|
||||
conv)
|
||||
sum = float_time_to_text(self.sum, conv)
|
||||
else:
|
||||
value = locale.format('%.2f', slice.fraction * self.sum)
|
||||
value = locale.format('%.2f',
|
||||
slice.fraction * self.sum)
|
||||
sum = locale.format('%.2f', self.sum)
|
||||
label = '%s (%s%%)\n%s/%s' % (self.labels[slice.xname],
|
||||
locale.format('%.2f', slice.fraction * 100),
|
||||
@@ -181,8 +186,8 @@ class Slice(object):
|
||||
cr.new_path()
|
||||
cr.move_to(centerx, centery)
|
||||
cr.arc(centerx, centery, radius,
|
||||
self.startAngle - (math.pi/2),
|
||||
self.endAngle - (math.pi/2))
|
||||
self.startAngle - (math.pi / 2),
|
||||
self.endAngle - (math.pi / 2))
|
||||
cr.line_to(centerx, centery)
|
||||
cr.close_path()
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ from tryton.common.cellrendererbinary import CellRendererBinary
|
||||
from tryton.pyson import PYSONEncoder
|
||||
from tryton.gui.window import Window
|
||||
from tryton.exceptions import TrytonServerError
|
||||
import os
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
@@ -34,7 +33,8 @@ def path_convert_id2pos(model, id_path):
|
||||
current_id = id_path.pop(0)
|
||||
try:
|
||||
record = group.get(current_id)
|
||||
group = record.children_group(model.children_field, check_load=True)
|
||||
group = record.children_group(model.children_field,
|
||||
check_load=True)
|
||||
except (KeyError, AttributeError):
|
||||
return None
|
||||
return model.on_get_path(record)
|
||||
@@ -49,7 +49,7 @@ class AdaptModelGroup(gtk.GenericTreeModel):
|
||||
self.sort_asc = True
|
||||
self.set_property('leak_references', False)
|
||||
self.children_field = children_field
|
||||
self.__removed = None # XXX dirty hack to allow update of has_child
|
||||
self.__removed = None # XXX dirty hack to allow update of has_child
|
||||
|
||||
def added(self, group, record):
|
||||
if (group is self.group
|
||||
@@ -80,7 +80,7 @@ class AdaptModelGroup(gtk.GenericTreeModel):
|
||||
len(record.children_group(self.children_field)) <= 1):
|
||||
path = self.on_get_path(record.parent)
|
||||
iter_ = self.get_iter(path)
|
||||
self.__removed = record # XXX check for thread
|
||||
self.__removed = record # XXX check for thread
|
||||
self.row_has_child_toggled(path, iter_)
|
||||
self.__removed = None
|
||||
|
||||
@@ -313,30 +313,34 @@ class ViewList(ParserView):
|
||||
if screen.readonly:
|
||||
dnd = False
|
||||
if dnd:
|
||||
self.widget_tree.drag_source_set(gtk.gdk.BUTTON1_MASK | gtk.gdk.BUTTON3_MASK,
|
||||
[('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0),],
|
||||
gtk.gdk.ACTION_MOVE)
|
||||
self.widget_tree.drag_source_set(
|
||||
gtk.gdk.BUTTON1_MASK | gtk.gdk.BUTTON3_MASK,
|
||||
[('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0)],
|
||||
gtk.gdk.ACTION_MOVE)
|
||||
self.widget_tree.drag_dest_set(gtk.DEST_DEFAULT_ALL,
|
||||
[('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0),],
|
||||
gtk.gdk.ACTION_MOVE)
|
||||
[('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0)],
|
||||
gtk.gdk.ACTION_MOVE)
|
||||
|
||||
self.widget_tree.connect('drag-begin', self.drag_begin)
|
||||
self.widget_tree.connect('drag-motion', self.drag_motion)
|
||||
self.widget_tree.connect('drag-drop', self.drag_drop)
|
||||
self.widget_tree.connect("drag-data-get", self.drag_data_get)
|
||||
self.widget_tree.connect('drag-data-received', self.drag_data_received)
|
||||
self.widget_tree.connect('drag-data-received',
|
||||
self.drag_data_received)
|
||||
self.widget_tree.connect('drag-data-delete', self.drag_data_delete)
|
||||
|
||||
self.widget_tree.connect('key_press_event', self.on_keypress)
|
||||
if self.children_field:
|
||||
self.widget_tree.connect('test-expand-row', self.test_expand_row)
|
||||
self.widget_tree.set_expander_column(self.widget_tree.get_column(0))
|
||||
self.widget_tree.set_expander_column(
|
||||
self.widget_tree.get_column(0))
|
||||
|
||||
def get_fields(self):
|
||||
return [col.name for col in self.widget_tree.get_columns() if col.name]
|
||||
|
||||
def on_keypress(self, widget, event):
|
||||
if event.keyval == gtk.keysyms.c and event.state & gtk.gdk.CONTROL_MASK:
|
||||
if (event.keyval == gtk.keysyms.c
|
||||
and event.state & gtk.gdk.CONTROL_MASK):
|
||||
self.on_copy()
|
||||
return False
|
||||
if event.keyval in (gtk.keysyms.Down, gtk.keysyms.Up):
|
||||
@@ -347,7 +351,7 @@ class ViewList(ParserView):
|
||||
if event.keyval == gtk.keysyms.Down:
|
||||
test = True
|
||||
for i in xrange(len(path)):
|
||||
iter_ = model.get_iter(path[0:i+1])
|
||||
iter_ = model.get_iter(path[0:i + 1])
|
||||
if model.iter_next(iter_):
|
||||
test = False
|
||||
if test:
|
||||
@@ -390,7 +394,7 @@ class ViewList(ParserView):
|
||||
try:
|
||||
for field in fields:
|
||||
record.__getitem__(field, True)
|
||||
except TrytonServerError, exception:
|
||||
except TrytonServerError:
|
||||
return True
|
||||
iter_ = model.iter_next(iter_)
|
||||
return False
|
||||
@@ -419,7 +423,7 @@ class ViewList(ParserView):
|
||||
clipboard.set_with_data(targets, self.copy_get_func,
|
||||
self.copy_clear_func, selection)
|
||||
|
||||
def copy_foreach(self, treemodel, path,iter, data):
|
||||
def copy_foreach(self, treemodel, path, iter, data):
|
||||
record = treemodel.get_value(iter, 0)
|
||||
values = []
|
||||
for col in self.widget_tree.get_columns():
|
||||
@@ -465,6 +469,7 @@ class ViewList(ParserView):
|
||||
def drag_data_get(self, treeview, context, selection, target_id,
|
||||
etime):
|
||||
treeview.emit_stop_by_name('drag-data-get')
|
||||
|
||||
def _func_sel_get(store, path, iter_, data):
|
||||
value = store.get_value(iter_, 0)
|
||||
data.append(json.dumps(value.get_path(store.group)))
|
||||
@@ -496,6 +501,7 @@ class ViewList(ParserView):
|
||||
record = store.group.get_by_path(data)
|
||||
record_path = store.on_get_path(record)
|
||||
drop_info = treeview.get_dest_row_at_pos(x, y)
|
||||
|
||||
def check_recursion(from_, to):
|
||||
if not from_ or not to:
|
||||
return True
|
||||
@@ -508,7 +514,8 @@ class ViewList(ParserView):
|
||||
if drop_info:
|
||||
path, position = drop_info
|
||||
check_path = path
|
||||
if position in (gtk.TREE_VIEW_DROP_BEFORE, gtk.TREE_VIEW_DROP_AFTER):
|
||||
if position in (gtk.TREE_VIEW_DROP_BEFORE,
|
||||
gtk.TREE_VIEW_DROP_AFTER):
|
||||
check_path = path[:-1]
|
||||
if not check_recursion(record_path, check_path):
|
||||
return
|
||||
@@ -634,28 +641,32 @@ class ViewList(ParserView):
|
||||
def __getitem__(self, name):
|
||||
return None
|
||||
|
||||
def destroy(self):
|
||||
if CONFIG['client.save_width_height']:
|
||||
fields = {}
|
||||
last_col = None
|
||||
for col in self.widget_tree.get_columns():
|
||||
if col.get_visible():
|
||||
last_col = col
|
||||
if not hasattr(col, 'name') or not hasattr(col, 'width'):
|
||||
continue
|
||||
if col.get_width() != col.width and col.get_visible():
|
||||
fields[col.name] = col.get_width()
|
||||
#Don't set width for last visible columns
|
||||
#as it depends of the screen size
|
||||
if last_col and last_col.name in fields:
|
||||
del fields[last_col.name]
|
||||
def save_width_height(self):
|
||||
if not CONFIG['client.save_width_height']:
|
||||
return
|
||||
fields = {}
|
||||
last_col = None
|
||||
for col in self.widget_tree.get_columns():
|
||||
if col.get_visible():
|
||||
last_col = col
|
||||
if not hasattr(col, 'name') or not hasattr(col, 'width'):
|
||||
continue
|
||||
if col.get_width() != col.width and col.get_visible():
|
||||
fields[col.name] = col.get_width()
|
||||
#Don't set width for last visible columns
|
||||
#as it depends of the screen size
|
||||
if last_col and last_col.name in fields:
|
||||
del fields[last_col.name]
|
||||
|
||||
if fields and any(fields.itervalues()):
|
||||
try:
|
||||
rpc.execute('model', 'ir.ui.view_tree_width', 'set_width',
|
||||
self.screen.model_name, fields, rpc.CONTEXT)
|
||||
except TrytonServerError:
|
||||
pass
|
||||
if fields and any(fields.itervalues()):
|
||||
try:
|
||||
rpc.execute('model', 'ir.ui.view_tree_width', 'set_width',
|
||||
self.screen.model_name, fields, rpc.CONTEXT)
|
||||
except TrytonServerError:
|
||||
pass
|
||||
|
||||
def destroy(self):
|
||||
self.save_width_height()
|
||||
self.widget_tree.destroy()
|
||||
self.screen = None
|
||||
self.widget_tree = None
|
||||
@@ -710,7 +721,6 @@ class ViewList(ParserView):
|
||||
return True
|
||||
self.update_children()
|
||||
|
||||
|
||||
def set_value(self):
|
||||
if hasattr(self.widget_tree, 'editable') \
|
||||
and self.widget_tree.editable:
|
||||
@@ -773,11 +783,12 @@ class ViewList(ParserView):
|
||||
value += field_value
|
||||
|
||||
if loaded:
|
||||
label_str = locale.format('%.' + str(self.children[child][3]) + 'f',
|
||||
value_selected, True)
|
||||
label_str = locale.format(
|
||||
'%.' + str(self.children[child][3]) + 'f', value_selected,
|
||||
True)
|
||||
label_str += ' / '
|
||||
label_str += locale.format('%.' + str(self.children[child][3]) + 'f',
|
||||
value, True)
|
||||
label_str += locale.format(
|
||||
'%.' + str(self.children[child][3]) + 'f', value, True)
|
||||
else:
|
||||
label_str = '-'
|
||||
self.children[child][2].set_text(label_str)
|
||||
@@ -807,7 +818,8 @@ class ViewList(ParserView):
|
||||
break
|
||||
if path[:-1]:
|
||||
self.widget_tree.expand_to_path(path[:-1])
|
||||
self.widget_tree.scroll_to_cell(path, focus_column, use_align=False)
|
||||
self.widget_tree.scroll_to_cell(path, focus_column,
|
||||
use_align=False)
|
||||
self.widget_tree.set_cursor(path, focus_column, new)
|
||||
|
||||
def sel_ids_get(self):
|
||||
|
||||
@@ -38,7 +38,6 @@ class EditableTreeView(gtk.TreeView):
|
||||
|
||||
def on_open_remote(self, current_record, fieldname, create, value,
|
||||
entry=None, callback=None):
|
||||
field = current_record[fieldname]
|
||||
cell = self.cells[fieldname]
|
||||
if value != cell.get_textual_value(current_record) or not value:
|
||||
changed = True
|
||||
@@ -151,6 +150,7 @@ class EditableTreeView(gtk.TreeView):
|
||||
else:
|
||||
txt = entry.get_active_text()
|
||||
entry.handler_block(entry.editing_done_id)
|
||||
|
||||
def callback():
|
||||
entry.handler_unblock(entry.editing_done_id)
|
||||
self.set_cursor(path, column, True)
|
||||
@@ -201,9 +201,9 @@ class EditableTreeView(gtk.TreeView):
|
||||
break
|
||||
entry.handler_block(entry.editing_done_id)
|
||||
if self.editable == 'top':
|
||||
new_path = self._key_up(path, model, col)
|
||||
self._key_up(path, model, col)
|
||||
else:
|
||||
new_path = self._key_down(path, model, column)
|
||||
self._key_down(path, model, column)
|
||||
entry.handler_unblock(entry.editing_done_id)
|
||||
elif event.keyval in (gtk.keysyms.F3, gtk.keysyms.F2):
|
||||
if isinstance(entry, gtk.Entry):
|
||||
@@ -211,6 +211,7 @@ class EditableTreeView(gtk.TreeView):
|
||||
else:
|
||||
value = entry.get_active_text()
|
||||
entry.handler_block(entry.editing_done_id)
|
||||
|
||||
def callback():
|
||||
cell = self.cells[column.name]
|
||||
value = cell.get_textual_value(record)
|
||||
@@ -221,13 +222,14 @@ class EditableTreeView(gtk.TreeView):
|
||||
entry.handler_unblock(entry.editing_done_id)
|
||||
self.set_cursor(path, column, True)
|
||||
self.on_open_remote(record, column.name,
|
||||
create=(event.keyval==gtk.keysyms.F3), value=value,
|
||||
create=(event.keyval == gtk.keysyms.F3), value=value,
|
||||
callback=callback)
|
||||
else:
|
||||
field = record[column.name]
|
||||
if isinstance(entry, gtk.Entry):
|
||||
entry.set_max_length(int(field.attrs.get('size', 0)))
|
||||
# store in the record the entry widget to get the value in set_value
|
||||
# store in the record the entry widget to get the value in
|
||||
# set_value
|
||||
field.editabletree_entry = entry
|
||||
record.modified_fields.setdefault(column.name)
|
||||
return False
|
||||
|
||||
@@ -12,7 +12,7 @@ from tryton.gui.window.view_form.screen import Screen
|
||||
from tryton.config import CONFIG
|
||||
import tryton.rpc as rpc
|
||||
from tryton.common import DT_FORMAT, DHM_FORMAT, COLORS, node_attributes, \
|
||||
TRYTON_ICON, HM_FORMAT, file_selection, file_open
|
||||
HM_FORMAT, file_selection, file_open
|
||||
import tryton.common as common
|
||||
from tryton.exceptions import TrytonError, TrytonServerError
|
||||
from tryton.common.cellrendererbutton import CellRendererButton
|
||||
@@ -25,7 +25,6 @@ from tryton.common.cellrendererfloat import CellRendererFloat
|
||||
from tryton.common.cellrendererbinary import CellRendererBinary
|
||||
from tryton.action import Action
|
||||
from tryton.translate import date_format
|
||||
from tryton.pyson import PYSONDecoder
|
||||
import gtk
|
||||
import locale
|
||||
import datetime
|
||||
@@ -35,6 +34,7 @@ import operator
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
def send_keys(renderer, editable, position, treeview):
|
||||
editable.connect('key_press_event', treeview.on_keypressed)
|
||||
editable.editing_done_id = editable.connect('editing_done',
|
||||
@@ -42,6 +42,7 @@ def send_keys(renderer, editable, position, treeview):
|
||||
if isinstance(editable, (gtk.ComboBoxEntry, gtk.ComboBox)):
|
||||
editable.connect('changed', treeview.on_editing_done)
|
||||
|
||||
|
||||
def sort_model(column, treeview, screen):
|
||||
for col in treeview.get_columns():
|
||||
if col != column:
|
||||
@@ -71,6 +72,7 @@ def sort_model(column, treeview, screen):
|
||||
for record in unsaved_records:
|
||||
store.group.append(record)
|
||||
|
||||
|
||||
class ParserTree(ParserInterface):
|
||||
|
||||
def __init__(self, parent=None, attrs=None, screen=None,
|
||||
@@ -142,6 +144,7 @@ class ParserTree(ParserInterface):
|
||||
render_pixbuf = gtk.CellRendererPixbuf()
|
||||
col.pack_start(render_pixbuf, expand=False)
|
||||
icon = node_attrs['icon']
|
||||
|
||||
def setter(column, cell, store, iter):
|
||||
record = store.get_value(iter, 0)
|
||||
value = record[icon].get_client(record) or ''
|
||||
@@ -242,6 +245,7 @@ class ParserTree(ParserInterface):
|
||||
col.set_widget(label)
|
||||
|
||||
col._type = 'button'
|
||||
col.set_cell_data_func(renderer, cell.setter)
|
||||
if 'width' in node_attrs:
|
||||
width = int(node_attrs['width'])
|
||||
else:
|
||||
@@ -328,7 +332,8 @@ class Char(object):
|
||||
bg_color = 'white'
|
||||
if not field.get_state_attrs(record).get('valid', True):
|
||||
bg_color = COLORS.get('invalid', 'white')
|
||||
elif bool(int(field.get_state_attrs(record).get('required', 0))):
|
||||
elif bool(int(
|
||||
field.get_state_attrs(record).get('required', 0))):
|
||||
bg_color = COLORS.get('required', 'white')
|
||||
cell.set_property('background', bg_color)
|
||||
if bg_color == 'white':
|
||||
@@ -366,6 +371,7 @@ class Char(object):
|
||||
if callback:
|
||||
callback()
|
||||
|
||||
|
||||
class Int(Char):
|
||||
|
||||
def __init__(self, field_name, model_name, treeview, attrs=None):
|
||||
@@ -383,6 +389,7 @@ class Int(Char):
|
||||
return locale.format('%d',
|
||||
record[self.field_name].get_client(record) or 0, True)
|
||||
|
||||
|
||||
class Boolean(Int):
|
||||
|
||||
def __init__(self, field_name, model_name, treeview, attrs=None):
|
||||
@@ -484,7 +491,7 @@ class Float(Char):
|
||||
def get_textual_value(self, record):
|
||||
field = record[self.field_name]
|
||||
digit = record.expr_eval(field.attrs.get('digits', (16, 2)))[1]
|
||||
return locale.format('%.'+str(digit)+'f',
|
||||
return locale.format('%.' + str(digit) + 'f',
|
||||
record[self.field_name].get_client(record) or 0.0, True)
|
||||
|
||||
def value_from_text(self, record, text, callback=None):
|
||||
@@ -513,7 +520,9 @@ class FloatTime(Char):
|
||||
|
||||
def value_from_text(self, record, text, callback=None):
|
||||
field = record[self.field_name]
|
||||
field.set_client(record, common.text_to_float_time(text, self.conv))
|
||||
digits = record.expr_eval(field.attrs.get('digits', (16, 2)))
|
||||
field.set_client(record,
|
||||
round(common.text_to_float_time(text, self.conv), digits[1]))
|
||||
if callback:
|
||||
callback()
|
||||
|
||||
@@ -582,7 +591,7 @@ class Binary(Char):
|
||||
filename = file_selection(_('Save As...'), filename=filename,
|
||||
action=gtk.FILE_CHOOSER_ACTION_SAVE)
|
||||
if filename:
|
||||
with open(filename,'wb') as fp:
|
||||
with open(filename, 'wb') as fp:
|
||||
fp.write(field.get_data(record))
|
||||
|
||||
def clear_binary(self, renderer, path):
|
||||
@@ -681,6 +690,7 @@ class M2O(Char):
|
||||
def search_remote(self, record, relation, ids=None, domain=None,
|
||||
context=None, callback=None):
|
||||
field = record.group.fields[self.field_name]
|
||||
|
||||
def search_callback(found):
|
||||
value = None
|
||||
if found:
|
||||
@@ -733,6 +743,7 @@ class O2M(Char):
|
||||
screen = Screen(relation, mode=['tree', 'form'],
|
||||
exclude_field=field.attrs.get('relation_field'))
|
||||
screen.group = group
|
||||
|
||||
def open_callback(result):
|
||||
if callback:
|
||||
callback()
|
||||
@@ -781,6 +792,7 @@ class M2M(Char):
|
||||
return
|
||||
if not callback:
|
||||
return
|
||||
|
||||
def winsearch_callback(result):
|
||||
field.set_client(record, result or [])
|
||||
if callback:
|
||||
@@ -807,20 +819,21 @@ class M2M(Char):
|
||||
field.set_client(record, False)
|
||||
if callback:
|
||||
callback()
|
||||
if ids and len(ids)==1:
|
||||
if ids and len(ids) == 1:
|
||||
field.set_client(record, ids)
|
||||
if callback:
|
||||
callback()
|
||||
return
|
||||
else:
|
||||
ids = [x.id for x in field.get_client(record)]
|
||||
|
||||
def open_callback(result):
|
||||
if result:
|
||||
field.set_client(record, result)
|
||||
if callback:
|
||||
callback()
|
||||
WinSearch(relation, open_callback, sel_multi=True, ids=ids, context=context,
|
||||
domain=domain)
|
||||
WinSearch(relation, open_callback, sel_multi=True, ids=ids,
|
||||
context=context, domain=domain)
|
||||
|
||||
|
||||
class Selection(Char):
|
||||
@@ -1002,6 +1015,15 @@ class Button(object):
|
||||
|
||||
self.renderer.connect('clicked', self.button_clicked)
|
||||
|
||||
def setter(self, column, cell, store, iter):
|
||||
record = store.get_value(iter, 0)
|
||||
states = record.expr_eval(self.attrs.get('states', {}),
|
||||
check_load=False)
|
||||
invisible = states.get('invisible', False)
|
||||
cell.set_property('visible', not invisible)
|
||||
# TODO readonly
|
||||
# TODO icon
|
||||
|
||||
def button_clicked(self, widget, path):
|
||||
if not path:
|
||||
return True
|
||||
|
||||
@@ -6,6 +6,7 @@ import tryton.common as common
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
class ScreenContainer(object):
|
||||
|
||||
def __init__(self):
|
||||
@@ -139,7 +140,7 @@ class ScreenContainer(object):
|
||||
model.append([r.strip()])
|
||||
|
||||
def clear(self, widget=None):
|
||||
self.search_entry.set_text('');
|
||||
self.search_entry.set_text('')
|
||||
|
||||
def get_text(self):
|
||||
return self.search_entry.get_text().strip().decode('utf-8')
|
||||
|
||||
@@ -20,11 +20,9 @@ class WinExport(object):
|
||||
|
||||
def __init__(self, model, ids, context=None):
|
||||
self.parent = common.get_toplevel_window()
|
||||
self.dialog = gtk.Dialog(
|
||||
title= _("Export to CSV"),
|
||||
parent=self.parent,
|
||||
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT
|
||||
| gtk.WIN_POS_CENTER_ON_PARENT)
|
||||
self.dialog = gtk.Dialog(title=_("Export to CSV"), parent=self.parent,
|
||||
flags=gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT |
|
||||
gtk.WIN_POS_CENTER_ON_PARENT)
|
||||
self.dialog.set_icon(TRYTON_ICON)
|
||||
|
||||
vbox = gtk.VBox()
|
||||
@@ -327,7 +325,7 @@ class WinExport(object):
|
||||
try:
|
||||
new_id = rpc.execute(*args)
|
||||
except TrytonServerError, exception:
|
||||
new_ids = common.process_exception(exception, self.dialog, *args)
|
||||
new_id = common.process_exception(exception, self.dialog, *args)
|
||||
if not new_id:
|
||||
return
|
||||
self.predef_model.append((
|
||||
@@ -419,7 +417,7 @@ class WinExport(object):
|
||||
row = []
|
||||
for val in data:
|
||||
if type(val) == types.StringType:
|
||||
row.append(val.replace('\n',' ').replace('\t',' '))
|
||||
row.append(val.replace('\n', ' ').replace('\t', ' '))
|
||||
else:
|
||||
row.append(val)
|
||||
writer.writerow(row)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#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.common import TRYTON_ICON
|
||||
from tryton.common import TRYTON_ICON, COLOR_SCHEMES
|
||||
import tryton.common as common
|
||||
from tryton.config import CONFIG
|
||||
import gtk
|
||||
@@ -8,6 +8,7 @@ import pango
|
||||
import gettext
|
||||
from tryton.exceptions import TrytonServerError
|
||||
from tryton.gui.window.nomodal import NoModal
|
||||
import tryton.rpc as rpc
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
@@ -56,7 +57,7 @@ class WinForm(NoModal):
|
||||
if new and many:
|
||||
self.but_ok.add_accelerator('clicked',
|
||||
self.accel_group, gtk.keysyms.Return,
|
||||
gtk.gdk.CONTROL_MASK|gtk.gdk.SHIFT_MASK,
|
||||
gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK,
|
||||
gtk.ACCEL_VISIBLE)
|
||||
|
||||
self.but_new = self.win.add_button(gtk.STOCK_NEW,
|
||||
@@ -130,7 +131,8 @@ class WinForm(NoModal):
|
||||
tooltips.set_tip(self.but_del, _('Delete selected record'))
|
||||
self.but_del.connect('clicked', self._sig_remove)
|
||||
img_del = gtk.Image()
|
||||
img_del.set_from_stock('tryton-delete', gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_del.set_from_stock('tryton-delete',
|
||||
gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_del.set_alignment(0.5, 0.5)
|
||||
self.but_del.add(img_del)
|
||||
self.but_del.set_relief(gtk.RELIEF_NONE)
|
||||
@@ -142,7 +144,8 @@ class WinForm(NoModal):
|
||||
tooltips.set_tip(but_pre, _('Previous'))
|
||||
but_pre.connect('clicked', self._sig_previous)
|
||||
img_pre = gtk.Image()
|
||||
img_pre.set_from_stock('tryton-go-previous', gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_pre.set_from_stock('tryton-go-previous',
|
||||
gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_pre.set_alignment(0.5, 0.5)
|
||||
but_pre.add(img_pre)
|
||||
but_pre.set_relief(gtk.RELIEF_NONE)
|
||||
@@ -155,7 +158,8 @@ class WinForm(NoModal):
|
||||
tooltips.set_tip(but_next, _('Next'))
|
||||
but_next.connect('clicked', self._sig_next)
|
||||
img_next = gtk.Image()
|
||||
img_next.set_from_stock('tryton-go-next', gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_next.set_from_stock('tryton-go-next',
|
||||
gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_next.set_alignment(0.5, 0.5)
|
||||
but_next.add(img_next)
|
||||
but_next.set_relief(gtk.RELIEF_NONE)
|
||||
@@ -167,7 +171,8 @@ class WinForm(NoModal):
|
||||
tooltips.set_tip(but_switch, _('Switch'))
|
||||
but_switch.connect('clicked', self.switch_view)
|
||||
img_switch = gtk.Image()
|
||||
img_switch.set_from_stock('tryton-fullscreen', gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_switch.set_from_stock('tryton-fullscreen',
|
||||
gtk.ICON_SIZE_SMALL_TOOLBAR)
|
||||
img_switch.set_alignment(0.5, 0.5)
|
||||
but_switch.add(img_switch)
|
||||
but_switch.set_relief(gtk.RELIEF_NONE)
|
||||
@@ -251,6 +256,7 @@ class WinForm(NoModal):
|
||||
from tryton.gui.window.win_search import WinSearch
|
||||
domain = []
|
||||
context = rpc.CONTEXT.copy()
|
||||
model_name = self.screen.model_name
|
||||
|
||||
try:
|
||||
if self.wid_text.get_text():
|
||||
@@ -258,7 +264,7 @@ class WinForm(NoModal):
|
||||
'%' + self.wid_text.get_text() + '%'), domain]
|
||||
else:
|
||||
dom = domain
|
||||
ids = rpc.execute('model', self.attrs['relation'], 'search', dom,
|
||||
ids = rpc.execute('model', model_name, 'search', dom,
|
||||
0, CONFIG['client.limit'], None, context)
|
||||
except TrytonServerError, exception:
|
||||
common.process_exception(exception)
|
||||
@@ -274,9 +280,8 @@ class WinForm(NoModal):
|
||||
self.wid_text.set_text('')
|
||||
|
||||
if len(ids) != 1:
|
||||
WinSearch(self.attrs['relation'], callback, sel_multi=True,
|
||||
ids=ids, context=context, domain=domain,
|
||||
views_preload=self.attrs.get('views', {}))
|
||||
WinSearch(model_name, callback, sel_multi=True,
|
||||
ids=ids, context=context, domain=domain)
|
||||
else:
|
||||
callback(ids)
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class WinImport(object):
|
||||
hbox_mapping.pack_start(vbox_buttons, False, True, 0)
|
||||
|
||||
button_add = gtk.Button(_("_Add"), stock=None, use_underline=True)
|
||||
button_add.set_alignment(0.0,0.0)
|
||||
button_add.set_alignment(0.0, 0.0)
|
||||
img_button = gtk.Image()
|
||||
img_button.set_from_stock('tryton-list-add', gtk.ICON_SIZE_BUTTON)
|
||||
button_add.set_image(img_button)
|
||||
@@ -54,7 +54,7 @@ class WinImport(object):
|
||||
|
||||
button_remove = gtk.Button(_("_Remove"), stock=None,
|
||||
use_underline=True)
|
||||
button_remove.set_alignment(0.0,0.0)
|
||||
button_remove.set_alignment(0.0, 0.0)
|
||||
img_button = gtk.Image()
|
||||
img_button.set_from_stock('tryton-list-remove', gtk.ICON_SIZE_BUTTON)
|
||||
button_remove.set_image(img_button)
|
||||
@@ -63,7 +63,7 @@ class WinImport(object):
|
||||
|
||||
button_remove_all = gtk.Button(_("Clear"), stock=None,
|
||||
use_underline=True)
|
||||
button_remove_all.set_alignment(0.0,0.0)
|
||||
button_remove_all.set_alignment(0.0, 0.0)
|
||||
img_button = gtk.Image()
|
||||
img_button.set_from_stock('tryton-clear', gtk.ICON_SIZE_BUTTON)
|
||||
button_remove_all.set_image(img_button)
|
||||
@@ -75,7 +75,7 @@ class WinImport(object):
|
||||
|
||||
button_autodetect = gtk.Button(_("Auto-Detect"), stock=None,
|
||||
use_underline=True)
|
||||
button_autodetect.set_alignment(0.0,0.0)
|
||||
button_autodetect.set_alignment(0.0, 0.0)
|
||||
img_button = gtk.Image()
|
||||
img_button.set_from_stock('tryton-find', gtk.ICON_SIZE_BUTTON)
|
||||
button_autodetect.set_image(img_button)
|
||||
@@ -132,15 +132,15 @@ class WinImport(object):
|
||||
table.attach(self.import_csv_sep, 1, 2, 0, 1)
|
||||
|
||||
label_import_csv_del = gtk.Label(_("Text Delimiter:"))
|
||||
label_import_csv_del.set_alignment( 1, 0.5)
|
||||
label_import_csv_del.set_alignment(1, 0.5)
|
||||
table.attach(label_import_csv_del, 2, 3, 0, 1)
|
||||
self.import_csv_del = gtk.Entry()
|
||||
self.import_csv_del.set_text( "\"")
|
||||
self.import_csv_del.set_width_chars( 1)
|
||||
self.import_csv_del.set_text("\"")
|
||||
self.import_csv_del.set_width_chars(1)
|
||||
table.attach(self.import_csv_del, 3, 4, 0, 1)
|
||||
|
||||
label_import_csv_enc = gtk.Label(_("Encoding:"))
|
||||
label_import_csv_enc.set_alignment( 1, 0.5)
|
||||
label_import_csv_enc.set_alignment(1, 0.5)
|
||||
table.attach(label_import_csv_enc, 0, 1, 1, 2)
|
||||
self.import_csv_enc = gtk.combo_box_new_text()
|
||||
self.import_csv_enc.append_text("UTF-8")
|
||||
@@ -148,7 +148,7 @@ class WinImport(object):
|
||||
table.attach(self.import_csv_enc, 1, 2, 1, 2)
|
||||
|
||||
label_import_csv_skip = gtk.Label(_("Lines to Skip:"))
|
||||
label_import_csv_skip.set_alignment( 1, 0.5)
|
||||
label_import_csv_skip.set_alignment(1, 0.5)
|
||||
table.attach(label_import_csv_skip, 2, 3, 1, 2)
|
||||
|
||||
self.import_csv_skip_adj = gtk.Adjustment(0, 0, 100, 1, 10)
|
||||
@@ -266,7 +266,6 @@ class WinImport(object):
|
||||
if word not in self.fields_invert and word not in self.fields:
|
||||
iter = self.model1.get_iter_first()
|
||||
prefix = ''
|
||||
prefix_name = ''
|
||||
for parent in word.split('/')[:-1]:
|
||||
while iter:
|
||||
if self.model1.get_value(iter, 0) == parent or \
|
||||
@@ -300,7 +299,7 @@ class WinImport(object):
|
||||
def _sig_sel_add(self, store, path, iter):
|
||||
num = self.model2.append()
|
||||
name = self.fields[store.get_value(iter, 1)][0]
|
||||
self.model2.set(num, 0, name, 1, store.get_value(iter,1))
|
||||
self.model2.set(num, 0, name, 1, store.get_value(iter, 1))
|
||||
|
||||
def sig_unsel(self, widget=None):
|
||||
store, paths = self.view2.get_selection().get_selected_rows()
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import gtk
|
||||
import gettext
|
||||
import tryton.common as common
|
||||
import tryton.rpc as rpc
|
||||
from tryton.gui.window.view_form.screen import Screen
|
||||
from tryton.config import TRYTON_ICON
|
||||
from tryton.gui.window.win_form import WinForm
|
||||
@@ -34,7 +33,8 @@ class WinSearch(NoModal):
|
||||
self.accel_group = gtk.AccelGroup()
|
||||
self.win.add_accel_group(self.accel_group)
|
||||
|
||||
self.but_cancel = self.win.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
|
||||
self.but_cancel = self.win.add_button(gtk.STOCK_CANCEL,
|
||||
gtk.RESPONSE_CANCEL)
|
||||
self.but_find = self.win.add_button(gtk.STOCK_FIND, gtk.RESPONSE_APPLY)
|
||||
self.but_ok = self.win.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
|
||||
self.but_ok.add_accelerator('clicked', self.accel_group,
|
||||
@@ -118,6 +118,7 @@ class WinSearch(NoModal):
|
||||
elif response_id == gtk.RESPONSE_ACCEPT:
|
||||
screen = Screen(self.model_name, domain=self.domain,
|
||||
context=self.context, mode=['form'])
|
||||
|
||||
def callback(result):
|
||||
if result and screen.save_current():
|
||||
res = [screen.current_record.id]
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#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 tryton.rpc as rpc
|
||||
from preference import *
|
||||
|
||||
|
||||
class Window(object):
|
||||
|
||||
@@ -9,7 +9,6 @@ import tryton.common as common
|
||||
from tryton.gui.window.view_form.screen import Screen
|
||||
from tryton.gui import Main
|
||||
from tryton.exceptions import TrytonServerError
|
||||
from tryton.config import CONFIG
|
||||
from tryton.gui.window.nomodal import NoModal
|
||||
from tryton.common.button import Button
|
||||
_ = gettext.gettext
|
||||
@@ -74,7 +73,9 @@ class Wizard(object):
|
||||
ctx['active_ids'] = self.ids
|
||||
ctx['active_model'] = self.model
|
||||
if self.screen:
|
||||
data = {self.screen_state: self.screen.get_on_change_value()}
|
||||
data = {
|
||||
self.screen_state: self.screen.get_on_change_value(),
|
||||
}
|
||||
else:
|
||||
data = {}
|
||||
rpcprogress = common.RPCProgress('execute', ('wizard',
|
||||
@@ -230,7 +231,7 @@ class Wizard(object):
|
||||
self.screen.set_cursor()
|
||||
|
||||
|
||||
class WizardForm(Wizard,SignalEvent):
|
||||
class WizardForm(Wizard, SignalEvent):
|
||||
"Wizard"
|
||||
|
||||
def __init__(self, name=False):
|
||||
|
||||
+2
-1
@@ -12,6 +12,7 @@ from tryton.config import get_config_dir
|
||||
|
||||
__all__ = ['Server', 'Client']
|
||||
|
||||
|
||||
class IPCServer(object):
|
||||
|
||||
thread = None
|
||||
@@ -112,7 +113,7 @@ class FIFOServer(IPCServer):
|
||||
pass
|
||||
|
||||
def _read(self):
|
||||
fifo = os.fdopen(os.open(self.filename, os.O_RDONLY|os.O_NONBLOCK))
|
||||
fifo = os.fdopen(os.open(self.filename, os.O_RDONLY | os.O_NONBLOCK))
|
||||
data = ''
|
||||
while self.running:
|
||||
try:
|
||||
|
||||
+11
-8
@@ -29,13 +29,14 @@ class ResponseError(xmlrpclib.ResponseError):
|
||||
class Fault(xmlrpclib.Fault):
|
||||
|
||||
def __init__(self, faultCode, faultString='', **extra):
|
||||
super(Fault, self).__init__(faultCode, str(faultString), **extra)
|
||||
super(Fault, self).__init__(faultCode, faultString, **extra)
|
||||
self.args = faultString
|
||||
|
||||
|
||||
class ProtocolError(xmlrpclib.ProtocolError):
|
||||
pass
|
||||
|
||||
|
||||
def object_hook(dct):
|
||||
if '__class__' in dct:
|
||||
if dct['__class__'] == 'datetime':
|
||||
@@ -109,7 +110,7 @@ class JSONUnmarshaller(object):
|
||||
class Transport(xmlrpclib.Transport, xmlrpclib.SafeTransport):
|
||||
|
||||
accept_gzip_encoding = True
|
||||
encode_threshold = 1400 # common MTU
|
||||
encode_threshold = 1400 # common MTU
|
||||
|
||||
def __init__(self, fingerprints=None, ca_certs=None):
|
||||
xmlrpclib.Transport.__init__(self)
|
||||
@@ -123,7 +124,8 @@ class Transport(xmlrpclib.Transport, xmlrpclib.SafeTransport):
|
||||
return parser, target
|
||||
|
||||
def get_host_info(self, host):
|
||||
host, extra_headers, x509 = xmlrpclib.Transport.get_host_info(self, host)
|
||||
host, extra_headers, x509 = xmlrpclib.Transport.get_host_info(
|
||||
self, host)
|
||||
if extra_headers is None:
|
||||
extra_headers = []
|
||||
extra_headers.append(('Connection', 'keep-alive'))
|
||||
@@ -151,11 +153,11 @@ class Transport(xmlrpclib.Transport, xmlrpclib.SafeTransport):
|
||||
return self._connection[1]
|
||||
host, extra_headers, x509 = self.get_host_info(host)
|
||||
|
||||
ca_certs = self.__ca_certs
|
||||
ca_certs = self.__ca_certs
|
||||
cert_reqs = ssl.CERT_REQUIRED if ca_certs else ssl.CERT_NONE
|
||||
|
||||
|
||||
class HTTPSConnection(httplib.HTTPSConnection):
|
||||
|
||||
def connect(self):
|
||||
sock = socket.create_connection((self.host, self.port),
|
||||
self.timeout)
|
||||
@@ -180,12 +182,13 @@ class Transport(xmlrpclib.Transport, xmlrpclib.SafeTransport):
|
||||
peercert = sock.getpeercert(True)
|
||||
except socket.error:
|
||||
peercert = None
|
||||
|
||||
def format_hash(value):
|
||||
return reduce(lambda x, y: x + y[1].upper() +
|
||||
((y[0] % 2 and y[0] + 1 < len(value)) and ':' or ''),
|
||||
enumerate(value), '')
|
||||
((y[0] % 2 and y[0] + 1 < len(value)) and ':' or ''),
|
||||
enumerate(value), '')
|
||||
return format_hash(hashlib.sha1(peercert).hexdigest())
|
||||
except ssl.SSLError, e:
|
||||
except ssl.SSLError:
|
||||
http_connection()
|
||||
|
||||
fingerprint = ''
|
||||
|
||||
@@ -21,11 +21,13 @@ if os.path.isdir(PLUGINS_PATH):
|
||||
continue
|
||||
module = os.path.splitext(plugin)[0]
|
||||
try:
|
||||
module = imp.load_module(module, *imp.find_module(module, [PLUGINS_PATH]))
|
||||
module = imp.load_module(module, *imp.find_module(module,
|
||||
[PLUGINS_PATH]))
|
||||
MODULES.append(module)
|
||||
except ImportError, exception:
|
||||
continue
|
||||
|
||||
|
||||
def execute(datas):
|
||||
result = {}
|
||||
|
||||
|
||||
@@ -5,12 +5,14 @@ import gettext
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
def translate_view(datas):
|
||||
model = datas['model']
|
||||
Window.create(False, 'ir.translation', res_id=False,
|
||||
domain=[('model', '=', model)],
|
||||
mode=['tree', 'form'])
|
||||
|
||||
|
||||
def get_plugins(model):
|
||||
return [
|
||||
(_('Translate view'), translate_view),
|
||||
|
||||
@@ -5,18 +5,21 @@ import gettext
|
||||
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
def workflow_print(datas):
|
||||
datas = datas.copy()
|
||||
datas['nested'] = False
|
||||
Action.exec_report('workflow.instance.graph', datas)
|
||||
return True
|
||||
|
||||
|
||||
def workflow_print_complex(datas):
|
||||
datas = datas.copy()
|
||||
datas['nested'] = True
|
||||
Action.exec_report('workflow.instance.graph', datas)
|
||||
return True
|
||||
|
||||
|
||||
def get_plugins(model):
|
||||
return [
|
||||
(_('Print Workflow'), workflow_print),
|
||||
|
||||
+3
-3
@@ -1,6 +1,5 @@
|
||||
#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 sys
|
||||
try:
|
||||
import simplejson as json
|
||||
except ImportError:
|
||||
@@ -22,7 +21,7 @@ class PYSON(object):
|
||||
raise NotImplementedError
|
||||
|
||||
def __invert__(self):
|
||||
if self.types()!= set([bool]):
|
||||
if self.types() != set([bool]):
|
||||
return Not(Bool(self))
|
||||
else:
|
||||
return Not(self)
|
||||
@@ -307,7 +306,8 @@ class If(PYSON):
|
||||
def __init__(self, condition, then_statement, else_statement=None):
|
||||
super(If, self).__init__()
|
||||
if isinstance(condition, PYSON):
|
||||
assert condition.types() == set([bool]), 'condition must be boolean'
|
||||
assert condition.types() == set([bool]), \
|
||||
'condition must be boolean'
|
||||
else:
|
||||
assert isinstance(condition, bool), 'condition must be boolean'
|
||||
if isinstance(then_statement, PYSON):
|
||||
|
||||
+15
-5
@@ -30,7 +30,9 @@ if not os.path.isfile(_CA_CERTS):
|
||||
_CA_CERTS = None
|
||||
_FINGERPRINTS = Fingerprints()
|
||||
|
||||
ServerProxy = partial(ServerProxy, fingerprints=_FINGERPRINTS, ca_certs=_CA_CERTS)
|
||||
ServerProxy = partial(ServerProxy, fingerprints=_FINGERPRINTS,
|
||||
ca_certs=_CA_CERTS)
|
||||
|
||||
|
||||
def db_list(host, port):
|
||||
try:
|
||||
@@ -46,6 +48,7 @@ def db_list(host, port):
|
||||
logging.getLogger('rpc.result').debug(repr(None))
|
||||
return None
|
||||
|
||||
|
||||
def db_exec(host, port, method, *args):
|
||||
connection = ServerProxy(host, port)
|
||||
logging.getLogger('rpc.request').info('common.db.%s(None, None, %s)' %
|
||||
@@ -54,6 +57,7 @@ def db_exec(host, port, method, *args):
|
||||
logging.getLogger('rpc.result').debug(repr(result))
|
||||
return result
|
||||
|
||||
|
||||
def server_version(host, port):
|
||||
try:
|
||||
connection = ServerProxy(host, port)
|
||||
@@ -65,9 +69,10 @@ def server_version(host, port):
|
||||
except (Fault, socket.error):
|
||||
raise
|
||||
|
||||
|
||||
def login(username, password, host, port, database):
|
||||
global CONNECTION, _USER, _USERNAME, _SESSION, _HOST, _PORT, _DATABASE, _VIEW_CACHE
|
||||
global _TOOLBAR_CACHE
|
||||
global CONNECTION, _USER, _USERNAME, _SESSION, _HOST, _PORT, _DATABASE
|
||||
global _VIEW_CACHE, _TOOLBAR_CACHE
|
||||
_VIEW_CACHE = {}
|
||||
_TOOLBAR_CACHE = {}
|
||||
try:
|
||||
@@ -99,9 +104,10 @@ def login(username, password, host, port, database):
|
||||
IPCServer(host, port, database).run()
|
||||
return 1
|
||||
|
||||
|
||||
def logout():
|
||||
global CONNECTION, _USER, _USERNAME, _SESSION, _HOST, _PORT, _DATABASE, _VIEW_CACHE
|
||||
global _TOOLBAR_CACHE
|
||||
global CONNECTION, _USER, _USERNAME, _SESSION, _HOST, _PORT, _DATABASE
|
||||
global _VIEW_CACHE, _TOOLBAR_CACHE
|
||||
if IPCServer.instance:
|
||||
IPCServer.instance.stop()
|
||||
if CONNECTION is not None:
|
||||
@@ -125,6 +131,7 @@ def logout():
|
||||
_VIEW_CACHE = {}
|
||||
_TOOLBAR_CACHE = {}
|
||||
|
||||
|
||||
def context_reload():
|
||||
global CONTEXT, TIMEZONE, _HOST, _PORT
|
||||
try:
|
||||
@@ -142,6 +149,7 @@ def context_reload():
|
||||
except Fault:
|
||||
pass
|
||||
|
||||
|
||||
def _execute(blocking, *args):
|
||||
global CONNECTION, _USER, _SESSION
|
||||
if CONNECTION is None:
|
||||
@@ -186,8 +194,10 @@ def _execute(blocking, *args):
|
||||
logging.getLogger('rpc.result').debug(repr(result))
|
||||
return result
|
||||
|
||||
|
||||
def execute(*args):
|
||||
return _execute(True, *args)
|
||||
|
||||
|
||||
def execute_nonblocking(*args):
|
||||
return _execute(False, *args)
|
||||
+3
-1
@@ -5,7 +5,7 @@ import os
|
||||
import locale
|
||||
import gettext
|
||||
from version import PACKAGE
|
||||
from tryton.client import CURRENT_DIR, PREFIX
|
||||
from tryton.config import CURRENT_DIR, PREFIX
|
||||
import logging
|
||||
import gtk
|
||||
import sys
|
||||
@@ -197,12 +197,14 @@ def setlang(lang=None, locale_dict=None):
|
||||
conv[field] = locale_dict[field]
|
||||
locale.localeconv = lambda: conv
|
||||
|
||||
|
||||
def set_language_direction(direction):
|
||||
if direction == 'rtl':
|
||||
gtk.widget_set_default_direction(gtk.TEXT_DIR_RTL)
|
||||
else:
|
||||
gtk.widget_set_default_direction(gtk.TEXT_DIR_LTR)
|
||||
|
||||
|
||||
def date_format():
|
||||
'''
|
||||
Return the locale date format
|
||||
|
||||
@@ -4,4 +4,3 @@ PACKAGE = "tryton"
|
||||
VERSION = "2.3.0"
|
||||
LICENSE = "GPL-3"
|
||||
WEBSITE = "http://www.tryton.org/"
|
||||
|
||||
Reference in new issue
Block a user