Use separate XML parser in Views
issue8131 review54561002
This commit is contained in:
1 parent
c27b618f96
commit
237826bb43
7 files changed
+527
-523
No files matched your search
@@ -1,3 +1,4 @@
|
||||
* Use separate XML parser in Views
|
||||
* Use between numbers in filter box
|
||||
* Rename email and calendar file
|
||||
* Use markup for styling
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
import gettext
|
||||
from collections import defaultdict
|
||||
|
||||
from tryton.common import node_attributes
|
||||
|
||||
_ = gettext.gettext
|
||||
list_ = list # list builtins is overridden by import .list
|
||||
|
||||
|
||||
class View(object):
|
||||
view_type = None
|
||||
@@ -12,13 +18,20 @@ class View(object):
|
||||
editable = None
|
||||
children_field = None
|
||||
scroll = None
|
||||
xml_parser = None
|
||||
|
||||
def __init__(self, screen, xml):
|
||||
self.screen = screen
|
||||
self.fields = {}
|
||||
self.widgets = defaultdict(list_)
|
||||
self.state_widgets = []
|
||||
self.attributes = node_attributes(xml)
|
||||
screen.set_on_write(self.attributes.get('on_write'))
|
||||
|
||||
self.xml_parser(
|
||||
self, self.screen.exclude_field,
|
||||
{k: f.attrs for k, f in self.screen.group.fields.items()}
|
||||
).parse(xml)
|
||||
|
||||
def set_value(self):
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -61,3 +74,52 @@ class View(object):
|
||||
return ViewGraph(screen, root)
|
||||
elif tagname == 'calendar':
|
||||
return ViewCalendar(screen, root)
|
||||
|
||||
|
||||
class XMLViewParser:
|
||||
|
||||
def __init__(self, view, exclude_field, field_attrs):
|
||||
self.view = view
|
||||
self.exclude_field = exclude_field
|
||||
self.field_attrs = field_attrs
|
||||
|
||||
def _node_attributes(self, node):
|
||||
node_attrs = node_attributes(node)
|
||||
if 'name' in node_attrs:
|
||||
field = self.field_attrs.get(node_attrs['name'], {})
|
||||
else:
|
||||
field = {}
|
||||
|
||||
for name in ['readonly', 'homogeneous']:
|
||||
if name in node_attrs:
|
||||
node_attrs[name] = bool(int(node_attrs[name]))
|
||||
for name in [
|
||||
'yexpand', 'yfill', 'xexpand', 'xfill', 'colspan', 'position']:
|
||||
if name in node_attrs:
|
||||
node_attrs[name] = int(node_attrs[name])
|
||||
for name in ['xalign', 'yalign']:
|
||||
if name in node_attrs:
|
||||
node_attrs[name] = float(node_attrs[name])
|
||||
|
||||
if field:
|
||||
node_attrs.setdefault('widget', field['type'])
|
||||
if node.tagName == 'label' and 'string' not in node_attrs:
|
||||
node_attrs['string'] = field['string'] + _(':')
|
||||
if node.tagName == 'field' and 'help' not in node_attrs:
|
||||
node_attrs['help'] = field['help']
|
||||
for name in [
|
||||
'relation', 'domain', 'selection', 'string', 'states',
|
||||
'relation_field', 'views', 'invisible', 'add_remove',
|
||||
'sort', 'context', 'size', 'filename', 'autocomplete',
|
||||
'translate', 'create', 'delete', 'selection_change_with',
|
||||
'schema_model']:
|
||||
if name in field:
|
||||
node_attrs.setdefault(name, field[name])
|
||||
return node_attrs
|
||||
|
||||
def parse(self, node):
|
||||
node_attrs = self._node_attributes(node)
|
||||
if node.nodeType != node.ELEMENT_NODE:
|
||||
return
|
||||
parser = getattr(self, '_parse_%s' % node.tagName)
|
||||
parser(node, node_attrs)
|
||||
@@ -7,8 +7,7 @@ import gettext
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
from tryton.common import node_attributes
|
||||
from . import View
|
||||
from . import View, XMLViewParser
|
||||
try:
|
||||
from .calendar_gtk.calendar_ import Calendar_
|
||||
from .calendar_gtk.toolbar import Toolbar
|
||||
@@ -29,29 +28,48 @@ def goocalendar_required(func):
|
||||
return wrapper
|
||||
|
||||
|
||||
class CalendarXMLViewParser(XMLViewParser):
|
||||
|
||||
def __init__(self, view, exclude_field, field_attrs):
|
||||
super().__init__(view, exclude_field, field_attrs)
|
||||
self.calendar_fields = []
|
||||
|
||||
def _parse_calendar(self, node, attributes):
|
||||
for child in node.childNodes:
|
||||
self.parse(child)
|
||||
goocalendar = Calendar_(
|
||||
self.view.attributes, self.view, self.calendar_fields)
|
||||
toolbar = Toolbar(goocalendar)
|
||||
self.view.scroll.add(goocalendar)
|
||||
self.view.widget.pack_start(
|
||||
toolbar, expand=False, fill=False, padding=0)
|
||||
self.view.widgets['goocalendar'] = goocalendar
|
||||
self.view.widgets['toolbar'] = toolbar
|
||||
|
||||
def _parse_field(self, node, attributes):
|
||||
self.calendar_fields.append(attributes)
|
||||
|
||||
|
||||
class ViewCalendar(View):
|
||||
editable = False
|
||||
view_type = 'calendar'
|
||||
xml_parser = CalendarXMLViewParser
|
||||
|
||||
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)
|
||||
self.widget = Gtk.VBox()
|
||||
|
||||
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
|
||||
self.widgets = {}
|
||||
return
|
||||
|
||||
self.scroll = scrolledWindow = Gtk.ScrolledWindow()
|
||||
self.widget.pack_end(
|
||||
scrolledWindow, expand=True, fill=True, padding=0)
|
||||
|
||||
super().__init__(screen, xml)
|
||||
|
||||
goocalendar = self.widgets['goocalendar']
|
||||
toolbar = self.widgets['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)
|
||||
@@ -59,12 +77,6 @@ class ViewCalendar(View):
|
||||
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
|
||||
self.scroll = scrolledWindow = Gtk.ScrolledWindow()
|
||||
scrolledWindow.add(goocalendar)
|
||||
vbox.pack_start(toolbar, expand=False, fill=False, padding=0)
|
||||
vbox.pack_start(scrolledWindow, expand=True, fill=True, padding=0)
|
||||
return vbox
|
||||
|
||||
def on_page_changed(self, goocalendar, day, toolbar):
|
||||
toolbar.update_displayed_date()
|
||||
|
||||
@@ -9,11 +9,11 @@ from .dates_period import DatesPeriod
|
||||
class Calendar_(goocalendar.Calendar):
|
||||
'Calendar'
|
||||
|
||||
def __init__(self, attrs, screen, fields, event_store=None):
|
||||
def __init__(self, attrs, view, fields, event_store=None):
|
||||
super(Calendar_, self).__init__(
|
||||
event_store, attrs.get('mode', 'month'))
|
||||
self.attrs = attrs
|
||||
self.screen = screen
|
||||
self.view_calendar = view
|
||||
self.fields = fields
|
||||
self.event_store = event_store
|
||||
self.current_domain_period = self.get_displayed_period()
|
||||
@@ -76,8 +76,8 @@ class Calendar_(goocalendar.Calendar):
|
||||
def display(self, group):
|
||||
dtstart = self.attrs['dtstart']
|
||||
dtend = self.attrs.get('dtend')
|
||||
if self.screen.current_record:
|
||||
record = self.screen.current_record
|
||||
if self.view_calendar.record:
|
||||
record = self.view_calendar.record
|
||||
date = record[dtstart].get(record)
|
||||
if date: # select the day of the current record
|
||||
self.select(date)
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
import operator
|
||||
import gettext
|
||||
from collections import defaultdict
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
from . import View
|
||||
from . import View, XMLViewParser
|
||||
from tryton.common.focus import (get_invisible_ancestor, find_focused_child,
|
||||
next_focus_widget, find_focusable_child, find_first_focus_widget)
|
||||
from tryton.common import Tooltips, node_attributes, IconFactory, get_align
|
||||
@@ -163,102 +162,123 @@ class HContainer(Container):
|
||||
self.table.pack_start(widget, expand=expand, fill=fill, padding=1)
|
||||
|
||||
|
||||
class ViewForm(View):
|
||||
editable = True
|
||||
class FormXMLViewParser(XMLViewParser):
|
||||
|
||||
def __init__(self, screen, xml):
|
||||
super(ViewForm, self).__init__(screen, xml)
|
||||
self.view_type = 'form'
|
||||
self.widgets = defaultdict(list)
|
||||
self.state_widgets = []
|
||||
self.notebooks = []
|
||||
self.expandables = []
|
||||
WIDGETS = {
|
||||
'biginteger': Integer,
|
||||
'binary': Binary,
|
||||
'boolean': CheckBox,
|
||||
'callto': CallTo,
|
||||
'char': Char,
|
||||
'date': Date,
|
||||
'datetime': DateTime,
|
||||
'dict': DictWidget,
|
||||
'email': Email,
|
||||
'float': Float,
|
||||
'image': Image2,
|
||||
'integer': Integer,
|
||||
'many2many': Many2Many,
|
||||
'many2one': Many2One,
|
||||
'multiselection': MultiSelection,
|
||||
'numeric': Float,
|
||||
'one2many': One2Many,
|
||||
'one2one': One2One,
|
||||
'password': Password,
|
||||
'progressbar': ProgressBar,
|
||||
'pyson': PYSON,
|
||||
'reference': Reference,
|
||||
'richtext': RichTextBox,
|
||||
'selection': Selection,
|
||||
'sip': SIP,
|
||||
'text': TextBox,
|
||||
'time': Time,
|
||||
'timedelta': TimeDelta,
|
||||
'timestamp': DateTime,
|
||||
'url': URL,
|
||||
}
|
||||
|
||||
container = self.parse(xml)
|
||||
def __init__(self, view, exclude_field, field_attrs):
|
||||
super().__init__(view, exclude_field, field_attrs)
|
||||
self._containers = []
|
||||
self._mnemonics = {}
|
||||
|
||||
vbox = Gtk.VBox()
|
||||
vp = Gtk.Viewport()
|
||||
vp.set_shadow_type(Gtk.ShadowType.NONE)
|
||||
vp.add(container.table)
|
||||
self.scroll = scroll = Gtk.ScrolledWindow()
|
||||
scroll.add(vp)
|
||||
scroll.set_policy(
|
||||
Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
scroll.set_placement(Gtk.CornerType.TOP_LEFT)
|
||||
viewport = Gtk.Viewport()
|
||||
viewport.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
|
||||
viewport.add(scroll)
|
||||
vbox.pack_start(viewport, expand=True, fill=True, padding=0)
|
||||
@property
|
||||
def container(self):
|
||||
if self._containers:
|
||||
return self._containers[-1]
|
||||
return None
|
||||
|
||||
self.widget = vbox
|
||||
self._viewport = vp
|
||||
def _parse_form(self, node, attributes):
|
||||
container_attributes = node_attributes(node)
|
||||
container = Container.constructor(
|
||||
int(container_attributes.get('col', 4)),
|
||||
container_attributes.get('homogeneous', False))
|
||||
self.view.viewport.add(container.table)
|
||||
self.parse_child(node, container)
|
||||
assert not self._containers
|
||||
|
||||
def parse(self, node, container=None):
|
||||
if not container:
|
||||
node_attrs = node_attributes(node)
|
||||
container = Container.constructor(
|
||||
int(node_attrs.get('col', 4)),
|
||||
node_attrs.get('homogeneous', False))
|
||||
mnemonics = {}
|
||||
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])
|
||||
def parse_child(self, node, container=None):
|
||||
if container:
|
||||
self._containers.append(container)
|
||||
for child in node.childNodes:
|
||||
self.parse(child)
|
||||
if container:
|
||||
self._containers.pop()
|
||||
|
||||
parser = getattr(self, '_parse_%s' % node.tagName)
|
||||
widget = parser(node, container, node_attrs)
|
||||
if not widget:
|
||||
continue
|
||||
name = node_attrs.get('name')
|
||||
if node.tagName == 'label' and name:
|
||||
mnemonics[name] = widget
|
||||
if node.tagName == 'field':
|
||||
if name in mnemonics and widget.mnemonic_widget:
|
||||
label = mnemonics.pop(name)
|
||||
label.set_label(set_underline(label.get_label()))
|
||||
label.set_use_underline(True)
|
||||
label.set_mnemonic_widget(widget.mnemonic_widget)
|
||||
return container
|
||||
def _parse_field(self, node, attributes):
|
||||
name = attributes['name']
|
||||
if name and name == self.exclude_field:
|
||||
self.container.add(None, attributes)
|
||||
return
|
||||
|
||||
def _parse_image(self, node, container, attributes):
|
||||
widget = self.WIDGETS[attributes['widget']](self.view, attributes)
|
||||
self.view.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)))
|
||||
|
||||
self.container.add(Alignment(widget.widget, attributes), attributes)
|
||||
|
||||
if name in self._mnemonics and widget.mnemonic_widget:
|
||||
label = self._mnemonics.pop(name)
|
||||
label.set_label(set_underline(label.get_label()))
|
||||
label.set_use_underline(True)
|
||||
label.set_mnemonic_widget(widget.mnemonic_widget)
|
||||
|
||||
def _parse_button(self, node, attributes):
|
||||
button = Button(attributes)
|
||||
button.connect('clicked', self.view.button_clicked)
|
||||
self.view.state_widgets.append(button)
|
||||
self.container.add(button, attributes)
|
||||
|
||||
def _parse_image(self, node, attributes):
|
||||
image = Image(attrs=attributes)
|
||||
self.state_widgets.append(image)
|
||||
container.add(image, attributes)
|
||||
self.view.state_widgets.append(image)
|
||||
self.container.add(image, attributes)
|
||||
|
||||
def _parse_separator(self, node, container, attributes):
|
||||
if 'name' in attributes:
|
||||
field = self.group.fields[attributes['name']]
|
||||
for attr in ('states', 'string'):
|
||||
if attr not in attributes and attr in field.attrs:
|
||||
attributes[attr] = field.attrs[attr]
|
||||
def _parse_separator(self, node, attributes):
|
||||
vbox = VBox(attrs=attributes)
|
||||
if attributes.get('string'):
|
||||
label = Label(label=attributes['string'], attrs=attributes)
|
||||
label.set_halign(get_align(attributes.get('xalign', 0.0)))
|
||||
label.set_valign(get_align(attributes.get('yalign', 0.5)))
|
||||
vbox.pack_start(label, expand=True, fill=True, padding=0)
|
||||
self.state_widgets.append(label)
|
||||
self.view.state_widgets.append(label)
|
||||
vbox.pack_start(Gtk.HSeparator(), expand=True, fill=True, padding=0)
|
||||
self.state_widgets.append(vbox)
|
||||
container.add(vbox, attributes)
|
||||
self.view.state_widgets.append(vbox)
|
||||
self.container.add(vbox, attributes)
|
||||
|
||||
def _parse_label(self, node, container, attributes):
|
||||
if 'name' in attributes:
|
||||
field = self.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:
|
||||
attributes['string'] = field.attrs['string'] + _(':')
|
||||
def _parse_label(self, node, attributes):
|
||||
name = attributes.get('name')
|
||||
if name and name == self.exclude_field:
|
||||
self.container.add(None, attributes)
|
||||
return
|
||||
if CONFIG['client.modepda']:
|
||||
attributes['xalign'] = 0.0
|
||||
|
||||
@@ -267,20 +287,15 @@ class ViewForm(View):
|
||||
label.set_valign(get_align(attributes.get('yalign', 0.5)))
|
||||
label.set_angle(int(attributes.get('angle', 0)))
|
||||
attributes.setdefault('xexpand', 0)
|
||||
self.state_widgets.append(label)
|
||||
container.add(label, attributes)
|
||||
return label
|
||||
self.view.state_widgets.append(label)
|
||||
self.container.add(label, attributes)
|
||||
if name:
|
||||
self._mnemonics[name] = label
|
||||
|
||||
def _parse_newline(self, node, container, attributes):
|
||||
container.add_row()
|
||||
def _parse_newline(self, node, attributes):
|
||||
self.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):
|
||||
def _parse_notebook(self, node, attributes):
|
||||
attributes.setdefault('yexpand', True)
|
||||
attributes.setdefault('yfill', True)
|
||||
attributes.setdefault('colspan', 4)
|
||||
@@ -295,28 +310,23 @@ class ViewForm(View):
|
||||
# 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:
|
||||
if not self.view.widget:
|
||||
# Not yet finish to parse
|
||||
return
|
||||
notebook.grab_focus()
|
||||
self.display()
|
||||
self.view.display()
|
||||
notebook.disconnect(handler_id)
|
||||
handler_id = notebook.connect('switch-page', switch)
|
||||
self.state_widgets.append(notebook)
|
||||
self.view.state_widgets.append(notebook)
|
||||
|
||||
self.notebooks.append(notebook)
|
||||
container.add(notebook, attributes)
|
||||
self.parse(node, notebook)
|
||||
self.view.notebooks.append(notebook)
|
||||
self.container.add(notebook, attributes)
|
||||
self.parse_child(node, notebook)
|
||||
|
||||
def _parse_page(self, node, notebook, attributes):
|
||||
def _parse_page(self, node, attributes):
|
||||
tab_box = Gtk.HBox(spacing=3)
|
||||
if 'name' in attributes:
|
||||
field = self.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 'name' in attributes and attributes['name'] == self.exclude_field:
|
||||
return
|
||||
label = Gtk.Label(label=set_underline(attributes['string']))
|
||||
label.set_use_underline(True)
|
||||
|
||||
@@ -335,133 +345,91 @@ class ViewForm(View):
|
||||
Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
scrolledwindow.add(viewport)
|
||||
scrolledwindow.show_all()
|
||||
self.state_widgets.append(scrolledwindow)
|
||||
notebook.append_page(scrolledwindow, tab_box)
|
||||
container = self.parse(node)
|
||||
self.view.state_widgets.append(scrolledwindow)
|
||||
self.container.append_page(scrolledwindow, tab_box)
|
||||
container = Container.constructor(
|
||||
int(attributes.get('col', 4)),
|
||||
attributes.get('homogeneous', False))
|
||||
self.parse_child(node, container)
|
||||
viewport.add(container.table)
|
||||
|
||||
def _parse_field(self, node, container, attributes):
|
||||
name = attributes['name']
|
||||
field = self.group.fields[name]
|
||||
def _parse_group(self, node, attributes):
|
||||
group = Container.constructor(
|
||||
int(attributes.get('col', 4)),
|
||||
attributes.get('homogeneous', False))
|
||||
self.parse_child(node, group)
|
||||
|
||||
if (name not in self.group.fields
|
||||
or name == self.screen.exclude_field):
|
||||
container.add(None, attributes)
|
||||
if 'name' in attributes and attributes['name'] == self.exclude_field:
|
||||
self.container.add(None, attributes)
|
||||
return
|
||||
|
||||
if 'widget' not in attributes:
|
||||
attributes['widget'] = field.attrs['type']
|
||||
|
||||
for i_field in ('width', 'height'):
|
||||
if i_field in attributes:
|
||||
attributes[i_field] = int(attributes[i_field])
|
||||
|
||||
for attr in ('relation', 'domain', 'selection',
|
||||
'relation_field', 'string', 'help', '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.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)
|
||||
return widget
|
||||
|
||||
def _parse_group(self, node, container, attributes):
|
||||
group = self.parse(node)
|
||||
if 'name' in attributes:
|
||||
field = self.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]
|
||||
|
||||
can_expand = attributes.get('expandable')
|
||||
if can_expand:
|
||||
widget = Expander(label=attributes.get('string'), attrs=attributes)
|
||||
widget.add(group.table)
|
||||
widget.set_expanded(can_expand == '1')
|
||||
self.expandables.append(widget)
|
||||
self.view.expandables.append(widget)
|
||||
else:
|
||||
widget = Frame(label=attributes.get('string'), attrs=attributes)
|
||||
widget.add(group.table)
|
||||
|
||||
self.state_widgets.append(widget)
|
||||
container.add(widget, attributes)
|
||||
self.view.state_widgets.append(widget)
|
||||
self.container.add(widget, attributes)
|
||||
|
||||
def _parse_paned(self, node, container, attributes, Paned):
|
||||
def _parse_hpaned(self, node, attributes):
|
||||
self._parse_paned(node, attributes, Gtk.HPaned)
|
||||
|
||||
def _parse_vpaned(self, node, attributes):
|
||||
self._parse_paned(node, attributes, Gtk.VPaned)
|
||||
|
||||
def _parse_paned(self, node, 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)
|
||||
self.container.add(paned, attributes)
|
||||
self.parse_child(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)
|
||||
def _parse_child(self, node, attributes):
|
||||
paned = self.container
|
||||
container = Container.constructor(
|
||||
int(attributes.get('col', 4)),
|
||||
attributes.get('homogeneous', False))
|
||||
self.parse_child(node, container)
|
||||
if not paned.get_child1():
|
||||
pack = paned.pack1
|
||||
else:
|
||||
pack = paned.pack2
|
||||
pack(container.table, resize=True, shrink=True)
|
||||
|
||||
WIDGETS = {
|
||||
'date': Date,
|
||||
'datetime': DateTime,
|
||||
'time': Time,
|
||||
'timestamp': DateTime,
|
||||
'float': Float,
|
||||
'numeric': Float,
|
||||
'integer': Integer,
|
||||
'biginteger': Integer,
|
||||
'selection': Selection,
|
||||
'char': Char,
|
||||
'password': Password,
|
||||
'timedelta': TimeDelta,
|
||||
'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,
|
||||
'pyson': PYSON,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_widget(cls, name):
|
||||
return cls.WIDGETS[name]
|
||||
class ViewForm(View):
|
||||
editable = True
|
||||
view_type = 'form'
|
||||
xml_parser = FormXMLViewParser
|
||||
|
||||
def __init__(self, screen, xml):
|
||||
self.notebooks = []
|
||||
self.expandables = []
|
||||
|
||||
vbox = Gtk.VBox()
|
||||
vp = Gtk.Viewport()
|
||||
vp.set_shadow_type(Gtk.ShadowType.NONE)
|
||||
self.scroll = scroll = Gtk.ScrolledWindow()
|
||||
scroll.add(vp)
|
||||
scroll.set_policy(
|
||||
Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
||||
scroll.set_placement(Gtk.CornerType.TOP_LEFT)
|
||||
viewport = Gtk.Viewport()
|
||||
viewport.set_shadow_type(Gtk.ShadowType.ETCHED_IN)
|
||||
viewport.add(scroll)
|
||||
vbox.pack_start(viewport, expand=True, fill=True, padding=0)
|
||||
|
||||
self.widget = vbox
|
||||
self.viewport = vp
|
||||
|
||||
super().__init__(screen, xml)
|
||||
|
||||
def get_fields(self):
|
||||
return list(self.widgets.keys())
|
||||
@@ -554,7 +522,7 @@ class ViewForm(View):
|
||||
focus_widget = find_focusable_child(self.widgets[
|
||||
self.attributes['cursor']][0].widget)
|
||||
else:
|
||||
child = find_focusable_child(self._viewport)
|
||||
child = find_focusable_child(self.viewport)
|
||||
if child:
|
||||
child.grab_focus()
|
||||
record = self.record
|
||||
@@ -568,7 +536,7 @@ class ViewForm(View):
|
||||
invalid_widgets.append(invalid_widget)
|
||||
if invalid_widgets:
|
||||
focus_widget = find_first_focus_widget(
|
||||
self._viewport, invalid_widgets)
|
||||
self.viewport, invalid_widgets)
|
||||
if focus_widget:
|
||||
for notebook in self.notebooks:
|
||||
for i in range(notebook.get_n_pages()):
|
||||
|
||||
@@ -6,7 +6,7 @@ import gettext
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
from . import View
|
||||
from . import View, XMLViewParser
|
||||
from .graph_gtk.bar import VerticalBar, HorizontalBar
|
||||
from .graph_gtk.line import Line
|
||||
from .graph_gtk.pie import Pie
|
||||
@@ -19,57 +19,60 @@ from tryton.gui import Main
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
class ViewGraph(View):
|
||||
|
||||
def __init__(self, screen, xml):
|
||||
super(ViewGraph, self).__init__(screen, xml)
|
||||
self.view_type = 'graph'
|
||||
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
|
||||
class GraphXMLViewParser(XMLViewParser):
|
||||
|
||||
WIDGETS = {
|
||||
'vbar': VerticalBar,
|
||||
'hbar': HorizontalBar,
|
||||
'line': Line,
|
||||
'pie': Pie,
|
||||
'vbar': VerticalBar,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_widget(cls, name):
|
||||
return cls.WIDGETS[name]
|
||||
def __init__(self, view, exclude_field, field_attrs):
|
||||
super().__init__(view, exclude_field, field_attrs)
|
||||
self._xfield = None
|
||||
self._yfields = []
|
||||
|
||||
def _node_attributes(self, node):
|
||||
node_attrs = node_attributes(node)
|
||||
if 'name' in node_attrs:
|
||||
if not node_attrs.get('string') and node_attrs['name'] != '#':
|
||||
field = self.field_attrs[node_attrs['name']]
|
||||
node_attrs['string'] = field['string']
|
||||
return node_attrs
|
||||
|
||||
def _parse_graph(self, node, attributes):
|
||||
for child in node.childNodes:
|
||||
self.parse(child)
|
||||
|
||||
Widget = self.WIDGETS.get(attributes.get('type', 'vbar'))
|
||||
widget = Widget(
|
||||
self.view, self._xfield, self._yfields)
|
||||
self.view.widget.add(widget)
|
||||
self.view.widgets['root'] = widget
|
||||
|
||||
def _parse_x(self, node, attributes):
|
||||
for child in node.childNodes:
|
||||
if not child.nodeType == child.ELEMENT_NODE:
|
||||
continue
|
||||
self._xfield = self._node_attributes(child)
|
||||
|
||||
def _parse_y(self, node, attributes):
|
||||
for child in node.childNodes:
|
||||
if not child.nodeType == child.ELEMENT_NODE:
|
||||
continue
|
||||
self._yfields.append(self._node_attributes(child))
|
||||
|
||||
|
||||
class ViewGraph(View):
|
||||
view_type = 'graph'
|
||||
editable = False
|
||||
xml_parser = GraphXMLViewParser
|
||||
|
||||
def __init__(self, screen, xml):
|
||||
self.widget = event = Gtk.EventBox()
|
||||
super().__init__(screen, xml)
|
||||
event.connect('button-press-event', self.button_press)
|
||||
|
||||
def __getitem__(self, name):
|
||||
return None
|
||||
|
||||
@@ -5,7 +5,6 @@ import json
|
||||
import locale
|
||||
import gettext
|
||||
from functools import wraps
|
||||
from collections import defaultdict
|
||||
|
||||
from gi.repository import Gdk, GLib, GObject, Gtk
|
||||
from pygtkcompat.generictreemodel import GenericTreeModel
|
||||
@@ -18,7 +17,7 @@ from tryton.common import RPCExecute, RPCException, node_attributes, Tooltips
|
||||
from tryton.common import domain_inversion, simplify, unique_value
|
||||
from tryton.pyson import PYSONDecoder
|
||||
import tryton.common as common
|
||||
from . import View
|
||||
from . import View, XMLViewParser
|
||||
from .list_gtk.editabletree import EditableTreeView, TreeView
|
||||
from .list_gtk.widget import (Affix, Char, Text, Int, Boolean, URL, Date,
|
||||
Time, Float, TimeDelta, Binary, M2O, O2O, O2M, M2M, Selection, Reference,
|
||||
@@ -250,26 +249,215 @@ class AdaptModelGroup(GenericTreeModel):
|
||||
return record.parent
|
||||
|
||||
|
||||
class TreeXMLViewParser(XMLViewParser):
|
||||
|
||||
WIDGETS = {
|
||||
'biginteger': Int,
|
||||
'binary': Binary,
|
||||
'boolean': Boolean,
|
||||
'callto': URL,
|
||||
'char': Char,
|
||||
'date': Date,
|
||||
'email': URL,
|
||||
'float': Float,
|
||||
'image': Image,
|
||||
'integer': Int,
|
||||
'many2many': M2M,
|
||||
'many2one': M2O,
|
||||
'numeric': Float,
|
||||
'one2many': O2M,
|
||||
'one2one': O2O,
|
||||
'progressbar': ProgressBar,
|
||||
'reference': Reference,
|
||||
'selection': Selection,
|
||||
'sip': URL,
|
||||
'text': Text,
|
||||
'time': Time,
|
||||
'timedelta': TimeDelta,
|
||||
'url': URL,
|
||||
}
|
||||
|
||||
def _parse_tree(self, node, attributes):
|
||||
for child in node.childNodes:
|
||||
self.parse(child)
|
||||
|
||||
def _parse_field(self, node, attributes):
|
||||
name = attributes['name']
|
||||
widget = self.WIDGETS[attributes['widget']](self.view, attributes)
|
||||
self.view.widgets[name].append(widget)
|
||||
|
||||
column = Gtk.TreeViewColumn(attributes['string'])
|
||||
column._type = 'field'
|
||||
column.name = name
|
||||
|
||||
prefixes = []
|
||||
suffixes = []
|
||||
if attributes['widget'] in ['url', 'email', 'callto', 'sip']:
|
||||
prefixes.append(
|
||||
Affix(self.view, attributes, protocol=attributes['widget']))
|
||||
if 'icon' in attributes:
|
||||
prefixes.append(Affix(self.view, attributes))
|
||||
|
||||
for affix in node.childNodes:
|
||||
affix_attrs = node_attributes(affix)
|
||||
if 'name' not in affix_attrs:
|
||||
affix_attrs['name'] = attributes['name']
|
||||
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=prefix.expand)
|
||||
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=suffix.expand)
|
||||
column.set_cell_data_func(suffix.renderer, suffix.setter)
|
||||
|
||||
self._set_column_widget(column, attributes, align=widget.align)
|
||||
self._set_column_width(column, attributes)
|
||||
|
||||
if (not self.view.attributes.get('sequence')
|
||||
and not self.view.children_field
|
||||
and self.field_attrs[name].get('sortable', True)):
|
||||
column.connect('clicked', self.view.sort_model)
|
||||
|
||||
self.view.treeview.append_column(column)
|
||||
|
||||
if 'sum' in attributes:
|
||||
text = attributes['sum'] + _(':')
|
||||
label, sum_ = Gtk.Label(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.view.sum_box.pack_start(
|
||||
hbox, expand=False, fill=False, padding=0)
|
||||
|
||||
self.view.sum_widgets.append((attributes['name'], sum_))
|
||||
|
||||
def _parse_button(self, node, attributes):
|
||||
button = Button(self.view, attributes)
|
||||
self.view.state_widgets.append(button)
|
||||
|
||||
column = Gtk.TreeViewColumn(
|
||||
attributes.get('string', ''), button.renderer)
|
||||
column._type = 'button'
|
||||
column.name = None
|
||||
column.set_cell_data_func(button.renderer, button.setter)
|
||||
|
||||
self._set_column_widget(column, attributes, arrow=False)
|
||||
self._set_column_width(column, attributes)
|
||||
|
||||
decoder = PYSONDecoder(self.view.screen.context)
|
||||
column.set_visible(
|
||||
not decoder.decode(attributes.get('tree_invisible', '0')))
|
||||
|
||||
self.view.treeview.append_column(column)
|
||||
|
||||
def _set_column_widget(self, column, attributes, arrow=True, align=0.5):
|
||||
hbox = Gtk.HBox(homogeneous=False, spacing=2)
|
||||
label = Gtk.Label(label=attributes['string'])
|
||||
field = self.field_attrs.get(attributes['name'], {})
|
||||
if field and self.view.editable:
|
||||
required = field.get('required')
|
||||
readonly = field.get('readonly')
|
||||
common.apply_label_attributes(label, readonly, required)
|
||||
label.show()
|
||||
help = attributes.get('help')
|
||||
if help:
|
||||
tooltips = Tooltips()
|
||||
tooltips.set_tip(label, help)
|
||||
tooltips.enable()
|
||||
if arrow:
|
||||
arrow_widget = Gtk.Arrow(
|
||||
arrow_type=Gtk.ArrowType.NONE, shadow_type=Gtk.ShadowType.NONE)
|
||||
arrow_widget.show()
|
||||
column.arrow = arrow_widget
|
||||
hbox.pack_start(label, expand=True, fill=True, padding=0)
|
||||
if arrow:
|
||||
hbox.pack_start(arrow_widget, expand=False, fill=False, padding=0)
|
||||
column.set_clickable(True)
|
||||
hbox.show()
|
||||
column.set_widget(hbox)
|
||||
column.set_alignment(align)
|
||||
|
||||
def _set_column_width(self, column, attributes):
|
||||
default_width = {
|
||||
'integer': 60,
|
||||
'biginteger': 60,
|
||||
'float': 80,
|
||||
'numeric': 80,
|
||||
'timedelta': 100,
|
||||
'date': 100,
|
||||
'datetime': 100,
|
||||
'time': 100,
|
||||
'selection': 90,
|
||||
'char': 100,
|
||||
'one2many': 50,
|
||||
'many2many': 50,
|
||||
'boolean': 20,
|
||||
'binary': 200,
|
||||
}
|
||||
|
||||
screen = self.view.screen
|
||||
width = screen.tree_column_width[screen.model_name].get(column.name)
|
||||
field_attrs = self.field_attrs.get(attributes['name'], {})
|
||||
if not width:
|
||||
if 'width' in attributes:
|
||||
width = int(attributes['width'])
|
||||
elif field_attrs:
|
||||
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)
|
||||
if attributes.get('widget') != 'text':
|
||||
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
|
||||
|
||||
|
||||
class ViewTree(View):
|
||||
view_type = 'tree'
|
||||
xml_parser = TreeXMLViewParser
|
||||
|
||||
def __init__(self, screen, xml, children_field):
|
||||
super(ViewTree, self).__init__(screen, xml)
|
||||
self.view_type = 'tree'
|
||||
self.widgets = defaultdict(list)
|
||||
self.state_widgets = []
|
||||
self.children_field = children_field
|
||||
self.sum_widgets = []
|
||||
self.sum_box = Gtk.HBox()
|
||||
self.reload = False
|
||||
if self.attributes.get('editable') and not screen.readonly:
|
||||
self.treeview = EditableTreeView(self.attributes['editable'], self)
|
||||
self.treeview = None
|
||||
editable = xml.getAttribute('editable')
|
||||
if editable and not screen.readonly:
|
||||
self.treeview = EditableTreeView(editable, self)
|
||||
grid_lines = Gtk.TreeViewGridLines.BOTH
|
||||
else:
|
||||
self.treeview = TreeView(self)
|
||||
grid_lines = Gtk.TreeViewGridLines.VERTICAL
|
||||
|
||||
super().__init__(screen, xml)
|
||||
|
||||
self.mnemonic_widget = self.treeview
|
||||
|
||||
self.parse(xml)
|
||||
# Add last column if necessary
|
||||
for column in self.treeview.get_columns():
|
||||
if column.get_expand():
|
||||
break
|
||||
else:
|
||||
column = Gtk.TreeViewColumn()
|
||||
column._type = 'fill'
|
||||
column.name = None
|
||||
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
|
||||
self.treeview.append_column(column)
|
||||
|
||||
self.treeview.set_property('rules-hint', True)
|
||||
self.treeview.set_property('enable-grid-lines', grid_lines)
|
||||
@@ -307,231 +495,12 @@ class ViewTree(View):
|
||||
|
||||
self.display()
|
||||
|
||||
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.group
|
||||
node_attrs = node_attributes(node)
|
||||
name = node_attrs['name']
|
||||
field = group.fields[name]
|
||||
for b_field in ('readonly', 'expand'):
|
||||
if b_field in node_attrs:
|
||||
node_attrs[b_field] = bool(int(node_attrs[b_field]))
|
||||
for i_field in ('width', 'height'):
|
||||
if i_field in node_attrs:
|
||||
node_attrs[i_field] = int(node_attrs[i_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)
|
||||
self.widgets[name].append(widget)
|
||||
|
||||
column = Gtk.TreeViewColumn(node_attrs['string'])
|
||||
column._type = 'field'
|
||||
column.name = name
|
||||
|
||||
prefixes = []
|
||||
suffixes = list(widget.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 'name' not in affix_attrs:
|
||||
affix_attrs['name'] = name
|
||||
if affix.tagName == 'prefix':
|
||||
list_ = prefixes
|
||||
else:
|
||||
list_ = suffixes
|
||||
list_.append(Affix(self, affix_attrs))
|
||||
prefixes.extend(widget.prefixes)
|
||||
|
||||
for prefix in prefixes:
|
||||
column.pack_start(prefix.renderer, expand=prefix.expand)
|
||||
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=suffix.expand)
|
||||
column.set_cell_data_func(suffix.renderer,
|
||||
suffix.setter)
|
||||
|
||||
self.set_column_widget(column, field, node_attrs, align=widget.align)
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
decoder = PYSONDecoder(self.screen.context)
|
||||
column.set_visible(
|
||||
not decoder.decode(node_attrs.get('tree_invisible', '0')))
|
||||
|
||||
self.treeview.append_column(column)
|
||||
|
||||
WIDGETS = {
|
||||
'char': Char,
|
||||
'many2one': M2O,
|
||||
'date': Date,
|
||||
'one2many': O2M,
|
||||
'many2many': M2M,
|
||||
'selection': Selection,
|
||||
'float': Float,
|
||||
'numeric': Float,
|
||||
'timedelta': TimeDelta,
|
||||
'integer': Int,
|
||||
'biginteger': Int,
|
||||
'time': Time,
|
||||
'boolean': Boolean,
|
||||
'text': Text,
|
||||
'url': URL,
|
||||
'email': URL,
|
||||
'callto': URL,
|
||||
'sip': URL,
|
||||
'progressbar': ProgressBar,
|
||||
'reference': Reference,
|
||||
'one2one': O2O,
|
||||
'binary': Binary,
|
||||
'image': Image,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def get_widget(cls, name):
|
||||
return cls.WIDGETS[name]
|
||||
|
||||
def set_column_widget(self, column, field, attributes,
|
||||
arrow=True, align=0.5):
|
||||
hbox = Gtk.HBox(homogeneous=False, spacing=2)
|
||||
label = Gtk.Label(label=attributes['string'])
|
||||
if field and self.editable:
|
||||
required = field.attrs.get('required')
|
||||
readonly = field.attrs.get('readonly')
|
||||
common.apply_label_attributes(label, readonly, required)
|
||||
label.show()
|
||||
help = None
|
||||
if field and field.attrs.get('help'):
|
||||
help = field.attrs['help']
|
||||
elif attributes.get('help'):
|
||||
help = attributes['help']
|
||||
if help:
|
||||
tooltips = Tooltips()
|
||||
tooltips.set_tip(label, help)
|
||||
tooltips.enable()
|
||||
if arrow:
|
||||
arrow_widget = Gtk.Arrow(
|
||||
arrow_type=Gtk.ArrowType.NONE, shadow_type=Gtk.ShadowType.NONE)
|
||||
arrow_widget.show()
|
||||
column.arrow = arrow_widget
|
||||
hbox.pack_start(label, expand=True, fill=True, padding=0)
|
||||
if arrow:
|
||||
hbox.pack_start(
|
||||
arrow_widget, expand=False, fill=False, padding=0)
|
||||
column.set_clickable(True)
|
||||
hbox.show()
|
||||
column.set_widget(hbox)
|
||||
column.set_alignment(align)
|
||||
|
||||
def set_column_width(self, column, field, attributes):
|
||||
default_width = {
|
||||
'integer': 60,
|
||||
'biginteger': 60,
|
||||
'float': 80,
|
||||
'numeric': 80,
|
||||
'timedelta': 100,
|
||||
'date': 100,
|
||||
'datetime': 100,
|
||||
'time': 100,
|
||||
'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)
|
||||
if attributes.get('widget') != 'text':
|
||||
column.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
|
||||
|
||||
def get_column_widget(self, column):
|
||||
'Return the widget of the column'
|
||||
idx = [c for c in self.treeview.get_columns()
|
||||
if c.name == column.name].index(column)
|
||||
return self.widgets[column.name][idx]
|
||||
|
||||
def add_sum(self, attributes):
|
||||
if 'sum' not in attributes:
|
||||
return
|
||||
text = attributes['sum'] + _(':')
|
||||
label, sum_ = Gtk.Label(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, padding=0)
|
||||
|
||||
self.sum_widgets.append((attributes['name'], sum_))
|
||||
|
||||
def sort_model(self, column):
|
||||
for col in self.treeview.get_columns():
|
||||
if col != column and getattr(col, 'arrow', None):
|
||||
@@ -577,17 +546,6 @@ class ViewTree(View):
|
||||
else:
|
||||
arrow.set(direction, Gtk.ShadowType.IN)
|
||||
|
||||
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.set_sizing(Gtk.TreeViewColumnSizing.FIXED)
|
||||
self.treeview.append_column(column)
|
||||
|
||||
def set_drag_and_drop(self):
|
||||
dnd = False
|
||||
if self.children_field:
|
||||
|
||||
Reference in new issue
Block a user