Allow cells to have multiple renderers
Each cell can define prefixes and suffixes renderer which will be added to the column using their expand property. We use this new feature to replace the CellRendererBinary by CellRendererClickablePixbuf's for each buttons. We implement also an editable cell for Reference fields by using a Selection and a Many2One cell. In order to be able to navigate between cells of the same column, we do not validate anymore the field when the cell is left for another of the same record (this is a similar behavior to the form). Also the next and previous columns are checked if they are editable if start_editing is set to ensure to stay in edition mode during the all navigation. issue8112 issue6918 review46761002
This commit is contained in:
1 parent
4d0a2f13ca
commit
77eb3c38ea
5 files changed
+455
-251
No files matched your search
@@ -1,3 +1,6 @@
|
||||
* Implement Reference cell with Selection and Many2One cells
|
||||
* Replace CellRendererBinary with clickable pixbuf cells
|
||||
* Allow cell to have multiple renderers
|
||||
* Fill 'to' with 'from' value on filter box
|
||||
* Add tab domain in URL
|
||||
* Use Popover for search filter
|
||||
|
||||
@@ -20,7 +20,7 @@ try:
|
||||
from http import HTTPStatus
|
||||
except ImportError:
|
||||
from http import client as HTTPStatus
|
||||
from functools import partial
|
||||
from functools import partial, wraps
|
||||
from tryton.config import CONFIG
|
||||
from tryton.config import TRYTON_ICON, PIXMAPS_DIR
|
||||
import sys
|
||||
@@ -48,7 +48,7 @@ except ImportError:
|
||||
from threading import Lock
|
||||
import dateutil.tz
|
||||
|
||||
from gi.repository import Gtk
|
||||
from gi.repository import Gtk, GLib
|
||||
|
||||
from tryton import __version__
|
||||
from tryton.exceptions import TrytonServerError, TrytonError
|
||||
@@ -1329,3 +1329,10 @@ def ellipsize(string, length):
|
||||
|
||||
def date_format(format_):
|
||||
return format_ or rpc.CONTEXT.get('locale', {}).get('date', '%x')
|
||||
|
||||
|
||||
def idle_add(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
GLib.idle_add(func, *args, **kwargs)
|
||||
return wrapper
|
||||
@@ -347,7 +347,7 @@ class ViewTree(View):
|
||||
column.name = name
|
||||
|
||||
prefixes = []
|
||||
suffixes = []
|
||||
suffixes = list(widget.suffixes)
|
||||
if node_attrs['widget'] in ('url', 'email', 'callto', 'sip'):
|
||||
prefixes.append(Affix(self, node_attrs,
|
||||
protocol=node_attrs['widget']))
|
||||
@@ -362,9 +362,10 @@ class ViewTree(View):
|
||||
else:
|
||||
list_ = suffixes
|
||||
list_.append(Affix(self, affix_attrs))
|
||||
prefixes.extend(widget.prefixes)
|
||||
|
||||
for prefix in prefixes:
|
||||
column.pack_start(prefix.renderer, expand=False)
|
||||
column.pack_start(prefix.renderer, expand=prefix.expand)
|
||||
column.set_cell_data_func(prefix.renderer,
|
||||
prefix.setter)
|
||||
|
||||
@@ -372,7 +373,7 @@ class ViewTree(View):
|
||||
column.set_cell_data_func(widget.renderer, widget.setter)
|
||||
|
||||
for suffix in suffixes:
|
||||
column.pack_start(suffix.renderer, expand=False)
|
||||
column.pack_start(suffix.renderer, expand=suffix.expand)
|
||||
column.set_cell_data_func(suffix.renderer,
|
||||
suffix.setter)
|
||||
|
||||
@@ -1141,7 +1142,8 @@ class ViewTree(View):
|
||||
path = self.record.get_index_path(model.group)
|
||||
if model.get_flags() & gtk.TREE_MODEL_LIST_ONLY:
|
||||
path = (path[0],)
|
||||
focus_column = self.treeview.next_column(path, editable=new)
|
||||
focus_column, focus_cell = self.treeview.next_column(
|
||||
path, editable=new)
|
||||
if path[:-1]:
|
||||
self.treeview.expand_to_path(gtk.TreePath(path[:-1]))
|
||||
self.treeview.scroll_to_cell(path, focus_column,
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
import gtk
|
||||
import gettext
|
||||
import gobject
|
||||
from itertools import islice, cycle
|
||||
from itertools import islice, cycle, chain
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
from tryton.common import MODELACCESS
|
||||
from tryton.common.datetime_ import Date, Time
|
||||
@@ -11,6 +13,13 @@ from tryton.common.datetime_ import Date, Time
|
||||
_ = gettext.gettext
|
||||
|
||||
|
||||
def focusable_cells(column, editable=True):
|
||||
for cell in column.get_cells():
|
||||
mode = cell.props.mode
|
||||
if not editable or mode & Gtk.CellRendererMode.EDITABLE:
|
||||
yield cell
|
||||
|
||||
|
||||
class TreeView(gtk.TreeView):
|
||||
display_counter = 0
|
||||
|
||||
@@ -18,7 +27,14 @@ class TreeView(gtk.TreeView):
|
||||
super(TreeView, self).__init__()
|
||||
self.view = view
|
||||
|
||||
def next_column(self, path, column=None, editable=True, _sign=1):
|
||||
def next_column(
|
||||
self, path, column=None, cell=None, editable=True, _sign=1):
|
||||
if cell:
|
||||
cells = list(focusable_cells(column, editable))
|
||||
if len(cells) > 1:
|
||||
idx = cells.index(cell) + _sign
|
||||
if 0 <= idx < len(cells):
|
||||
return (column, cells[idx])
|
||||
columns = self.get_columns()
|
||||
if column is None:
|
||||
column = columns[-1 if _sign > 0 else 0]
|
||||
@@ -43,11 +59,17 @@ class TreeView(gtk.TreeView):
|
||||
else:
|
||||
readonly = False
|
||||
if not (invisible or readonly):
|
||||
return column
|
||||
cells = list(focusable_cells(column, editable))
|
||||
if cells:
|
||||
cell = cells[0 if _sign > 0 else -1]
|
||||
else:
|
||||
continue
|
||||
return (column, cell)
|
||||
return (None, None)
|
||||
|
||||
def prev_column(self, path, column=None, editable=True):
|
||||
return self.next_column(path, column=column, editable=editable,
|
||||
_sign=-1)
|
||||
def prev_column(self, path, column=None, cell=None, editable=True):
|
||||
return self.next_column(
|
||||
path, column=column, cell=cell, editable=editable, _sign=-1)
|
||||
|
||||
|
||||
class EditableTreeView(TreeView):
|
||||
@@ -60,7 +82,8 @@ class EditableTreeView(TreeView):
|
||||
super(EditableTreeView, self).__init__(view)
|
||||
self.editable = position
|
||||
|
||||
def on_quit_cell(self, current_record, column, value, callback=None):
|
||||
def on_quit_cell(
|
||||
self, current_record, column, renderer, value, callback=None):
|
||||
field = current_record[column.name]
|
||||
widget = self.view.get_column_widget(column)
|
||||
|
||||
@@ -70,6 +93,12 @@ class EditableTreeView(TreeView):
|
||||
if callback:
|
||||
callback()
|
||||
return
|
||||
if widget.renderer != renderer:
|
||||
for widget in chain(widget.prefixes, widget.suffixes):
|
||||
if widget.renderer == renderer:
|
||||
break
|
||||
else:
|
||||
raise ValueError("Unknown renderer")
|
||||
widget.value_from_text(current_record, value, callback=callback)
|
||||
|
||||
def on_open_remote(self, current_record, column, create, value,
|
||||
@@ -103,15 +132,19 @@ class EditableTreeView(TreeView):
|
||||
model.group.set_sequence(field=sequence)
|
||||
return res
|
||||
|
||||
def set_cursor(self, path, focus_column=None, start_editing=False):
|
||||
def set_cursor(
|
||||
self, path, focus_column=None, cell=None, start_editing=False):
|
||||
self.grab_focus()
|
||||
if focus_column:
|
||||
widget = self.view.get_column_widget(focus_column)
|
||||
if isinstance(widget.renderer, gtk.CellRendererToggle):
|
||||
start_editing = False
|
||||
self.scroll_to_cell(path, focus_column, use_align=False)
|
||||
super(EditableTreeView, self).set_cursor(path, focus_column,
|
||||
start_editing)
|
||||
if cell:
|
||||
self.set_cursor_on_cell(path, focus_column, cell, start_editing)
|
||||
else:
|
||||
super(EditableTreeView, self).set_cursor(
|
||||
path, focus_column, start_editing)
|
||||
|
||||
def set_value(self):
|
||||
path, column = self.get_cursor()
|
||||
@@ -120,10 +153,10 @@ class EditableTreeView(TreeView):
|
||||
for renderer in column.get_cell_renderers():
|
||||
if renderer.props.editing:
|
||||
widget = self.view.get_column_widget(column)
|
||||
self.on_editing_done(widget.editable)
|
||||
self.on_editing_done(widget.editable, renderer)
|
||||
return True
|
||||
|
||||
def on_keypressed(self, entry, event):
|
||||
def on_keypressed(self, entry, event, renderer):
|
||||
path, column = self.get_cursor()
|
||||
model = self.get_model()
|
||||
record = model.get_value(model.get_iter(path), 0)
|
||||
@@ -159,50 +192,47 @@ class EditableTreeView(TreeView):
|
||||
|
||||
def callback():
|
||||
entry.handler_unblock(entry.editing_done_id)
|
||||
field = record[column.name]
|
||||
# Must wait the edited entry came back in valid state
|
||||
if field.validate(record):
|
||||
if (keyval in (gtk.keysyms.Tab, gtk.keysyms.KP_Enter)
|
||||
or (keyval == gtk.keysyms.Right and leaving)):
|
||||
gobject.idle_add(self.set_cursor, path,
|
||||
self.next_column(path, column), True)
|
||||
elif (keyval == gtk.keysyms.ISO_Left_Tab
|
||||
or (keyval == gtk.keysyms.Left and leaving)):
|
||||
gobject.idle_add(self.set_cursor, path,
|
||||
self.prev_column(path, column), True)
|
||||
elif keyval in self.leaving_record_events:
|
||||
fields = list(self.view.widgets.keys())
|
||||
if not record.validate(fields):
|
||||
invalid_fields = record.invalid_fields
|
||||
col = None
|
||||
for col in self.get_columns():
|
||||
if col.name in invalid_fields:
|
||||
break
|
||||
gobject.idle_add(self.set_cursor, path, col, True)
|
||||
return
|
||||
if ((self.view.screen.pre_validate
|
||||
and not record.pre_validate())
|
||||
or (not self.view.screen.parent
|
||||
and not record.save())):
|
||||
gobject.idle_add(self.set_cursor, path, column,
|
||||
True)
|
||||
return
|
||||
entry.handler_block(entry.editing_done_id)
|
||||
if keyval == gtk.keysyms.Up:
|
||||
self._key_up(path, model, column)
|
||||
elif keyval == gtk.keysyms.Down:
|
||||
self._key_down(path, model, column)
|
||||
elif keyval == gtk.keysyms.Return:
|
||||
if self.editable == 'top':
|
||||
new_path = self._key_up(path, model)
|
||||
else:
|
||||
new_path = self._key_down(path, model)
|
||||
gobject.idle_add(self.set_cursor, new_path,
|
||||
self.next_column(new_path), True)
|
||||
entry.handler_unblock(entry.editing_done_id)
|
||||
if (keyval in (gtk.keysyms.Tab, gtk.keysyms.KP_Enter)
|
||||
or (keyval == gtk.keysyms.Right and leaving)):
|
||||
gobject.idle_add(self.set_cursor, path,
|
||||
*self.next_column(path, column, renderer), True)
|
||||
elif (keyval == gtk.keysyms.ISO_Left_Tab
|
||||
or (keyval == gtk.keysyms.Left and leaving)):
|
||||
gobject.idle_add(self.set_cursor, path,
|
||||
*self.prev_column(path, column, renderer), True)
|
||||
elif keyval in self.leaving_record_events:
|
||||
fields = list(self.view.widgets.keys())
|
||||
if not record.validate(fields):
|
||||
invalid_fields = record.invalid_fields
|
||||
col = None
|
||||
for col in self.get_columns():
|
||||
if col.name in invalid_fields:
|
||||
break
|
||||
gobject.idle_add(self.set_cursor, path, col, True)
|
||||
return
|
||||
if ((self.view.screen.pre_validate
|
||||
and not record.pre_validate())
|
||||
or (not self.view.screen.parent
|
||||
and not record.save())):
|
||||
gobject.idle_add(self.set_cursor, path, column,
|
||||
True)
|
||||
return
|
||||
entry.handler_block(entry.editing_done_id)
|
||||
if keyval == gtk.keysyms.Up:
|
||||
self._key_up(path, model, column)
|
||||
elif keyval == gtk.keysyms.Down:
|
||||
self._key_down(path, model, column)
|
||||
elif keyval == gtk.keysyms.Return:
|
||||
if self.editable == 'top':
|
||||
new_path = self._key_up(path, model)
|
||||
else:
|
||||
new_path = self._key_down(path, model)
|
||||
gobject.idle_add(self.set_cursor, new_path,
|
||||
*self.next_column(new_path), True)
|
||||
entry.handler_unblock(entry.editing_done_id)
|
||||
else:
|
||||
gobject.idle_add(self.set_cursor, path, column, True)
|
||||
self.on_quit_cell(record, column, txt, callback=callback)
|
||||
self.on_quit_cell(record, column, renderer, txt, callback=callback)
|
||||
return True
|
||||
elif event.keyval in (gtk.keysyms.F3, gtk.keysyms.F2):
|
||||
if isinstance(entry, gtk.Entry):
|
||||
@@ -236,7 +266,7 @@ class EditableTreeView(TreeView):
|
||||
self.on_create_line()
|
||||
new_path = (path[0] + 1) % len(model)
|
||||
if not column:
|
||||
column = self.next_column(new_path)
|
||||
column, cell = self.next_column(new_path)
|
||||
self.set_cursor(new_path, column, True)
|
||||
self.scroll_to_cell(new_path)
|
||||
return new_path
|
||||
@@ -248,12 +278,12 @@ class EditableTreeView(TreeView):
|
||||
else:
|
||||
new_path = (path[0] - 1) % len(model)
|
||||
if not column:
|
||||
column = self.next_column(new_path)
|
||||
column, cell = self.next_column(new_path)
|
||||
self.set_cursor(new_path, column, True)
|
||||
self.scroll_to_cell(new_path)
|
||||
return new_path
|
||||
|
||||
def on_editing_done(self, entry):
|
||||
def on_editing_done(self, entry, renderer):
|
||||
path, column = self.get_cursor()
|
||||
if not path:
|
||||
return True
|
||||
@@ -261,8 +291,9 @@ class EditableTreeView(TreeView):
|
||||
record = model.get_value(model.get_iter(path), 0)
|
||||
if isinstance(entry, (Date, Time)):
|
||||
entry.activate()
|
||||
self.on_quit_cell(record, column, entry.props.value)
|
||||
elif isinstance(entry, gtk.Entry):
|
||||
self.on_quit_cell(record, column, entry.get_text())
|
||||
text = entry.props.value
|
||||
elif isinstance(entry, (gtk.ComboBoxEntry, gtk.ComboBox)):
|
||||
self.on_quit_cell(record, column, entry.get_active_text())
|
||||
text = entry.get_active_text
|
||||
else:
|
||||
text = entry.get_text()
|
||||
self.on_quit_cell(record, column, renderer, text)
|
||||
@@ -6,9 +6,9 @@ import gtk
|
||||
import gettext
|
||||
import webbrowser
|
||||
|
||||
from functools import wraps, partial
|
||||
from gi.repository import Gtk, GObject
|
||||
|
||||
import gobject
|
||||
from functools import wraps, partial
|
||||
|
||||
from tryton.gui.window.win_search import WinSearch
|
||||
from tryton.gui.window.win_form import WinForm
|
||||
@@ -22,7 +22,6 @@ from tryton.common.cellrenderertoggle import CellRendererToggle
|
||||
from tryton.common.cellrenderercombo import CellRendererCombo
|
||||
from tryton.common.cellrendererinteger import CellRendererInteger
|
||||
from tryton.common.cellrendererfloat import CellRendererFloat
|
||||
from tryton.common.cellrendererbinary import CellRendererBinary
|
||||
from tryton.common.cellrendererclickablepixbuf import \
|
||||
CellRendererClickablePixbuf
|
||||
from tryton.common import data2pixbuf
|
||||
@@ -36,15 +35,15 @@ _ = gettext.gettext
|
||||
|
||||
|
||||
def send_keys(renderer, editable, position, treeview):
|
||||
editable.connect('key_press_event', treeview.on_keypressed)
|
||||
editable.connect('key_press_event', treeview.on_keypressed, renderer)
|
||||
editable.editing_done_id = editable.connect('editing_done',
|
||||
treeview.on_editing_done)
|
||||
treeview.on_editing_done, renderer)
|
||||
if isinstance(editable, (gtk.ComboBoxEntry, gtk.ComboBox)):
|
||||
def changed(combobox):
|
||||
# "changed" signal is also triggered by text editing
|
||||
# so only trigger editing-done if a row is active
|
||||
if combobox.get_active_iter():
|
||||
treeview.on_editing_done(combobox)
|
||||
treeview.on_editing_done(combobox, renderer)
|
||||
editable.connect('changed', changed)
|
||||
|
||||
|
||||
@@ -91,20 +90,20 @@ class CellCache(list):
|
||||
@classmethod
|
||||
def cache(cls, func):
|
||||
@wraps(func)
|
||||
def wrapper(self, column, cell, store, iter):
|
||||
def wrapper(self, column, cell, store, iter_):
|
||||
if not hasattr(self, 'display_counters'):
|
||||
self.display_counters = {}
|
||||
if not hasattr(self, 'cell_caches'):
|
||||
self.cell_caches = {}
|
||||
record = store.get_value(iter, 0)
|
||||
record = store.get_value(iter_, 0)
|
||||
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)
|
||||
func(self, column, cell, store, iter_)
|
||||
else:
|
||||
cache = cls()
|
||||
cache.decorate(cell)
|
||||
func(self, column, cell, store, iter)
|
||||
func(self, column, cell, store, iter_)
|
||||
cache.undecorate(cell)
|
||||
self.cell_caches[record.id] = cache
|
||||
self.display_counters[record.id] = counter
|
||||
@@ -114,10 +113,31 @@ class CellCache(list):
|
||||
|
||||
|
||||
class Cell(object):
|
||||
pass
|
||||
renderer = None
|
||||
setter = None
|
||||
expand = True
|
||||
attrs = None
|
||||
view = None
|
||||
prefixes = []
|
||||
suffixes = []
|
||||
|
||||
def _get_record_field_from_path(self, path, store=None):
|
||||
if not store:
|
||||
store = self.view.treeview.get_model()
|
||||
record = store.get_value(store.get_iter(path), 0)
|
||||
field = record.group.fields[self.attrs['name']]
|
||||
return record, field
|
||||
|
||||
def _get_record_field_from_iter(self, iter_, store=None):
|
||||
if not store:
|
||||
store = self.view.treeview.get_model()
|
||||
record = store.get_value(iter_, 0)
|
||||
field = record[self.attrs['name']]
|
||||
return record, field
|
||||
|
||||
|
||||
class Affix(Cell):
|
||||
expand = False
|
||||
|
||||
def __init__(self, view, attrs, protocol=None):
|
||||
super(Affix, self).__init__()
|
||||
@@ -138,7 +158,7 @@ class Affix(Cell):
|
||||
@realized
|
||||
@CellCache.cache
|
||||
def setter(self, column, cell, store, iter_):
|
||||
record = store.get_value(iter_, 0)
|
||||
record, field = self._get_record_field_from_iter(iter_, store)
|
||||
field = record[self.attrs['name']]
|
||||
field.state_set(record, states=('invisible',))
|
||||
invisible = field.get_state_attrs(record).get('invisible', False)
|
||||
@@ -157,8 +177,7 @@ class Affix(Cell):
|
||||
cell.set_property('text', text)
|
||||
|
||||
def clicked(self, renderer, path):
|
||||
store = self.view.treeview.get_model()
|
||||
record = store.get_value(store.get_iter(path), 0)
|
||||
record, field = self._get_record_field_from_path(path)
|
||||
value = record[self.attrs['name']].get(record)
|
||||
if value:
|
||||
if self.protocol == 'email':
|
||||
@@ -181,10 +200,10 @@ class GenericText(Cell):
|
||||
renderer = CellRendererText
|
||||
self.renderer = renderer()
|
||||
self.renderer.connect('editing-started', self.editing_started)
|
||||
if not isinstance(self.renderer, CellRendererBinary):
|
||||
self.renderer.connect_after('editing-started', send_keys,
|
||||
view.treeview)
|
||||
self.renderer.connect_after(
|
||||
'editing-started', send_keys, view.treeview)
|
||||
self.renderer.set_property('yalign', 0)
|
||||
self.renderer.set_property('xalign', self.align)
|
||||
self.view = view
|
||||
|
||||
@property
|
||||
@@ -197,8 +216,8 @@ class GenericText(Cell):
|
||||
|
||||
@realized
|
||||
@CellCache.cache
|
||||
def setter(self, column, cell, store, iter):
|
||||
record = store.get_value(iter, 0)
|
||||
def setter(self, column, cell, store, iter_):
|
||||
record = store.get_value(iter_, 0)
|
||||
text = self.get_textual_value(record)
|
||||
|
||||
if isinstance(cell, CellRendererToggle):
|
||||
@@ -244,8 +263,6 @@ class GenericText(Cell):
|
||||
if isinstance(cell, CellRendererToggle):
|
||||
cell.set_property('activatable', False)
|
||||
|
||||
cell.set_property('xalign', self.align)
|
||||
|
||||
def open_remote(self, record, create, changed=False, text=None,
|
||||
callback=None):
|
||||
raise NotImplementedError
|
||||
@@ -268,12 +285,6 @@ class GenericText(Cell):
|
||||
editable.connect('remove-widget', remove)
|
||||
return False
|
||||
|
||||
def _get_record_field(self, path):
|
||||
store = self.view.treeview.get_model()
|
||||
record = store.get_value(store.get_iter(path), 0)
|
||||
field = record.group.fields[self.attrs['name']]
|
||||
return record, field
|
||||
|
||||
|
||||
class Char(GenericText):
|
||||
|
||||
@@ -321,9 +332,7 @@ class Boolean(GenericText):
|
||||
self.renderer.connect('toggled', self._sig_toggled)
|
||||
|
||||
def _sig_toggled(self, renderer, path):
|
||||
store = self.view.treeview.get_model()
|
||||
record = store.get_value(store.get_iter(path), 0)
|
||||
field = record[self.attrs['name']]
|
||||
record, field = self._get_record_field_from_path(path)
|
||||
if not self.attrs.get('readonly',
|
||||
field.get_state_attrs(record).get('readonly', False)):
|
||||
value = record[self.attrs['name']].get_client(record)
|
||||
@@ -336,10 +345,9 @@ class URL(Char):
|
||||
|
||||
@realized
|
||||
@CellCache.cache
|
||||
def setter(self, column, cell, store, iter):
|
||||
super(URL, self).setter(column, cell, store, iter)
|
||||
record = store.get_value(iter, 0)
|
||||
field = record[self.attrs['name']]
|
||||
def setter(self, column, cell, store, iter_):
|
||||
super(URL, self).setter(column, cell, store, iter_)
|
||||
record, field = self._get_record_field_from_iter(iter_, store)
|
||||
field.state_set(record, states=('readonly',))
|
||||
readonly = field.get_state_attrs(record).get('readonly', False)
|
||||
cell.set_property('visible', not readonly)
|
||||
@@ -353,11 +361,10 @@ class Date(GenericText):
|
||||
super(Date, self).__init__(view, attrs, renderer=renderer)
|
||||
|
||||
@realized
|
||||
def setter(self, column, cell, store, iter):
|
||||
record = store.get_value(iter, 0)
|
||||
field = record[self.attrs['name']]
|
||||
def setter(self, column, cell, store, iter_):
|
||||
record, field = self._get_record_field_from_iter(iter_, store)
|
||||
self.renderer.props.format = self.get_format(record, field)
|
||||
super(Date, self).setter(column, cell, store, iter)
|
||||
super(Date, self).setter(column, cell, store, iter_)
|
||||
|
||||
def get_format(self, record, field):
|
||||
if field and record:
|
||||
@@ -412,35 +419,44 @@ class Float(Int):
|
||||
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.attrs['name']]
|
||||
def setter(self, column, cell, store, iter_):
|
||||
super(Float, self).setter(column, cell, store, iter_)
|
||||
record, field = self._get_record_field_from_iter(iter_, store)
|
||||
digits = field.digits(record, factor=self.factor)
|
||||
cell.digits = digits
|
||||
|
||||
|
||||
class Binary(GenericText):
|
||||
align = 0.5
|
||||
align = 1
|
||||
|
||||
def __init__(self, view, attrs, renderer=None):
|
||||
self.filename = attrs.get('filename')
|
||||
if renderer is None:
|
||||
renderer = partial(CellRendererBinary, bool(self.filename))
|
||||
renderer = CellRendererText
|
||||
super(Binary, self).__init__(view, attrs, renderer=renderer)
|
||||
self.renderer.connect(
|
||||
'select',
|
||||
lambda *args: gobject.idle_add(self.select_binary, *args))
|
||||
self.renderer.connect(
|
||||
'open',
|
||||
lambda *args: gobject.idle_add(self.open_binary, *args))
|
||||
self.renderer.connect(
|
||||
'save',
|
||||
lambda *args: gobject.idle_add(self.save_binary, *args))
|
||||
self.renderer.connect('clear', self.clear_binary)
|
||||
self.renderer.set_property('editable', False)
|
||||
self.renderer.set_property('xalign', self.align)
|
||||
self.renderer_save = _BinarySave(self)
|
||||
self.renderer_select = _BinarySelect(self)
|
||||
if self.attrs.get('filename'):
|
||||
self.renderer_open = _BinaryOpen(self)
|
||||
else:
|
||||
self.renderer_open = None
|
||||
|
||||
@property
|
||||
def prefixes(self):
|
||||
return filter(None, [self.renderer_open])
|
||||
|
||||
@property
|
||||
def suffixes(self):
|
||||
return [self.renderer_save, self.renderer_select]
|
||||
|
||||
def get_textual_value(self, record):
|
||||
pass
|
||||
field = record[self.attrs['name']]
|
||||
if hasattr(field, 'get_size'):
|
||||
size = field.get_size(record)
|
||||
else:
|
||||
size = len(field.get(record))
|
||||
return common.humanize(size) if size else ''
|
||||
|
||||
def value_from_text(self, record, text, callback=None):
|
||||
if callback:
|
||||
@@ -448,14 +464,10 @@ class Binary(GenericText):
|
||||
|
||||
@realized
|
||||
@CellCache.cache
|
||||
def setter(self, column, cell, store, iter):
|
||||
record = store.get_value(iter, 0)
|
||||
field = record[self.attrs['name']]
|
||||
if hasattr(field, 'get_size'):
|
||||
size = field.get_size(record)
|
||||
else:
|
||||
size = len(field.get(record))
|
||||
cell.set_property('size', common.humanize(size) if size else '')
|
||||
def setter(self, column, cell, store, iter_):
|
||||
record, field = self._get_record_field_from_iter(iter_, store)
|
||||
text = self.get_textual_value(record)
|
||||
cell.set_property('text', text)
|
||||
|
||||
states = ('invisible',)
|
||||
if getattr(self.view.treeview, 'editable', False):
|
||||
@@ -465,49 +477,6 @@ class Binary(GenericText):
|
||||
invisible = field.get_state_attrs(record).get('invisible', False)
|
||||
cell.set_property('visible', not invisible)
|
||||
|
||||
if getattr(self.view.treeview, 'editable', False):
|
||||
readonly = self.attrs.get('readonly',
|
||||
field.get_state_attrs(record).get('readonly', False))
|
||||
if invisible:
|
||||
readonly = True
|
||||
cell.set_property('editable', not readonly)
|
||||
|
||||
def select_binary(self, renderer, path):
|
||||
record, field = self._get_record_field(path)
|
||||
filename = ''
|
||||
filename = file_selection(_('Open...'))
|
||||
if filename:
|
||||
field.set_client(record, open(filename, 'rb').read())
|
||||
if self.filename:
|
||||
filename_field = record.group.fields[self.filename]
|
||||
filename_field.set_client(record, os.path.basename(filename))
|
||||
|
||||
def open_binary(self, renderer, path):
|
||||
if not self.filename:
|
||||
return
|
||||
record, field = self._get_record_field(path)
|
||||
filename_field = record.group.fields.get(self.filename)
|
||||
filename = filename_field.get(record)
|
||||
if not filename:
|
||||
return
|
||||
file_path = file_write(filename, self.get_data(record, field))
|
||||
root, type_ = os.path.splitext(filename)
|
||||
if type_:
|
||||
type_ = type_[1:]
|
||||
file_open(file_path, type_)
|
||||
|
||||
def save_binary(self, renderer, path):
|
||||
filename = ''
|
||||
record, field = self._get_record_field(path)
|
||||
if self.filename:
|
||||
filename_field = record.group.fields.get(self.filename)
|
||||
filename = filename_field.get(record)
|
||||
filename = file_selection(_('Save As...'), filename=filename,
|
||||
action=gtk.FILE_CHOOSER_ACTION_SAVE)
|
||||
if filename:
|
||||
with open(filename, 'wb') as fp:
|
||||
fp.write(self.get_data(record, field))
|
||||
|
||||
def get_data(self, record, field):
|
||||
if hasattr(field, 'get_data'):
|
||||
data = field.get_data(record)
|
||||
@@ -517,12 +486,132 @@ class Binary(GenericText):
|
||||
data = data.encode('utf-8')
|
||||
return data
|
||||
|
||||
def clear_binary(self, renderer, path):
|
||||
record, field = self._get_record_field(path)
|
||||
if self.filename:
|
||||
filename_field = record.group.fields[self.filename]
|
||||
filename_field.set_client(record, None)
|
||||
field.set_client(record, None)
|
||||
|
||||
class _BinaryIcon(Cell):
|
||||
expand = False
|
||||
|
||||
def __init__(self, binary):
|
||||
super().__init__()
|
||||
self.binary = binary
|
||||
self.renderer = CellRendererClickablePixbuf()
|
||||
self.renderer.connect('clicked', self.clicked)
|
||||
|
||||
@property
|
||||
def attrs(self):
|
||||
return self.binary.attrs
|
||||
|
||||
@property
|
||||
def view(self):
|
||||
return self.binary.view
|
||||
|
||||
|
||||
class _BinarySave(_BinaryIcon):
|
||||
icon_name = 'tryton-save'
|
||||
|
||||
def __init__(self, binary):
|
||||
super().__init__(binary)
|
||||
pixbuf = common.IconFactory.get_pixbuf(
|
||||
self.icon_name, Gtk.IconSize.BUTTON)
|
||||
self.renderer.set_property('pixbuf', pixbuf)
|
||||
|
||||
@common.idle_add
|
||||
def clicked(self, renderer, path):
|
||||
filename = ''
|
||||
record, field = self._get_record_field_from_path(path)
|
||||
if self.attrs.get('filename'):
|
||||
filename_field = record.group.fields.get(self.attrs['filename'])
|
||||
filename = filename_field.get(record)
|
||||
filename = file_selection(_('Save As...'), filename=filename,
|
||||
action=gtk.FILE_CHOOSER_ACTION_SAVE)
|
||||
if filename:
|
||||
with open(filename, 'wb') as fp:
|
||||
fp.write(self.binary.get_data(record, field))
|
||||
|
||||
@realized
|
||||
@CellCache.cache
|
||||
def setter(self, column, cell, store, iter_):
|
||||
record, field = self._get_record_field_from_iter(iter_, store)
|
||||
if hasattr(field, 'get_size'):
|
||||
size = field.get_size(record)
|
||||
else:
|
||||
size = len(field.get(record))
|
||||
field.state_set(record, states=['invisible'])
|
||||
invisible = field.get_state_attrs(record).get('invisible', False)
|
||||
cell.set_property('visible', not invisible and size)
|
||||
|
||||
|
||||
class _BinarySelect(_BinaryIcon):
|
||||
def clicked(self, renderer, path):
|
||||
record, field = self._get_record_field_from_path(path)
|
||||
if hasattr(field, 'get_size'):
|
||||
size = field.get_size(record)
|
||||
else:
|
||||
size = len(field.get(record))
|
||||
if self.attrs.get('filename'):
|
||||
filename_field = record.group.fields[self.attrs['filename']]
|
||||
else:
|
||||
filename_field = None
|
||||
if size:
|
||||
if filename_field:
|
||||
filename_field.set_client(record, None)
|
||||
field.set_client(record, None)
|
||||
else:
|
||||
def _select():
|
||||
filename = file_selection(_('Open...'))
|
||||
if filename:
|
||||
with open(filename, 'rb') as fp:
|
||||
field.set_client(record, fp.read())
|
||||
if filename_field:
|
||||
filename_field.set_client(
|
||||
record, os.path.basename(filename))
|
||||
GObject.idle_add(_select)
|
||||
|
||||
@realized
|
||||
@CellCache.cache
|
||||
def setter(self, column, cell, store, iter_):
|
||||
record, field = self._get_record_field_from_iter(iter_, store)
|
||||
if hasattr(field, 'get_size'):
|
||||
size = field.get_size(record)
|
||||
else:
|
||||
size = len(field.get(record))
|
||||
if size:
|
||||
icon = 'tryton-clear'
|
||||
else:
|
||||
icon = 'tryton-search'
|
||||
pixbuf = common.IconFactory.get_pixbuf(icon, Gtk.IconSize.BUTTON)
|
||||
cell.set_property('pixbuf', pixbuf)
|
||||
field.state_set(record, states=['invisible', 'readonly'])
|
||||
invisible = field.get_state_attrs(record).get('invisible', False)
|
||||
readonly = self.attrs.get('readonly',
|
||||
field.get_state_attrs(record).get('readonly', False))
|
||||
if readonly and size:
|
||||
cell.set_property('visible', False)
|
||||
else:
|
||||
cell.set_property('visible', not invisible)
|
||||
|
||||
|
||||
class _BinaryOpen(_BinarySave):
|
||||
icon_name = 'tryton-open'
|
||||
|
||||
def clicked(self, renderer, path):
|
||||
record, field = self._get_record_field_from_path(path)
|
||||
filename_field = record.group.fields.get(self.attrs.get('filename'))
|
||||
filename = filename_field.get(record)
|
||||
file_path = file_write(filename, self.binary.get_data(record, field))
|
||||
root, type_ = os.path.splitext(filename)
|
||||
if type_:
|
||||
type_ = type_[1:]
|
||||
GObject.idle_add(file_open, file_path, type_)
|
||||
|
||||
@realized
|
||||
@CellCache.cache
|
||||
def setter(self, column, cell, store, iter_):
|
||||
super().setter(column, cell, store, iter_)
|
||||
record, field = self._get_record_field_from_iter(iter_, store)
|
||||
filename_field = record.group.fields.get(self.attrs.get('filename'))
|
||||
filename = filename_field.get(record)
|
||||
if not filename:
|
||||
cell.set_property('visible', False)
|
||||
|
||||
|
||||
class Image(GenericText):
|
||||
@@ -538,8 +627,7 @@ class Image(GenericText):
|
||||
@realized
|
||||
@CellCache.cache
|
||||
def setter(self, column, cell, store, iter_):
|
||||
record = store.get_value(iter_, 0)
|
||||
field = record[self.field_name]
|
||||
record, field = self._get_record_field_from_iter(iter_, store)
|
||||
value = field.get_client(record)
|
||||
if isinstance(value, int):
|
||||
if value > CONFIG['image.max_size']:
|
||||
@@ -566,31 +654,50 @@ class M2O(GenericText):
|
||||
renderer = partial(CellRendererTextCompletion, self.set_completion)
|
||||
super(M2O, self).__init__(view, attrs, renderer=renderer)
|
||||
|
||||
def get_model(self, record, field):
|
||||
return self.attrs['relation']
|
||||
|
||||
def has_target(self, value):
|
||||
return value is not None
|
||||
|
||||
def value_from_id(self, model, id_, str_=None):
|
||||
if str_ is None:
|
||||
str_ = ''
|
||||
return id_, str_
|
||||
|
||||
def id_from_value(self, value):
|
||||
return value
|
||||
|
||||
def value_from_text(self, record, text, callback=None):
|
||||
field = record.group.fields[self.attrs['name']]
|
||||
model = self.get_model(record, field)
|
||||
if not text:
|
||||
field.set_client(record, (None, ''))
|
||||
field.set_client(
|
||||
record, self.value_from_id(model, None, ''))
|
||||
if callback:
|
||||
callback()
|
||||
return
|
||||
|
||||
field = record[self.attrs['name']]
|
||||
win = self.search_remote(record, field, text, callback=callback)
|
||||
if len(win.screen.group) == 1:
|
||||
win.response(None, gtk.RESPONSE_OK)
|
||||
else:
|
||||
win.show()
|
||||
if model:
|
||||
field = record[self.attrs['name']]
|
||||
win = self.search_remote(record, field, text, callback=callback)
|
||||
if len(win.screen.group) == 1:
|
||||
win.response(None, gtk.RESPONSE_OK)
|
||||
else:
|
||||
win.show()
|
||||
|
||||
def editing_started(self, cell, editable, path):
|
||||
super(M2O, self).editing_started(cell, editable, path)
|
||||
record, field = self._get_record_field(path)
|
||||
record, field = self._get_record_field_from_path(path)
|
||||
model = self.get_model(record, field)
|
||||
|
||||
def changed(editable):
|
||||
text = editable.get_text()
|
||||
if field.get_client(record) != text:
|
||||
field.set_client(record, (None, ''))
|
||||
if self.get_textual_value(record) != text:
|
||||
field.set_client(
|
||||
record, self.value_from_id(model, None, ''))
|
||||
|
||||
if field.get(record):
|
||||
if self.has_target(field.get(record)):
|
||||
icon1, tooltip1 = 'tryton-open', _("Open the record <F2>")
|
||||
icon2, tooltip2 = 'tryton-clear', _("Clear the field <Del>")
|
||||
else:
|
||||
@@ -609,10 +716,13 @@ class M2O(GenericText):
|
||||
|
||||
def icon_press(editable, icon_pos, event):
|
||||
value = field.get(record)
|
||||
if icon_pos == gtk.ENTRY_ICON_SECONDARY and value:
|
||||
field.set_client(record, (None, ''))
|
||||
if not model:
|
||||
return
|
||||
if icon_pos == gtk.ENTRY_ICON_SECONDARY and self.has_target(value):
|
||||
field.set_client(
|
||||
record, self.value_from_id(model, None, ''))
|
||||
editable.set_text('')
|
||||
elif value:
|
||||
elif self.has_target(value):
|
||||
self.open_remote(record, create=False, changed=False)
|
||||
else:
|
||||
self.open_remote(
|
||||
@@ -627,9 +737,9 @@ class M2O(GenericText):
|
||||
def open_remote(self, record, create=True, changed=False, text=None,
|
||||
callback=None):
|
||||
field = record.group.fields[self.attrs['name']]
|
||||
relation = field.attrs['relation']
|
||||
model = self.get_model(record, field)
|
||||
|
||||
access = common.MODELACCESS[relation]
|
||||
access = common.MODELACCESS[model]
|
||||
if (create
|
||||
and not (self.attrs.get('create', True) and access['create'])):
|
||||
return
|
||||
@@ -638,27 +748,26 @@ class M2O(GenericText):
|
||||
|
||||
domain = field.domain_get(record)
|
||||
context = field.get_context(record)
|
||||
if create:
|
||||
obj_id = None
|
||||
elif not changed:
|
||||
obj_id = field.get(record)
|
||||
else:
|
||||
if not create and changed:
|
||||
self.search_remote(record, field, text, callback=callback).show()
|
||||
return
|
||||
target_id = self.id_from_value(field.get(record))
|
||||
|
||||
screen = Screen(relation, domain=domain, context=context,
|
||||
screen = Screen(model, domain=domain, context=context,
|
||||
mode=['form'], view_ids=self.attrs.get('view_ids', '').split(','),
|
||||
exclude_field=field.attrs.get('relation_field'))
|
||||
|
||||
def open_callback(result):
|
||||
if result:
|
||||
value = (screen.current_record.id,
|
||||
value = self.value_from_id(
|
||||
model,
|
||||
screen.current_record.id,
|
||||
screen.current_record.rec_name())
|
||||
field.set_client(record, value, force_change=True)
|
||||
if callback:
|
||||
callback()
|
||||
if obj_id:
|
||||
screen.load([obj_id])
|
||||
if target_id and target_id >= 0:
|
||||
screen.load([target_id])
|
||||
WinForm(screen, open_callback, save_current=True,
|
||||
title=field.attrs.get('string'))
|
||||
else:
|
||||
@@ -666,21 +775,21 @@ class M2O(GenericText):
|
||||
title=field.attrs.get('string'), rec_name=text)
|
||||
|
||||
def search_remote(self, record, field, text, callback=None):
|
||||
relation = field.attrs['relation']
|
||||
model = self.get_model(record, field)
|
||||
domain = field.domain_get(record)
|
||||
context = field.get_search_context(record)
|
||||
order = field.get_search_order(record)
|
||||
access = common.MODELACCESS[relation]
|
||||
access = common.MODELACCESS[model]
|
||||
create_access = self.attrs.get('create', True) and access['create']
|
||||
|
||||
def search_callback(found):
|
||||
value = None
|
||||
if found:
|
||||
value = found[0]
|
||||
field.set_client(record, value)
|
||||
value = self.value_from_id(model, *found[0])
|
||||
field.set_client(record, value)
|
||||
if callback:
|
||||
callback()
|
||||
win = WinSearch(relation, search_callback, sel_multi=False,
|
||||
win = WinSearch(model, search_callback, sel_multi=False,
|
||||
context=context, domain=domain,
|
||||
order=order, view_ids=self.attrs.get('view_ids', '').split(','),
|
||||
new=create_access, title=self.attrs.get('string'))
|
||||
@@ -688,14 +797,18 @@ class M2O(GenericText):
|
||||
return win
|
||||
|
||||
def set_completion(self, entry, path):
|
||||
record, field = self._get_record_field_from_path(path)
|
||||
if entry.get_completion():
|
||||
entry.set_completion(None)
|
||||
access = common.MODELACCESS[self.attrs['relation']]
|
||||
model = self.get_model(record, field)
|
||||
if not model:
|
||||
return
|
||||
access = common.MODELACCESS[model]
|
||||
completion = get_completion(
|
||||
search=access['read'],
|
||||
create=self.attrs.get('create', True) and access['create'])
|
||||
completion.connect('match-selected', self._completion_match_selected,
|
||||
path)
|
||||
path, model)
|
||||
completion.connect('action-activated',
|
||||
self._completion_action_activated, path)
|
||||
entry.set_completion(completion)
|
||||
@@ -703,18 +816,19 @@ class M2O(GenericText):
|
||||
entry.connect('changed', self._update_completion, path)
|
||||
|
||||
def _key_press(self, entry, event, path):
|
||||
record, field = self._get_record_field(path)
|
||||
if (field.get(record) is not None
|
||||
record, field = self._get_record_field_from_path(path)
|
||||
if (self.has_target(field.get(record))
|
||||
and event.keyval in (gtk.keysyms.Delete,
|
||||
gtk.keysyms.BackSpace)):
|
||||
entry.set_text('')
|
||||
field.set_client(record, None)
|
||||
return False
|
||||
|
||||
def _completion_match_selected(self, completion, model, iter_, path):
|
||||
record, field = self._get_record_field(path)
|
||||
def _completion_match_selected(
|
||||
self, completion, model, iter_, path, model_name):
|
||||
record, field = self._get_record_field_from_path(path)
|
||||
rec_name, record_id = model.get(iter_, 0, 1)
|
||||
field.set_client(record, (record_id, rec_name))
|
||||
field.set_client(
|
||||
record, self.value_from_id(model_name, record_id, rec_name))
|
||||
|
||||
completion.get_entry().set_text(rec_name)
|
||||
completion_model = completion.get_model()
|
||||
@@ -723,14 +837,17 @@ class M2O(GenericText):
|
||||
return True
|
||||
|
||||
def _update_completion(self, entry, path):
|
||||
record, field = self._get_record_field(path)
|
||||
if field.get(record) is not None:
|
||||
return
|
||||
model = field.attrs['relation']
|
||||
record, field = self._get_record_field_from_path(path)
|
||||
value = field.get(record)
|
||||
if self.has_target(value):
|
||||
id_ = self.id_from_value(value)
|
||||
if id_ is not None and id_ >= 0:
|
||||
return
|
||||
model = self.get_model(record, field)
|
||||
update_completion(entry, record, field, model)
|
||||
|
||||
def _completion_action_activated(self, completion, index, path):
|
||||
record, field = self._get_record_field(path)
|
||||
record, field = self._get_record_field_from_path(path)
|
||||
entry = completion.get_entry()
|
||||
entry.handler_block(entry.editing_done_id)
|
||||
|
||||
@@ -819,10 +936,13 @@ class Selection(GenericText, SelectionMixin, PopdownMixin):
|
||||
self.renderer.set_property('model', model)
|
||||
self.renderer.set_property('text-column', 0)
|
||||
|
||||
def get_value(self, record, field):
|
||||
return field.get(record)
|
||||
|
||||
def get_textual_value(self, record):
|
||||
field = record[self.attrs['name']]
|
||||
self.update_selection(record, field)
|
||||
value = field.get(record)
|
||||
value = self.get_value(record, field)
|
||||
text = dict(self.selection).get(value, '')
|
||||
if value and not text:
|
||||
text = self.get_inactive_selection(value)
|
||||
@@ -834,9 +954,7 @@ class Selection(GenericText, SelectionMixin, PopdownMixin):
|
||||
|
||||
def editing_started(self, cell, editable, path):
|
||||
super(Selection, self).editing_started(cell, editable, path)
|
||||
store = self.view.treeview.get_model()
|
||||
record = store.get_value(store.get_iter(path), 0)
|
||||
field = record[self.attrs['name']]
|
||||
record, field = self._get_record_field_from_path(path)
|
||||
|
||||
def set_value(*a):
|
||||
return self.set_value(editable, record, field)
|
||||
@@ -847,7 +965,7 @@ class Selection(GenericText, SelectionMixin, PopdownMixin):
|
||||
self.update_selection(record, field)
|
||||
self.set_popdown(self.selection, editable)
|
||||
|
||||
value = field.get(record)
|
||||
value = self.get_value(record, field)
|
||||
if not self.set_popdown_value(editable, value):
|
||||
self.get_inactive_selection(value)
|
||||
self.set_popdown_value(editable, value)
|
||||
@@ -861,28 +979,72 @@ class Selection(GenericText, SelectionMixin, PopdownMixin):
|
||||
return False
|
||||
|
||||
|
||||
class Reference(GenericText, SelectionMixin):
|
||||
class Reference(M2O):
|
||||
|
||||
def __init__(self, view, attrs, renderer=None):
|
||||
super(Reference, self).__init__(view, attrs, renderer=renderer)
|
||||
self.init_selection()
|
||||
self.renderer_selection = _ReferenceSelection(view, attrs)
|
||||
|
||||
@property
|
||||
def prefixes(self):
|
||||
return [self.renderer_selection]
|
||||
|
||||
def get_model(self, record, field):
|
||||
value = field.get_client(record)
|
||||
if value:
|
||||
model, value = value
|
||||
return model
|
||||
|
||||
def has_target(self, value):
|
||||
if not value:
|
||||
return False
|
||||
model, value = value.split(',')
|
||||
if not value:
|
||||
value = None
|
||||
else:
|
||||
try:
|
||||
value = int(value)
|
||||
except ValueError:
|
||||
value = None
|
||||
return model and value >= 0
|
||||
|
||||
def value_from_id(self, model, id_, str_=None):
|
||||
if str_ is None:
|
||||
str_ = ''
|
||||
return model, (id_, str_)
|
||||
|
||||
def id_from_value(self, value):
|
||||
_, value = value.split(',')
|
||||
return int(value)
|
||||
|
||||
def get_textual_value(self, record):
|
||||
field = record[self.attrs['name']]
|
||||
self.update_selection(record, field)
|
||||
value = field.get_client(record)
|
||||
if not value:
|
||||
model, name = '', ''
|
||||
value = super().get_textual_value(record)
|
||||
if value:
|
||||
model, value = value
|
||||
else:
|
||||
model, name = value
|
||||
if model:
|
||||
return dict(self.selection).get(model, model) + ',' + name
|
||||
else:
|
||||
return name
|
||||
value = ''
|
||||
return value
|
||||
|
||||
def value_from_text(self, record, text, callback=None):
|
||||
if callback:
|
||||
callback()
|
||||
|
||||
class _ReferenceSelection(Selection):
|
||||
|
||||
def get_value(self, record, field):
|
||||
value = field.get_client(record)
|
||||
if value:
|
||||
model, value = value
|
||||
else:
|
||||
model = None
|
||||
return model
|
||||
|
||||
def set_value(self, editable, record, field):
|
||||
value = self.get_popdown_value(editable)
|
||||
if value != self.get_value(record, field):
|
||||
if value:
|
||||
value = (value, (-1, ''))
|
||||
else:
|
||||
value = ('', '')
|
||||
field.set_client(record, value)
|
||||
return False
|
||||
|
||||
|
||||
class ProgressBar(object):
|
||||
@@ -909,9 +1071,8 @@ class ProgressBar(object):
|
||||
|
||||
@realized
|
||||
@CellCache.cache
|
||||
def setter(self, column, cell, store, iter):
|
||||
record = store.get_value(iter, 0)
|
||||
field = record[self.attrs['name']]
|
||||
def setter(self, column, cell, store, iter_):
|
||||
record, field = self._get_record_field_from_iter(iter_, store)
|
||||
field.state_set(record, states=('invisible',))
|
||||
invisible = field.get_state_attrs(record).get('invisible', False)
|
||||
cell.set_property('visible', not invisible)
|
||||
@@ -936,7 +1097,7 @@ class ProgressBar(object):
|
||||
callback()
|
||||
|
||||
|
||||
class Button(object):
|
||||
class Button(Cell):
|
||||
|
||||
def __init__(self, view, attrs):
|
||||
super(Button, self).__init__()
|
||||
@@ -949,8 +1110,8 @@ class Button(object):
|
||||
|
||||
@realized
|
||||
@CellCache.cache
|
||||
def setter(self, column, cell, store, iter):
|
||||
record = store.get_value(iter, 0)
|
||||
def setter(self, column, cell, store, iter_):
|
||||
record = store.get_value(iter_, 0)
|
||||
states = record.expr_eval(self.attrs.get('states', {}))
|
||||
if record.group.readonly or record.readonly:
|
||||
states['readonly'] = True
|
||||
|
||||
Reference in new issue
Block a user