tryton 7.0.21__py3-none-any.whl → 7.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of tryton might be problematic. Click here for more details.
- tryton/__init__.py +1 -1
- tryton/cache.py +34 -0
- tryton/common/common.py +125 -69
- tryton/common/completion.py +2 -2
- tryton/common/domain_inversion.py +1 -2
- tryton/common/domain_parser.py +7 -17
- tryton/common/selection.py +6 -3
- tryton/common/tempfile.py +34 -0
- tryton/config.py +3 -2
- tryton/data/locale/bg/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/bg/LC_MESSAGES/tryton.po +28 -3
- tryton/data/locale/ca/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ca/LC_MESSAGES/tryton.po +33 -5
- tryton/data/locale/cs/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/cs/LC_MESSAGES/tryton.po +28 -3
- tryton/data/locale/de/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/de/LC_MESSAGES/tryton.po +32 -4
- tryton/data/locale/es/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/es/LC_MESSAGES/tryton.po +32 -4
- tryton/data/locale/es_419/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/es_419/LC_MESSAGES/tryton.po +29 -4
- tryton/data/locale/et/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/et/LC_MESSAGES/tryton.po +32 -5
- tryton/data/locale/fa/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fa/LC_MESSAGES/tryton.po +32 -6
- tryton/data/locale/fi/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fi/LC_MESSAGES/tryton.po +28 -3
- tryton/data/locale/fr/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fr/LC_MESSAGES/tryton.po +32 -4
- tryton/data/locale/hu/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/hu/LC_MESSAGES/tryton.po +32 -5
- tryton/data/locale/id/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/id/LC_MESSAGES/tryton.po +30 -3
- tryton/data/locale/it/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/it/LC_MESSAGES/tryton.po +31 -5
- tryton/data/locale/ja_JP/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/lo/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/lo/LC_MESSAGES/tryton.po +31 -5
- tryton/data/locale/lt/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/lt/LC_MESSAGES/tryton.po +32 -5
- tryton/data/locale/nl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/nl/LC_MESSAGES/tryton.po +32 -4
- tryton/data/locale/pl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/pl/LC_MESSAGES/tryton.po +32 -5
- tryton/data/locale/pt/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/pt/LC_MESSAGES/tryton.po +31 -5
- tryton/data/locale/ro/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ro/LC_MESSAGES/tryton.po +43 -16
- tryton/data/locale/ru/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ru/LC_MESSAGES/tryton.po +29 -5
- tryton/data/locale/sl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/sl/LC_MESSAGES/tryton.po +32 -4
- tryton/data/locale/tr/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/tr/LC_MESSAGES/tryton.po +28 -3
- tryton/data/locale/uk/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/uk/LC_MESSAGES/tryton.po +32 -5
- tryton/data/locale/zh_CN/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/zh_CN/LC_MESSAGES/tryton.po +32 -4
- tryton/device_cookie.py +1 -1
- tryton/gui/main.py +3 -2
- tryton/gui/window/about.py +1 -1
- tryton/gui/window/dblogin.py +2 -2
- tryton/gui/window/email_.py +1 -1
- tryton/gui/window/form.py +4 -3
- tryton/gui/window/log.py +24 -2
- tryton/gui/window/view_form/model/field.py +56 -62
- tryton/gui/window/view_form/model/group.py +3 -1
- tryton/gui/window/view_form/model/record.py +55 -16
- tryton/gui/window/view_form/screen/screen.py +22 -22
- tryton/gui/window/view_form/view/calendar_gtk/calendar_.py +7 -12
- tryton/gui/window/view_form/view/form.py +4 -14
- tryton/gui/window/view_form/view/form_gtk/binary.py +3 -3
- tryton/gui/window/view_form/view/form_gtk/dictionary.py +33 -27
- tryton/gui/window/view_form/view/form_gtk/document.py +10 -9
- tryton/gui/window/view_form/view/form_gtk/many2many.py +17 -7
- tryton/gui/window/view_form/view/form_gtk/many2one.py +21 -13
- tryton/gui/window/view_form/view/form_gtk/one2many.py +25 -6
- tryton/gui/window/view_form/view/form_gtk/state_widget.py +6 -2
- tryton/gui/window/view_form/view/list.py +47 -56
- tryton/gui/window/view_form/view/list_gtk/widget.py +36 -23
- tryton/gui/window/view_form/view/screen_container.py +5 -3
- tryton/gui/window/win_export.py +1 -2
- tryton/gui/window/win_form.py +6 -8
- tryton/gui/window/wizard.py +11 -10
- tryton/jsonrpc.py +41 -27
- tryton/pyson.py +54 -4
- tryton/rpc.py +18 -0
- tryton/tests/test_common_domain_parser.py +0 -8
- {tryton-7.0.21.data → tryton-7.2.0.data}/scripts/tryton +1 -2
- {tryton-7.0.21.dist-info → tryton-7.2.0.dist-info}/METADATA +6 -20
- {tryton-7.0.21.dist-info → tryton-7.2.0.dist-info}/RECORD +94 -92
- {tryton-7.0.21.dist-info → tryton-7.2.0.dist-info}/WHEEL +1 -1
- {tryton-7.0.21.dist-info → tryton-7.2.0.dist-info}/LICENSE +0 -0
- {tryton-7.0.21.dist-info → tryton-7.2.0.dist-info}/top_level.txt +0 -0
|
@@ -37,7 +37,7 @@ class Many2One(Widget):
|
|
|
37
37
|
lambda x, y: self._focus_out())
|
|
38
38
|
self.wid_text.connect('changed', self.sig_changed)
|
|
39
39
|
self.changed = True
|
|
40
|
-
self.
|
|
40
|
+
self._popup = False
|
|
41
41
|
|
|
42
42
|
if int(self.attrs.get('completion', 1)):
|
|
43
43
|
self.wid_text.connect('changed', self._update_completion)
|
|
@@ -103,13 +103,16 @@ class Many2One(Widget):
|
|
|
103
103
|
model = self.get_model()
|
|
104
104
|
if not model or not common.MODELACCESS[model]['read']:
|
|
105
105
|
return
|
|
106
|
-
if not self.
|
|
106
|
+
if not self.field:
|
|
107
107
|
return
|
|
108
108
|
self.changed = False
|
|
109
109
|
value = self.field.get(self.record)
|
|
110
110
|
model = self.get_model()
|
|
111
111
|
|
|
112
|
-
self.
|
|
112
|
+
if self._popup:
|
|
113
|
+
return
|
|
114
|
+
else:
|
|
115
|
+
self._popup = True
|
|
113
116
|
if model and not self.has_target(value):
|
|
114
117
|
if (not self._readonly
|
|
115
118
|
and (self.wid_text.get_text()
|
|
@@ -126,7 +129,7 @@ class Many2One(Widget):
|
|
|
126
129
|
self.value_from_id(*result[0]), force_change=True)
|
|
127
130
|
else:
|
|
128
131
|
self.wid_text.set_text('')
|
|
129
|
-
self.
|
|
132
|
+
self._popup = False
|
|
130
133
|
self.changed = True
|
|
131
134
|
|
|
132
135
|
win = WinSearch(model, callback, sel_multi=False,
|
|
@@ -139,7 +142,7 @@ class Many2One(Widget):
|
|
|
139
142
|
win.screen.search_filter(quote(text))
|
|
140
143
|
win.show()
|
|
141
144
|
return
|
|
142
|
-
self.
|
|
145
|
+
self._popup = False
|
|
143
146
|
self.changed = True
|
|
144
147
|
return
|
|
145
148
|
|
|
@@ -164,7 +167,10 @@ class Many2One(Widget):
|
|
|
164
167
|
def sig_new(self, defaults=None):
|
|
165
168
|
if not self.create_access:
|
|
166
169
|
return
|
|
167
|
-
self.
|
|
170
|
+
if self._popup:
|
|
171
|
+
return
|
|
172
|
+
else:
|
|
173
|
+
self._popup = True
|
|
168
174
|
screen = self.get_screen(search=True)
|
|
169
175
|
defaults = defaults.copy() if defaults is not None else {}
|
|
170
176
|
defaults['rec_name'] = self.wid_text.get_text()
|
|
@@ -174,7 +180,7 @@ class Many2One(Widget):
|
|
|
174
180
|
self.field.set_client(self.record,
|
|
175
181
|
self.value_from_id(screen.current_record.id,
|
|
176
182
|
screen.current_record.rec_name()))
|
|
177
|
-
self.
|
|
183
|
+
self._popup = False
|
|
178
184
|
WinForm(
|
|
179
185
|
screen, callback, new=True, save_current=True, defaults=defaults)
|
|
180
186
|
|
|
@@ -184,10 +190,9 @@ class Many2One(Widget):
|
|
|
184
190
|
model = self.get_model()
|
|
185
191
|
if not model or not common.MODELACCESS[model]['read']:
|
|
186
192
|
return
|
|
187
|
-
if not self.
|
|
193
|
+
if not self.field:
|
|
188
194
|
return
|
|
189
195
|
self.changed = False
|
|
190
|
-
self.focus_out = False
|
|
191
196
|
value = self.field.get(self.record)
|
|
192
197
|
|
|
193
198
|
if (icon_pos == Gtk.EntryIconPosition.SECONDARY
|
|
@@ -196,9 +201,12 @@ class Many2One(Widget):
|
|
|
196
201
|
self.field.set_client(self.record, self.value_from_id(None, ''))
|
|
197
202
|
self.wid_text.set_text('')
|
|
198
203
|
self.changed = True
|
|
199
|
-
self.focus_out = True
|
|
200
204
|
return
|
|
201
205
|
|
|
206
|
+
if self._popup:
|
|
207
|
+
return
|
|
208
|
+
else:
|
|
209
|
+
self._popup = True
|
|
202
210
|
if self.has_target(value):
|
|
203
211
|
m2o_id = self.id_from_value(self.field.get(self.record))
|
|
204
212
|
screen = self.get_screen()
|
|
@@ -211,7 +219,7 @@ class Many2One(Widget):
|
|
|
211
219
|
self.value_from_id(screen.current_record.id,
|
|
212
220
|
screen.current_record.rec_name()),
|
|
213
221
|
force_change=True)
|
|
214
|
-
self.
|
|
222
|
+
self._popup = False
|
|
215
223
|
self.changed = True
|
|
216
224
|
WinForm(screen, callback, save_current=True)
|
|
217
225
|
return
|
|
@@ -225,7 +233,7 @@ class Many2One(Widget):
|
|
|
225
233
|
if result:
|
|
226
234
|
self.field.set_client(self.record,
|
|
227
235
|
self.value_from_id(*result[0]), force_change=True)
|
|
228
|
-
self.
|
|
236
|
+
self._popup = False
|
|
229
237
|
self.changed = True
|
|
230
238
|
win = WinSearch(model, callback, sel_multi=False,
|
|
231
239
|
context=context, domain=domain, order=order,
|
|
@@ -236,7 +244,7 @@ class Many2One(Widget):
|
|
|
236
244
|
win.screen.search_filter(quote(text))
|
|
237
245
|
win.show()
|
|
238
246
|
return
|
|
239
|
-
self.
|
|
247
|
+
self._popup = False
|
|
240
248
|
self.changed = True
|
|
241
249
|
|
|
242
250
|
def sig_key_press(self, widget, event, *args):
|
|
@@ -75,7 +75,6 @@ class One2Many(Widget):
|
|
|
75
75
|
|
|
76
76
|
hbox.pack_start(Gtk.VSeparator(), expand=False, fill=True, padding=0)
|
|
77
77
|
|
|
78
|
-
self.focus_out = True
|
|
79
78
|
self.wid_completion = None
|
|
80
79
|
if attrs.get('add_remove'):
|
|
81
80
|
|
|
@@ -182,6 +181,8 @@ class One2Many(Widget):
|
|
|
182
181
|
if self.attrs.get('add_remove'):
|
|
183
182
|
self.wid_text.connect('key_press_event', self.on_keypress)
|
|
184
183
|
|
|
184
|
+
self._popup = False
|
|
185
|
+
|
|
185
186
|
def get_access(self, type_):
|
|
186
187
|
model = self.attrs['relation']
|
|
187
188
|
if model:
|
|
@@ -370,12 +371,17 @@ class One2Many(Widget):
|
|
|
370
371
|
self._new_single(defaults)
|
|
371
372
|
|
|
372
373
|
def _new_single(self, defaults=None):
|
|
374
|
+
if self._popup:
|
|
375
|
+
return
|
|
376
|
+
else:
|
|
377
|
+
self._popup = True
|
|
373
378
|
sequence = self._sequence()
|
|
374
379
|
|
|
375
380
|
def update_sequence():
|
|
376
381
|
if sequence:
|
|
377
382
|
self.screen.group.set_sequence(
|
|
378
383
|
field=sequence, position=self.screen.new_position)
|
|
384
|
+
self._popup = False
|
|
379
385
|
|
|
380
386
|
if self.screen.current_view.creatable:
|
|
381
387
|
self.screen.new()
|
|
@@ -392,6 +398,10 @@ class One2Many(Widget):
|
|
|
392
398
|
fields = self.attrs['product'].split(',')
|
|
393
399
|
product = {}
|
|
394
400
|
|
|
401
|
+
if self._popup:
|
|
402
|
+
return
|
|
403
|
+
else:
|
|
404
|
+
self._popup = True
|
|
395
405
|
first = self.screen.new(default=False)
|
|
396
406
|
default = first.default_get(defaults=defaults)
|
|
397
407
|
first.set_default(default)
|
|
@@ -420,6 +430,7 @@ class One2Many(Widget):
|
|
|
420
430
|
win_search.show()
|
|
421
431
|
|
|
422
432
|
def make_product():
|
|
433
|
+
self._popup = False
|
|
423
434
|
self.screen.group.remove(first, remove=True)
|
|
424
435
|
if not product:
|
|
425
436
|
return
|
|
@@ -448,7 +459,14 @@ class One2Many(Widget):
|
|
|
448
459
|
return
|
|
449
460
|
record = self.screen.current_record
|
|
450
461
|
if record:
|
|
451
|
-
|
|
462
|
+
if self._popup:
|
|
463
|
+
return
|
|
464
|
+
else:
|
|
465
|
+
self._popup = True
|
|
466
|
+
|
|
467
|
+
def callback(result):
|
|
468
|
+
self._popup = False
|
|
469
|
+
WinForm(self.screen, callback)
|
|
452
470
|
|
|
453
471
|
def _sig_next(self, widget):
|
|
454
472
|
if not self._validate():
|
|
@@ -475,8 +493,6 @@ class One2Many(Widget):
|
|
|
475
493
|
self.screen.unremove()
|
|
476
494
|
|
|
477
495
|
def _sig_add(self, *args):
|
|
478
|
-
if not self.focus_out:
|
|
479
|
-
return
|
|
480
496
|
if not self.write_access or not self.read_access:
|
|
481
497
|
return
|
|
482
498
|
self.view.set_value()
|
|
@@ -487,12 +503,14 @@ class One2Many(Widget):
|
|
|
487
503
|
domain = ['OR', domain, ('id', 'in', removed_ids)]
|
|
488
504
|
text = self.wid_text.get_text()
|
|
489
505
|
|
|
490
|
-
self.
|
|
506
|
+
if self._popup:
|
|
507
|
+
return
|
|
508
|
+
else:
|
|
509
|
+
self._popup = True
|
|
491
510
|
|
|
492
511
|
sequence = self._sequence()
|
|
493
512
|
|
|
494
513
|
def callback(result):
|
|
495
|
-
self.focus_out = True
|
|
496
514
|
if result:
|
|
497
515
|
ids = [x[0] for x in result]
|
|
498
516
|
self.screen.load(ids, modified=True)
|
|
@@ -501,6 +519,7 @@ class One2Many(Widget):
|
|
|
501
519
|
field=sequence, position=self.screen.new_position)
|
|
502
520
|
self.screen.set_cursor()
|
|
503
521
|
self.wid_text.set_text('')
|
|
522
|
+
self._popup = False
|
|
504
523
|
|
|
505
524
|
order = self.field.get_search_order(self.record)
|
|
506
525
|
win = WinSearch(self.attrs['relation'], callback, sel_multi=True,
|
|
@@ -50,6 +50,8 @@ class Label(StateMixin, Gtk.Label):
|
|
|
50
50
|
readonly = ((field and field.attrs.get('readonly'))
|
|
51
51
|
or state_changes.get('readonly', not bool(field)))
|
|
52
52
|
common.apply_label_attributes(self, readonly, required)
|
|
53
|
+
self.set_max_width_chars(80)
|
|
54
|
+
self.set_line_wrap(True)
|
|
53
55
|
|
|
54
56
|
|
|
55
57
|
class VBox(StateMixin, Gtk.VBox):
|
|
@@ -187,14 +189,16 @@ class Link(StateMixin, Gtk.Button):
|
|
|
187
189
|
['AND', domain, tab_domain], 0, 100, context=context,
|
|
188
190
|
callback=functools.partial(
|
|
189
191
|
self._set_count, idx=i, current=self._current,
|
|
190
|
-
counter=counter, label=label)
|
|
192
|
+
counter=counter, label=label),
|
|
193
|
+
process_exception=False)
|
|
191
194
|
else:
|
|
192
195
|
common.RPCExecute(
|
|
193
196
|
'model', action['res_model'], 'search_count',
|
|
194
197
|
domain, 0, 100, context=context,
|
|
195
198
|
callback=functools.partial(
|
|
196
199
|
self._set_count, current=self._current,
|
|
197
|
-
counter=counter, label=label)
|
|
200
|
+
counter=counter, label=label),
|
|
201
|
+
process_exception=False)
|
|
198
202
|
|
|
199
203
|
def _set_count(self, value, idx=0, current=None, counter=None, label=''):
|
|
200
204
|
if current != self._current or not self.get_parent():
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
2
2
|
# this repository contains the full copyright notices and license terms.
|
|
3
|
+
import csv
|
|
3
4
|
import gettext
|
|
4
5
|
import json
|
|
5
6
|
import locale
|
|
6
7
|
import sys
|
|
7
8
|
from functools import wraps
|
|
9
|
+
from io import StringIO
|
|
8
10
|
|
|
9
11
|
from gi.repository import Gdk, GLib, GObject, Gtk
|
|
10
12
|
from pygtkcompat.generictreemodel import GenericTreeModel
|
|
@@ -333,7 +335,7 @@ class TreeXMLViewParser(XMLViewParser):
|
|
|
333
335
|
|
|
334
336
|
self.view.treeview.append_column(column)
|
|
335
337
|
|
|
336
|
-
if 'optional' in attributes
|
|
338
|
+
if 'optional' in attributes:
|
|
337
339
|
self.view.optionals.append(column)
|
|
338
340
|
|
|
339
341
|
def _parse_button(self, node, attributes):
|
|
@@ -720,48 +722,42 @@ class ViewTree(View):
|
|
|
720
722
|
record.cancel()
|
|
721
723
|
iter_ = model.iter_next(iter_)
|
|
722
724
|
|
|
723
|
-
def on_copy(self):
|
|
725
|
+
def on_copy(self, columns=None):
|
|
726
|
+
if columns is None:
|
|
727
|
+
columns = self.treeview.get_columns()
|
|
728
|
+
|
|
729
|
+
def copy_foreach(treemodel, path, iter, add):
|
|
730
|
+
record = treemodel.get_value(iter, 0)
|
|
731
|
+
values = []
|
|
732
|
+
for col in columns:
|
|
733
|
+
if not col.get_visible() or not col.name:
|
|
734
|
+
continue
|
|
735
|
+
widget = self.get_column_widget(col)
|
|
736
|
+
values.append(widget.get_textual_value(record))
|
|
737
|
+
add(values)
|
|
738
|
+
|
|
724
739
|
for clipboard_type in [
|
|
725
740
|
Gdk.SELECTION_CLIPBOARD, Gdk.SELECTION_PRIMARY]:
|
|
726
741
|
clipboard = self.treeview.get_clipboard(clipboard_type)
|
|
727
742
|
selection = self.treeview.get_selection()
|
|
728
|
-
data =
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
def copy_foreach(self, treemodel, path, iter, data):
|
|
733
|
-
record = treemodel.get_value(iter, 0)
|
|
734
|
-
values = []
|
|
735
|
-
for col in self.treeview.get_columns():
|
|
736
|
-
if not col.get_visible() or not col.name:
|
|
737
|
-
continue
|
|
738
|
-
widget = self.get_column_widget(col)
|
|
739
|
-
values.append('"'
|
|
740
|
-
+ str(widget.get_textual_value(record)).replace('"', '""')
|
|
741
|
-
+ '"')
|
|
742
|
-
data.append('\t'.join(values))
|
|
743
|
-
return
|
|
743
|
+
data = StringIO()
|
|
744
|
+
writer = csv.writer(data, delimiter='\t', lineterminator='\n')
|
|
745
|
+
selection.selected_foreach(copy_foreach, writer.writerow)
|
|
746
|
+
clipboard.set_text(data.getvalue(), -1)
|
|
744
747
|
|
|
745
748
|
def on_paste(self):
|
|
746
749
|
if not self.editable:
|
|
747
750
|
return
|
|
748
751
|
|
|
749
|
-
|
|
750
|
-
if value[:1] == '"' and value[-1:] == '"':
|
|
751
|
-
return value[1:-1]
|
|
752
|
-
return value
|
|
753
|
-
data = []
|
|
752
|
+
reader = None
|
|
754
753
|
for clipboard_type in [
|
|
755
754
|
Gdk.SELECTION_CLIPBOARD, Gdk.SELECTION_PRIMARY]:
|
|
756
755
|
clipboard = self.treeview.get_clipboard(clipboard_type)
|
|
757
756
|
text = clipboard.wait_for_text()
|
|
758
757
|
if not text:
|
|
759
758
|
continue
|
|
760
|
-
|
|
761
|
-
for l in text.splitlines()]
|
|
759
|
+
reader = csv.reader(StringIO(text), delimiter='\t')
|
|
762
760
|
break
|
|
763
|
-
else:
|
|
764
|
-
return
|
|
765
761
|
col = self.treeview.get_cursor()[1]
|
|
766
762
|
columns = [c for c in self.treeview.get_columns()
|
|
767
763
|
if c.get_visible() and c.name]
|
|
@@ -776,7 +772,7 @@ class ViewTree(View):
|
|
|
776
772
|
group = self.group
|
|
777
773
|
idx = len(group)
|
|
778
774
|
default = None
|
|
779
|
-
for line in
|
|
775
|
+
for line in reader:
|
|
780
776
|
if idx >= len(group):
|
|
781
777
|
record = group.new(default=False)
|
|
782
778
|
if default is None:
|
|
@@ -787,25 +783,13 @@ class ViewTree(View):
|
|
|
787
783
|
for col, value in zip(columns, line):
|
|
788
784
|
widget = self.get_column_widget(col)
|
|
789
785
|
if widget.get_textual_value(record) != value:
|
|
790
|
-
|
|
791
|
-
field = record.group.fields[widget.attrs['name']]
|
|
792
|
-
win = widget.search_remote(record, field, value)
|
|
793
|
-
if len(win.screen.group) == 1:
|
|
794
|
-
target, = win.screen.group
|
|
795
|
-
field.set_client(
|
|
796
|
-
record, (target.id, target.rec_name()))
|
|
797
|
-
else:
|
|
798
|
-
field.set_client(record, (None, ''))
|
|
799
|
-
win.response(win, Gtk.ResponseType.CANCEL)
|
|
800
|
-
else:
|
|
801
|
-
widget.value_from_text(record, value)
|
|
786
|
+
widget.value_from_text(record, value)
|
|
802
787
|
if value and not widget.get_textual_value(record):
|
|
803
788
|
# Stop setting value if a value is correctly set
|
|
804
789
|
idx = len(group)
|
|
805
790
|
break
|
|
806
791
|
if not record.validate():
|
|
807
792
|
break
|
|
808
|
-
record.save()
|
|
809
793
|
idx += 1
|
|
810
794
|
self.record = record
|
|
811
795
|
self.screen.display(set_cursor=True)
|
|
@@ -914,12 +898,27 @@ class ViewTree(View):
|
|
|
914
898
|
except TypeError:
|
|
915
899
|
# Outside row
|
|
916
900
|
return False
|
|
901
|
+
selection = treeview.get_selection()
|
|
917
902
|
menu = Gtk.Menu()
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
903
|
+
if selection.count_selected_rows():
|
|
904
|
+
if selection.count_selected_rows() == 1:
|
|
905
|
+
copy_item = Gtk.MenuItem(label=_("Copy"))
|
|
906
|
+
copy_item.connect(
|
|
907
|
+
'activate', lambda x: self.on_copy([col]))
|
|
908
|
+
menu.append(copy_item)
|
|
909
|
+
copy_row_label = _("Copy Row")
|
|
910
|
+
else:
|
|
911
|
+
copy_row_label = _("Copy Rows")
|
|
912
|
+
if col:
|
|
913
|
+
copy_column_item = Gtk.MenuItem(label=_("Copy Column"))
|
|
914
|
+
copy_column_item.connect(
|
|
915
|
+
'activate', lambda x: self.on_copy(columns=[col]))
|
|
916
|
+
menu.append(copy_column_item)
|
|
917
|
+
copy_row_item = Gtk.MenuItem(label=copy_row_label)
|
|
918
|
+
copy_row_item.connect('activate', lambda x: self.on_copy())
|
|
919
|
+
menu.append(copy_row_item)
|
|
921
920
|
if self.editable:
|
|
922
|
-
paste_item = Gtk.MenuItem(label=_(
|
|
921
|
+
paste_item = Gtk.MenuItem(label=_("Paste Rows"))
|
|
923
922
|
paste_item.connect('activate', lambda x: self.on_paste())
|
|
924
923
|
menu.append(paste_item)
|
|
925
924
|
|
|
@@ -953,7 +952,6 @@ class ViewTree(View):
|
|
|
953
952
|
menu, model, record_id, title=label, field=field,
|
|
954
953
|
context=context)
|
|
955
954
|
|
|
956
|
-
selection = treeview.get_selection()
|
|
957
955
|
if selection.count_selected_rows() == 1:
|
|
958
956
|
group = self.group
|
|
959
957
|
if selection.get_mode() == Gtk.SelectionMode.SINGLE:
|
|
@@ -1055,12 +1053,9 @@ class ViewTree(View):
|
|
|
1055
1053
|
elif tree_sel.get_mode() == Gtk.SelectionMode.MULTIPLE:
|
|
1056
1054
|
model, paths = tree_sel.get_selected_rows()
|
|
1057
1055
|
if model and paths:
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
records.append(model.get_value(iter_, 0))
|
|
1062
|
-
if self.record not in records:
|
|
1063
|
-
self.record = records[0]
|
|
1056
|
+
iter_ = model.get_iter(paths[0])
|
|
1057
|
+
record = model.get_value(iter_, 0)
|
|
1058
|
+
self.record = record
|
|
1064
1059
|
else:
|
|
1065
1060
|
self.record = None
|
|
1066
1061
|
|
|
@@ -1100,10 +1095,6 @@ class ViewTree(View):
|
|
|
1100
1095
|
def display(self, force=False):
|
|
1101
1096
|
self.treeview.display_counter += 1
|
|
1102
1097
|
current_record = self.record
|
|
1103
|
-
if current_record and current_record not in current_record.group:
|
|
1104
|
-
# current record may have been removed by on_change calls without
|
|
1105
|
-
# changing the current record of screen before the display
|
|
1106
|
-
current_record = None
|
|
1107
1098
|
if (force
|
|
1108
1099
|
or not self.treeview.get_model()
|
|
1109
1100
|
or self.group != self.treeview.get_model().group):
|
|
@@ -215,8 +215,12 @@ class Affix(Cell):
|
|
|
215
215
|
else:
|
|
216
216
|
value = self.icon
|
|
217
217
|
if self.attrs.get('icon_type') == 'url':
|
|
218
|
+
def callback(pixbuf):
|
|
219
|
+
self.display_counters.pop(record.id, None)
|
|
220
|
+
self.view.treeview.queue_resize() # trigger a redraw
|
|
218
221
|
pixbuf = common.IconFactory.get_pixbuf_url(
|
|
219
|
-
value, size_param=self.attrs.get('url_size')
|
|
222
|
+
value, size_param=self.attrs.get('url_size'),
|
|
223
|
+
callback=callback)
|
|
220
224
|
else:
|
|
221
225
|
pixbuf = common.IconFactory.get_pixbuf(
|
|
222
226
|
value, Gtk.IconSize.BUTTON)
|
|
@@ -410,19 +414,19 @@ class Int(GenericText):
|
|
|
410
414
|
self.symbol = attrs.get('symbol')
|
|
411
415
|
self.grouping = bool(int(attrs.get('grouping', 1)))
|
|
412
416
|
if self.symbol:
|
|
413
|
-
self.
|
|
414
|
-
self.
|
|
417
|
+
self._cell_prefix = Symbol(view, attrs, 0)
|
|
418
|
+
self._cell_suffix = Symbol(view, attrs, 1)
|
|
415
419
|
|
|
416
420
|
@property
|
|
417
421
|
def prefixes(self):
|
|
418
422
|
if self.symbol:
|
|
419
|
-
return [self.
|
|
423
|
+
return [self._cell_prefix]
|
|
420
424
|
return []
|
|
421
425
|
|
|
422
426
|
@property
|
|
423
427
|
def suffixes(self):
|
|
424
428
|
if self.symbol:
|
|
425
|
-
return [self.
|
|
429
|
+
return [self._cell_suffix]
|
|
426
430
|
return []
|
|
427
431
|
|
|
428
432
|
@catch_errors()
|
|
@@ -574,20 +578,20 @@ class Binary(GenericText):
|
|
|
574
578
|
super(Binary, self).__init__(view, attrs, renderer=renderer)
|
|
575
579
|
self.renderer.set_property('editable', False)
|
|
576
580
|
self.renderer.set_property('xalign', self.align)
|
|
577
|
-
self.
|
|
578
|
-
self.
|
|
581
|
+
self._cell_save = _BinarySave(self)
|
|
582
|
+
self._cell_select = _BinarySelect(self)
|
|
579
583
|
if self.attrs.get('filename'):
|
|
580
|
-
self.
|
|
584
|
+
self._cell_open = _BinaryOpen(self)
|
|
581
585
|
else:
|
|
582
|
-
self.
|
|
586
|
+
self._cell_open = None
|
|
583
587
|
|
|
584
588
|
@property
|
|
585
589
|
def prefixes(self):
|
|
586
|
-
return filter(None, [self.
|
|
590
|
+
return filter(None, [self._cell_open])
|
|
587
591
|
|
|
588
592
|
@property
|
|
589
593
|
def suffixes(self):
|
|
590
|
-
return [self.
|
|
594
|
+
return [self._cell_save, self._cell_select]
|
|
591
595
|
|
|
592
596
|
@catch_errors()
|
|
593
597
|
def get_textual_value(self, record):
|
|
@@ -738,7 +742,7 @@ class _BinarySelect(_BinaryIcon):
|
|
|
738
742
|
invisible = field.get_state_attrs(record).get('invisible', False)
|
|
739
743
|
readonly = self.attrs.get('readonly',
|
|
740
744
|
field.get_state_attrs(record).get('readonly', False))
|
|
741
|
-
if readonly
|
|
745
|
+
if readonly and size:
|
|
742
746
|
cell.set_property('visible', False)
|
|
743
747
|
else:
|
|
744
748
|
cell.set_property('visible', not invisible)
|
|
@@ -822,6 +826,7 @@ class M2O(GenericText):
|
|
|
822
826
|
if renderer is None and int(attrs.get('completion', 1)):
|
|
823
827
|
renderer = partial(CellRendererTextCompletion, self.set_completion)
|
|
824
828
|
super(M2O, self).__init__(view, attrs, renderer=renderer)
|
|
829
|
+
self._popup = False
|
|
825
830
|
|
|
826
831
|
def get_model(self, record, field):
|
|
827
832
|
return self.attrs['relation']
|
|
@@ -850,7 +855,8 @@ class M2O(GenericText):
|
|
|
850
855
|
if model and common.get_toplevel_window().get_focus():
|
|
851
856
|
field = record[self.attrs['name']]
|
|
852
857
|
win = self.search_remote(record, field, text, callback=callback)
|
|
853
|
-
win
|
|
858
|
+
if win:
|
|
859
|
+
win.show()
|
|
854
860
|
|
|
855
861
|
def editing_started(self, cell, editable, path):
|
|
856
862
|
super(M2O, self).editing_started(cell, editable, path)
|
|
@@ -919,7 +925,9 @@ class M2O(GenericText):
|
|
|
919
925
|
domain = field.domain_get(record)
|
|
920
926
|
context = field.get_context(record)
|
|
921
927
|
if not create and changed:
|
|
922
|
-
self.search_remote(record, field, text, callback=callback)
|
|
928
|
+
win = self.search_remote(record, field, text, callback=callback)
|
|
929
|
+
if win:
|
|
930
|
+
win.show()
|
|
923
931
|
return
|
|
924
932
|
target_id = self.id_from_value(field.get(record))
|
|
925
933
|
|
|
@@ -959,6 +967,11 @@ class M2O(GenericText):
|
|
|
959
967
|
access = common.MODELACCESS[model]
|
|
960
968
|
create_access = int(self.attrs.get('create', 1)) and access['create']
|
|
961
969
|
|
|
970
|
+
if self._popup:
|
|
971
|
+
return
|
|
972
|
+
else:
|
|
973
|
+
self._popup = True
|
|
974
|
+
|
|
962
975
|
def search_callback(found):
|
|
963
976
|
value = None
|
|
964
977
|
if found:
|
|
@@ -966,6 +979,7 @@ class M2O(GenericText):
|
|
|
966
979
|
field.set_client(record, value)
|
|
967
980
|
if callback:
|
|
968
981
|
callback()
|
|
982
|
+
self._popup = False
|
|
969
983
|
win = WinSearch(model, search_callback, sel_multi=False,
|
|
970
984
|
context=context, domain=domain,
|
|
971
985
|
order=order, view_ids=self.attrs.get('view_ids', '').split(','),
|
|
@@ -1253,11 +1267,11 @@ class Reference(M2O):
|
|
|
1253
1267
|
|
|
1254
1268
|
def __init__(self, view, attrs, renderer=None):
|
|
1255
1269
|
super(Reference, self).__init__(view, attrs, renderer=renderer)
|
|
1256
|
-
self.
|
|
1270
|
+
self._cell_selection = _ReferenceSelection(view, attrs)
|
|
1257
1271
|
|
|
1258
1272
|
@property
|
|
1259
1273
|
def prefixes(self):
|
|
1260
|
-
return [self.
|
|
1274
|
+
return [self._cell_selection]
|
|
1261
1275
|
|
|
1262
1276
|
def get_model(self, record, field):
|
|
1263
1277
|
value = field.get_client(record)
|
|
@@ -1405,13 +1419,12 @@ class Button(Cell):
|
|
|
1405
1419
|
cell.set_property('visible', not invisible)
|
|
1406
1420
|
readonly = states.get('readonly', False)
|
|
1407
1421
|
cell.set_property('sensitive', not readonly)
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
parent = parent.parent
|
|
1422
|
+
parent = record.parent if record else None
|
|
1423
|
+
while parent:
|
|
1424
|
+
if parent.modified:
|
|
1425
|
+
cell.set_property('sensitive', False)
|
|
1426
|
+
break
|
|
1427
|
+
parent = parent.parent
|
|
1415
1428
|
# TODO icon
|
|
1416
1429
|
self._set_visual(cell, record)
|
|
1417
1430
|
|
|
@@ -185,8 +185,7 @@ class Selection(Gtk.ScrolledWindow):
|
|
|
185
185
|
|
|
186
186
|
class ScreenContainer(object):
|
|
187
187
|
|
|
188
|
-
def __init__(self,
|
|
189
|
-
self.screen = screen
|
|
188
|
+
def __init__(self, tab_domain):
|
|
190
189
|
self.viewport = Gtk.Viewport()
|
|
191
190
|
self.viewport.set_shadow_type(Gtk.ShadowType.NONE)
|
|
192
191
|
self.vbox = Gtk.VBox(spacing=3)
|
|
@@ -352,9 +351,12 @@ class ScreenContainer(object):
|
|
|
352
351
|
def widget_get(self):
|
|
353
352
|
return self.vbox
|
|
354
353
|
|
|
355
|
-
def
|
|
354
|
+
def set_screen(self, screen):
|
|
355
|
+
self.screen = screen
|
|
356
356
|
self.but_bookmark.set_sensitive(bool(list(self.bookmarks())))
|
|
357
357
|
self.bookmark_match()
|
|
358
|
+
|
|
359
|
+
def show_filter(self):
|
|
358
360
|
if self.filter_vbox:
|
|
359
361
|
self.filter_vbox.show()
|
|
360
362
|
if self.notebook:
|
tryton/gui/window/win_export.py
CHANGED
|
@@ -7,7 +7,6 @@ import gettext
|
|
|
7
7
|
import json
|
|
8
8
|
import locale
|
|
9
9
|
import os
|
|
10
|
-
import tempfile
|
|
11
10
|
import urllib.parse
|
|
12
11
|
from itertools import zip_longest
|
|
13
12
|
from numbers import Number
|
|
@@ -15,7 +14,7 @@ from numbers import Number
|
|
|
15
14
|
from gi.repository import Gdk, GObject, Gtk
|
|
16
15
|
|
|
17
16
|
import tryton.common as common
|
|
18
|
-
from tryton.common import RPCException, RPCExecute
|
|
17
|
+
from tryton.common import RPCException, RPCExecute, tempfile
|
|
19
18
|
from tryton.config import CONFIG
|
|
20
19
|
from tryton.gui.window.win_csv import WinCSV
|
|
21
20
|
from tryton.jsonrpc import JSONEncoder
|
tryton/gui/window/win_form.py
CHANGED
|
@@ -69,7 +69,7 @@ class WinForm(NoModal, InfoBar):
|
|
|
69
69
|
self.accel_group = Gtk.AccelGroup()
|
|
70
70
|
self.win.add_accel_group(self.accel_group)
|
|
71
71
|
|
|
72
|
-
readonly = self.screen.group.readonly
|
|
72
|
+
readonly = self.screen.readonly or self.screen.group.readonly
|
|
73
73
|
|
|
74
74
|
self.but_ok = None
|
|
75
75
|
self.but_new = None
|
|
@@ -360,7 +360,7 @@ class WinForm(NoModal, InfoBar):
|
|
|
360
360
|
deletable = True
|
|
361
361
|
if self.screen.current_record:
|
|
362
362
|
deletable = self.screen.current_record.deletable
|
|
363
|
-
readonly = self.screen.group.readonly
|
|
363
|
+
readonly = self.screen.readonly or self.screen.group.readonly
|
|
364
364
|
if position >= 1:
|
|
365
365
|
name = str(position)
|
|
366
366
|
if self.domain is not None:
|
|
@@ -373,11 +373,9 @@ class WinForm(NoModal, InfoBar):
|
|
|
373
373
|
self.but_pre.set_sensitive(True)
|
|
374
374
|
else:
|
|
375
375
|
self.but_pre.set_sensitive(False)
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
and deletable))
|
|
380
|
-
self.but_undel.set_sensitive(bool(not readonly))
|
|
376
|
+
if access['delete'] and not readonly and deletable:
|
|
377
|
+
self.but_del.set_sensitive(True)
|
|
378
|
+
self.but_undel.set_sensitive(True)
|
|
381
379
|
else:
|
|
382
380
|
self.but_del.set_sensitive(False)
|
|
383
381
|
self.but_undel.set_sensitive(False)
|
|
@@ -401,7 +399,7 @@ class WinForm(NoModal, InfoBar):
|
|
|
401
399
|
cancel_responses = [
|
|
402
400
|
Gtk.ResponseType.CANCEL, Gtk.ResponseType.DELETE_EVENT]
|
|
403
401
|
self.screen.current_view.set_value()
|
|
404
|
-
readonly = self.screen.group.readonly
|
|
402
|
+
readonly = self.screen.readonly or self.screen.group.readonly
|
|
405
403
|
if (response_id not in cancel_responses
|
|
406
404
|
and not readonly
|
|
407
405
|
and self.screen.current_record is not None):
|