diff --git a/tryton/gui/main.py b/tryton/gui/main.py
index 83a7ab9100..56d11bc52f 100644
--- a/tryton/gui/main.py
+++ b/tryton/gui/main.py
@@ -1054,13 +1054,13 @@ class Main(object):
screen.switch_view(view_type=screen.current_view.view_type)
vbox.pack_start(screen.screen_container.alternate_viewport, True, True)
- screen.current_view.widget_tree.set_headers_visible(False)
+ treeview = screen.current_view.treeview
+ treeview.set_headers_visible(False)
self.menu_expander.add(vbox)
self.menu_expander.show()
# Favorite column
- treeview = screen.current_view.widget_tree
column = gtk.TreeViewColumn()
column.name = None
column._type = None
diff --git a/tryton/gui/window/view_board/action.py b/tryton/gui/window/view_board/action.py
index 77b17c06e8..747815dbf3 100644
--- a/tryton/gui/window/view_board/action.py
+++ b/tryton/gui/window/view_board/action.py
@@ -94,7 +94,7 @@ class Action(SignalEvent):
return
if (self.screen.current_view.view_type == 'tree' and
- self.screen.current_view.widget_tree.keyword_open):
+ self.screen.current_view.treeview.keyword_open):
GenericAction.exec_keyword('tree_open', {
'model': self.screen.model_name,
'id': self.screen.id_get(),
diff --git a/tryton/gui/window/view_board/parser.py b/tryton/gui/window/view_board/parser.py
deleted file mode 100644
index e22021cf80..0000000000
--- a/tryton/gui/window/view_board/parser.py
+++ /dev/null
@@ -1,161 +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.
-'Parser'
-import gtk
-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
-
-_ = gettext.gettext
-
-
-class ParserBoard(object):
-
- def __init__(self, context=None):
- self.title = None
- self.context = context
-
- def parse(self, root_node, notebook=None, paned=None, tooltips=None):
- widgets = []
-
- attrs = common.node_attributes(root_node)
- if not tooltips:
- tooltips = common.Tooltips()
-
- container = _container(tooltips)
- container.new(col=int(attrs.get('col', 4)))
-
- if not self.title:
- self.title = attrs.get('string', 'Unknown')
-
- for node in root_node.childNodes:
- if not node.nodeType == node.ELEMENT_NODE:
- continue
- attrs = common.node_attributes(node)
- yexpand = int(attrs.get('yexpand', 0))
- yfill = int(attrs.get('yfill', 0))
- xexpand = int(attrs.get('xexpand', 1))
- xfill = int(attrs.get('xfill', 1))
- colspan = int(attrs.get('colspan', 1))
- if node.localName == 'image':
- common.ICONFACTORY.register_icon(attrs['name'])
- icon = gtk.Image()
- icon.set_from_stock(attrs['name'], gtk.ICON_SIZE_DIALOG)
- container.wid_add(icon,
- help_tip=attrs.get('help', False),
- colspan=colspan,
- yexpand=yexpand, yfill=yfill, ypadding=10,
- xexpand=xexpand, xfill=xfill)
- elif node.localName == 'separator':
- text = attrs.get('string', '')
- vbox = VBox(attrs=attrs)
- if text:
- label = gtk.Label(text)
- label.set_use_markup(True)
- label.set_alignment(float(attrs.get('align', 0.0)), 0.5)
- vbox.pack_start(label)
- vbox.pack_start(gtk.HSeparator())
- container.wid_add(vbox,
- help_tip=attrs.get('help', False),
- colspan=colspan,
- yexpand=yexpand, yfill=yfill, ypadding=10,
- xexpand=xexpand, xfill=xfill)
- elif node.localName == 'label':
- text = attrs.get('string', '')
- if not text:
- container.empty_add(int(attrs.get('colspan', 1)))
- continue
- label = gtk.Label(text)
- label.set_use_markup(True)
- label.set_alignment(float(attrs.get('xalign', 1.0)),
- float(attrs.get('yalign', 0.0)))
- label.set_angle(int(attrs.get('angle', 0)))
- xexpand = bool(attrs.get('xexpand', 0))
- container.wid_add(label,
- help_tip=attrs.get('help', False),
- colspan=colspan,
- yexpand=yexpand, yfill=yfill,
- xexpand=xexpand, xfill=xfill)
- elif node.localName == 'newline':
- container.newline()
- elif node.localName == 'notebook':
- notebook = gtk.Notebook()
- if CONFIG['client.form_tab'] == 'top':
- pos = gtk.POS_TOP
- elif CONFIG['client.form_tab'] == 'left':
- pos = gtk.POS_LEFT
- elif CONFIG['client.form_tab'] == 'right':
- pos = gtk.POS_RIGHT
- elif CONFIG['client.form_tab'] == 'bottom':
- pos = gtk.POS_BOTTOM
- notebook.set_tab_pos(pos)
- notebook.set_border_width(3)
- container.wid_add(notebook,
- colspan=int(attrs.get('colspan', 4)),
- yexpand=True, yfill=True)
- widget, new_widgets = self.parse(node, notebook,
- tooltips=tooltips)
- widgets += new_widgets
- elif node.localName == 'page':
- if CONFIG['client.form_tab'] == 'left':
- angle = 90
- elif CONFIG['client.form_tab'] == 'right':
- angle = -90
- else:
- angle = 0
- label = gtk.Label(attrs.get('string', _('No String Attr.')))
- label.set_angle(angle)
- widget, new_widgets = self.parse(node, notebook,
- tooltips=tooltips)
- widgets += new_widgets
- notebook.append_page(widget, label)
- elif node.localName == 'group':
- widget, new_widgets = self.parse(node, tooltips=tooltips)
- widgets += new_widgets
- if attrs.get('string', None):
- frame = gtk.Frame(attrs['string'])
- frame.add(widget)
- else:
- frame = widget
- container.wid_add(frame,
- colspan=colspan,
- yexpand=yexpand, yfill=yfill, ypadding=0,
- xexpand=xexpand, xfill=xfill, xpadding=0)
- elif node.localName == 'hpaned':
- 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)
- widgets += new_widgets
- if 'position' in attrs:
- hpaned.set_position(int(attrs['position']))
- elif node.localName == 'vpaned':
- 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)
- 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)
- 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':
- widget_act = Action(attrs, self.context)
- widgets.append(widget_act)
- yexpand = bool(attrs.get('yexpand', 1))
- yfill = bool(attrs.get('yfill', 1))
- container.wid_add(widget_act.widget,
- colspan=colspan,
- yexpand=yexpand, yfill=yfill,
- xexpand=xexpand, xfill=xfill)
- return container.pop(), widgets
diff --git a/tryton/gui/window/view_board/view_board.py b/tryton/gui/window/view_board/view_board.py
index 688e0cff2b..10d3ddb32e 100644
--- a/tryton/gui/window/view_board/view_board.py
+++ b/tryton/gui/window/view_board/view_board.py
@@ -1,10 +1,15 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
-'View board'
+import gtk
+import gettext
import xml.dom.minidom
-from parser import ParserBoard
-from tryton.gui.window.view_board.action import Action
+from tryton.gui.window.view_form.view.form import Container
+from tryton.common import node_attributes, ICONFACTORY
+from tryton.config import CONFIG
+from .action import Action
+
+_ = gettext.gettext
class ViewBoard(object):
@@ -12,21 +17,174 @@ class ViewBoard(object):
def __init__(self, arch, context=None):
self.context = context
+ self.widgets = []
+ self.actions = []
xml_dom = xml.dom.minidom.parseString(arch)
- parser = ParserBoard(context)
for node in xml_dom.childNodes:
- if not node.nodeType == node.ELEMENT_NODE:
- continue
- self.widget, self.widgets = parser.parse(node)
- break
- self.actions = [x for x in self.widgets if isinstance(x, Action)]
- for action in self.actions:
- action.signal_connect(self, 'active-changed',
- self._active_changed)
+ if node.nodeType == node.ELEMENT_NODE:
+ break
+
+ self.attributes = node_attributes(node)
+ self.widget = self.parse(node).table
self.widget.show_all()
+
self._active_changed(None)
+ def parse(self, node, container=None):
+ if not container:
+ node_attrs = node_attributes(node)
+ container = Container(int(node_attrs.get('col', 4)))
+ for node in node.childNodes:
+ if node.nodeType != node.ELEMENT_NODE:
+ continue
+ node_attrs = node_attributes(node)
+ for i_field in ('yexpand', 'yfill', 'xexpand', 'xfill', 'colspan',
+ 'position'):
+ if i_field in node_attrs:
+ node_attrs[i_field] = int(node_attrs[i_field])
+
+ parser = getattr(self, '_parse_%s' % node.tagName)
+ parser(node, container, node_attrs)
+ return container
+
+ def _parse_image(self, node, container, attributes):
+ ICONFACTORY.register_icon(attributes['name'])
+ image = gtk.Image()
+ image.set_from_stock(attributes['name'], gtk.ICON_SIZE_DIALOG)
+ container.add(image, attributes)
+
+ def _parse_separator(self, node, container, attributes):
+ vbox = gtk.VBox()
+ if attributes.get('string'):
+ label = gtk.Label(attributes['string'])
+ label.set_alignment(float(attributes.get('xalign', 0.0)),
+ float(attributes.get('yalign', 0.5)))
+ vbox.pack_start(label)
+ vbox.pack_start(gtk.HSeparator())
+ container.add(vbox, attributes)
+
+ def _parse_label(self, node, container, attributes):
+ if not attributes.get('string'):
+ container.add(None, attributes)
+ return
+ label = gtk.Label(attributes['string'])
+ label.set_alignment(float(attributes.get('xalign', 0.0)),
+ float(attributes.get('yalign', 0.5)))
+ label.set_angle(int(attributes.get('angle', 0)))
+ attributes.setdefault('xexpand', 0)
+ container.add(label, attributes)
+
+ def _parse_newline(self, node, container, attributes):
+ container.add_row()
+
+ def _parse_notebook(self, node, container, attributes):
+ attributes.setdefault('yexpand', True)
+ attributes.setdefault('yfill', True)
+ notebook = gtk.Notebook()
+ notebook.set_scrollable(True)
+ positions = {
+ 'top': gtk.POS_TOP,
+ 'left': gtk.POS_LEFT,
+ 'right': gtk.POS_RIGHT,
+ 'bottom': gtk.POS_BOTTOM,
+ }
+ notebook.set_tab_pos(positions[CONFIG['client.form_tab']])
+ container.add(notebook, attributes)
+ self.parse(node, notebook)
+
+ def _parse_page(self, node, notebook, attributes):
+ if CONFIG['client.form_tab'] == 'left':
+ angle = 90
+ tab_box = gtk.VBox(spacing=3)
+ image_pos, image_rotate = ('end',
+ gtk.gdk.PIXBUF_ROTATE_COUNTERCLOCKWISE)
+ elif CONFIG['client.form_tab'] == 'right':
+ angle = -90
+ tab_box = gtk.VBox(spacing=3)
+ image_pos, image_rotate = ('start',
+ gtk.gdk.PIXBUF_ROTATE_CLOCKWISE)
+ else:
+ angle = 0
+ tab_box = gtk.HBox(spacing=3)
+ image_pos, image_rotate = ('start',
+ gtk.gdk.PIXBUF_ROTATE_NONE)
+ if '_' not in attributes['string']:
+ attributes['string'] = '_' + attributes['string']
+ label = gtk.Label(attributes['string'])
+ label.set_angle(angle)
+ label.set_use_underline(True)
+ tab_box.pack_start(label)
+
+ if 'icon' in attributes:
+ ICONFACTORY.register_icon(attributes['icon'])
+ pixbuf = tab_box.render_icon(attributes['icon'],
+ gtk.ICON_SIZE_SMALL_TOOLBAR)
+ pixbuf = pixbuf.rotate_simple(image_rotate)
+ icon = gtk.Image()
+ icon.set_from_pixbuf(pixbuf)
+ if image_pos == 'end':
+ tab_box.pack_end(icon)
+ else:
+ tab_box.pack_start(icon)
+ tab_box.show_all()
+
+ viewport = gtk.Viewport()
+ viewport.set_shadow_type(gtk.SHADOW_NONE)
+ scrolledwindow = gtk.ScrolledWindow()
+ scrolledwindow.set_shadow_type(gtk.SHADOW_NONE)
+ scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scrolledwindow.add(viewport)
+ scrolledwindow.show_all()
+ notebook.append_page(scrolledwindow, tab_box)
+ container = self.parse(node)
+ viewport.add(container.table)
+
+ def _parse_group(self, node, container, attributes):
+ group = self.parse(node)
+ group.table.set_homogeneous(attributes.get('homogeneous', False))
+ frame = gtk.Frame(attributes.get('string'))
+ if not attributes.get('string'):
+ frame.set_shadow_type(gtk.SHADOW_NONE)
+ frame.set_border_width(0)
+ frame.add(group.table)
+ container.add(frame, attributes)
+
+ def _parse_paned(self, node, container, attributes, Paned):
+ attributes.setdefault('yexpand', True)
+ attributes.setdefault('yfill', True)
+ paned = Paned()
+ if 'position' in attributes:
+ paned.set_position(attributes['position'])
+ container.add(paned, attributes)
+ self.parse(node, paned)
+
+ def _parse_hpaned(self, node, container, attributes):
+ self._parse_paned(node, container, attributes, gtk.HPaned)
+
+ def _parse_vpaned(self, node, container, attributes):
+ self._parse_paned(node, container, attributes, gtk.VPaned)
+
+ def _parse_child(self, node, paned, attributes):
+ container = self.parse(node)
+ if not paned.get_child1():
+ pack = paned.pack1
+ else:
+ pack = paned.pack2
+ pack(container.table, resize=True, shrink=True)
+
+ def _parse_action(self, node, container, attributes):
+ attributes.setdefault('yexpand', True)
+ attributes.setdefault('yfill', True)
+ action = Action(attributes, self.context)
+ action.signal_connect(self, 'active-changed', self._active_changed)
+ self.actions.append(action)
+ container.add(action.widget, attributes)
+
+ @property
+ def title(self):
+ self.attributes.get('string', '')
+
def widget_get(self):
return self.widget
diff --git a/tryton/gui/window/view_form/screen/screen.py b/tryton/gui/window/view_form/screen/screen.py
index 95c08fc710..96b75ec911 100644
--- a/tryton/gui/window/view_form/screen/screen.py
+++ b/tryton/gui/window/view_form/screen/screen.py
@@ -18,6 +18,7 @@ import logging
from tryton.gui.window.view_form.model.group import Group
from tryton.gui.window.view_form.view.screen_container import ScreenContainer
+from tryton.gui.window.view_form.view import View
from tryton.signal_event import SignalEvent
from tryton.config import CONFIG
from tryton.exceptions import TrytonServerError, TrytonServerUnavailable
@@ -329,7 +330,7 @@ class Screen(SignalEvent):
def default_row_activate(self):
if (self.current_view.view_type == 'tree' and
- self.current_view.widget_tree.keyword_open):
+ self.current_view.attributes.get('keyword_open')):
return Action.exec_keyword('tree_open', {
'model': self.model_name,
'id': self.id_get(),
@@ -402,14 +403,13 @@ class Screen(SignalEvent):
view_id = view['view_id']
xml_dom = xml.dom.minidom.parseString(arch)
- for node in xml_dom.childNodes:
- if node.localName == 'tree':
- self.fields_view_tree = view
- break
+ root, = xml_dom.childNodes
+ if root.tagName == 'tree':
+ self.fields_view_tree = view
# Ensure that loading is always lazy for fields on form view
# and always eager for fields on tree or graph view
- if node.localName == 'form':
+ if root.tagName == 'form':
loading = 'lazy'
else:
loading = 'eager'
@@ -419,18 +419,11 @@ class Screen(SignalEvent):
else:
fields[field]['loading'] = \
self.group.fields[field].attrs['loading']
-
- children_field = view.get('field_childs')
-
- from tryton.gui.window.view_form.view.widget_parse import WidgetParse
self.group.add_fields(fields)
-
- parser = WidgetParse(parent=self.parent)
- view = parser.parse(self, xml_dom, self.group.fields,
- children_field=children_field)
+ view = View.parse(self, xml_dom, view.get('field_childs'))
view.view_id = view_id
-
self.views.append(view)
+
return view
def new(self, default=True):
@@ -457,8 +450,7 @@ class Screen(SignalEvent):
def new_model_position(self):
position = -1
if (self.current_view and self.current_view.view_type == 'tree'
- and hasattr(self.current_view.widget_tree, 'editable')
- and self.current_view.widget_tree.editable == 'top'):
+ and self.current_view.attributes.get('editable') == 'top'):
position = 0
return position
@@ -723,8 +715,8 @@ class Screen(SignalEvent):
view.set_value()
self.set_cursor(reset_view=False)
if view.view_type == 'tree' and len(self.group):
- start, end = view.widget_tree.get_visible_range()
- vadjustment = view.widget_tree.get_vadjustment()
+ start, end = view.treeview.get_visible_range()
+ vadjustment = view.treeview.get_vadjustment()
vadjustment.value = vadjustment.value + vadjustment.page_increment
store = view.store
iter_ = store.get_iter(end)
@@ -788,8 +780,8 @@ class Screen(SignalEvent):
view.set_value()
self.set_cursor(reset_view=False)
if view.view_type == 'tree' and len(self.group):
- start, end = view.widget_tree.get_visible_range()
- vadjustment = view.widget_tree.get_vadjustment()
+ start, end = view.treeview.get_visible_range()
+ vadjustment = view.treeview.get_vadjustment()
vadjustment.value = vadjustment.value - vadjustment.page_increment
store = view.store
iter_ = store.get_iter(start)
diff --git a/tryton/gui/window/view_form/view/__init__.py b/tryton/gui/window/view_form/view/__init__.py
index c86640b67f..ef1268c957 100644
--- a/tryton/gui/window/view_form/view/__init__.py
+++ b/tryton/gui/window/view_form/view/__init__.py
@@ -1,2 +1,49 @@
#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 node_attributes
+
+
+class View(object):
+ view_type = None
+ widget = None
+ view_id = None
+ modified = None
+ editable = None
+ children_field = None
+
+ def __init__(self, screen, xml):
+ self.screen = screen
+ self.fields = {}
+ self.attributes = node_attributes(xml)
+ screen.set_on_write(self.attributes.get('on_write'))
+
+ def set_value(self):
+ raise NotImplementedError
+
+ def get_fields(self):
+ raise NotImplementedError
+
+ def get_buttons(self):
+ raise NotImplementedError
+
+ @property
+ def title(self):
+ return self.attributes.get('string', '')
+
+ @staticmethod
+ def parse(screen, xml, children_field):
+ from .list import ViewTree
+ from .form import ViewForm
+ from .graph import ViewGraph
+ from .calendar_ import ViewCalendar
+
+ root, = xml.childNodes
+ tagname = root.tagName
+ if tagname == 'tree':
+ return ViewTree(screen, root, children_field)
+ elif tagname == 'form':
+ return ViewForm(screen, root)
+ elif tagname == 'graph':
+ return ViewGraph(screen, root)
+ elif tagname == 'calendar':
+ return ViewCalendar(screen, root)
diff --git a/tryton/gui/window/view_form/view/calendar_.py b/tryton/gui/window/view_form/view/calendar_.py
index 93d4c746a0..a3a8a2466d 100644
--- a/tryton/gui/window/view_form/view/calendar_.py
+++ b/tryton/gui/window/view_form/view/calendar_.py
@@ -2,27 +2,112 @@
#this repository contains the full copyright notices and license terms.
from functools import wraps
-from interface import ParserView
+import gtk
+import datetime
+import gettext
+
+from tryton.common import node_attributes
+from . import View
+try:
+ from .calendar_gtk.calendar_ import Calendar_
+ from .calendar_gtk.toolbar import Toolbar
+except ImportError:
+ Calendar_ = None
+ Toolbar = None
+
+_ = gettext.gettext
def goocalendar_required(func):
"Decorator for goocalendar required"
@wraps(func)
def wrapper(self, *args, **kwargs):
- if 'goocalendar' not in self.children:
+ if 'goocalendar' not in self.widgets:
return
return func(self, *args, **kwargs)
return wrapper
-class ViewCalendar(ParserView):
+class ViewCalendar(View):
- def __init__(self, screen, widget, children=None, buttons=None,
- notebooks=None, cursor_widget=None, children_field=None):
- super(ViewCalendar, self).__init__(screen, widget, children, buttons,
- notebooks, cursor_widget, children_field)
+ def __init__(self, screen, xml):
+ super(ViewCalendar, self).__init__(screen, xml)
self.view_type = 'calendar'
self.editable = False
+ self.widgets = {}
+ self.widget = self.parse(xml)
+
+ def parse(self, node):
+ vbox = gtk.VBox()
+ if not Calendar_:
+ return vbox
+ fields = []
+ for node in node.childNodes:
+ if node.nodeType != node.ELEMENT_NODE:
+ continue
+ if node.tagName == 'field':
+ fields.append(node_attributes(node))
+ goocalendar = Calendar_(attrs=self.attributes, screen=self.screen,
+ fields=fields)
+ toolbar = Toolbar(goocalendar)
+ self.widgets['toolbar'] = toolbar
+ goocalendar.connect('view-changed', self.on_view_changed, toolbar)
+ goocalendar.connect('page-changed', self.on_page_changed, toolbar)
+ goocalendar.connect('event-pressed', self.on_event_pressed)
+ goocalendar.connect('event-activated', self.on_event_activated)
+ goocalendar.connect('event-released', self.on_event_released)
+ goocalendar.connect('day-pressed', self.on_day_pressed)
+ goocalendar.connect('day-activated', self.on_day_activated)
+ self.widgets['goocalendar'] = goocalendar
+ scrolledWindow = gtk.ScrolledWindow()
+ scrolledWindow.add_with_viewport(goocalendar)
+ vbox.pack_start(toolbar, False, False)
+ vbox.pack_start(scrolledWindow, True, True)
+ return vbox
+
+ def on_page_changed(self, goocalendar, day, toolbar):
+ toolbar.update_displayed_date()
+ if goocalendar.update_domain():
+ self.screen.search_filter()
+
+ def on_view_changed(self, goocalendar, view, toolbar):
+ toolbar.update_displayed_date()
+ if goocalendar.update_domain():
+ self.screen.search_filter()
+
+ def on_event_pressed(self, goocalendar, event):
+ self.screen.current_record = event.record
+
+ def on_event_activated(self, goocalendar, event):
+ self.screen.switch_view('form')
+
+ def on_event_released(self, goocalendar, event):
+ dtstart = self.attributes.get('dtstart')
+ dtend = self.attributes.get('dtend') or dtstart
+ record = event.record
+ group = record.group
+ previous_start = record[dtstart].get(record)
+ new_start = event.start
+ new_end = event.end
+ if not isinstance(previous_start, datetime.datetime):
+ new_start = event.start.date()
+ new_end = event.end.date() if event.end else None
+ if previous_start <= new_start:
+ if new_end:
+ group.fields[dtend].set_client(record, new_end)
+ group.fields[dtstart].set_client(record, new_start)
+ else:
+ group.fields[dtstart].set_client(record, new_start)
+ if new_end:
+ group.fields[dtend].set_client(record, new_end)
+ goocalendar.select(new_start)
+ record.save()
+
+ def on_day_pressed(self, goocalendar, day):
+ self.screen.current_record = None
+
+ def on_day_activated(self, goocalendar, day):
+ self.screen.new()
def __getitem__(self, name):
return None
@@ -30,19 +115,19 @@ class ViewCalendar(ParserView):
@goocalendar_required
def destroy(self):
self.widget.destroy()
- self.children['goocalendar'].destroy()
+ self.widgets['goocalendar'].destroy()
@goocalendar_required
def get_selected_date(self):
- return self.children['goocalendar'].selected_date
+ return self.widgets['goocalendar'].selected_date
@goocalendar_required
def set_default_date(self, record, selected_date):
- self.children['goocalendar'].set_default_date(record, selected_date)
+ self.widgets['goocalendar'].set_default_date(record, selected_date)
def current_domain(self):
- if 'goocalendar' in self.children:
- return self.children['goocalendar'].current_domain()
+ if 'goocalendar' in self.widgets:
+ return self.widgets['goocalendar'].current_domain()
else:
# No need to load any record as nothing will be shown
return [('id', '=', -1)]
@@ -55,14 +140,14 @@ class ViewCalendar(ParserView):
@goocalendar_required
def display(self):
- self.children['goocalendar'].display(self.screen.group)
- gtkcal = self.children['toolbar'].gtkcal
+ self.widgets['goocalendar'].display(self.screen.group)
+ gtkcal = self.widgets['toolbar'].gtkcal
if gtkcal and not gtkcal.is_drawable():
import goocanvas
# disable gtk.Calendar if it is not drawable anymore
- self.children['toolbar'].gtkcal_item.set_property('visibility',
+ self.widgets['toolbar'].gtkcal_item.set_property('visibility',
goocanvas.ITEM_INVISIBLE)
- self.children['toolbar'].current_page.set_active(False)
+ self.widgets['toolbar'].current_page.set_active(False)
def set_cursor(self, new=False, reset_view=True):
pass
diff --git a/tryton/gui/window/view_form/view/calendar_gtk/__init__.py b/tryton/gui/window/view_form/view/calendar_gtk/__init__.py
index 5218f22bd6..c86640b67f 100644
--- a/tryton/gui/window/view_form/view/calendar_gtk/__init__.py
+++ b/tryton/gui/window/view_form/view/calendar_gtk/__init__.py
@@ -1,3 +1,2 @@
#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 parser import *
diff --git a/tryton/gui/window/view_form/view/calendar_gtk/calendar_.py b/tryton/gui/window/view_form/view/calendar_gtk/calendar_.py
index bc799ab46a..a30e26259f 100644
--- a/tryton/gui/window/view_form/view/calendar_gtk/calendar_.py
+++ b/tryton/gui/window/view_form/view/calendar_gtk/calendar_.py
@@ -3,27 +3,22 @@
import calendar
import datetime
import goocalendar
-import tryton.common as common
from dates_period import DatesPeriod
class Calendar_(goocalendar.Calendar):
'Calendar'
- def __init__(self, attrs, model, root_node, fields, screen,
- event_store=None):
+ def __init__(self, attrs, screen, fields, event_store=None):
super(Calendar_, self).__init__(event_store)
self.attrs = attrs
- self.model = model
- self.root_node = root_node
- self.fields = fields
self.screen = screen
+ self.fields = fields
self.event_store = event_store
self.current_domain_period = self.get_displayed_period()
def set_default_date(self, record, selected_date):
- attrs = common.node_attributes(self.root_node)
- dtstart = attrs.get('dtstart')
+ dtstart = self.attrs.get('dtstart')
record[dtstart].set(record, datetime.datetime.combine(selected_date,
datetime.time(0)))
@@ -54,9 +49,8 @@ class Calendar_(goocalendar.Calendar):
def current_domain(self):
first_datetime, last_datetime = \
self.current_domain_period.get_dates(True)
- attrs = common.node_attributes(self.root_node)
- dtstart = attrs.get('dtstart')
- dtend = attrs.get('dtend') or dtstart
+ dtstart = self.attrs.get('dtstart')
+ dtend = self.attrs.get('dtend') or dtstart
domain = ['OR',
['AND', (dtstart, '>=', first_datetime),
(dtstart, '<', last_datetime)],
@@ -67,9 +61,8 @@ class Calendar_(goocalendar.Calendar):
return domain
def display(self, group):
- attrs = common.node_attributes(self.root_node)
- dtstart = attrs.get('dtstart')
- dtend = attrs.get('dtend') or dtstart
+ dtstart = self.attrs.get('dtstart')
+ dtend = self.attrs.get('dtend') or dtstart
if self.screen.current_record:
record = self.screen.current_record
date = record[dtstart].get(record)
@@ -82,14 +75,6 @@ class Calendar_(goocalendar.Calendar):
event_store = goocalendar.EventStore()
self.event_store = event_store
- fields = []
- for node in self.root_node.childNodes:
- if not node.nodeType == node.ELEMENT_NODE:
- continue
- if node.localName == 'field':
- attrs = common.node_attributes(node)
- fields.append(attrs.get('name'))
-
for record in group:
if not record[dtstart].get(record):
continue
@@ -107,8 +92,8 @@ class Calendar_(goocalendar.Calendar):
all_day = True
# TODO define color code
- label = '\n'.join(record[name].get_client(record)
- for name in fields).rstrip()
+ label = '\n'.join(record[attrs['name']].get_client(record)
+ for attrs in self.fields).rstrip()
event = goocalendar.Event(label, start, end,
bg_color='lightblue', all_day=all_day)
event.record = record
diff --git a/tryton/gui/window/view_form/view/calendar_gtk/parser.py b/tryton/gui/window/view_form/view/calendar_gtk/parser.py
deleted file mode 100644
index 579f05972e..0000000000
--- a/tryton/gui/window/view_form/view/calendar_gtk/parser.py
+++ /dev/null
@@ -1,85 +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.
-import datetime
-import gettext
-import gtk
-from tryton.gui.window.view_form.view.interface import ParserInterface
-import tryton.common as common
-try:
- from calendar_ import Calendar_
- from toolbar import Toolbar
-except ImportError:
- Calendar_ = None
- Toolbar = None
-
-_ = gettext.gettext
-
-
-class ParserCalendar(ParserInterface):
-
- def on_page_changed(self, goocalendar, day, toolbar):
- toolbar.update_displayed_date()
- if goocalendar.update_domain():
- self.screen.search_filter()
-
- def on_view_changed(self, goocalendar, view, toolbar):
- toolbar.update_displayed_date()
- if goocalendar.update_domain():
- self.screen.search_filter()
-
- def on_event_pressed(self, goocalendar, event):
- self.screen.current_record = event.record
-
- def on_event_activated(self, goocalendar, event):
- self.screen.switch_view('form')
-
- def on_event_released(self, goocalendar, event, attrs):
- dtstart = attrs.get('dtstart')
- dtend = attrs.get('dtend') or dtstart
- record = event.record
- group = record.group
- previous_start = record[dtstart].get(record)
- new_start = event.start
- new_end = event.end
- if not isinstance(previous_start, datetime.datetime):
- new_start = event.start.date()
- new_end = event.end.date() if event.end else None
- if previous_start <= new_start:
- if new_end:
- group.fields[dtend].set_client(record, new_end)
- group.fields[dtstart].set_client(record, new_start)
- else:
- group.fields[dtstart].set_client(record, new_start)
- if new_end:
- group.fields[dtend].set_client(record, new_end)
- goocalendar.select(new_start)
- record.save()
-
- def on_day_pressed(self, goocalendar, day):
- self.screen.current_record = None
-
- def on_day_activated(self, goocalendar, day):
- self.screen.new()
-
- def parse(self, model, root_node, fields):
- attrs = common.node_attributes(root_node)
- self.title = attrs.get('string', 'Unknown')
- vbox = gtk.VBox()
- if not Calendar_:
- return vbox, {}, [], '', [], None
- goocalendar = Calendar_(attrs=attrs, model=model, root_node=root_node,
- fields=fields, screen=self.screen)
- toolbar = Toolbar(goocalendar)
- goocalendar.connect('view-changed', self.on_view_changed, toolbar)
- goocalendar.connect('page-changed', self.on_page_changed, toolbar)
- goocalendar.connect('event-pressed', self.on_event_pressed)
- goocalendar.connect('event-activated', self.on_event_activated)
- goocalendar.connect('event-released', self.on_event_released, attrs)
- goocalendar.connect('day-pressed', self.on_day_pressed)
- goocalendar.connect('day-activated', self.on_day_activated)
- scrolledWindow = gtk.ScrolledWindow()
- scrolledWindow.add_with_viewport(goocalendar)
- vbox.pack_start(toolbar, False, False)
- vbox.pack_start(scrolledWindow, True, True)
- return vbox, {'root': scrolledWindow, 'toolbar': toolbar,
- 'goocalendar': goocalendar}, [], '', [], None
diff --git a/tryton/gui/window/view_form/view/form.py b/tryton/gui/window/view_form/view/form.py
index 62a18f0b19..ab0ad3b9b4 100644
--- a/tryton/gui/window/view_form/view/form.py
+++ b/tryton/gui/window/view_form/view/form.py
@@ -4,45 +4,114 @@ import operator
from functools import reduce
import gtk
import gettext
-from interface import ParserView
+from collections import defaultdict
+
+from . import View
from tryton.common.focus import (get_invisible_ancestor, find_focused_child,
next_focus_widget)
+from tryton.common import Tooltips, node_attributes, ICONFACTORY
+from tryton.common.button import Button
+from tryton.config import CONFIG
+from .form_gtk.calendar import Calendar, DateTime, Time
+from .form_gtk.float import Float
+from .form_gtk.integer import Integer
+from .form_gtk.selection import Selection
+from .form_gtk.char import Char, Password
+from .form_gtk.float_time import FloatTime
+from .form_gtk.checkbox import CheckBox
+from .form_gtk.reference import Reference
+from .form_gtk.binary import Binary
+from .form_gtk.textbox import TextBox
+from .form_gtk.one2many import One2Many
+from .form_gtk.many2many import Many2Many
+from .form_gtk.many2one import Many2One
+from .form_gtk.url import Email, URL, CallTo, SIP
+from .form_gtk.image import Image as Image2
+from .form_gtk.progressbar import ProgressBar
+from .form_gtk.one2one import One2One
+from .form_gtk.richtextbox import RichTextBox
+from .form_gtk.dictionary import DictWidget
+from .form_gtk.multiselection import MultiSelection
+from .form_gtk.state_widget import (Label, VBox, Image, Frame, ScrolledWindow,
+ Notebook, Alignment)
_ = gettext.gettext
-class ViewForm(ParserView):
+class Container(object):
+ def __init__(self, col=4):
+ if CONFIG['client.modepda']:
+ col = 1
+ self.col = col
+ self.table = gtk.Table(1, col)
+ self.table.set_homogeneous(False)
+ self.table.set_col_spacings(0)
+ self.table.set_row_spacings(0)
+ self.table.set_border_width(0)
+ self.last = (0, 0)
+ self.tooltips = Tooltips()
+ self.tooltips.enable()
- def __init__(self, screen, widget, children=None, state_widgets=None,
- notebooks=None, cursor_widget='', children_field=None):
- super(ViewForm, self).__init__(screen, widget, children, state_widgets,
- notebooks, cursor_widget, children_field)
+ def add_row(self):
+ height, width = self.last
+ self.table.resize(height + 1, self.col)
+ self.last = (height + 1, 0)
+
+ def add(self, widget, attributes):
+ height, width = self.last
+
+ colspan = attributes.get('colspan', 1)
+ if colspan > self.col:
+ colspan = self.col
+
+ if width + colspan > self.col:
+ self.add_row()
+ height, width = self.last
+ self.last = height, width + colspan
+
+ if not widget:
+ return
+
+ yopt = 0
+ if attributes.get('yexpand'):
+ yopt |= gtk.EXPAND
+ if attributes.get('yfill'):
+ yopt |= gtk.FILL
+
+ xopt = 0
+ if attributes.get('xexpand', True):
+ xopt |= gtk.EXPAND
+ if attributes.get('xfill', True):
+ xopt |= gtk.FILL
+
+ if attributes.get('help'):
+ self.tooltips.set_tip(widget, attributes['help'])
+
+ widget.show_all()
+ self.table.attach(widget,
+ width, width + colspan,
+ height, height + 1,
+ yoptions=yopt, xoptions=xopt,
+ ypadding=1, xpadding=2)
+
+
+class ViewForm(View):
+ editable = True
+
+ def __init__(self, screen, xml):
+ super(ViewForm, self).__init__(screen, xml)
self.view_type = 'form'
- self.editable = True
+ self.widget_id = 0
+ self.widgets = defaultdict(list)
+ self.state_widgets = []
+ self.notebooks = []
- for button in self.get_buttons():
- button.connect('clicked', self.button_clicked)
-
- # Force to display the first time it switches on a page
- # This avoids glitch in position of widgets
- display_done = {}
- for notebook in notebooks:
- def switch(notebook, page, page_num):
- if page_num not in display_done.setdefault(notebook, []):
- notebook.grab_focus()
- display_done[notebook].append(page_num)
- self.display()
- notebook.connect('switch-page', switch)
-
- self.widgets = children
- for widgets in self.widgets.itervalues():
- for widget in widgets:
- widget.view = self
+ container = self.parse(xml)
vbox = gtk.VBox()
vp = gtk.Viewport()
vp.set_shadow_type(gtk.SHADOW_NONE)
- vp.add(self.widget)
+ vp.add(container.table)
scroll = gtk.ScrolledWindow()
scroll.add(vp)
scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
@@ -54,6 +123,281 @@ class ViewForm(ParserView):
self.widget = vbox
+ def parse(self, node, container=None):
+ if not container:
+ node_attrs = node_attributes(node)
+ container = Container(int(node_attrs.get('col', 4)))
+ for node in node.childNodes:
+ if node.nodeType != node.ELEMENT_NODE:
+ continue
+ node_attrs = node_attributes(node)
+ for b_field in ('readonly', 'homogeneous'):
+ if b_field in node_attrs:
+ node_attrs[b_field] = bool(int(node_attrs[b_field]))
+ for i_field in ('yexpand', 'yfill', 'xexpand', 'xfill', 'colspan',
+ 'position'):
+ if i_field in node_attrs:
+ node_attrs[i_field] = int(node_attrs[i_field])
+
+ parser = getattr(self, '_parse_%s' % node.tagName)
+ parser(node, container, node_attrs)
+ return container
+
+ def _parse_image(self, node, container, attributes):
+ ICONFACTORY.register_icon(attributes['name'])
+ image = Image(attrs=attributes)
+ image.set_from_stock(attributes['name'], gtk.ICON_SIZE_DIALOG)
+ self.state_widgets.append(image)
+ container.add(image, attributes)
+
+ def _parse_separator(self, node, container, attributes):
+ if 'name' in attributes:
+ field = self.screen.group.fields[attributes['name']]
+ for attr in ('states', 'string'):
+ if attr not in attributes and attr in field.attrs:
+ attributes[attr] = field.attrs[attr]
+ vbox = VBox(attrs=attributes)
+ if attributes.get('string'):
+ label = gtk.Label(attributes['string'])
+ label.set_alignment(float(attributes.get('xalign', 0.0)),
+ float(attributes.get('yalign', 0.5)))
+ vbox.pack_start(label)
+ vbox.pack_start(gtk.HSeparator())
+ self.state_widgets.append(vbox)
+ container.add(vbox, attributes)
+
+ def _parse_label(self, node, container, attributes):
+ if 'name' in attributes:
+ field = self.screen.group.fields[attributes['name']]
+ if attributes['name'] == self.screen.exclude_field:
+ container.add(None, attributes)
+ return
+ if 'states' not in attributes and 'states' in field.attrs:
+ attributes['states'] = field.attrs['states']
+ if 'string' not in attributes:
+ if gtk.widget_get_default_direction() == \
+ gtk.TEXT_DIR_RTL:
+ attributes['string'] = _(':') + field.attrs['string']
+ else:
+ attributes['string'] = field.attrs['string'] + _(':')
+ if CONFIG['client.modepda']:
+ attributes['xalign'] = 0.0
+
+ label = Label(attributes.get('string', ''), attrs=attributes)
+ label.set_alignment(float(attributes.get('xalign', 1.0)),
+ float(attributes.get('yalign', 0.0)))
+ label.set_angle(int(attributes.get('angle', 0)))
+ attributes.setdefault('xexpand', 0)
+ self.state_widgets.append(label)
+ container.add(label, attributes)
+
+ def _parse_newline(self, node, container, attributes):
+ container.add_row()
+
+ def _parse_button(self, node, container, attributes):
+ button = Button(attributes)
+ button.connect('clicked', self.button_clicked)
+ self.state_widgets.append(button)
+ container.add(button, attributes)
+
+ def _parse_notebook(self, node, container, attributes):
+ attributes.setdefault('yexpand', True)
+ attributes.setdefault('yfill', True)
+ attributes.setdefault('colspan', 4)
+ notebook = Notebook(attrs=attributes)
+ notebook.set_scrollable(True)
+ positions = {
+ 'top': gtk.POS_TOP,
+ 'left': gtk.POS_LEFT,
+ 'right': gtk.POS_RIGHT,
+ 'bottom': gtk.POS_BOTTOM,
+ }
+ notebook.set_tab_pos(positions[CONFIG['client.form_tab']])
+ notebook.set_border_width(3)
+
+ # Force to display the first time it switches on a page
+ # This avoids glitch in position of widgets
+ def switch(notebook, page, page_num):
+ if not self.widget:
+ # Not yet finish to parse
+ return
+ notebook.grab_focus()
+ self.display()
+ notebook.disconnect(handler_id)
+ handler_id = notebook.connect('switch-page', switch)
+ self.state_widgets.append(notebook)
+
+ self.notebooks.append(notebook)
+ container.add(notebook, attributes)
+ self.parse(node, notebook)
+
+ def _parse_page(self, node, notebook, attributes):
+ if CONFIG['client.form_tab'] == 'left':
+ angle = 90
+ tab_box = gtk.VBox(spacing=3)
+ image_pos, image_rotate = ('end',
+ gtk.gdk.PIXBUF_ROTATE_COUNTERCLOCKWISE)
+ elif CONFIG['client.form_tab'] == 'right':
+ angle = -90
+ tab_box = gtk.VBox(spacing=3)
+ image_pos, image_rotate = ('start',
+ gtk.gdk.PIXBUF_ROTATE_CLOCKWISE)
+ else:
+ angle = 0
+ tab_box = gtk.HBox(spacing=3)
+ image_pos, image_rotate = ('start',
+ gtk.gdk.PIXBUF_ROTATE_NONE)
+ if 'name' in attributes:
+ field = self.screen.group.fields[attributes['name']]
+ if attributes['name'] == self.screen.exclude_field:
+ return
+ for attr in ('states', 'string'):
+ if attr not in attributes and attr in field.attrs:
+ attributes[attr] = field.attrs[attr]
+ if '_' not in attributes['string']:
+ attributes['string'] = '_' + attributes['string']
+ label = gtk.Label(attributes['string'])
+ label.set_angle(angle)
+ label.set_use_underline(True)
+ tab_box.pack_start(label)
+
+ if 'icon' in attributes:
+ ICONFACTORY.register_icon(attributes['icon'])
+ pixbuf = tab_box.render_icon(attributes['icon'],
+ gtk.ICON_SIZE_SMALL_TOOLBAR)
+ pixbuf = pixbuf.rotate_simple(image_rotate)
+ icon = gtk.Image()
+ icon.set_from_pixbuf(pixbuf)
+ if image_pos == 'end':
+ tab_box.pack_end(icon)
+ else:
+ tab_box.pack_start(icon)
+ tab_box.show_all()
+
+ viewport = gtk.Viewport()
+ viewport.set_shadow_type(gtk.SHADOW_NONE)
+ scrolledwindow = ScrolledWindow(attrs=attributes)
+ scrolledwindow.set_shadow_type(gtk.SHADOW_NONE)
+ scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+ scrolledwindow.add(viewport)
+ scrolledwindow.show_all()
+ self.state_widgets.append(scrolledwindow)
+ notebook.append_page(scrolledwindow, tab_box)
+ container = self.parse(node)
+ viewport.add(container.table)
+
+ def _parse_field(self, node, container, attributes):
+ name = attributes['name']
+ field = self.screen.group.fields[name]
+
+ if (name not in self.screen.group.fields
+ or name == self.screen.exclude_field):
+ container.add(None, attributes)
+ return
+
+ if 'widget' not in attributes:
+ attributes['widget'] = field.attrs['type']
+
+ for attr in ('relation', 'domain', 'selection',
+ 'relation_field', 'string', 'views',
+ 'add_remove', 'sort', 'context', 'size', 'filename',
+ 'autocomplete', 'translate', 'create', 'delete',
+ 'selection_change_with', 'schema_model'):
+ if attr in field.attrs and attr not in attributes:
+ attributes[attr] = field.attrs[attr]
+
+ Widget = self.get_widget(attributes['widget'])
+ widget = Widget(self, attributes)
+ self.widget_id += 1
+ widget.position = self.widget_id
+ self.widgets[name].append(widget)
+
+ if Widget.expand:
+ attributes.setdefault('yexpand', True)
+ attributes.setdefault('yfill', True)
+
+ if attributes.get('height') or attributes.get('width'):
+ widget.widget.set_size_request(
+ int(attributes.get('width', -1)),
+ int(attributes.get('height', -1)))
+ container.add(Alignment(widget.widget, attributes), attributes)
+
+ def _parse_group(self, node, container, attributes):
+ group = self.parse(node)
+ group.table.set_homogeneous(attributes.get('homogeneous', False))
+ if 'name' in attributes:
+ field = self.screen.group.fields[attributes['name']]
+ if attributes['name'] == self.screen.exclude_field:
+ container.add(None, attributes)
+ return
+ for attr in ('states', 'string'):
+ if attr not in attributes and attr in field.attrs:
+ attributes[attr] = field.attrs[attr]
+
+ frame = Frame(attributes.get('string'), attrs=attributes)
+ frame.add(group.table)
+ self.state_widgets.append(frame)
+ container.add(frame, attributes)
+
+ def _parse_paned(self, node, container, attributes, Paned):
+ paned = Paned()
+ if 'position' in attributes:
+ paned.set_position(attributes['position'])
+ container.add(paned, attributes)
+ self.parse(node, paned)
+
+ def _parse_hpaned(self, node, container, attributes):
+ self._parse_paned(node, container, attributes, gtk.HPaned)
+
+ def _parse_vpaned(self, node, container, attributes):
+ self._parse_paned(node, container, attributes, gtk.VPaned)
+
+ def _parse_child(self, node, paned, attributes):
+ container = self.parse(node)
+ if not paned.get_child1():
+ pack = paned.pack1
+ else:
+ pack = paned.pack2
+ pack(container.table, resize=True, shrink=True)
+
+ WIDGETS = {
+ 'date': Calendar,
+ 'datetime': DateTime,
+ 'time': Time,
+ 'float': Float,
+ 'numeric': Float,
+ 'integer': Integer,
+ 'biginteger': Integer,
+ 'selection': Selection,
+ 'char': Char,
+ 'password': Password,
+ 'float_time': FloatTime,
+ 'boolean': CheckBox,
+ 'reference': Reference,
+ 'binary': Binary,
+ 'text': TextBox,
+ 'one2many': One2Many,
+ 'many2many': Many2Many,
+ 'many2one': Many2One,
+ 'email': Email,
+ 'url': URL,
+ 'callto': CallTo,
+ 'sip': SIP,
+ 'image': Image2,
+ 'progressbar': ProgressBar,
+ 'one2one': One2One,
+ 'richtext': RichTextBox,
+ 'dict': DictWidget,
+ 'multiselection': MultiSelection,
+ }
+
+ @classmethod
+ def get_widget(cls, name):
+ return cls.WIDGETS[name]
+
+ def get_fields(self):
+ return self.widgets.keys()
+
def __getitem__(self, name):
return self.widgets[name][0]
@@ -135,8 +479,8 @@ class ViewForm(ParserView):
if reset_view:
for notebook in self.notebooks:
notebook.set_current_page(0)
- if self.cursor_widget in self.widgets:
- focus_widget = self.widgets[self.cursor_widget][0]
+ if self.attributes.get('cursor') in self.widgets:
+ focus_widget = self.widgets[self.attributes['cursor']][0]
record = self.screen.current_record
position = reduce(lambda x, y: x + len(y), self.widgets, 0)
if record:
diff --git a/tryton/gui/window/view_form/view/form_gtk/__init__.py b/tryton/gui/window/view_form/view/form_gtk/__init__.py
index 5218f22bd6..c86640b67f 100644
--- a/tryton/gui/window/view_form/view/form_gtk/__init__.py
+++ b/tryton/gui/window/view_form/view/form_gtk/__init__.py
@@ -1,3 +1,2 @@
#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 parser import *
diff --git a/tryton/gui/window/view_form/view/form_gtk/binary.py b/tryton/gui/window/view_form/view/form_gtk/binary.py
index 26f704a722..c7ecca48bf 100644
--- a/tryton/gui/window/view_form/view/form_gtk/binary.py
+++ b/tryton/gui/window/view_form/view/form_gtk/binary.py
@@ -6,16 +6,16 @@ import os
import tempfile
from tryton.common import common
from tryton.common import file_selection, Tooltips, file_open, slugify
-from interface import WidgetInterface
+from .widget import Widget
_ = gettext.gettext
-class Binary(WidgetInterface):
+class Binary(Widget):
"Binary"
- def __init__(self, field_name, model_name, attrs=None):
- super(Binary, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(Binary, self).__init__(view, attrs)
self.filename = attrs.get('filename')
diff --git a/tryton/gui/window/view_form/view/form_gtk/calendar.py b/tryton/gui/window/view_form/view/form_gtk/calendar.py
index bfab5539db..64682c3ce6 100644
--- a/tryton/gui/window/view_form/view/form_gtk/calendar.py
+++ b/tryton/gui/window/view_form/view/form_gtk/calendar.py
@@ -3,18 +3,18 @@
import gtk
import gettext
-from interface import WidgetInterface
+from .widget import Widget
from tryton.common.date_widget import DateEntry
from tryton.translate import date_format
_ = gettext.gettext
-class Calendar(WidgetInterface):
+class Calendar(Widget):
"Calendar"
- def __init__(self, field_name, model_name, attrs=None):
- super(Calendar, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(Calendar, self).__init__(view, attrs)
self.widget = gtk.HBox()
self.entry = DateEntry('')
@@ -76,9 +76,6 @@ class Calendar(WidgetInterface):
class DateTime(Calendar):
"DateTime"
- def __init__(self, field_name, model_name, attrs=None):
- super(DateTime, self).__init__(field_name, model_name, attrs=attrs)
-
def get_format(self, record, field):
return date_format() + ' ' + field.time_format(record)
@@ -86,8 +83,8 @@ class DateTime(Calendar):
class Time(Calendar):
"Time"
- def __init__(self, field_name, model_name, attrs=None):
- super(Time, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(Time, self).__init__(view, attrs)
self.entry.set_icon_from_stock(gtk.ENTRY_ICON_SECONDARY, None)
def get_format(self, record, field):
diff --git a/tryton/gui/window/view_form/view/form_gtk/char.py b/tryton/gui/window/view_form/view/form_gtk/char.py
index 3cf5a2a73c..28e7be5149 100644
--- a/tryton/gui/window/view_form/view/form_gtk/char.py
+++ b/tryton/gui/window/view_form/view/form_gtk/char.py
@@ -4,18 +4,18 @@ import gettext
import gobject
import gtk
-from interface import WidgetInterface, TranslateMixin
+from .widget import Widget, TranslateMixin
from tryton.common import Tooltips
from tryton.common.entry_position import manage_entry_position
_ = gettext.gettext
-class Char(WidgetInterface, TranslateMixin):
+class Char(Widget, TranslateMixin):
"Char"
- def __init__(self, field_name, model_name, attrs=None):
- super(Char, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(Char, self).__init__(view, attrs)
self.widget = gtk.HBox()
self.autocomplete = bool(attrs.get('autocomplete'))
@@ -153,8 +153,8 @@ class Char(WidgetInterface, TranslateMixin):
class Password(Char):
- def __init__(self, field_name, model_name, attrs=None):
- super(Password, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(Password, self).__init__(view, attrs)
self.entry.props.visibility = False
self.visibility_checkbox = gtk.CheckButton()
diff --git a/tryton/gui/window/view_form/view/form_gtk/checkbox.py b/tryton/gui/window/view_form/view/form_gtk/checkbox.py
index e6f94e3b63..25a6b88009 100644
--- a/tryton/gui/window/view_form/view/form_gtk/checkbox.py
+++ b/tryton/gui/window/view_form/view/form_gtk/checkbox.py
@@ -1,16 +1,16 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import gtk
-from interface import WidgetInterface
+from .widget import Widget
import gettext
_ = gettext.gettext
-class CheckBox(WidgetInterface):
+class CheckBox(Widget):
- def __init__(self, field_name, model_name, attrs=None):
- super(CheckBox, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(CheckBox, self).__init__(view, attrs)
self.widget = gtk.CheckButton()
self.widget.connect('focus-out-event', lambda x, y: self._focus_out())
self.widget.connect_after('toggled', self.sig_activate)
diff --git a/tryton/gui/window/view_form/view/form_gtk/dictionary.py b/tryton/gui/window/view_form/view/form_gtk/dictionary.py
index 87d1cce075..3801312b41 100644
--- a/tryton/gui/window/view_form/view/form_gtk/dictionary.py
+++ b/tryton/gui/window/view_form/view/form_gtk/dictionary.py
@@ -9,7 +9,7 @@ import decimal
import gettext
from decimal import Decimal
-from interface import WidgetInterface
+from .widget import Widget
from tryton.config import CONFIG
from tryton.gui.window.win_search import WinSearch
from tryton.common import RPCExecute, RPCException, timezoned_date, \
@@ -306,10 +306,10 @@ DICT_ENTRIES = {
}
-class DictWidget(WidgetInterface):
+class DictWidget(Widget):
- def __init__(self, field_name, model_name, attrs=None):
- super(DictWidget, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(DictWidget, self).__init__(view, attrs)
self.schema_model = attrs['schema_model']
self.keys = {}
self.fields = {}
diff --git a/tryton/gui/window/view_form/view/form_gtk/float.py b/tryton/gui/window/view_form/view/form_gtk/float.py
index 2ae2f77545..c0555f370b 100644
--- a/tryton/gui/window/view_form/view/form_gtk/float.py
+++ b/tryton/gui/window/view_form/view/form_gtk/float.py
@@ -2,14 +2,14 @@
#this repository contains the full copyright notices and license terms.
import gtk
import locale
-from integer import Integer
+from .integer import Integer
class Float(Integer):
"Float"
- def __init__(self, field_name, model_name, attrs=None):
- super(Float, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(Float, self).__init__(view, attrs)
self.entry.connect('key-press-event', self.key_press_event)
def display(self, record, field):
diff --git a/tryton/gui/window/view_form/view/form_gtk/float_time.py b/tryton/gui/window/view_form/view/form_gtk/float_time.py
index 93d759236e..573530740b 100644
--- a/tryton/gui/window/view_form/view/form_gtk/float_time.py
+++ b/tryton/gui/window/view_form/view/form_gtk/float_time.py
@@ -1,15 +1,15 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import gtk
-from interface import WidgetInterface
+from .widget import Widget
import tryton.common as common
import tryton.rpc as rpc
-class FloatTime(WidgetInterface):
+class FloatTime(Widget):
- def __init__(self, field_name, model_name, attrs=None):
- super(FloatTime, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(FloatTime, self).__init__(view, attrs)
self.widget = gtk.HBox()
self.entry = gtk.Entry()
diff --git a/tryton/gui/window/view_form/view/form_gtk/image.py b/tryton/gui/window/view_form/view/form_gtk/image.py
index ba5bdd0df4..4139e45709 100644
--- a/tryton/gui/window/view_form/view/form_gtk/image.py
+++ b/tryton/gui/window/view_form/view/form_gtk/image.py
@@ -7,7 +7,7 @@ import os
import tempfile
from tryton.common import file_selection, Tooltips, file_open, slugify
from tryton.config import PIXMAPS_DIR
-from interface import WidgetInterface
+from .widget import Widget
import urllib
_ = gettext.gettext
@@ -15,10 +15,10 @@ _ = gettext.gettext
NOIMAGE = open(os.path.join(PIXMAPS_DIR, 'tryton-noimage.png'), 'rb').read()
-class Image(WidgetInterface):
+class Image(Widget):
- def __init__(self, field_name, model_name, attrs=None):
- super(Image, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(Image, self).__init__(view, attrs)
self.filename = attrs.get('filename')
self.height = int(attrs.get('img_height', 100))
diff --git a/tryton/gui/window/view_form/view/form_gtk/integer.py b/tryton/gui/window/view_form/view/form_gtk/integer.py
index cc8ee4db22..256c3449f5 100644
--- a/tryton/gui/window/view_form/view/form_gtk/integer.py
+++ b/tryton/gui/window/view_form/view/form_gtk/integer.py
@@ -1,14 +1,14 @@
#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 char import Char
+from .char import Char
import locale
class Integer(Char):
"Integer"
- def __init__(self, field_name, model_name, attrs=None):
- super(Integer, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(Integer, self).__init__(view, attrs)
self.entry.set_width_chars(8)
_, _, padding, pack_type = self.widget.query_child_packing(
self.entry)
diff --git a/tryton/gui/window/view_form/view/form_gtk/many2many.py b/tryton/gui/window/view_form/view/form_gtk/many2many.py
index fbc0dd6f5e..d2229a9b0d 100644
--- a/tryton/gui/window/view_form/view/form_gtk/many2many.py
+++ b/tryton/gui/window/view_form/view/form_gtk/many2many.py
@@ -3,7 +3,7 @@
import gtk
from tryton.gui.window.view_form.screen import Screen
-from interface import WidgetInterface
+from .widget import Widget
from tryton.gui.window.win_search import WinSearch
from tryton.gui.window.win_form import WinForm
from tryton.config import CONFIG
@@ -16,10 +16,11 @@ from tryton.common.completion import get_completion, update_completion
_ = gettext.gettext
-class Many2Many(WidgetInterface):
+class Many2Many(Widget):
+ expand = True
- def __init__(self, field_name, model_name, attrs=None):
- super(Many2Many, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(Many2Many, self).__init__(view, attrs)
self.widget = gtk.VBox(homogeneous=False, spacing=5)
self._readonly = True
@@ -97,8 +98,8 @@ class Many2Many(WidgetInterface):
self.wid_text.connect('key_press_event', self.on_keypress)
def _color_widget(self):
- if hasattr(self.screen.current_view, 'widget_tree'):
- return self.screen.current_view.widget_tree
+ if hasattr(self.screen.current_view, 'treeview'):
+ return self.screen.current_view.treeview
return super(Many2Many, self)._color_widget()
def grab_focus(self):
diff --git a/tryton/gui/window/view_form/view/form_gtk/many2one.py b/tryton/gui/window/view_form/view/form_gtk/many2one.py
index 04f2d2313f..a4692ba19b 100644
--- a/tryton/gui/window/view_form/view/form_gtk/many2one.py
+++ b/tryton/gui/window/view_form/view/form_gtk/many2one.py
@@ -3,7 +3,8 @@
import gtk
import gobject
import gettext
-from interface import WidgetInterface
+
+from .widget import Widget
import tryton.common as common
from tryton.gui.window.view_form.screen import Screen
from tryton.gui.window.win_search import WinSearch
@@ -17,10 +18,10 @@ from tryton.common.entry_position import manage_entry_position
_ = gettext.gettext
-class Many2One(WidgetInterface):
+class Many2One(Widget):
- def __init__(self, field_name, model_name, attrs=None):
- super(Many2One, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(Many2One, self).__init__(view, attrs)
self.widget = gtk.HBox(spacing=0)
self.widget.set_property('sensitive', True)
diff --git a/tryton/gui/window/view_form/view/form_gtk/multiselection.py b/tryton/gui/window/view_form/view/form_gtk/multiselection.py
index 99b2abf782..6e60ed82eb 100644
--- a/tryton/gui/window/view_form/view/form_gtk/multiselection.py
+++ b/tryton/gui/window/view_form/view/form_gtk/multiselection.py
@@ -3,16 +3,16 @@
import gtk
import gobject
-from .interface import WidgetInterface
+from .widget import Widget
from tryton.common.selection import SelectionMixin
from tryton.common.treeviewcontrol import TreeViewControl
-class MultiSelection(WidgetInterface, SelectionMixin):
+class MultiSelection(Widget, SelectionMixin):
+ expand = True
- def __init__(self, field_name, model_name, attrs=None):
- super(MultiSelection, self).__init__(field_name, model_name,
- attrs=attrs)
+ def __init__(self, view, attrs):
+ super(MultiSelection, self).__init__(view, attrs)
self.widget = gtk.ScrolledWindow()
self.widget.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
diff --git a/tryton/gui/window/view_form/view/form_gtk/one2many.py b/tryton/gui/window/view_form/view/form_gtk/one2many.py
index ca9f02cbce..604a03a886 100644
--- a/tryton/gui/window/view_form/view/form_gtk/one2many.py
+++ b/tryton/gui/window/view_form/view/form_gtk/one2many.py
@@ -3,7 +3,7 @@
import gtk
import gettext
-from interface import WidgetInterface
+from .widget import Widget
from tryton.gui.window.view_form.screen import Screen
from tryton.gui.window.win_search import WinSearch
from tryton.gui.window.win_form import WinForm
@@ -16,10 +16,11 @@ from tryton.common.completion import get_completion, update_completion
_ = gettext.gettext
-class One2Many(WidgetInterface):
+class One2Many(Widget):
+ expand = True
- def __init__(self, field_name, model_name, attrs=None):
- super(One2Many, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(One2Many, self).__init__(view, attrs)
self.widget = gtk.VBox(homogeneous=False, spacing=2)
self._readonly = True
@@ -192,8 +193,8 @@ class One2Many(WidgetInterface):
but_switch.props.sensitive = self.screen.number_of_views > 1
def _color_widget(self):
- if hasattr(self.screen.current_view, 'widget_tree'):
- return self.screen.current_view.widget_tree
+ if hasattr(self.screen.current_view, 'treeview'):
+ return self.screen.current_view.treeview
return super(One2Many, self)._color_widget()
def grab_focus(self):
@@ -324,7 +325,7 @@ class One2Many(WidgetInterface):
sequence = None
for view in self.screen.views:
if view.view_type == 'tree':
- sequence = view.widget_tree.sequence
+ sequence = view.attributes.get('sequence')
if sequence:
break
@@ -402,7 +403,7 @@ class One2Many(WidgetInterface):
sequence = None
if self.screen.current_view.view_type == 'tree':
- sequence = self.screen.current_view.widget_tree.sequence
+ sequence = self.screen.current_view.attributes.get('sequence')
def callback(result):
self.focus_out = True
diff --git a/tryton/gui/window/view_form/view/form_gtk/one2one.py b/tryton/gui/window/view_form/view/form_gtk/one2one.py
index 3af021f7f5..334c8af02f 100644
--- a/tryton/gui/window/view_form/view/form_gtk/one2one.py
+++ b/tryton/gui/window/view_form/view/form_gtk/one2one.py
@@ -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 many2one import Many2One
+from .many2one import Many2One
class One2One(Many2One):
diff --git a/tryton/gui/window/view_form/view/form_gtk/parser.py b/tryton/gui/window/view_form/view/form_gtk/parser.py
deleted file mode 100644
index e7c71f2cee..0000000000
--- a/tryton/gui/window/view_form/view/form_gtk/parser.py
+++ /dev/null
@@ -1,572 +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.
-import gtk
-import gettext
-import logging
-from tryton.gui.window.view_form.view.interface import ParserInterface
-import tryton.common as common
-from tryton.config import CONFIG
-from tryton.common.button import Button
-
-_ = gettext.gettext
-
-
-class StateMixin(object):
-
- def __init__(self, *args, **kwargs):
- self.attrs = kwargs.pop('attrs')
- super(StateMixin, self).__init__(*args, **kwargs)
-
- def state_set(self, record):
- if record:
- state_changes = record.expr_eval(self.attrs.get('states', {}))
- else:
- state_changes = {}
- if state_changes.get('invisible', self.attrs.get('invisible')):
- self.hide()
- else:
- self.show()
-
-
-class Label(StateMixin, gtk.Label):
-
- def state_set(self, record):
- super(Label, self).state_set(record)
- if not self.attrs.get('string', True) and 'name' in self.attrs:
- if record:
- field = record.group.fields[self.attrs['name']]
- text = field.get_client(record) or ''
- else:
- text = ''
- self.set_text(text)
-
-
-class VBox(StateMixin, gtk.VBox):
- pass
-
-
-class Image(StateMixin, gtk.Image):
- pass
-
-
-class Frame(StateMixin, gtk.Frame):
-
- def __init__(self, label=None, attrs=None, widgets=None):
- if not label: # label must be None to have no label widget
- label = None
- super(Frame, self).__init__(label=label, attrs=attrs)
- self.widgets = widgets or {}
- if not label:
- self.set_shadow_type(gtk.SHADOW_NONE)
- self.set_border_width(0)
-
-
-class ScrolledWindow(StateMixin, gtk.ScrolledWindow):
- pass
-
-
-class Notebook(StateMixin, gtk.Notebook):
-
- def state_set(self, record):
- super(Notebook, self).state_set(record)
- if record:
- state_changes = record.expr_eval(self.attrs.get('states', {}))
- else:
- state_changes = {}
- if state_changes.get('readonly', self.attrs.get('readonly')):
- for widgets in self.widgets.itervalues():
- for widget in widgets:
- widget._readonly_set(True)
- widget.color_set('readonly')
-
-
-class Alignment(gtk.Alignment):
-
- def __init__(self, widget, attrs):
- super(Alignment, self).__init__(
- float(attrs.get('xalign', 0.0)),
- float(attrs.get('yalign', 0.5)),
- float(attrs.get('xexpand', 1.0)),
- float(attrs.get('yexpand', 1.0)))
- self.add(widget)
- widget.connect('show', lambda *a: self.show())
- widget.connect('hide', lambda *a: self.hide())
-
-
-class _container(object):
- def __init__(self, tooltips):
- self.cont = []
- self.col = []
- self.tooltips = tooltips
-
- def new(self, col=4):
- table = gtk.Table(1, col)
- table.set_homogeneous(False)
- 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)
-
- def get(self):
- return self.cont[-1][0]
-
- def pop(self):
- table = self.cont.pop()[0]
- self.col.pop()
- return table
-
- def newline(self):
- (table, width, height) = self.cont[-1]
- if width > 0:
- self.cont[-1] = (table, 0, height + 1)
- table.resize(height + 1, self.col[-1])
-
- def wid_add(self, widget, name='', yexpand=False, ypadding=2, rowspan=1,
- colspan=1, 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]
- if colspan + width > self.col[-1]:
- self.newline()
- (table, width, height) = self.cont[-1]
- yopt = False
- if yexpand:
- yopt = yopt | gtk.EXPAND
- if yfill:
- yopt = yopt | gtk.FILL
- xopt = False
- if xexpand:
- xopt = xopt | gtk.EXPAND
- if xfill:
- xopt = xopt | gtk.FILL
- if help_tip:
- self.tooltips.set_tip(widget, help_tip)
- self.tooltips.enable()
- widget.show_all()
- table.attach(widget, width, width + colspan,
- height, height + rowspan,
- yoptions=yopt, ypadding=ypadding,
- xoptions=xopt, xpadding=xpadding)
- self.cont[-1] = (table, width + colspan, height)
- wid_list = table.get_children()
- wid_list.reverse()
- table.set_focus_chain(wid_list)
-
- def empty_add(self, colspan=1):
- (table, width, height) = self.cont[-1]
- if colspan > self.col[-1]:
- colspan = self.col[-1]
- if colspan + width > self.col[-1]:
- self.newline()
- (table, width, height) = self.cont[-1]
- self.cont[-1] = (table, width + colspan, height)
-
-
-class ParserForm(ParserInterface):
-
- def __init__(self, parent=None, attrs=None, screen=None,
- children_field=None):
- super(ParserForm, self).__init__(parent=parent, attrs=attrs,
- screen=screen, children_field=children_field)
- self.widget_id = 0
-
- def parse(self, model_name, root_node, fields, notebook=None, paned=None,
- tooltips=None):
- dict_widget = {}
- state_widgets = []
- notebook_list = []
- attrs = common.node_attributes(root_node)
- on_write = attrs.get('on_write', '')
- if not tooltips:
- tooltips = common.Tooltips()
- container = _container(tooltips)
- if CONFIG['client.modepda']:
- container.new(col=1)
- else:
- container.new(col=int(attrs.get('col', 4)))
- cursor_widget = attrs.get('cursor')
-
- if not self.title:
- self.title = attrs.get('string', 'Unknown')
-
- for node in root_node.childNodes:
- if not node.nodeType == node.ELEMENT_NODE:
- continue
- attrs = common.node_attributes(node)
- if not cursor_widget:
- if (attrs.get('name') and fields.get(attrs['name'])
- and not int(attrs.get('invisible', 0))
- and not int(attrs.get('readonly', 0))
- and attrs['name'] != self.screen.exclude_field):
- cursor_widget = attrs.get('name')
- yexpand = int(attrs.get('yexpand', 0))
- yfill = int(attrs.get('yfill', 0))
- xexpand = int(attrs.get('xexpand', 1))
- xfill = int(attrs.get('xfill', 1))
- colspan = int(attrs.get('colspan', 1))
- if node.localName == 'image':
- common.ICONFACTORY.register_icon(attrs['name'])
- icon = Image(attrs=attrs)
- state_widgets.append(icon)
- icon.set_from_stock(attrs['name'], gtk.ICON_SIZE_DIALOG)
- container.wid_add(icon,
- help_tip=attrs.get('help', False),
- colspan=colspan,
- yexpand=yexpand, yfill=yfill, ypadding=10,
- xexpand=xexpand, xfill=xfill)
- elif node.localName == 'separator':
- text = attrs.get('string', '')
- if 'name' in attrs:
- for attr_name in ('states', 'invisible'):
- if attr_name not in attrs and attrs['name'] in fields:
- if attr_name in fields[attrs['name']].attrs:
- attrs[attr_name] = fields[attrs['name']
- ].attrs[attr_name]
- if not text:
- text = fields[attrs['name']].attrs['string']
- vbox = VBox(attrs=attrs)
- state_widgets.append(vbox)
- if text:
- label = gtk.Label(text)
- label.set_alignment(float(attrs.get('xalign', 0.0)), 0.5)
- vbox.pack_start(label)
- vbox.pack_start(gtk.HSeparator())
- container.wid_add(vbox,
- help_tip=attrs.get('help', False),
- colspan=colspan,
- yexpand=yexpand, yfill=yfill, ypadding=10,
- xexpand=xexpand, xfill=xfill)
- elif node.localName == 'label':
- text = attrs.get('string', '')
- if 'name' in attrs and attrs['name'] in fields:
- if attrs['name'] == self.screen.exclude_field:
- container.empty_add(int(attrs.get('colspan', 1)))
- continue
- for attr_name in ('states', 'invisible'):
- if attr_name not in attrs \
- and attr_name in fields[attrs['name']].attrs:
- attrs[attr_name] = fields[attrs['name']
- ].attrs[attr_name]
- if 'string' not in attrs:
- if gtk.widget_get_default_direction() == \
- gtk.TEXT_DIR_RTL:
- text = _(':') + \
- fields[attrs['name']].attrs['string']
- else:
- text = fields[attrs['name']].attrs['string'] + \
- _(':')
- if 'xalign' not in attrs:
- attrs['xalign'] = 1.0
- elif not text:
- for node in node.childNodes:
- if node.nodeType == node.TEXT_NODE:
- text += node.data
- else:
- text += node.toxml()
- label = Label(text, attrs=attrs)
- state_widgets.append(label)
- if CONFIG['client.modepda']:
- attrs['xalign'] = 0.0
- label.set_alignment(float(attrs.get('xalign', 1.0)),
- float(attrs.get('yalign', 0.0)))
- label.set_angle(int(attrs.get('angle', 0)))
- xexpand = bool(attrs.get('xexpand', 0))
- container.wid_add(label,
- help_tip=attrs.get('help', False),
- colspan=colspan,
- yexpand=yexpand, yfill=yfill,
- xexpand=xexpand, xfill=xfill)
-
- elif node.localName == 'newline':
- container.newline()
-
- elif node.localName == 'button':
- button = Button(attrs)
- state_widgets.append(button)
- container.wid_add(button,
- help_tip=attrs.get('help', False),
- colspan=colspan,
- yexpand=yexpand, yfill=yfill,
- xexpand=xexpand, xfill=xfill)
-
- elif node.localName == 'notebook':
- notebook = Notebook(attrs=attrs)
- state_widgets.append(notebook)
- notebook.set_scrollable(True)
- notebook_list.append(notebook)
- if CONFIG['client.form_tab'] == 'top':
- pos = gtk.POS_TOP
- elif CONFIG['client.form_tab'] == 'left':
- pos = gtk.POS_LEFT
- elif CONFIG['client.form_tab'] == 'right':
- pos = gtk.POS_RIGHT
- elif CONFIG['client.form_tab'] == 'bottom':
- pos = gtk.POS_BOTTOM
- notebook.set_tab_pos(pos)
- notebook.set_border_width(3)
- colspan = int(attrs.get('colspan', 4))
- yexpand = bool(attrs.get('yexpand', 1))
- yfill = bool(attrs.get('yfill', 1))
- container.wid_add(notebook,
- colspan=colspan,
- yexpand=yexpand, yfill=yfill,
- xexpand=xexpand, xfill=xfill)
- (widget, widgets, state_widgets2, 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)
- state_widgets += state_widgets2
- for widget_name, widgets in widgets.items():
- dict_widget.setdefault(widget_name, [])
- dict_widget[widget_name].extend(widgets)
-
- elif node.localName == 'page':
- if CONFIG['client.form_tab'] == 'left':
- angle = 90
- tab_box = gtk.VBox(spacing=3)
- image_pos, image_rotate = ('end',
- gtk.gdk.PIXBUF_ROTATE_COUNTERCLOCKWISE)
- elif CONFIG['client.form_tab'] == 'right':
- angle = -90
- tab_box = gtk.VBox(spacing=3)
- image_pos, image_rotate = ('start',
- gtk.gdk.PIXBUF_ROTATE_CLOCKWISE)
- else:
- angle = 0
- tab_box = gtk.HBox(spacing=3)
- image_pos, image_rotate = ('start',
- gtk.gdk.PIXBUF_ROTATE_NONE)
- text = attrs.get('string', '')
- if 'name' in attrs and attrs['name'] in fields:
- if attrs['name'] == self.screen.exclude_field:
- continue
- for attr_name in ('states', 'invisible'):
- if attr_name in fields[attrs['name']].attrs:
- attrs[attr_name] = \
- fields[attrs['name']].attrs[attr_name]
- if not text:
- text = fields[attrs['name']].attrs['string']
- if not text:
- text = _('No String Attr.')
- if '_' not in text:
- text = '_' + text
- tab_label = gtk.Label(text)
- tab_label.set_angle(angle)
- tab_label.set_use_underline(True)
- tab_box.pack_start(tab_label)
- if 'icon' in attrs:
- common.ICONFACTORY.register_icon(attrs['icon'])
- pixbuf = tab_box.render_icon(attrs['icon'],
- gtk.ICON_SIZE_SMALL_TOOLBAR)
- pixbuf = pixbuf.rotate_simple(image_rotate)
- icon = gtk.Image()
- icon.set_from_pixbuf(pixbuf)
- if image_pos == 'end':
- tab_box.pack_end(icon)
- else:
- tab_box.pack_start(icon)
- tab_box.show_all()
- (widget, widgets, state_widgets2, 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)
- state_widgets += state_widgets2
- for widget_name, widgets in widgets.iteritems():
- dict_widget.setdefault(widget_name, [])
- dict_widget[widget_name].extend(widgets)
-
- viewport = gtk.Viewport()
- viewport.set_shadow_type(gtk.SHADOW_NONE)
- viewport.add(widget)
- viewport.show()
- scrolledwindow = ScrolledWindow(attrs=attrs)
- scrolledwindow.set_shadow_type(gtk.SHADOW_NONE)
- scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC,
- gtk.POLICY_AUTOMATIC)
- scrolledwindow.add(viewport)
-
- state_widgets.append(scrolledwindow)
- notebook.append_page(scrolledwindow, tab_box)
-
- elif node.localName == 'field':
- name = str(attrs['name'])
- if name == self.screen.exclude_field:
- container.empty_add(int(attrs.get('colspan', 1)))
- continue
- if name not in fields:
- container.empty_add(int(attrs.get('colspan', 1)))
- log = logging.getLogger(__name__)
- log.error('Unknown field "%s"' % str(name))
- continue
- ftype = attrs.get('widget', fields[name].attrs['type'])
- if not ftype in WIDGETS_TYPE:
- container.empty_add(int(attrs.get('colspan', 1)))
- continue
- for attr_name in ('relation', 'domain', 'selection',
- 'relation_field', 'string', 'views', 'invisible',
- 'add_remove', 'sort', 'context', 'size', 'filename',
- 'autocomplete', 'translate', 'create', 'delete',
- 'selection_change_with', 'schema_model'):
- if attr_name in fields[name].attrs and \
- not attr_name in attrs:
- attrs[attr_name] = fields[name].attrs[attr_name]
-
- widget_act = WIDGETS_TYPE[ftype][0](name, model_name, attrs)
- self.widget_id += 1
- widget_act.position = self.widget_id
- dict_widget.setdefault(name, [])
- dict_widget[name].append(widget_act)
- yexpand = bool(attrs.get('yexpand', WIDGETS_TYPE[ftype][2]))
- yfill = bool(attrs.get('yfill', WIDGETS_TYPE[ftype][3]))
- hlp = fields[name].attrs.get('help', attrs.get('help', False))
- if attrs.get('height', False) or attrs.get('width', False):
- widget_act.widget.set_size_request(
- int(attrs.get('width', -1)),
- int(attrs.get('height', -1)))
- container.wid_add(Alignment(widget_act.widget, attrs),
- fields[name].attrs['string'], fname=name,
- help_tip=hlp,
- colspan=colspan,
- yexpand=yexpand, yfill=yfill,
- xexpand=xexpand, xfill=xfill)
-
- elif node.localName == 'group':
- (widget, widgets, state_widgets2, 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)
- for widget_name, lwidgets in widgets.iteritems():
- dict_widget.setdefault(widget_name, [])
- dict_widget[widget_name].extend(lwidgets)
- state_widgets += state_widgets2
- text = ''
- if 'name' in attrs and attrs['name'] in fields:
- if attrs['name'] == self.screen.exclude_field:
- container.empty_add(int(attrs.get('colspan', 1)))
- continue
- for attr_name in ('states', 'invisible'):
- if attr_name in fields[attrs['name']].attrs:
- attrs[attr_name] = fields[attrs['name']
- ].attrs[attr_name]
- text = fields[attrs['name']].attrs['string']
- if attrs.get('string'):
- text = attrs['string']
- widget.set_homogeneous(bool(int(attrs.get('homogeneous', 0))))
-
- frame = Frame(text, attrs=attrs, widgets=widgets)
- frame.add(widget)
- state_widgets.append(frame)
- container.wid_add(frame,
- colspan=colspan,
- yexpand=yexpand, yfill=yfill, ypadding=0,
- xexpand=xexpand, xfill=xfill, xpadding=0)
- elif node.localName == 'hpaned':
- hpaned = gtk.HPaned()
- container.wid_add(hpaned, colspan=int(attrs.get('colspan', 4)),
- yexpand=True, yfill=True)
- (widget, widgets, state_widgets2, 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)
- state_widgets += state_widgets2
- for widget_name, widgets in widgets.iteritems():
- dict_widget.setdefault(widget_name, [])
- dict_widget[widget_name].extend(widgets)
- if 'position' in attrs:
- hpaned.set_position(int(attrs['position']))
- elif node.localName == 'vpaned':
- vpaned = gtk.VPaned()
- container.wid_add(vpaned, colspan=int(attrs.get('colspan', 4)),
- yexpand=True, yfill=True)
- (widget, widgets, state_widgets2, spam, notebook_list2,
- 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)
- state_widgets += state_widgets2
- for widget_name, widgets in widgets.iteritems():
- dict_widget.setdefault(widget_name, [])
- dict_widget[widget_name].extend(widgets)
- if 'position' in attrs:
- vpaned.set_position(int(attrs['position']))
- elif node.localName == 'child':
- (widget, widgets, state_widgets2, 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)
- state_widgets += state_widgets2
- for widget_name, widgets in widgets.iteritems():
- dict_widget.setdefault(widget_name, [])
- dict_widget[widget_name].extend(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)
- return container.pop(), dict_widget, state_widgets, on_write, \
- notebook_list, cursor_widget
-
-from calendar import Calendar, DateTime, Time
-from float import Float
-from integer import Integer
-from selection import Selection
-from char import Char, Password
-from float_time import FloatTime
-from checkbox import CheckBox
-from reference import Reference
-from binary import Binary
-from textbox import TextBox
-from one2many import One2Many
-from many2many import Many2Many
-from many2one import Many2One
-from url import Email, URL, CallTo, SIP
-from image import Image as Image2
-from progressbar import ProgressBar
-from one2one import One2One
-from richtextbox import RichTextBox
-from dictionary import DictWidget
-from multiselection import MultiSelection
-
-
-WIDGETS_TYPE = {
- 'date': (Calendar, 1, False, False),
- 'datetime': (DateTime, 1, False, False),
- 'time': (Time, 1, False, False),
- 'float': (Float, 1, False, False),
- 'numeric': (Float, 1, False, False),
- 'integer': (Integer, 1, False, False),
- 'biginteger': (Integer, 1, False, False),
- 'selection': (Selection, 1, False, False),
- 'char': (Char, 1, False, False),
- 'password': (Password, 1, False, False),
- 'float_time': (FloatTime, 1, False, False),
- 'boolean': (CheckBox, 1, False, False),
- 'reference': (Reference, 1, False, False),
- 'binary': (Binary, 1, False, False),
- 'text': (TextBox, 1, True, True),
- '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),
- 'progressbar': (ProgressBar, 1, False, False),
- 'one2one': (One2One, 1, False, False),
- 'richtext': (RichTextBox, 1, True, True),
- 'dict': (DictWidget, 1, False, False),
- 'multiselection': (MultiSelection, 1, True, True),
-}
diff --git a/tryton/gui/window/view_form/view/form_gtk/progressbar.py b/tryton/gui/window/view_form/view/form_gtk/progressbar.py
index 23e9b6ea29..40511492e8 100644
--- a/tryton/gui/window/view_form/view/form_gtk/progressbar.py
+++ b/tryton/gui/window/view_form/view/form_gtk/progressbar.py
@@ -1,11 +1,11 @@
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
import gtk
-from interface import WidgetInterface
+from .widget import Widget
import locale
-class ProgressBar(WidgetInterface):
+class ProgressBar(Widget):
'Progress Bar'
orientations = {
'left_to_right': gtk.PROGRESS_LEFT_TO_RIGHT,
@@ -14,8 +14,8 @@ class ProgressBar(WidgetInterface):
'top_to_bottom': gtk.PROGRESS_TOP_TO_BOTTOM,
}
- def __init__(self, field_name, model_name, attrs=None):
- super(ProgressBar, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(ProgressBar, self).__init__(view, attrs)
self.widget = gtk.ProgressBar()
orientation = self.orientations.get(attrs.get('orientation',
'left_to_right'), gtk.PROGRESS_LEFT_TO_RIGHT)
diff --git a/tryton/gui/window/view_form/view/form_gtk/reference.py b/tryton/gui/window/view_form/view/form_gtk/reference.py
index 0455e23e55..4faa4be4cc 100644
--- a/tryton/gui/window/view_form/view/form_gtk/reference.py
+++ b/tryton/gui/window/view_form/view/form_gtk/reference.py
@@ -3,7 +3,7 @@
import gtk
import gettext
-from many2one import Many2One
+from .many2one import Many2One
from tryton.common.selection import SelectionMixin, PopdownMixin
_ = gettext.gettext
@@ -11,8 +11,8 @@ _ = gettext.gettext
class Reference(Many2One, SelectionMixin, PopdownMixin):
- def __init__(self, field_name, model_name, attrs=None):
- super(Reference, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(Reference, self).__init__(view, attrs)
self.widget_combo = gtk.ComboBoxEntry()
child = self.widget_combo.get_child()
diff --git a/tryton/gui/window/view_form/view/form_gtk/richtextbox.py b/tryton/gui/window/view_form/view/form_gtk/richtextbox.py
index 69b85847df..d804f6379a 100644
--- a/tryton/gui/window/view_form/view/form_gtk/richtextbox.py
+++ b/tryton/gui/window/view_form/view/form_gtk/richtextbox.py
@@ -5,7 +5,7 @@ import gtk
import pango
import re
from gettext import gettext as _
-from textbox import TextBox
+from .textbox import TextBox
from tryton.common import get_toplevel_window
@@ -64,8 +64,8 @@ def formalize_text_markup(text):
class RichTextBox(TextBox):
'''Implements a rich text editor as widget to Tryton Client'''
- def __init__(self, field_name, model_name, attrs=None):
- super(RichTextBox, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(RichTextBox, self).__init__(view, attrs)
self.table_tag = gtk.TextTagTable()
self.text_buffer = gtk.TextBuffer(self.table_tag)
self.textview.set_buffer(self.text_buffer)
diff --git a/tryton/gui/window/view_form/view/form_gtk/selection.py b/tryton/gui/window/view_form/view/form_gtk/selection.py
index 09a977b7ae..622944b57c 100644
--- a/tryton/gui/window/view_form/view/form_gtk/selection.py
+++ b/tryton/gui/window/view_form/view/form_gtk/selection.py
@@ -3,15 +3,15 @@
import gtk
import gobject
-from interface import WidgetInterface
+from .widget import Widget
from tryton.common.selection import SelectionMixin, selection_shortcuts, \
PopdownMixin
-class Selection(WidgetInterface, SelectionMixin, PopdownMixin):
+class Selection(Widget, SelectionMixin, PopdownMixin):
- def __init__(self, field_name, model_name, attrs=None):
- super(Selection, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(Selection, self).__init__(view, attrs)
self.widget = gtk.HBox(spacing=3)
self.entry = gtk.ComboBoxEntry()
diff --git a/tryton/gui/window/view_form/view/form_gtk/state_widget.py b/tryton/gui/window/view_form/view/form_gtk/state_widget.py
new file mode 100644
index 0000000000..a88369b969
--- /dev/null
+++ b/tryton/gui/window/view_form/view/form_gtk/state_widget.py
@@ -0,0 +1,84 @@
+#This file is part of Tryton. The COPYRIGHT file at the top level of
+#this repository contains the full copyright notices and license terms.
+import gtk
+
+
+class StateMixin(object):
+
+ def __init__(self, *args, **kwargs):
+ self.attrs = kwargs.pop('attrs')
+ super(StateMixin, self).__init__(*args, **kwargs)
+
+ def state_set(self, record):
+ if record:
+ state_changes = record.expr_eval(self.attrs.get('states', {}))
+ else:
+ state_changes = {}
+ if state_changes.get('invisible', self.attrs.get('invisible')):
+ self.hide()
+ else:
+ self.show()
+
+
+class Label(StateMixin, gtk.Label):
+
+ def state_set(self, record):
+ super(Label, self).state_set(record)
+ if not self.attrs.get('string', True) and 'name' in self.attrs:
+ if record:
+ field = record.group.fields[self.attrs['name']]
+ text = field.get_client(record) or ''
+ else:
+ text = ''
+ self.set_text(text)
+
+
+class VBox(StateMixin, gtk.VBox):
+ pass
+
+
+class Image(StateMixin, gtk.Image):
+ pass
+
+
+class Frame(StateMixin, gtk.Frame):
+
+ def __init__(self, label=None, attrs=None):
+ if not label: # label must be None to have no label widget
+ label = None
+ super(Frame, self).__init__(label=label, attrs=attrs)
+ if not label:
+ self.set_shadow_type(gtk.SHADOW_NONE)
+ self.set_border_width(0)
+
+
+class ScrolledWindow(StateMixin, gtk.ScrolledWindow):
+ pass
+
+
+class Notebook(StateMixin, gtk.Notebook):
+
+ def state_set(self, record):
+ super(Notebook, self).state_set(record)
+ if record:
+ state_changes = record.expr_eval(self.attrs.get('states', {}))
+ else:
+ state_changes = {}
+ if state_changes.get('readonly', self.attrs.get('readonly')):
+ for widgets in self.widgets.itervalues():
+ for widget in widgets:
+ widget._readonly_set(True)
+ widget.color_set('readonly')
+
+
+class Alignment(gtk.Alignment):
+
+ def __init__(self, widget, attrs):
+ super(Alignment, self).__init__(
+ float(attrs.get('xalign', 0.0)),
+ float(attrs.get('yalign', 0.5)),
+ float(attrs.get('xexpand', 1.0)),
+ float(attrs.get('yexpand', 1.0)))
+ self.add(widget)
+ widget.connect('show', lambda *a: self.show())
+ widget.connect('hide', lambda *a: self.hide())
diff --git a/tryton/gui/window/view_form/view/form_gtk/textbox.py b/tryton/gui/window/view_form/view/form_gtk/textbox.py
index 176189bf19..19306da54b 100644
--- a/tryton/gui/window/view_form/view/form_gtk/textbox.py
+++ b/tryton/gui/window/view_form/view/form_gtk/textbox.py
@@ -1,7 +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 gtk
-from interface import WidgetInterface, TranslateMixin
+from .widget import Widget, TranslateMixin
from tryton.config import CONFIG
try:
@@ -10,10 +10,11 @@ except ImportError:
gtkspell = None
-class TextBox(WidgetInterface, TranslateMixin):
+class TextBox(Widget, TranslateMixin):
+ expand = True
- def __init__(self, field_name, model_name, attrs=None):
- super(TextBox, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(TextBox, self).__init__(view, attrs)
self.widget = gtk.VBox()
self.scrolledwindow = gtk.ScrolledWindow()
diff --git a/tryton/gui/window/view_form/view/form_gtk/url.py b/tryton/gui/window/view_form/view/form_gtk/url.py
index 870e0ff925..9edb0729b5 100644
--- a/tryton/gui/window/view_form/view/form_gtk/url.py
+++ b/tryton/gui/window/view_form/view/form_gtk/url.py
@@ -1,7 +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 gtk
-from char import Char
+from .char import Char
import webbrowser
import tryton.common as common
@@ -9,8 +9,8 @@ import tryton.common as common
class URL(Char):
"url"
- def __init__(self, field_name, model_name, attrs=None):
- super(URL, self).__init__(field_name, model_name, attrs=attrs)
+ def __init__(self, view, attrs):
+ super(URL, self).__init__(view, attrs)
self.tooltips = common.Tooltips()
self.button = gtk.Button()
diff --git a/tryton/gui/window/view_form/view/form_gtk/interface.py b/tryton/gui/window/view_form/view/form_gtk/widget.py
similarity index 94%
rename from tryton/gui/window/view_form/view/form_gtk/interface.py
rename to tryton/gui/window/view_form/view/form_gtk/widget.py
index 22ef029413..98f720f2eb 100644
--- a/tryton/gui/window/view_form/view/form_gtk/interface.py
+++ b/tryton/gui/window/view_form/view/form_gtk/widget.py
@@ -13,35 +13,36 @@ from tryton.common import RPCExecute, RPCException
_ = gettext.gettext
-class WidgetInterface(object):
+class Widget(object):
+ expand = False
- def __init__(self, field_name, model_name, attrs=None):
- super(WidgetInterface, self).__init__()
- self.field_name = field_name
- self.model_name = model_name
- self.view = None # Filled by ViewForm
- self.attrs = attrs or {}
- for attr_name in ('readonly', 'invisible'):
- if attr_name in self.attrs:
- self.attrs[attr_name] = bool(int(self.attrs[attr_name]))
+ def __init__(self, view, attrs):
+ super(Widget, self).__init__()
+ self.view = view
+ self.attrs = attrs
self.widget = None
self.position = 0
self.colors = {}
self.visible = True
self.color_name = None
- def __get_record(self):
- if self.view and self.view.screen:
- return self.view.screen.current_record
+ @property
+ def field_name(self):
+ return self.attrs['name']
- record = property(__get_record)
+ @property
+ def model_name(self):
+ return self.view.screen.model_name
- def __get_field(self):
+ @property
+ def record(self):
+ return self.view.screen.current_record
+
+ @property
+ def field(self):
if self.record:
return self.record.group.fields[self.field_name]
- field = property(__get_field)
-
def destroy(self):
pass
diff --git a/tryton/gui/window/view_form/view/graph.py b/tryton/gui/window/view_form/view/graph.py
index 850d608ff6..21051305e7 100644
--- a/tryton/gui/window/view_form/view/graph.py
+++ b/tryton/gui/window/view_form/view/graph.py
@@ -1,26 +1,80 @@
#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 interface import ParserView
+import sys
+import os
+
+import gtk
+import gettext
+
+from . import View
+from tryton.common import node_attributes, get_toplevel_window, message
+from tryton.config import TRYTON_ICON, CONFIG
+from .graph_gtk.bar import VerticalBar, HorizontalBar
+from .graph_gtk.line import Line
+from .graph_gtk.pie import Pie
+
+_ = gettext.gettext
-class ViewGraph(ParserView):
+class ViewGraph(View):
- def __init__(self, screen, widget, children=None, state_widgets=None,
- notebooks=None, cursor_widget=None, children_field=None):
- super(ViewGraph, self).__init__(screen, widget, children,
- state_widgets, notebooks, cursor_widget, children_field)
+ def __init__(self, screen, xml):
+ super(ViewGraph, self).__init__(screen, xml)
self.view_type = 'graph'
- self.widgets = children
- self.editable = False
+ self.widgets = {}
+ self.widget = self.parse(xml)
+
+ def parse(self, node):
+ xfield = None
+ yfields = []
+
+ for node in node.childNodes:
+ if node.nodeType != node.ELEMENT_NODE:
+ continue
+ if node.tagName == 'x':
+ for child in node.childNodes:
+ if not child.nodeType == child.ELEMENT_NODE:
+ continue
+ xfield = node_attributes(child)
+ field = self.screen.group.fields[xfield['name']]
+ if not xfield.get('string'):
+ xfield['string'] = field.attrs['string']
+ elif node.tagName == 'y':
+ for child in node.childNodes:
+ if not child.nodeType == child.ELEMENT_NODE:
+ continue
+ yattrs = node_attributes(child)
+ if not yattrs.get('string') and yattrs['name'] != '#':
+ field = self.screen.group.fields[yattrs['name']]
+ yattrs['string'] = field.attrs['string']
+ yfields.append(yattrs)
+
+ Widget = self.get_widget(self.attributes.get('type', 'vbar'))
+ widget = Widget(self, xfield, yfields)
+ self.widgets['root'] = widget
+ event = gtk.EventBox()
+ event.add(widget)
+ event.connect('button-press-event', self.button_press)
+ return event
+
+ WIDGETS = {
+ 'vbar': VerticalBar,
+ 'hbar': HorizontalBar,
+ 'line': Line,
+ 'pie': Pie,
+ }
+
+ @classmethod
+ def get_widget(cls, name):
+ return cls.WIDGETS[name]
def __getitem__(self, name):
return None
def destroy(self):
self.widget.destroy()
- for widget in self.widgets.keys():
- self.widgets[widget].destroy()
- del self.widgets[widget]
+ self.widgets['root'].destroy()
+ self.widgets.clear()
def set_value(self):
pass
@@ -29,8 +83,7 @@ class ViewGraph(ParserView):
pass
def display(self):
- for widget in self.widgets.itervalues():
- widget.display(self.screen.group)
+ self.widgets['root'].display(self.screen.group)
return True
def set_cursor(self, new=False, reset_view=True):
@@ -38,3 +91,98 @@ class ViewGraph(ParserView):
def get_fields(self):
return []
+
+ def get_buttons(self):
+ return []
+
+ def save(self, widget, graph):
+ parent = 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))
+ dia.set_icon(TRYTON_ICON)
+ dia.set_has_separator(True)
+ dia.set_default_response(gtk.RESPONSE_OK)
+
+ dia.vbox.set_spacing(5)
+ dia.vbox.set_homogeneous(False)
+
+ title = gtk.Label('' + _('Image Size') + '')
+ title.set_alignment(0.0, 0.5)
+ title.set_use_markup(True)
+ dia.vbox.pack_start(title)
+
+ table = gtk.Table(2, 2)
+ table.set_col_spacings(3)
+ table.set_row_spacings(3)
+ table.set_border_width(1)
+ table.attach(gtk.Label(_('Width:')), 0, 1, 0, 1, yoptions=False,
+ xoptions=gtk.FILL)
+ spinwidth = gtk.SpinButton(gtk.Adjustment(400.0,
+ 0.0, sys.maxint, 1.0, 10.0))
+ spinwidth.set_numeric(True)
+ spinwidth.set_activates_default(True)
+ table.attach(spinwidth, 1, 2, 0, 1, yoptions=False, xoptions=gtk.FILL)
+ table.attach(gtk.Label(_('Height:')), 0, 1, 1, 2, yoptions=False,
+ xoptions=gtk.FILL)
+ spinheight = gtk.SpinButton(gtk.Adjustment(200.0,
+ 0.0, sys.maxint, 1.0, 10.0))
+ spinheight.set_numeric(True)
+ spinheight.set_activates_default(True)
+ table.attach(spinheight, 1, 2, 1, 2, yoptions=False, xoptions=gtk.FILL)
+ dia.vbox.pack_start(table)
+
+ filechooser = gtk.FileChooserWidget(gtk.FILE_CHOOSER_ACTION_SAVE, None)
+ filechooser.set_current_folder(CONFIG['client.default_path'])
+ filter = gtk.FileFilter()
+ filter.set_name(_('PNG image (*.png)'))
+ filter.add_mime_type('image/png')
+ filter.add_pattern('*.png')
+ filechooser.add_filter(filter)
+ dia.vbox.pack_start(filechooser)
+
+ dia.show_all()
+
+ while True:
+ response = dia.run()
+ width = spinwidth.get_value_as_int()
+ height = spinheight.get_value_as_int()
+ filename = filechooser.get_filename()
+ if filename:
+ filename = filename.decode('utf-8')
+ try:
+ CONFIG['client.default_path'] = \
+ os.path.dirname(filename)
+ CONFIG.save()
+ except IOError:
+ pass
+ if response == gtk.RESPONSE_OK:
+ if width and height and filename:
+ if not filename.endswith('.png'):
+ filename = filename + '.png'
+ try:
+ graph.export_png(filename, width, height)
+ break
+ except MemoryError:
+ message(_('Image size too large!'), dia,
+ gtk.MESSAGE_ERROR)
+ else:
+ break
+ parent.present()
+ dia.destroy()
+
+ def button_press(self, widget, event):
+ if event.button == 3:
+ menu = gtk.Menu()
+ item = gtk.ImageMenuItem(_('Save As...'))
+ img = gtk.Image()
+ img.set_from_stock('tryton-save-as', gtk.ICON_SIZE_MENU)
+ item.set_image(img)
+ item.connect('activate', self.save)
+ item.show()
+ menu.append(item)
+ menu.popup(None, None, None, event.button, event.time)
+ return True
+ elif event.button == 1:
+ self.widgets['root'].action()
diff --git a/tryton/gui/window/view_form/view/graph_gtk/graph.py b/tryton/gui/window/view_form/view/graph_gtk/graph.py
index 8038ff52bb..76a4a05963 100644
--- a/tryton/gui/window/view_form/view/graph_gtk/graph.py
+++ b/tryton/gui/window/view_form/view/graph_gtk/graph.py
@@ -71,8 +71,9 @@ class Graph(gtk.DrawingArea):
__gsignals__ = {"expose-event": "override"}
- def __init__(self, xfield, yfields, attrs, model):
+ def __init__(self, view, xfield, yfields):
super(Graph, self).__init__()
+ self.view = view
self.xfield = xfield
self.yfields = yfields
self.datas = {}
@@ -84,8 +85,14 @@ class Graph(gtk.DrawingArea):
self.connect('motion-notify-event', self.motion)
self.connect('leave-notify-event', self.leave)
self.popup = Popup(self)
- self.attrs = attrs
- self.model = model
+
+ @property
+ def attrs(self):
+ return self.view.attributes
+
+ @property
+ def model(self):
+ return self.view.screen.model_name
def destroy(self):
self.popup.destroy()
diff --git a/tryton/gui/window/view_form/view/graph_gtk/parser.py b/tryton/gui/window/view_form/view/graph_gtk/parser.py
deleted file mode 100644
index 396fb1dd82..0000000000
--- a/tryton/gui/window/view_form/view/graph_gtk/parser.py
+++ /dev/null
@@ -1,155 +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.
-from tryton.gui.window.view_form.view.interface import ParserInterface
-import tryton.common as common
-import gtk
-from bar import VerticalBar, HorizontalBar
-from line import Line
-from pie import Pie
-from tryton.config import TRYTON_ICON, CONFIG
-import sys
-import os
-import gettext
-
-_ = gettext.gettext
-
-GRAPH_TYPE = {
- 'vbar': VerticalBar,
- 'hbar': HorizontalBar,
- 'line': Line,
- '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))
- dia.set_icon(TRYTON_ICON)
- dia.set_has_separator(True)
- dia.set_default_response(gtk.RESPONSE_OK)
-
- dia.vbox.set_spacing(5)
- dia.vbox.set_homogeneous(False)
-
- title = gtk.Label('' + _('Image Size') + '')
- title.set_alignment(0.0, 0.5)
- title.set_use_markup(True)
- dia.vbox.pack_start(title)
-
- table = gtk.Table(2, 2)
- table.set_col_spacings(3)
- table.set_row_spacings(3)
- table.set_border_width(1)
- table.attach(gtk.Label(_('Width:')), 0, 1, 0, 1, yoptions=False,
- xoptions=gtk.FILL)
- spinwidth = gtk.SpinButton(gtk.Adjustment(400.0,
- 0.0, sys.maxint, 1.0, 10.0))
- spinwidth.set_numeric(True)
- spinwidth.set_activates_default(True)
- table.attach(spinwidth, 1, 2, 0, 1, yoptions=False, xoptions=gtk.FILL)
- table.attach(gtk.Label(_('Height:')), 0, 1, 1, 2, yoptions=False,
- xoptions=gtk.FILL)
- spinheight = gtk.SpinButton(gtk.Adjustment(200.0,
- 0.0, sys.maxint, 1.0, 10.0))
- spinheight.set_numeric(True)
- spinheight.set_activates_default(True)
- table.attach(spinheight, 1, 2, 1, 2, yoptions=False, xoptions=gtk.FILL)
- dia.vbox.pack_start(table)
-
- filechooser = gtk.FileChooserWidget(gtk.FILE_CHOOSER_ACTION_SAVE, None)
- filechooser.set_current_folder(CONFIG['client.default_path'])
- filter = gtk.FileFilter()
- filter.set_name(_('PNG image (*.png)'))
- filter.add_mime_type('image/png')
- filter.add_pattern('*.png')
- filechooser.add_filter(filter)
- dia.vbox.pack_start(filechooser)
-
- dia.show_all()
-
- while True:
- response = dia.run()
- width = spinwidth.get_value_as_int()
- height = spinheight.get_value_as_int()
- filename = filechooser.get_filename()
- if filename:
- filename = filename.decode('utf-8')
- try:
- CONFIG['client.default_path'] = \
- os.path.dirname(filename)
- CONFIG.save()
- except IOError:
- pass
- if response == gtk.RESPONSE_OK:
- if width and height and filename:
- if not filename.endswith('.png'):
- filename = filename + '.png'
- try:
- graph.export_png(filename, width, height)
- break
- except MemoryError:
- common.message(_('Image size too large!'), dia,
- gtk.MESSAGE_ERROR)
- else:
- break
- parent.present()
- dia.destroy()
- return
-
-
-def button_press(widget, event, graph):
- if event.button == 3:
- menu = gtk.Menu()
- item = gtk.ImageMenuItem(_('Save As...'))
- img = gtk.Image()
- img.set_from_stock('tryton-save-as', gtk.ICON_SIZE_MENU)
- item.set_image(img)
- item.connect('activate', save, graph)
- item.show()
- menu.append(item)
- menu.popup(None, None, None, event.button, event.time)
- return True
- elif event.button == 1:
- graph.action()
-
-
-class ParserGraph(ParserInterface):
-
- def parse(self, model, root_node, fields):
- attrs = common.node_attributes(root_node)
- self.title = attrs.get('string', 'Unknown')
- xfield = None
- yfields = []
-
- for node in root_node.childNodes:
- if not node.nodeType == node.ELEMENT_NODE:
- continue
-
- if node.localName == 'x':
- for child in node.childNodes:
- if not child.nodeType == child.ELEMENT_NODE:
- continue
- xfield = common.node_attributes(child)
- if not xfield.get('string'):
- xfield['string'] = fields[xfield['name']
- ].attrs['string']
- break
- elif node.localName == 'y':
- for child in node.childNodes:
- if not child.nodeType == child.ELEMENT_NODE:
- continue
- yattrs = common.node_attributes(child)
- if not yattrs.get('string') and yattrs['name'] != '#':
- yattrs['string'] = fields[yattrs['name']
- ].attrs['string']
- yfields.append(yattrs)
-
- 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)
-
- return event, {'root': widget}, [], '', [], None
diff --git a/tryton/gui/window/view_form/view/interface.py b/tryton/gui/window/view_form/view/interface.py
deleted file mode 100644
index 051beb56c5..0000000000
--- a/tryton/gui/window/view_form/view/interface.py
+++ /dev/null
@@ -1,47 +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.
-"Interfaces"
-
-
-class ParserInterface(object):
-
- def __init__(self, parent=None, attrs=None, screen=None,
- children_field=None):
- self.parent = parent
- self.attrs = attrs
- self.title = None
- self.state_widgets = {}
- self.screen = screen
- self.children_field = children_field
-
-
-class ParserView(object):
-
- def __init__(self, screen, widget, children=None, state_widgets=None,
- notebooks=None, cursor_widget=None, children_field=None):
- self.screen = screen
- self.widget = widget
- self.children = children
- if state_widgets is None:
- state_widgets = []
- self.state_widgets = state_widgets
- if notebooks is None:
- notebooks = []
- self.notebooks = notebooks
- self.cursor_widget = cursor_widget
- self.children_field = children_field
- self.view_id = None
-
- @property
- def selected_records(self):
- return []
-
- def get_fields(self):
- return self.children.keys()
-
- def get_buttons(self):
- return []
-
- @property
- def modified(self):
- return False
diff --git a/tryton/gui/window/view_form/view/list.py b/tryton/gui/window/view_form/view/list.py
index 6a8978d7bd..ef0cf1041b 100644
--- a/tryton/gui/window/view_form/view/list.py
+++ b/tryton/gui/window/view_form/view/list.py
@@ -8,14 +8,19 @@ try:
except ImportError:
import json
import locale
-from interface import ParserView
import gettext
+
from tryton.config import CONFIG
from tryton.common.cellrendererbutton import CellRendererButton
from tryton.common.cellrenderertoggle import CellRendererToggle
from tryton.gui.window import Window
from tryton.common.popup_menu import populate
-from tryton.common import RPCExecute, RPCException
+from tryton.common import RPCExecute, RPCException, node_attributes, Tooltips
+from . import View
+from .list_gtk.editabletree import EditableTreeView, TreeView
+from .list_gtk.widget import (Affix, Char, Int, Boolean, URL, Date, Datetime,
+ Time, Float, FloatTime, Binary, M2O, O2O, O2M, M2M, Selection, Reference,
+ ProgressBar, Button)
_ = gettext.gettext
@@ -238,90 +243,338 @@ class AdaptModelGroup(gtk.GenericTreeModel):
return record.parent
-class ViewList(ParserView):
+class ViewTree(View):
- def __init__(self, screen, widget, children=None, state_widgets=None,
- notebooks=None, cursor_widget=None, children_field=None):
- super(ViewList, self).__init__(screen, widget, children, state_widgets,
- notebooks, cursor_widget, children_field)
- self.store = None
+ def __init__(self, screen, xml, children_field):
+ super(ViewTree, self).__init__(screen, xml)
self.view_type = 'tree'
+ self.widgets = {}
+ self.state_widgets = []
+ self.children_field = children_field
+ self.sum_widgets = []
+ self.sum_box = gtk.HBox()
+ self.reload = False
+ if self.attributes.get('editable'):
+ self.treeview = EditableTreeView(self.attributes['editable'], self)
+ else:
+ self.treeview = TreeView()
- vbox = gtk.VBox()
+ self.parse(xml)
+
+ self.treeview.set_property('rules-hint', True)
+ self.treeview.set_fixed_height_mode(True)
+ self.treeview.connect('button-press-event', self.__button_press)
+ self.treeview.connect('key-press-event', self.on_keypress)
+ self.treeview.connect_after('row-activated', self.__sig_switch)
+ if self.children_field:
+ self.treeview.connect('test-expand-row', self.test_expand_row)
+ self.treeview.set_expander_column(self.treeview.get_column(0))
+ self.treeview.set_rubber_banding(True)
+
+ selection = self.treeview.get_selection()
+ selection.set_mode(gtk.SELECTION_MULTIPLE)
+ selection.connect('changed', self.__select_changed)
+
+ self.set_drag_and_drop()
+
+ self.widget = gtk.VBox()
scroll = gtk.ScrolledWindow()
- scroll.add(self.widget)
+ scroll.add(self.treeview)
scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
scroll.set_placement(gtk.CORNER_TOP_LEFT)
viewport = gtk.Viewport()
viewport.set_shadow_type(gtk.SHADOW_ETCHED_IN)
viewport.add(scroll)
- self.widget_tree = self.widget
+ self.widget.pack_start(viewport, expand=True, fill=True)
- vbox.pack_start(viewport, expand=True, fill=True)
-
- self.widget_tree.screen = screen
-
- self.widget = vbox
- self.reload = False
- self.children = children
-
- if children:
- hbox = gtk.HBox()
- self.widget.pack_start(hbox, expand=False, fill=False, padding=2)
- keys = children.keys()
- keys.sort()
- for i in keys:
- hbox2 = gtk.HBox()
- hbox2.pack_start(children[i][1], expand=True, fill=False)
- hbox2.pack_start(children[i][2], expand=True, fill=False)
- hbox.pack_start(hbox2, expand=False, fill=False, padding=12)
- hbox.show_all()
+ self.sum_box.show()
+ self.widget.pack_start(self.sum_box, expand=False, fill=False)
self.display()
- self.widget_tree.connect('button-press-event', self.__button_press)
- self.widget_tree.connect_after('row-activated', self.__sig_switch)
- if hasattr(self.widget_tree, 'set_rubber_banding'):
- self.widget_tree.set_rubber_banding(True)
- selection = self.widget_tree.get_selection()
- selection.set_mode(gtk.SELECTION_MULTIPLE)
- selection.connect('changed', self.__select_changed)
+ def parse(self, xml):
+ for node in xml.childNodes:
+ if node.nodeType != node.ELEMENT_NODE:
+ continue
+ if node.tagName == 'field':
+ self._parse_field(node)
+ elif node.tagName == 'button':
+ self._parse_button(node)
+ self.add_last_column()
+
+ def _parse_field(self, node):
+ group = self.screen.group
+ node_attrs = node_attributes(node)
+ name = node_attrs['name']
+ field = group.fields[name]
+ for b_field in ('readonly', 'expand', 'tree_invisible'):
+ if b_field in node_attrs:
+ node_attrs[b_field] = bool(int(node_attrs[b_field]))
+ if 'widget' not in node_attrs:
+ node_attrs['widget'] = field.attrs['type']
+
+ for attr in ('relation', 'domain', 'selection',
+ 'relation_field', 'string', 'views', 'invisible',
+ 'add_remove', 'sort', 'context', 'size', 'filename',
+ 'autocomplete', 'translate', 'create', 'delete',
+ 'selection_change_with', 'schema_model'):
+ if (attr in field.attrs
+ and attr not in node_attrs):
+ node_attrs[attr] = field.attrs[attr]
+
+ Widget = self.get_widget(node_attrs['widget'])
+ widget = Widget(self, node_attrs)
+ assert name not in self.widgets
+ self.widgets[name] = widget
+
+ column = gtk.TreeViewColumn(field.attrs['string'])
+ column._type = 'field'
+ column.name = name
+
+ prefixes = []
+ suffixes = []
+ if node_attrs['widget'] in ('url', 'email', 'callto', 'sip'):
+ prefixes.append(Affix(self, node_attrs,
+ protocol=node_attrs['widget']))
+ if 'icon' in node_attrs:
+ prefixes.append(Affix(self, node_attrs))
+ for affix in node.childNodes:
+ affix_attrs = node_attributes(affix)
+ if affix.tagName == 'prefix':
+ list_ = prefixes
+ else:
+ list_ = suffixes
+ list_.append(Affix(self, affix_attrs))
+
+ for prefix in prefixes:
+ column.pack_start(prefix.renderer, expand=False)
+ column.set_cell_data_func(prefix.renderer,
+ prefix.setter)
+
+ column.pack_start(widget.renderer, expand=True)
+ column.set_cell_data_func(widget.renderer, widget.setter)
+
+ for suffix in suffixes:
+ column.pack_start(suffix.renderer, expand=False)
+ column.set_cell_data_func(suffix.renderer,
+ suffix.setter)
+
+ self.set_column_widget(column, field, node_attrs)
+ self.set_column_width(column, field, node_attrs)
+
+ if (not self.attributes.get('sequence')
+ and not self.children_field
+ and field.attrs.get('sortable', True)):
+ column.connect('clicked', self.sort_model)
+ column.set_visible(not node_attrs.get('tree_invisible', False))
+ if name == self.screen.exclude_field:
+ column.set_visible(False)
+
+ self.treeview.append_column(column)
+
+ self.add_sum(node_attrs)
+
+ def _parse_button(self, node):
+ node_attrs = node_attributes(node)
+ widget = Button(self, node_attrs)
+ self.state_widgets.append(widget)
+
+ column = gtk.TreeViewColumn(node_attrs.get('string', ''),
+ widget.renderer)
+ column._type = 'button'
+ column.name = None
+ column.set_cell_data_func(widget.renderer, widget.setter)
+
+ self.set_column_widget(column, None, node_attrs, arrow=False)
+ self.set_column_width(column, None, node_attrs)
+
+ column.set_visible(not node_attrs.get('tree_invisible', False))
+
+ self.treeview.append_column(column)
+
+ WIDGETS = {
+ 'char': Char,
+ 'many2one': M2O,
+ 'date': Date,
+ 'one2many': O2M,
+ 'many2many': M2M,
+ 'selection': Selection,
+ 'float': Float,
+ 'numeric': Float,
+ 'float_time': FloatTime,
+ 'integer': Int,
+ 'biginteger': Int,
+ 'datetime': Datetime,
+ 'time': Time,
+ 'boolean': Boolean,
+ 'text': Char,
+ 'url': URL,
+ 'email': URL,
+ 'callto': URL,
+ 'sip': URL,
+ 'progressbar': ProgressBar,
+ 'reference': Reference,
+ 'one2one': O2O,
+ 'binary': Binary,
+ }
+
+ @classmethod
+ def get_widget(cls, name):
+ return cls.WIDGETS[name]
+
+ @staticmethod
+ def set_column_widget(column, field, attributes, arrow=True):
+ tooltips = Tooltips()
+ hbox = gtk.HBox(False, 2)
+ label = gtk.Label(attributes['string'])
+ label.show()
+ help = attributes['string']
+ if field and field.attrs.get('help'):
+ help += '\n' + field.attrs['help']
+ elif attributes.get('help'):
+ help += '\n' + attributes['help']
+ tooltips.set_tip(label, help)
+ tooltips.enable()
+ arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_IN)
+ column.arrow = arrow
+ column.arrow_show = False
+ hbox.pack_start(label, True, True, 0)
+ if arrow:
+ hbox.pack_start(arrow, False, False, 0)
+ column.set_clickable(True)
+ hbox.show()
+ column.set_widget(hbox)
+
+ def set_column_width(self, column, field, attributes):
+ default_width = {
+ 'integer': 60,
+ 'biginteger': 60,
+ 'float': 80,
+ 'numeric': 80,
+ 'float_time': 100,
+ 'date': 110,
+ 'datetime': 160,
+ 'selection': 90,
+ 'char': 100,
+ 'one2many': 50,
+ 'many2many': 50,
+ 'boolean': 20,
+ 'binary': 200,
+ }
+
+ width = self.screen.tree_column_width[self.screen.model_name].get(
+ column.name)
+ if not width:
+ if 'width' in attributes:
+ width = int(attributes['width'])
+ elif field:
+ width = default_width.get(field.attrs['type'], 100)
+ else:
+ width = 80
+ column.width = width
+ if width > 0:
+ column.set_fixed_width(width)
+ column.set_min_width(1)
+
+ expand = attributes.get('expand', False)
+ column.set_expand(expand)
+ column.set_resizable(True)
+ column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+
+ def add_sum(self, attributes):
+ if 'sum' not in attributes:
+ return
+ if gtk.widget_get_default_direction() == gtk.TEXT_DIR_RTL:
+ text = _(':') + attributes['sum']
+ else:
+ text = attributes['sum'] + _(':')
+ label, sum_ = gtk.Label(text), gtk.Label()
+
+ hbox = gtk.HBox()
+ hbox.pack_start(label, expand=True, fill=False, padding=2)
+ hbox.pack_start(sum_, expand=True, fill=False, padding=2)
+ hbox.show_all()
+ self.sum_box.pack_start(hbox, expand=False, fill=False)
+
+ self.sum_widgets.append((attributes['name'], sum_))
+
+ def sort_model(self, column):
+ for col in self.treeview.get_columns():
+ if col != column:
+ col.arrow_show = False
+ col.arrow.hide()
+ self.screen.order = None
+ if not column.arrow_show:
+ column.arrow_show = True
+ column.arrow.set(gtk.ARROW_DOWN, gtk.SHADOW_IN)
+ column.arrow.show()
+ self.screen.order = [(column.name, 'ASC')]
+ else:
+ if column.arrow.get_property('arrow-type') == gtk.ARROW_DOWN:
+ column.arrow.set(gtk.ARROW_UP, gtk.SHADOW_IN)
+ self.screen.order = [(column.name, 'DESC')]
+ else:
+ column.arrow_show = False
+ column.arrow.hide()
+ model = self.treeview.get_model()
+ unsaved_records = [x for x in model.group if x.id < 0]
+ search_string = self.screen.screen_container.get_text() or None
+ if (self.screen.search_count == len(model)
+ or unsaved_records
+ or self.screen.parent):
+ ids = self.screen.search_filter(
+ search_string=search_string, only_ids=True)
+ model.sort(ids)
+ else:
+ self.screen.search_filter(search_string=search_string)
+
+ def add_last_column(self):
+ for column in self.treeview.get_columns():
+ if column.get_expand():
+ break
+ else:
+ column = gtk.TreeViewColumn()
+ column._type = 'fill'
+ column.name = None
+ column.arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_IN)
+ column.arrow_show = False
+ column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
+ self.treeview.append_column(column)
+
+ def set_drag_and_drop(self):
dnd = False
if self.children_field:
- children_field = self.widget_tree.cells.get(self.children_field)
- if children_field:
- parent_name = children_field.attrs.get('relation_field')
- dnd = parent_name in self.widget_tree.cells
- elif self.widget_tree.sequence:
+ children = self.screen.group.fields.get(self.children_field)
+ if children:
+ parent_name = children.attrs.get('relation_field')
+ dnd = parent_name in self.widgets
+ elif self.attributes.get('sequence'):
dnd = True
# Disable DnD on mac until it is fully supported
if sys.platform == 'darwin':
dnd = False
- if screen.readonly:
+ if self.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_dest_set(gtk.DEST_DEFAULT_ALL,
- [('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-delete', self.drag_data_delete)
+ if not dnd:
+ return
- 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.treeview.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.treeview.drag_dest_set(gtk.DEST_DEFAULT_ALL,
+ [('MY_TREE_MODEL_ROW', gtk.TARGET_SAME_WIDGET, 0)],
+ gtk.gdk.ACTION_MOVE)
+
+ self.treeview.connect('drag-begin', self.drag_begin)
+ self.treeview.connect('drag-motion', self.drag_motion)
+ self.treeview.connect('drag-drop', self.drag_drop)
+ self.treeview.connect("drag-data-get", self.drag_data_get)
+ self.treeview.connect('drag-data-received',
+ self.drag_data_received)
+ self.treeview.connect('drag-data-delete', self.drag_data_delete)
@property
def modified(self):
@@ -329,10 +582,10 @@ class ViewList(ParserView):
@property
def editable(self):
- return bool(getattr(self.widget_tree, 'editable', False))
+ return bool(getattr(self.treeview, 'editable', False))
def get_fields(self):
- return [col.name for col in self.widget_tree.get_columns() if col.name]
+ return [col.name for col in self.treeview.get_columns() if col.name]
def get_buttons(self):
return [b for b in self.state_widgets
@@ -390,7 +643,7 @@ class ViewList(ParserView):
iter_ = model.iter_children(iter_)
if not iter_:
return False
- fields = [col.name for col in self.widget_tree.get_columns()
+ fields = [col.name for col in self.treeview.get_columns()
if col.name]
while iter_:
record = model.get_value(iter_, 0)
@@ -405,14 +658,14 @@ class ViewList(ParserView):
def on_copy(self):
for clipboard_type in (gtk.gdk.SELECTION_CLIPBOARD,
gtk.gdk.SELECTION_PRIMARY):
- clipboard = self.widget_tree.get_clipboard(clipboard_type)
+ clipboard = self.treeview.get_clipboard(clipboard_type)
targets = [
('STRING', 0, 0),
('TEXT', 0, 1),
('COMPOUND_TEXT', 0, 2),
('UTF8_STRING', 0, 3)
]
- selection = self.widget_tree.get_selection()
+ selection = self.treeview.get_selection()
# Set to clipboard directly if not too much selected rows
# to speed up paste
# Don't use set_with_data on mac see:
@@ -429,11 +682,11 @@ class ViewList(ParserView):
def copy_foreach(self, treemodel, path, iter, data):
record = treemodel.get_value(iter, 0)
values = []
- for col in self.widget_tree.get_columns():
+ for col in self.treeview.get_columns():
if not col.get_visible() or not col.name:
continue
- cell = self.widget_tree.cells[col.name]
- values.append('"' + str(cell.get_textual_value(record)) + '"')
+ widget = self.treeview.widgets[col.name]
+ values.append('"' + str(widget.get_textual_value(record)) + '"')
data.append('\t'.join(values))
return
@@ -459,15 +712,15 @@ class ViewList(ParserView):
data = []
for clipboard_type in (gtk.gdk.SELECTION_CLIPBOARD,
gtk.gdk.SELECTION_PRIMARY):
- clipboard = self.widget_tree.get_clipboard(clipboard_type)
+ clipboard = self.treeview.get_clipboard(clipboard_type)
text = clipboard.wait_for_text()
if not text:
continue
data = [[unquote(v) for v in l.split('\t')]
for l in text.splitlines()]
break
- col = self.widget_tree.get_cursor()[1]
- columns = [c for c in self.widget_tree.get_columns()
+ col = self.treeview.get_cursor()[1]
+ columns = [c for c in self.treeview.get_columns()
if c.get_visible() and c.name]
if col in columns:
idx = columns.index(col)
@@ -489,10 +742,10 @@ class ViewList(ParserView):
group.add(record)
record = group[idx]
for col, value in zip(columns, line):
- cell = self.widget_tree.cells[col.name]
- if cell.get_textual_value(record) != value:
- cell.value_from_text(record, value)
- if value and not cell.get_textual_value(record):
+ widget = self.treeview.widgets[col.name]
+ if widget.get_textual_value(record) != value:
+ widget.value_from_text(record, value)
+ if value and not widget.get_textual_value(record):
# Stop setting value if a value is correctly set
idx = len(group)
break
@@ -527,9 +780,9 @@ class ViewList(ParserView):
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)))
+ def _func_sel_get(model, path, iter_, data):
+ value = model.get_value(iter_, 0)
+ data.append(json.dumps(value.get_path(model.group)))
data = []
treeselection = treeview.get_selection()
treeselection.selected_foreach(_func_sel_get, data)
@@ -542,8 +795,8 @@ class ViewList(ParserView):
def drag_data_received(self, treeview, context, x, y, selection,
info, etime):
treeview.emit_stop_by_name('drag-data-received')
- if treeview.sequence:
- field = self.screen.group.fields[treeview.sequence]
+ if self.attributes.get('sequence'):
+ field = self.screen.group.fields[self.attributes['sequence']]
for record in self.screen.group:
if field.get_state_attrs(
record).get('readonly', False):
@@ -559,13 +812,13 @@ class ViewList(ParserView):
if renderer.props.editing:
return
- store = treeview.get_model()
+ model = treeview.get_model()
try:
data = json.loads(selection.data)
except ValueError:
return
- record = store.group.get_by_path(data)
- record_path = store.on_get_path(record)
+ record = model.group.get_by_path(data)
+ record_path = model.on_get_path(record)
drop_info = treeview.get_dest_row_at_pos(x, y)
def check_recursion(from_, to):
@@ -586,16 +839,16 @@ class ViewList(ParserView):
if not check_recursion(record_path, check_path):
return
if position == gtk.TREE_VIEW_DROP_BEFORE:
- store.move_before(record, path)
+ model.move_before(record, path)
elif position == gtk.TREE_VIEW_DROP_AFTER:
- store.move_after(record, path)
+ model.move_after(record, path)
elif self.children_field:
- store.move_into(record, path)
+ model.move_into(record, path)
else:
- store.move_after(record, (len(store) - 1,))
+ model.move_after(record, (len(model) - 1,))
context.drop_finish(False, etime)
- if treeview.sequence:
- record.group.set_sequence(field=treeview.sequence)
+ if self.attributes.get('sequence'):
+ record.group.set_sequence(field=self.attributes['sequence'])
return True
def drag_data_delete(self, treeview, context):
@@ -634,7 +887,7 @@ class ViewList(ParserView):
parent = parent.parent
else:
populate(menu, group.model_name, record)
- for col in self.widget_tree.get_columns():
+ for col in self.treeview.get_columns():
if not col.get_visible() or not col.name:
continue
field = group.fields[col.name]
@@ -662,11 +915,12 @@ class ViewList(ParserView):
return False
def group_list_changed(self, group, signal):
- if self.store is not None:
+ model = self.treeview.get_model()
+ if model is not None:
if signal[0] == 'record-added':
- self.store.added(group, signal[1])
+ model.added(group, signal[1])
elif signal[0] == 'record-removed':
- self.store.removed(group, signal[1])
+ model.removed(group, signal[1])
self.display()
def __str__(self):
@@ -680,7 +934,7 @@ class ViewList(ParserView):
return
fields = {}
last_col = None
- for col in self.widget_tree.get_columns():
+ for col in self.treeview.get_columns():
if col.get_visible():
last_col = col
if not hasattr(col, 'name') or not hasattr(col, 'width'):
@@ -704,7 +958,7 @@ class ViewList(ParserView):
def destroy(self):
self.save_width_height()
- self.widget_tree.destroy()
+ self.treeview.destroy()
def __sig_switch(self, treeview, path, column):
if column._type == 'button':
@@ -768,41 +1022,39 @@ class ViewList(ParserView):
go_previous()
# Delay the pre_validate to let GTK process the current event
gobject.idle_add(pre_validate)
- self.update_children()
+ self.update_sum()
def set_value(self):
if self.editable:
- self.widget_tree.set_value()
+ self.treeview.set_value()
def reset(self):
pass
- # self.widget.set_model(self.store) could be removed if the store
- # has not changed -> better ergonomy. To test
def display(self):
- self.widget_tree.display_counter += 1
+ self.treeview.display_counter += 1
current_record = self.screen.current_record
if (self.reload
- or not self.widget_tree.get_model()
+ or not self.treeview.get_model()
or (self.screen.group !=
- self.widget_tree.get_model().group)):
- self.store = AdaptModelGroup(self.screen.group,
+ self.treeview.get_model().group)):
+ model = AdaptModelGroup(self.screen.group,
self.children_field)
- self.widget_tree.set_model(self.store)
+ self.treeview.set_model(model)
# __select_changed resets current_record to None
self.screen.current_record = current_record
if current_record:
- selection = self.widget_tree.get_selection()
- path = self.store.on_get_path(current_record)
+ selection = self.treeview.get_selection()
+ path = model.on_get_path(current_record)
selection.select_path(path)
self.reload = False
if not current_record:
- selection = self.widget_tree.get_selection()
+ selection = self.treeview.get_selection()
selection.unselect_all()
- self.widget_tree.queue_draw()
+ self.treeview.queue_draw()
if self.editable:
self.set_state()
- self.update_children()
+ self.update_sum()
def set_state(self):
record = self.screen.current_record
@@ -812,62 +1064,63 @@ class ViewList(ParserView):
if field:
field.state_set(record)
- def update_children(self):
+ def update_sum(self):
selected_records = self.selected_records
- for child in self.children:
- value = 0
- value_selected = 0
+ for name, label in self.sum_widgets:
+ sum_ = 0
+ selected_sum = 0
loaded = True
- child_fieldname = self.children[child][0]
+ digit = 0
for record in self.screen.group:
- if not record.get_loaded([child_fieldname]):
+ if not record.get_loaded([name]):
loaded = False
break
- field_value = record.fields_get()[child_fieldname].get(record)
- if field_value is not None:
- value += field_value
+ field = record[name]
+ value = field.get(record)
+ if value is not None:
+ sum_ += value
if record in selected_records or not selected_records:
- value_selected += field_value
+ selected_sum += value
+ digit = max(field.digits(record)[1], digit)
if loaded:
- label_str = locale.format('%.*f',
- (self.children[child][3], value_selected), True)
- label_str += ' / '
- label_str += locale.format('%.*f',
- (self.children[child][3], value), True)
+ text = '%s / %s' % (
+ locale.format('%.*f', (digit, selected_sum), True),
+ locale.format('%.*f', (digit, sum_), True))
else:
- label_str = '-'
- self.children[child][2].set_text(label_str)
+ text = '-'
+ label.set_text(text)
def set_cursor(self, new=False, reset_view=True):
- self.widget_tree.grab_focus()
+ self.treeview.grab_focus()
if self.screen.current_record:
- path = self.store.on_get_path(self.screen.current_record)
- if self.store.get_flags() & gtk.TREE_MODEL_LIST_ONLY:
+ model = self.treeview.get_model()
+ path = model.on_get_path(self.screen.current_record)
+ if model.get_flags() & gtk.TREE_MODEL_LIST_ONLY:
path = (path[0],)
- focus_column = self.widget_tree.next_column(path, editable=new)
+ focus_column = self.treeview.next_column(path, editable=new)
if path[:-1]:
- self.widget_tree.expand_to_path(path[:-1])
- self.widget_tree.scroll_to_cell(path, focus_column,
+ self.treeview.expand_to_path(path[:-1])
+ self.treeview.scroll_to_cell(path, focus_column,
use_align=False)
- current_path = self.widget_tree.get_cursor()[0]
+ current_path = self.treeview.get_cursor()[0]
selected_path = \
- self.widget_tree.get_selection().get_selected_rows()[1]
+ self.treeview.get_selection().get_selected_rows()[1]
if (current_path != path and path not in selected_path) or new:
- self.widget_tree.set_cursor(path, focus_column, new)
+ self.treeview.set_cursor(path, focus_column, new)
@property
def selected_records(self):
- def _func_sel_get(store, path, iter, records):
- records.append(store.on_get_iter(path))
+ def _func_sel_get(model, path, iter, records):
+ records.append(model.on_get_iter(path))
records = []
- sel = self.widget_tree.get_selection()
+ sel = self.treeview.get_selection()
sel.selected_foreach(_func_sel_get, records)
return records
def unset_editable(self):
- self.widget_tree.editable = False
- for col in self.widget_tree.get_columns():
+ self.treeview.editable = False
+ for col in self.treeview.get_columns():
for renderer in col.get_cell_renderers():
if isinstance(renderer, CellRendererToggle):
renderer.set_property('activatable', False)
@@ -879,7 +1132,7 @@ class ViewList(ParserView):
renderer.set_property('editable', False)
def get_selected_paths(self):
- selection = self.widget_tree.get_selection()
+ selection = self.treeview.get_selection()
model, rows = selection.get_selected_rows()
id_paths = []
for row in rows:
@@ -892,17 +1145,17 @@ class ViewList(ParserView):
return id_paths
def select_nodes(self, nodes):
- selection = self.widget_tree.get_selection()
+ selection = self.treeview.get_selection()
if not nodes:
return
selection.unselect_all()
scroll = False
for node in nodes:
- path = path_convert_id2pos(self.store, node)
+ path = path_convert_id2pos(self.treeview.get_model(), node)
if path:
selection.select_path(path)
if not scroll:
- self.widget_tree.scroll_to_cell(path)
+ self.treeview.scroll_to_cell(path)
scroll = True
def get_expanded_paths(self, starting_path=None, starting_id_path=None):
@@ -913,12 +1166,13 @@ class ViewList(ParserView):
if not starting_id_path:
starting_id_path = []
id_paths = []
- record = self.store.on_get_iter(starting_path)
- for path_idx in range(self.store.on_iter_n_children(record)):
+ model = self.treeview.get_model()
+ record = model.on_get_iter(starting_path)
+ for path_idx in range(model.on_iter_n_children(record)):
path = starting_path + [path_idx]
- expanded = self.widget_tree.row_expanded(tuple(path))
+ expanded = self.treeview.row_expanded(tuple(path))
if expanded:
- expanded_record = self.store.on_get_iter(path)
+ expanded_record = model.on_get_iter(path)
id_path = starting_id_path + [expanded_record.id]
id_paths.append(id_path)
child_id_paths = self.get_expanded_paths(path, id_path)
@@ -926,7 +1180,8 @@ class ViewList(ParserView):
return id_paths
def expand_nodes(self, nodes):
+ model = self.treeview.get_model()
for node in nodes:
- expand_path = path_convert_id2pos(self.store, node)
+ expand_path = path_convert_id2pos(model, node)
if expand_path:
- self.widget_tree.expand_to_path(expand_path)
+ self.treeview.expand_to_path(expand_path)
diff --git a/tryton/gui/window/view_form/view/list_gtk/__init__.py b/tryton/gui/window/view_form/view/list_gtk/__init__.py
index 5218f22bd6..c86640b67f 100644
--- a/tryton/gui/window/view_form/view/list_gtk/__init__.py
+++ b/tryton/gui/window/view_form/view/list_gtk/__init__.py
@@ -1,3 +1,2 @@
#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 parser import *
diff --git a/tryton/gui/window/view_form/view/list_gtk/editabletree.py b/tryton/gui/window/view_form/view/list_gtk/editabletree.py
index 2f6670172f..eb24be4e85 100644
--- a/tryton/gui/window/view_form/view/list_gtk/editabletree.py
+++ b/tryton/gui/window/view_form/view/list_gtk/editabletree.py
@@ -1,7 +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.
import gtk
-import parser
import gettext
import gobject
from itertools import islice, cycle
@@ -13,10 +12,7 @@ _ = gettext.gettext
class TreeView(gtk.TreeView):
-
- def __init__(self):
- super(TreeView, self).__init__()
- self.cells = {}
+ display_counter = 0
def next_column(self, path, column=None, editable=True, _sign=1):
columns = self.get_columns()
@@ -53,44 +49,41 @@ class EditableTreeView(TreeView):
leaving_events = leaving_record_events + (gtk.keysyms.Tab,
gtk.keysyms.ISO_Left_Tab, gtk.keysyms.KP_Enter)
- def __init__(self, position):
+ def __init__(self, position, view):
super(EditableTreeView, self).__init__()
self.editable = position
+ self.view = view
def on_quit_cell(self, current_record, fieldname, value, callback=None):
field = current_record[fieldname]
- cell = self.cells[fieldname]
+ widget = self.view.widgets[fieldname]
# The value has not changed and is valid ... do nothing.
- if value == cell.get_textual_value(current_record) \
+ if value == widget.get_textual_value(current_record) \
and field.validate(current_record):
if callback:
callback()
return
-
- try:
- cell.value_from_text(current_record, value, callback=callback)
- except parser.UnsettableColumn:
- return
+ widget.value_from_text(current_record, value, callback=callback)
def on_open_remote(self, current_record, fieldname, create, value,
entry=None, callback=None):
- cell = self.cells[fieldname]
- if value != cell.get_textual_value(current_record) or not value:
+ widget = self.view.widgets[fieldname]
+ if value != widget.get_textual_value(current_record) or not value:
changed = True
else:
changed = False
try:
- cell.open_remote(current_record, create, changed, value,
+ widget.open_remote(current_record, create, changed, value,
callback=callback)
except NotImplementedError:
pass
def on_create_line(self):
- access = MODELACCESS[self.screen.model_name]
+ access = MODELACCESS[self.view.screen.model_name]
model = self.get_model()
- if not access['create'] or (self.screen.size_limit is not None
- and (len(model) >= self.screen.size_limit >= 0)):
+ if not access['create'] or (self.view.screen.size_limit is not None
+ and (len(model) >= self.view.screen.size_limit >= 0)):
return
if self.editable == 'top':
method = model.prepend
@@ -171,7 +164,7 @@ class EditableTreeView(TreeView):
gobject.idle_add(self.set_cursor, path,
self.prev_column(path, column), True)
elif keyval in self.leaving_record_events:
- fields = self.cells.keys()
+ fields = self.view.widgets.keys()
if not record.validate(fields):
invalid_fields = record.invalid_fields
col = None
@@ -180,9 +173,9 @@ class EditableTreeView(TreeView):
break
gobject.idle_add(self.set_cursor, path, col, True)
return
- if ((self.screen.pre_validate
+ if ((self.view.screen.pre_validate
and not record.pre_validate())
- or (not self.screen.parent
+ or (not self.view.screen.parent
and not record.save())):
gobject.idle_add(self.set_cursor, path, column,
True)
@@ -212,8 +205,8 @@ class EditableTreeView(TreeView):
entry.handler_block(entry.editing_done_id)
def callback():
- cell = self.cells[column.name]
- value = cell.get_textual_value(record)
+ widget = self.view.widgets[column.name]
+ value = widget.get_textual_value(record)
if isinstance(entry, gtk.Entry):
entry.set_text(value)
else:
@@ -230,7 +223,7 @@ class EditableTreeView(TreeView):
# set_value
field.editabletree_entry = entry
- def remove_widget(cell):
+ def remove_widget(widget):
if hasattr(field, 'editabletree_entry'):
del field.editabletree_entry
entry.connect('remove-widget', remove_widget)
diff --git a/tryton/gui/window/view_form/view/list_gtk/parser.py b/tryton/gui/window/view_form/view/list_gtk/widget.py
similarity index 62%
rename from tryton/gui/window/view_form/view/list_gtk/parser.py
rename to tryton/gui/window/view_form/view/list_gtk/widget.py
index 33426653cf..820d43b6a5 100644
--- a/tryton/gui/window/view_form/view/list_gtk/parser.py
+++ b/tryton/gui/window/view_form/view/list_gtk/widget.py
@@ -10,14 +10,11 @@ import webbrowser
from functools import wraps, partial
-from editabletree import EditableTreeView, TreeView
-from tryton.gui.window.view_form.view.interface import ParserInterface
from tryton.gui.window.win_search import WinSearch
from tryton.gui.window.win_form import WinForm
from tryton.gui.window.view_form.screen import Screen
import tryton.rpc as rpc
-from tryton.common import COLORS, node_attributes, \
- file_selection, file_open, slugify
+from tryton.common import COLORS, file_selection, file_open, slugify
import tryton.common as common
from tryton.common.cellrendererbutton import CellRendererButton
from tryton.common.cellrendererdate import CellRendererDate
@@ -46,40 +43,12 @@ def send_keys(renderer, editable, position, treeview):
editable.connect('changed', treeview.on_editing_done)
-def sort_model(column, treeview, screen):
- for col in treeview.get_columns():
- if col != column:
- col.arrow_show = False
- col.arrow.hide()
- screen.order = None
- if not column.arrow_show:
- column.arrow_show = True
- column.arrow.set(gtk.ARROW_DOWN, gtk.SHADOW_IN)
- column.arrow.show()
- screen.order = [(column.name, 'ASC')]
- else:
- if column.arrow.get_property('arrow-type') == gtk.ARROW_DOWN:
- column.arrow.set(gtk.ARROW_UP, gtk.SHADOW_IN)
- screen.order = [(column.name, 'DESC')]
- else:
- column.arrow_show = False
- column.arrow.hide()
- store = treeview.get_model()
- unsaved_records = [x for x in store.group if x.id < 0]
- search_string = screen.screen_container.get_text() or None
- if screen.search_count == len(store) or unsaved_records or screen.parent:
- ids = screen.search_filter(search_string=search_string, only_ids=True)
- store.sort(ids)
- else:
- screen.search_filter(search_string=search_string)
-
-
def realized(func):
"Decorator for treeview realized"
@wraps(func)
def wrapper(self, *args, **kwargs):
- if (hasattr(self.treeview, 'get_realized')
- and not self.treeview.get_realized()):
+ if (hasattr(self.view.treeview, 'get_realized')
+ and not self.view.treeview.get_realized()):
return
return func(self, *args, **kwargs)
return wrapper
@@ -123,7 +92,7 @@ class CellCache(list):
if not hasattr(self, 'cell_caches'):
self.cell_caches = {}
record = store.get_value(iter, 0)
- counter = self.treeview.display_counter
+ counter = self.view.treeview.display_counter
if (self.display_counters.get(record.id) != counter):
if getattr(cell, 'decorated', None):
func(self, column, cell, store, iter)
@@ -139,223 +108,10 @@ class CellCache(list):
return wrapper
-class ParserTree(ParserInterface):
-
- def __init__(self, parent=None, attrs=None, screen=None,
- children_field=None):
- super(ParserTree, self).__init__(parent, attrs, screen,
- children_field=children_field)
- self.treeview = None
-
- def parse(self, model_name, root_node, fields):
- dict_widget = {}
- state_widgets = []
- attrs = node_attributes(root_node)
- on_write = attrs.get('on_write', '')
- editable = attrs.get('editable', False)
- if editable:
- treeview = EditableTreeView(editable)
- else:
- treeview = TreeView()
- treeview.cells = {}
- treeview.display_counter = 0
- treeview.sequence = attrs.get('sequence', False)
- treeview.colors = attrs.get('colors', '"black"')
- treeview.keyword_open = attrs.get('keyword_open', False)
- self.treeview = treeview
- treeview.set_property('rules-hint', True)
- if not self.title:
- self.title = attrs.get('string', 'Unknown')
- tooltips = common.Tooltips()
- expandable = False
-
- for node in root_node.childNodes:
- node_attrs = node_attributes(node)
- if node.localName == 'field':
- fname = str(node_attrs['name'])
- for boolean_fields in ('readonly', 'required', 'expand'):
- if boolean_fields in node_attrs:
- node_attrs[boolean_fields] = \
- bool(int(node_attrs[boolean_fields]))
- if fname not in fields:
- continue
- for attr_name in ('relation', 'domain', 'selection',
- 'relation_field', 'string', 'views', 'invisible',
- 'add_remove', 'sort', 'context', 'filename',
- 'selection_change_with'):
- if attr_name in fields[fname].attrs and \
- not attr_name in node_attrs:
- node_attrs[attr_name] = fields[fname].attrs[attr_name]
- cell_type = node_attrs.get('widget',
- fields[fname].attrs['type'])
- cell = CELLTYPES.get(cell_type)(fname, model_name,
- treeview, node_attrs)
- treeview.cells[fname] = cell
- renderer = cell.renderer
-
- readonly = not (editable and not node_attrs.get('readonly',
- fields[fname].attrs.get('readonly', False)))
- if isinstance(renderer, CellRendererToggle):
- renderer.set_property('activatable', not readonly)
- elif isinstance(renderer,
- (gtk.CellRendererProgress, CellRendererButton)):
- pass
- else:
- renderer.set_property('editable', not readonly)
- if (not readonly
- and not isinstance(renderer, CellRendererBinary)):
- renderer.connect_after('editing-started', send_keys,
- treeview)
-
- col = gtk.TreeViewColumn(fields[fname].attrs['string'])
-
- prefixes = []
- suffixes = []
- if cell_type in ('url', 'email', 'callto', 'sip'):
- prefixes.append(Affix(fname, self.treeview, node_attrs,
- protocol=cell_type))
- if 'icon' in node_attrs:
- prefixes.append(Affix(fname, self.treeview, node_attrs))
- for affix in node.childNodes:
- affix_attrs = node_attributes(affix)
- if affix.localName == 'prefix':
- list_ = prefixes
- else:
- list_ = suffixes
- list_.append(Affix(fname, self.treeview, affix_attrs))
-
- for prefix in prefixes:
- col.pack_start(prefix.renderer, expand=False)
- col.set_cell_data_func(prefix.renderer, prefix.setter)
-
- col.pack_start(renderer, expand=True)
- col.set_cell_data_func(renderer, cell.setter)
- col.name = fname
-
- for suffix in suffixes:
- col.pack_start(suffix.renderer, expand=False)
- col.set_cell_data_func(suffix.renderer, suffix.setter)
-
- hbox = gtk.HBox(False, 2)
- label = gtk.Label(fields[fname].attrs['string'])
- label.show()
- help = fields[fname].attrs['string']
- if fields[fname].attrs.get('help'):
- help += '\n' + fields[fname].attrs['help']
- tooltips.set_tip(label, help)
- tooltips.enable()
- arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_IN)
- col.arrow = arrow
- col.arrow_show = False
- hbox.pack_start(label, True, True, 0)
- hbox.pack_start(arrow, False, False, 0)
- hbox.show()
- col.set_widget(hbox)
-
- col._type = fields[fname].attrs['type']
- col.set_clickable(True)
- twidth = {
- 'integer': 60,
- 'biginteger': 60,
- 'float': 80,
- 'numeric': 80,
- 'float_time': 100,
- 'date': 110,
- 'datetime': 160,
- 'selection': 90,
- 'char': 100,
- 'one2many': 50,
- 'many2many': 50,
- 'boolean': 20,
- 'binary': 200,
- }
- width = self.screen.tree_column_width[model_name].get(fname)
- if not width:
- if 'width' in node_attrs:
- width = int(node_attrs['width'])
- else:
- width = twidth.get(fields[fname].attrs['type'], 100)
- col.width = width
- if width > 0:
- col.set_fixed_width(width)
- col.set_min_width(1)
- expand = node_attrs.get('expand', False)
- col.set_expand(expand)
- expandable |= expand
- if (not treeview.sequence
- and not self.children_field
- and fields[fname].attrs.get('sortable', True)):
- col.connect('clicked', sort_model, treeview, self.screen)
- col.set_resizable(True)
- col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
- col.set_visible(not node_attrs.get('tree_invisible',
- fields[fname].attrs.get('tree_invisible', False)))
- if fname == self.screen.exclude_field:
- col.set_visible(False)
- i = treeview.append_column(col)
- if 'sum' in node_attrs and fields[fname].attrs['type'] \
- in ('integer', 'biginteger', 'float', 'numeric',
- 'float_time'):
- label = gtk.Label(node_attrs['sum'] + _(': '))
- label_sum = gtk.Label()
- if isinstance(fields[fname].attrs.get('digits'),
- basestring):
- digits = 2
- else:
- digits = fields[fname].attrs.get('digits', (16, 2))[1]
- dict_widget[i] = (fname, label, label_sum, digits)
- elif node.localName == 'button':
- #TODO add shortcut
- cell = Button(treeview, self.screen, node_attrs)
- state_widgets.append(cell)
- renderer = cell.renderer
- string = node_attrs.get('string', _('Unknown'))
- col = gtk.TreeViewColumn(string, renderer)
- col.name = None
- col.set_visible(not node_attrs.get('tree_invisible', False))
-
- label = gtk.Label(string)
- label.show()
- help = string
- if node_attrs.get('help'):
- help += '\n' + node_attrs['help']
- tooltips.set_tip(label, help)
- tooltips.enable()
- arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_IN)
- col.arrow = arrow
- col.arrow_show = False
- 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:
- width = 80
- col.width = width
- if width > 0:
- col.set_fixed_width(width)
- col.set_resizable(True)
- col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
- i = treeview.append_column(col)
- if not expandable:
- col = gtk.TreeViewColumn()
- col.name = None
- arrow = gtk.Arrow(gtk.ARROW_DOWN, gtk.SHADOW_IN)
- col.arrow = arrow
- col.arrow_show = False
- col._type = 'fill'
- col.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED)
- treeview.append_column(col)
- treeview.set_fixed_height_mode(True)
- return treeview, dict_widget, state_widgets, on_write, [], None
-
-
class Affix(object):
- def __init__(self, field_name, treeview, attrs, protocol=None):
+ def __init__(self, view, attrs, protocol=None):
super(Affix, self).__init__()
- self.field_name = attrs.get('name', field_name)
self.attrs = attrs
self.protocol = protocol
self.icon = attrs.get('icon')
@@ -368,13 +124,13 @@ class Affix(object):
self.renderer = gtk.CellRendererPixbuf()
else:
self.renderer = gtk.CellRendererText()
- self.treeview = treeview
+ self.view = view
@realized
@CellCache.cache
def setter(self, column, cell, store, iter_):
record = store.get_value(iter_, 0)
- field = record[self.field_name]
+ field = record[self.attrs['name']]
field.state_set(record, states=('invisible',))
invisible = field.get_state_attrs(record).get('invisible', False)
cell.set_property('visible', not invisible)
@@ -384,7 +140,7 @@ class Affix(object):
else:
value = self.icon
common.ICONFACTORY.register_icon(value)
- pixbuf = self.treeview.render_icon(stock_id=value,
+ pixbuf = self.view.treeview.render_icon(stock_id=value,
size=gtk.ICON_SIZE_BUTTON, detail=None)
cell.set_property('pixbuf', pixbuf)
else:
@@ -394,9 +150,9 @@ class Affix(object):
cell.set_property('text', text)
def clicked(self, renderer, path):
- store = self.treeview.get_model()
+ store = self.view.treeview.get_model()
record = store.get_value(store.get_iter(path), 0)
- value = record[self.field_name].get(record)
+ value = record[self.attrs['name']].get(record)
if value:
if self.protocol == 'email':
value = 'mailto:%s' % value
@@ -409,17 +165,25 @@ class Affix(object):
class Char(object):
- def __init__(self, field_name, model_name, treeview, attrs=None,
- renderer=None):
+ def __init__(self, view, attrs, renderer=None):
super(Char, self).__init__()
- self.field_name = field_name
- self.model_name = model_name
- self.attrs = attrs or {}
+ self.attrs = attrs
if renderer is None:
renderer = CellRendererText
self.renderer = renderer()
self.renderer.connect('editing-started', self.editing_started)
- self.treeview = treeview
+ if not isinstance(self.renderer, CellRendererBinary):
+ self.renderer.connect_after('editing-started', send_keys,
+ view.treeview)
+ self.view = view
+
+ @property
+ def field_name(self):
+ return self.attrs['name']
+
+ @property
+ def model_name(self):
+ return self.view.screen.model_name
@realized
@CellCache.cache
@@ -442,7 +206,7 @@ class Char(object):
else:
cell.set_property('foreground-set', True)
- field = record[self.field_name]
+ field = record[self.attrs['name']]
if self.attrs.get('type', field.attrs.get('type')) in \
('float', 'integer', 'biginteger', 'boolean',
@@ -451,17 +215,16 @@ class Char(object):
else:
align = 0
+ editable = getattr(self.view.treeview, 'editable', False)
states = ('invisible',)
- if hasattr(self.treeview, 'editable') \
- and self.treeview.editable:
+ if editable:
states = ('readonly', 'required', 'invisible')
field.state_set(record, states=states)
invisible = field.get_state_attrs(record).get('invisible', False)
cell.set_property('visible', not invisible)
- if hasattr(self.treeview, 'editable') \
- and self.treeview.editable:
+ if editable:
readonly = self.attrs.get('readonly',
field.get_state_attrs(record).get('readonly', False))
if invisible:
@@ -493,7 +256,7 @@ class Char(object):
cell.set_property('xalign', align)
def get_color(self, record):
- return record.expr_eval(self.treeview.colors)
+ return record.expr_eval(self.view.attributes.get('colors', '"black"'))
def open_remote(self, record, create, changed=False, text=None,
callback=None):
@@ -502,10 +265,10 @@ class Char(object):
def get_textual_value(self, record):
if not record:
return ''
- return record[self.field_name].get_client(record)
+ return record[self.attrs['name']].get_client(record)
def value_from_text(self, record, text, callback=None):
- field = record[self.field_name]
+ field = record[self.attrs['name']]
field.set_client(record, text)
if callback:
callback()
@@ -514,29 +277,28 @@ class Char(object):
return False
def _get_record_field(self, path):
- store = self.treeview.get_model()
+ store = self.view.treeview.get_model()
record = store.get_value(store.get_iter(path), 0)
- field = record.group.fields[self.field_name]
+ field = record.group.fields[self.attrs['name']]
return record, field
class Int(Char):
- def __init__(self, field_name, model_name, treeview, attrs=None,
- renderer=None):
+ def __init__(self, view, attrs, renderer=None):
if renderer is None:
renderer = CellRendererInteger
- super(Int, self).__init__(field_name, model_name, treeview,
- attrs=attrs, renderer=renderer)
+ super(Int, self).__init__(view, attrs, renderer=renderer)
self.factor = float(attrs.get('factor', 1))
def get_textual_value(self, record):
if not record:
return ''
- return record[self.field_name].get_client(record, factor=self.factor)
+ return record[self.attrs['name']].get_client(
+ record, factor=self.factor)
def value_from_text(self, record, text, callback=None):
- field = record[self.field_name]
+ field = record[self.attrs['name']]
field.set_client(record, text, factor=self.factor)
if callback:
callback()
@@ -544,23 +306,22 @@ class Int(Char):
class Boolean(Char):
- def __init__(self, field_name, model_name, treeview, attrs=None,
+ def __init__(self, view, attrs=None,
renderer=None):
if renderer is None:
renderer = CellRendererToggle
- super(Boolean, self).__init__(field_name, model_name, treeview,
- attrs=attrs, renderer=renderer)
+ super(Boolean, self).__init__(view, attrs, renderer=renderer)
self.renderer.connect('toggled', self._sig_toggled)
def _sig_toggled(self, renderer, path):
- store = self.treeview.get_model()
+ store = self.view.treeview.get_model()
record = store.get_value(store.get_iter(path), 0)
- field = record[self.field_name]
+ field = record[self.attrs['name']]
if not self.attrs.get('readonly',
field.get_state_attrs(record).get('readonly', False)):
- value = record[self.field_name].get_client(record)
- record[self.field_name].set_client(record, int(not value))
- self.treeview.set_cursor(path)
+ value = record[self.attrs['name']].get_client(record)
+ record[self.attrs['name']].set_client(record, int(not value))
+ self.view.treeview.set_cursor(path)
return True
@@ -571,7 +332,7 @@ class URL(Char):
def setter(self, column, cell, store, iter):
super(URL, self).setter(column, cell, store, iter)
record = store.get_value(iter, 0)
- field = record[self.field_name]
+ field = record[self.attrs['name']]
field.state_set(record, states=('readonly',))
readonly = field.get_state_attrs(record).get('readonly', False)
cell.set_property('visible', not readonly)
@@ -579,12 +340,10 @@ class URL(Char):
class Date(Char):
- def __init__(self, field_name, model_name, treeview, attrs=None,
- renderer=None):
+ def __init__(self, view, attrs, renderer=None):
if renderer is None:
renderer = partial(CellRendererDate, date_format())
- super(Date, self).__init__(field_name, model_name, treeview,
- attrs=attrs, renderer=renderer)
+ super(Date, self).__init__(view, attrs, renderer=renderer)
self.renderer.connect('editing-started', self.editing_started)
@@ -594,7 +353,7 @@ class Datetime(Date):
def setter(self, column, cell, store, iter):
super(Datetime, self).setter(column, cell, store, iter)
record = store.get_value(iter, 0)
- field = record[self.field_name]
+ field = record[self.attrs['name']]
time_format = field.time_format(record)
self.renderer.format = date_format() + ' ' + time_format
@@ -605,45 +364,41 @@ class Time(Date):
def setter(self, column, cell, store, iter):
super(Time, self).setter(column, cell, store, iter)
record = store.get_value(iter, 0)
- field = record[self.field_name]
+ field = record[self.attrs['name']]
time_format = field.time_format(record)
self.renderer.format = time_format
class Float(Int):
- def __init__(self, field_name, model_name, treeview, attrs=None,
- renderer=None):
+ def __init__(self, view, attrs, renderer=None):
if renderer is None:
renderer = CellRendererFloat
- super(Float, self).__init__(field_name, model_name, treeview,
- attrs=attrs, renderer=renderer)
+ super(Float, self).__init__(view, attrs, renderer=renderer)
@realized
def setter(self, column, cell, store, iter):
super(Float, self).setter(column, cell, store, iter)
record = store.get_value(iter, 0)
- field = record[self.field_name]
+ field = record[self.attrs['name']]
digits = field.digits(record, factor=self.factor)
cell.digits = digits
class FloatTime(Char):
- def __init__(self, field_name, model_name, treeview, attrs=None,
- renderer=None):
- super(FloatTime, self).__init__(field_name, model_name, treeview,
- attrs=attrs, renderer=renderer)
+ def __init__(self, view, attrs, renderer=None):
+ super(FloatTime, self).__init__(view, attrs, renderer=renderer)
self.conv = None
if attrs and attrs.get('float_time'):
self.conv = rpc.CONTEXT.get(attrs['float_time'])
def get_textual_value(self, record):
- val = record[self.field_name].get(record)
+ val = record[self.attrs['name']].get(record)
return common.float_time_to_text(val, self.conv)
def value_from_text(self, record, text, callback=None):
- field = record[self.field_name]
+ field = record[self.attrs['name']]
digits = field.digits(record)
field.set_client(record,
common.text_to_float_time(text, self.conv, digits[1]))
@@ -653,13 +408,11 @@ class FloatTime(Char):
class Binary(Char):
- def __init__(self, field_name, model_name, treeview, attrs=None,
- renderer=None):
+ def __init__(self, view, attrs, renderer=None):
self.filename = attrs.get('filename')
if renderer is None:
renderer = partial(CellRendererBinary, bool(self.filename))
- super(Binary, self).__init__(field_name, model_name, treeview,
- attrs=attrs, renderer=renderer)
+ super(Binary, self).__init__(view, attrs, renderer=renderer)
self.renderer.connect('new', self.new_binary)
self.renderer.connect('open', self.open_binary)
self.renderer.connect('save', self.save_binary)
@@ -677,7 +430,7 @@ class Binary(Char):
@CellCache.cache
def setter(self, column, cell, store, iter):
record = store.get_value(iter, 0)
- field = record[self.field_name]
+ field = record[self.attrs['name']]
if hasattr(field, 'get_size'):
size = field.get_size(record)
else:
@@ -685,14 +438,14 @@ class Binary(Char):
cell.set_property('size', common.humanize(size) if size else '')
states = ('invisible',)
- if getattr(self.treeview, 'editable', False):
+ if getattr(self.view.treeview, 'editable', False):
states = ('readonly', 'required', 'invisible')
field.state_set(record, states=states)
invisible = field.get_state_attrs(record).get('invisible', False)
cell.set_property('visible', not invisible)
- if getattr(self.treeview, 'editable', False):
+ if getattr(self.view.treeview, 'editable', False):
readonly = self.attrs.get('readonly',
field.get_state_attrs(record).get('readonly', False))
if invisible:
@@ -758,24 +511,22 @@ class Binary(Char):
class M2O(Char):
- def __init__(self, field_name, model_name, treeview, attrs=None,
- renderer=None):
+ def __init__(self, view, attrs, renderer=None):
if renderer is None and int(attrs.get('completion', 1)):
renderer = partial(CellRendererTextCompletion, self.set_completion)
- super(M2O, self).__init__(field_name, model_name, treeview,
- attrs=attrs, renderer=renderer)
+ super(M2O, self).__init__(view, attrs, renderer=renderer)
def value_from_text(self, record, text, callback=None):
- field = record.group.fields[self.field_name]
+ field = record.group.fields[self.attrs['name']]
if not text:
field.set_client(record, (None, ''))
if callback:
callback()
return
- relation = record[self.field_name].attrs['relation']
- domain = record[self.field_name].domain_get(record)
- context = record[self.field_name].context_get(record)
+ relation = record[self.attrs['name']].attrs['relation']
+ domain = record[self.attrs['name']].domain_get(record)
+ context = record[self.attrs['name']].context_get(record)
dom = [('rec_name', 'ilike', '%' + text + '%'), domain]
try:
ids = RPCExecute('model', relation, 'search', dom, 0, None, None,
@@ -795,7 +546,7 @@ class M2O(Char):
def open_remote(self, record, create=True, changed=False, text=None,
callback=None):
- field = record.group.fields[self.field_name]
+ field = record.group.fields[self.attrs['name']]
relation = field.attrs['relation']
access = common.MODELACCESS[relation]
@@ -849,7 +600,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]
+ field = record.group.fields[self.attrs['name']]
def search_callback(found):
value = None
@@ -917,12 +668,6 @@ class O2O(M2O):
pass
-class UnsettableColumn(Exception):
-
- def __init__(self):
- Exception.__init__()
-
-
class O2M(Char):
@realized
@@ -931,7 +676,7 @@ class O2M(Char):
cell.set_property('xalign', 0.5)
def get_textual_value(self, record):
- return '( ' + str(len(record[self.field_name]
+ return '( ' + str(len(record[self.attrs['name']]
.get_eval(record))) + ' )'
def value_from_text(self, record, text, callback=None):
@@ -940,8 +685,8 @@ class O2M(Char):
def open_remote(self, record, create=True, changed=False, text=None,
callback=None):
- group = record.value[self.field_name]
- field = record.group.fields[self.field_name]
+ group = record.value[self.attrs['name']]
+ field = record.group.fields[self.attrs['name']]
relation = field.attrs['relation']
context = field.context_get(record)
@@ -964,8 +709,8 @@ class M2M(O2M):
def open_remote(self, record, create=True, changed=False, text=None,
callback=None):
- group = record.value[self.field_name]
- field = record.group.fields[self.field_name]
+ group = record.value[self.attrs['name']]
+ field = record.group.fields[self.attrs['name']]
relation = field.attrs['relation']
context = field.context_get(record)
domain = field.domain_get(record)
@@ -988,11 +733,12 @@ class Selection(Char, SelectionMixin, PopdownMixin):
self.renderer = CellRendererCombo()
self.renderer.connect('editing-started', self.editing_started)
self.init_selection()
- self.renderer.set_property('model', self.get_popdown_model(self.selection)[0])
+ self.renderer.set_property('model',
+ self.get_popdown_model(self.selection)[0])
self.renderer.set_property('text-column', 0)
def get_textual_value(self, record):
- field = record[self.field_name]
+ field = record[self.attrs['name']]
self.update_selection(record, field)
value = field.get(record)
text = dict(self.selection).get(value, '')
@@ -1006,9 +752,9 @@ class Selection(Char, SelectionMixin, PopdownMixin):
def editing_started(self, cell, editable, path):
super(Selection, self).editing_started(cell, editable, path)
- store = self.treeview.get_model()
+ store = self.view.treeview.get_model()
record = store.get_value(store.get_iter(path), 0)
- field = record[self.field_name]
+ field = record[self.attrs['name']]
set_value = lambda *a: self.set_value(editable, record, field)
editable.child.connect('activate', set_value)
@@ -1034,14 +780,12 @@ class Selection(Char, SelectionMixin, PopdownMixin):
class Reference(Char, SelectionMixin):
- def __init__(self, field_name, model_name, treeview, attrs=None,
- renderer=None):
- super(Reference, self).__init__(field_name, model_name, treeview,
- attrs=attrs, renderer=renderer)
+ def __init__(self, view, attrs, renderer=None):
+ super(Reference, self).__init__(view, attrs, renderer=renderer)
self.init_selection()
def get_textual_value(self, record):
- field = record[self.field_name]
+ field = record[self.attrs['name']]
self.update_selection(record, field)
value = field.get_client(record)
if not value:
@@ -1066,22 +810,19 @@ class ProgressBar(object):
'top_to_bottom': gtk.PROGRESS_TOP_TO_BOTTOM,
}
- def __init__(self, field_name, model_name, treeview, attrs=None):
+ def __init__(self, view, attrs):
super(ProgressBar, self).__init__()
- self.field_name = field_name
- self.model_name = model_name
- self.attrs = attrs or {}
+ self.attrs = attrs
self.renderer = gtk.CellRendererProgress()
orientation = self.orientations.get(self.attrs.get('orientation',
'left_to_right'), gtk.PROGRESS_LEFT_TO_RIGHT)
self.renderer.set_property('orientation', orientation)
- self.treeview = treeview
@realized
@CellCache.cache
def setter(self, column, cell, store, iter):
record = store.get_value(iter, 0)
- field = record[self.field_name]
+ field = record[self.attrs['name']]
value = float(self.get_textual_value(record) or 0.0)
cell.set_property('value', value)
digit = field.digits(record)[1]
@@ -1093,10 +834,10 @@ class ProgressBar(object):
raise NotImplementedError
def get_textual_value(self, record):
- return record[self.field_name].get_client(record) or ''
+ return record[self.attrs['name']].get_client(record) or ''
def value_from_text(self, record, text, callback=None):
- field = record[self.field_name]
+ field = record[self.attrs['name']]
field.set_client(record, float(text))
if callback:
callback()
@@ -1104,12 +845,11 @@ class ProgressBar(object):
class Button(object):
- def __init__(self, treeview, screen, attrs=None):
+ def __init__(self, view, attrs):
super(Button, self).__init__()
- self.attrs = attrs or {}
+ self.attrs = attrs
self.renderer = CellRendererButton(attrs.get('string', _('Unknown')))
- self.treeview = treeview
- self.screen = screen
+ self.view = view
self.renderer.connect('clicked', self.button_clicked)
@@ -1135,7 +875,7 @@ class Button(object):
def button_clicked(self, widget, path):
if not path:
return True
- store = self.treeview.get_model()
+ store = self.view.treeview.get_model()
record = store.get_value(store.get_iter(path), 0)
state_changes = record.expr_eval(
@@ -1143,30 +883,4 @@ class Button(object):
if state_changes.get('invisible') \
or state_changes.get('readonly'):
return True
- self.screen.button(self.attrs)
-
-CELLTYPES = {
- 'char': Char,
- 'many2one': M2O,
- 'date': Date,
- 'one2many': O2M,
- 'many2many': M2M,
- 'selection': Selection,
- 'float': Float,
- 'numeric': Float,
- 'float_time': FloatTime,
- 'integer': Int,
- 'biginteger': Int,
- 'datetime': Datetime,
- 'time': Time,
- 'boolean': Boolean,
- 'text': Char,
- 'url': URL,
- 'email': URL,
- 'callto': URL,
- 'sip': URL,
- 'progressbar': ProgressBar,
- 'reference': Reference,
- 'one2one': O2O,
- 'binary': Binary,
-}
+ self.view.screen.button(self.attrs)
diff --git a/tryton/gui/window/view_form/view/widget_parse.py b/tryton/gui/window/view_form/view/widget_parse.py
deleted file mode 100644
index 45a33112a3..0000000000
--- a/tryton/gui/window/view_form/view/widget_parse.py
+++ /dev/null
@@ -1,47 +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.
-from interface import ParserInterface
-import form_gtk
-import list_gtk
-import graph_gtk
-import calendar_gtk
-from form import ViewForm
-from list import ViewList
-from graph import ViewGraph
-from calendar_ import ViewCalendar
-from tryton.exceptions import TrytonError
-
-PARSERS = {
- 'form': form_gtk.ParserForm,
- 'tree': list_gtk.ParserTree,
- 'graph': graph_gtk.ParserGraph,
- 'calendar': calendar_gtk.ParserCalendar,
- }
-
-PARSERS2 = {
- 'form': ViewForm,
- 'tree': ViewList,
- 'graph': ViewGraph,
- 'calendar': ViewCalendar,
- }
-
-
-class WidgetParse(ParserInterface):
-
- def parse(self, screen, root_node, fields, children_field=None):
- for node in root_node.childNodes:
- if not node.nodeType == node.ELEMENT_NODE:
- continue
- if node.localName in PARSERS:
- widget = PARSERS[node.localName](self.parent, self.attrs,
- screen, children_field)
- (wid, child, state_widgets, on_write, notebooks,
- cursor_widget) = widget.parse(screen.model_name, node,
- fields)
- screen.set_on_write(on_write)
- res = PARSERS2[node.localName](screen, wid, child,
- state_widgets, notebooks, cursor_widget, children_field)
- res.title = widget.title
- return res
- else:
- raise TrytonError('Unknow view mode: %s' % node.localName)
diff --git a/tryton/gui/window/win_search.py b/tryton/gui/window/win_search.py
index c8d20265fc..5ee48c72ca 100644
--- a/tryton/gui/window/win_search.py
+++ b/tryton/gui/window/win_search.py
@@ -60,7 +60,7 @@ class WinSearch(NoModal):
self.view.unset_editable()
# Prevent to set tree_state
self.screen.tree_states_done.add(id(self.view))
- sel = self.view.widget_tree.get_selection()
+ sel = self.view.treeview.get_selection()
if not sel_multi:
sel.set_mode(gtk.SELECTION_SINGLE)
@@ -89,7 +89,7 @@ class WinSearch(NoModal):
common.center_window(self.win, self.parent, self.sensible_widget)
def sig_activate(self, *args):
- self.view.widget_tree.emit_stop_by_name('row_activated')
+ self.view.treeview.emit_stop_by_name('row_activated')
self.win.response(gtk.RESPONSE_OK)
return True