tryton 7.2.13__py3-none-any.whl → 7.4.4__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/common/common.py +12 -4
- tryton/common/domain_parser.py +5 -4
- tryton/common/popup_menu.py +1 -1
- tryton/data/locale/bg/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/bg/LC_MESSAGES/tryton.po +27 -16
- tryton/data/locale/ca/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ca/LC_MESSAGES/tryton.po +23 -17
- tryton/data/locale/cs/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/cs/LC_MESSAGES/tryton.po +26 -17
- tryton/data/locale/de/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/de/LC_MESSAGES/tryton.po +27 -21
- tryton/data/locale/es/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/es/LC_MESSAGES/tryton.po +22 -16
- tryton/data/locale/es_419/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/es_419/LC_MESSAGES/tryton.po +31 -19
- tryton/data/locale/et/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/et/LC_MESSAGES/tryton.po +27 -17
- tryton/data/locale/fa/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fa/LC_MESSAGES/tryton.po +28 -18
- tryton/data/locale/fi/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fi/LC_MESSAGES/tryton.po +22 -16
- tryton/data/locale/fr/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fr/LC_MESSAGES/tryton.po +28 -22
- tryton/data/locale/hu/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/hu/LC_MESSAGES/tryton.po +29 -17
- tryton/data/locale/id/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/id/LC_MESSAGES/tryton.po +31 -21
- tryton/data/locale/it/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/it/LC_MESSAGES/tryton.po +30 -20
- 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 +29 -19
- tryton/data/locale/lt/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/lt/LC_MESSAGES/tryton.po +27 -17
- tryton/data/locale/nl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/nl/LC_MESSAGES/tryton.po +26 -20
- tryton/data/locale/pl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/pl/LC_MESSAGES/tryton.po +29 -18
- tryton/data/locale/pt/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/pt/LC_MESSAGES/tryton.po +28 -18
- tryton/data/locale/ro/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ro/LC_MESSAGES/tryton.po +37 -26
- tryton/data/locale/ru/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ru/LC_MESSAGES/tryton.po +29 -19
- tryton/data/locale/sl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/sl/LC_MESSAGES/tryton.po +45 -35
- tryton/data/locale/tr/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/tr/LC_MESSAGES/tryton.po +23 -16
- tryton/data/locale/uk/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/uk/LC_MESSAGES/tryton.po +31 -19
- tryton/data/locale/zh_CN/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/zh_CN/LC_MESSAGES/tryton.po +32 -21
- tryton/gui/main.py +11 -10
- tryton/gui/window/email_.py +1 -1
- tryton/gui/window/form.py +4 -1
- tryton/gui/window/tabcontent.py +2 -2
- tryton/gui/window/view_form/model/group.py +4 -1
- tryton/gui/window/view_form/model/record.py +9 -19
- tryton/gui/window/view_form/screen/screen.py +15 -1
- tryton/gui/window/view_form/view/form_gtk/dictionary.py +12 -5
- tryton/gui/window/view_form/view/form_gtk/document.py +6 -0
- tryton/gui/window/view_form/view/form_gtk/many2many.py +32 -0
- tryton/gui/window/view_form/view/form_gtk/multiselection.py +15 -5
- tryton/gui/window/view_form/view/form_gtk/one2many.py +17 -4
- tryton/gui/window/view_form/view/form_gtk/url.py +8 -4
- tryton/gui/window/view_form/view/list.py +49 -14
- tryton/gui/window/win_csv.py +6 -12
- tryton/gui/window/win_export.py +49 -25
- tryton/gui/window/win_import.py +36 -11
- tryton/jsonrpc.py +29 -6
- tryton/pyson.py +2 -1
- tryton/tests/test_common_domain_parser.py +23 -2
- {tryton-7.2.13.dist-info → tryton-7.4.4.dist-info}/METADATA +2 -2
- {tryton-7.2.13.dist-info → tryton-7.4.4.dist-info}/RECORD +79 -79
- {tryton-7.2.13.data → tryton-7.4.4.data}/scripts/tryton +0 -0
- {tryton-7.2.13.dist-info → tryton-7.4.4.dist-info}/LICENSE +0 -0
- {tryton-7.2.13.dist-info → tryton-7.4.4.dist-info}/WHEEL +0 -0
- {tryton-7.2.13.dist-info → tryton-7.4.4.dist-info}/top_level.txt +0 -0
|
@@ -140,9 +140,9 @@ msgstr "并行操作异常"
|
|
|
140
140
|
msgid "Could not get a session."
|
|
141
141
|
msgstr "无法获取会话。"
|
|
142
142
|
|
|
143
|
-
#, python-format
|
|
143
|
+
#, fuzzy, python-format
|
|
144
144
|
msgid "Error: \"%s\". Try again later."
|
|
145
|
-
msgstr ""
|
|
145
|
+
msgstr "错误:“%s”。请稍后重试。"
|
|
146
146
|
|
|
147
147
|
msgid "Too many requests. Try again later."
|
|
148
148
|
msgstr "请求太多,请稍后重试."
|
|
@@ -240,8 +240,8 @@ msgstr "报告..."
|
|
|
240
240
|
msgid "Print..."
|
|
241
241
|
msgstr "打印..."
|
|
242
242
|
|
|
243
|
-
msgid "
|
|
244
|
-
msgstr "
|
|
243
|
+
msgid "Email..."
|
|
244
|
+
msgstr "电子邮件(_E)..."
|
|
245
245
|
|
|
246
246
|
msgid "Bold"
|
|
247
247
|
msgstr "粗体"
|
|
@@ -351,12 +351,6 @@ msgstr "帮助"
|
|
|
351
351
|
msgid "No result found."
|
|
352
352
|
msgstr "无符合条件的数据."
|
|
353
353
|
|
|
354
|
-
msgid "Favorites"
|
|
355
|
-
msgstr "收藏(_V)"
|
|
356
|
-
|
|
357
|
-
msgid "Manage..."
|
|
358
|
-
msgstr "管理..."
|
|
359
|
-
|
|
360
354
|
msgid "Action"
|
|
361
355
|
msgstr "操作"
|
|
362
356
|
|
|
@@ -587,6 +581,9 @@ msgstr "连接 Tryton 服务器"
|
|
|
587
581
|
msgid "Profile:"
|
|
588
582
|
msgstr "配置:"
|
|
589
583
|
|
|
584
|
+
msgid "Manage..."
|
|
585
|
+
msgstr "管理..."
|
|
586
|
+
|
|
590
587
|
msgid "Host / Database information"
|
|
591
588
|
msgstr "主机/数据库信息"
|
|
592
589
|
|
|
@@ -596,8 +593,8 @@ msgstr "用户名:"
|
|
|
596
593
|
msgid "Unable to complete email entry"
|
|
597
594
|
msgstr "无法完成电子邮件输入"
|
|
598
595
|
|
|
599
|
-
#, python-format
|
|
600
|
-
msgid "
|
|
596
|
+
#, fuzzy, python-format
|
|
597
|
+
msgid "Email %s"
|
|
601
598
|
msgstr "电子邮件 %s"
|
|
602
599
|
|
|
603
600
|
msgid "To:"
|
|
@@ -845,10 +842,12 @@ msgstr "报告(_R)..."
|
|
|
845
842
|
msgid "_Print..."
|
|
846
843
|
msgstr "打印(_P)..."
|
|
847
844
|
|
|
848
|
-
|
|
849
|
-
|
|
845
|
+
#, fuzzy
|
|
846
|
+
msgid "_Email..."
|
|
847
|
+
msgstr "电子邮件(_E)..."
|
|
850
848
|
|
|
851
|
-
|
|
849
|
+
#, fuzzy
|
|
850
|
+
msgid "Send an email using the record"
|
|
852
851
|
msgstr "使用当前记录发送电子邮件"
|
|
853
852
|
|
|
854
853
|
msgid "_Export Data..."
|
|
@@ -900,6 +899,9 @@ msgstr "字段名"
|
|
|
900
899
|
msgid "CSV Export: %s"
|
|
901
900
|
msgstr "CSV导出:%s"
|
|
902
901
|
|
|
902
|
+
msgid "Open"
|
|
903
|
+
msgstr "打开"
|
|
904
|
+
|
|
903
905
|
msgid "_Save Export"
|
|
904
906
|
msgstr "保存导出设置(_S)"
|
|
905
907
|
|
|
@@ -915,12 +917,6 @@ msgstr "<b>预定义导出设置</b>"
|
|
|
915
917
|
msgid "Name"
|
|
916
918
|
msgstr "名称"
|
|
917
919
|
|
|
918
|
-
msgid "Open"
|
|
919
|
-
msgstr "打开"
|
|
920
|
-
|
|
921
|
-
msgid "Save"
|
|
922
|
-
msgstr "保存"
|
|
923
|
-
|
|
924
920
|
msgid "Listed Records"
|
|
925
921
|
msgstr "已列出记录"
|
|
926
922
|
|
|
@@ -978,6 +974,9 @@ msgstr "保存和新建"
|
|
|
978
974
|
msgid "Add and New"
|
|
979
975
|
msgstr "添加和新建"
|
|
980
976
|
|
|
977
|
+
msgid "Save"
|
|
978
|
+
msgstr "保存"
|
|
979
|
+
|
|
981
980
|
msgid "Add"
|
|
982
981
|
msgstr "添加"
|
|
983
982
|
|
|
@@ -1003,6 +1002,10 @@ msgstr "已选记录取消删除 <Ins>"
|
|
|
1003
1002
|
msgid "CSV Import: %s"
|
|
1004
1003
|
msgstr "CSV导入: %s"
|
|
1005
1004
|
|
|
1005
|
+
#, fuzzy
|
|
1006
|
+
msgid "Import"
|
|
1007
|
+
msgstr "报告(_R)"
|
|
1008
|
+
|
|
1006
1009
|
msgid "_Auto-Detect"
|
|
1007
1010
|
msgstr "自动设置(_A)"
|
|
1008
1011
|
|
|
@@ -1206,6 +1209,10 @@ msgstr "添加已存在记录"
|
|
|
1206
1209
|
msgid "Remove selected record"
|
|
1207
1210
|
msgstr "删除所选记录"
|
|
1208
1211
|
|
|
1212
|
+
#, fuzzy
|
|
1213
|
+
msgid "Restore selected record"
|
|
1214
|
+
msgstr "删除所选记录"
|
|
1215
|
+
|
|
1209
1216
|
msgid "Open the record <F2>"
|
|
1210
1217
|
msgstr "打开记录 <F2>"
|
|
1211
1218
|
|
|
@@ -1221,6 +1228,10 @@ msgstr "编辑所选记录<F2>"
|
|
|
1221
1228
|
msgid "Delete selected record"
|
|
1222
1229
|
msgstr "删除所选记录"
|
|
1223
1230
|
|
|
1231
|
+
#, fuzzy
|
|
1232
|
+
msgid "Undelete selected record"
|
|
1233
|
+
msgstr "删除所选记录"
|
|
1234
|
+
|
|
1224
1235
|
#, python-format
|
|
1225
1236
|
msgid "%s%%"
|
|
1226
1237
|
msgstr "%s%%"
|
tryton/gui/main.py
CHANGED
|
@@ -463,10 +463,6 @@ class Main(Gtk.Application):
|
|
|
463
463
|
'id': id_,
|
|
464
464
|
})
|
|
465
465
|
|
|
466
|
-
def _manage_favorites(widget):
|
|
467
|
-
Window.create(self.menu_screen.model_name + '.favorite',
|
|
468
|
-
mode=['tree', 'form'],
|
|
469
|
-
name=_("Favorites"))
|
|
470
466
|
try:
|
|
471
467
|
favorites = RPCExecute('model',
|
|
472
468
|
self.menu_screen.model_name + '.favorite', 'get',
|
|
@@ -477,11 +473,6 @@ class Main(Gtk.Application):
|
|
|
477
473
|
menuitem = Gtk.MenuItem(label=name)
|
|
478
474
|
menuitem.connect('activate', _action_favorite, id_)
|
|
479
475
|
self.menu_favorite.add(menuitem)
|
|
480
|
-
self.menu_favorite.add(Gtk.SeparatorMenuItem())
|
|
481
|
-
manage_favorites = Gtk.MenuItem(label=_("Manage..."),
|
|
482
|
-
use_underline=True)
|
|
483
|
-
manage_favorites.connect('activate', _manage_favorites)
|
|
484
|
-
self.menu_favorite.add(manage_favorites)
|
|
485
476
|
self.menu_favorite.show_all()
|
|
486
477
|
|
|
487
478
|
def favorite_unset(self):
|
|
@@ -772,6 +763,9 @@ class Main(Gtk.Application):
|
|
|
772
763
|
expand=True, fill=True, padding=0)
|
|
773
764
|
treeview = screen.current_view.treeview
|
|
774
765
|
treeview.set_headers_visible(False)
|
|
766
|
+
for col in treeview.get_columns():
|
|
767
|
+
if col._type in {'selection', 'optional', 'drag'}:
|
|
768
|
+
col.set_visible(False)
|
|
775
769
|
|
|
776
770
|
# Favorite column
|
|
777
771
|
column = Gtk.TreeViewColumn()
|
|
@@ -864,7 +858,14 @@ class Main(Gtk.Application):
|
|
|
864
858
|
halign=Gtk.Align.START)
|
|
865
859
|
self.tooltips.set_tip(label, page.name)
|
|
866
860
|
self.tooltips.enable()
|
|
867
|
-
|
|
861
|
+
|
|
862
|
+
def remove_book_middle_click(widget, event):
|
|
863
|
+
if event.button == 2:
|
|
864
|
+
return self._sig_remove_book(widget, page.widget)
|
|
865
|
+
eventbox = Gtk.EventBox()
|
|
866
|
+
eventbox.connect('button-press-event', remove_book_middle_click)
|
|
867
|
+
eventbox.add(label)
|
|
868
|
+
hbox.pack_start(eventbox, expand=True, fill=True, padding=0)
|
|
868
869
|
|
|
869
870
|
button = Gtk.Button()
|
|
870
871
|
button.set_relief(Gtk.ReliefStyle.NONE)
|
tryton/gui/window/email_.py
CHANGED
|
@@ -106,7 +106,7 @@ class Email(NoModal):
|
|
|
106
106
|
self.dialog.set_default_size(*self.default_size())
|
|
107
107
|
self.dialog.connect('response', self.response)
|
|
108
108
|
|
|
109
|
-
self.dialog.set_title(_('
|
|
109
|
+
self.dialog.set_title(_('Email %s') % name)
|
|
110
110
|
|
|
111
111
|
grid = Gtk.Grid(
|
|
112
112
|
column_spacing=3, row_spacing=3,
|
tryton/gui/window/form.py
CHANGED
|
@@ -579,6 +579,7 @@ class Form(TabContent):
|
|
|
579
579
|
name = str(position) if position else '_'
|
|
580
580
|
selected = len(self.screen.selected_records)
|
|
581
581
|
view_type = self.screen.current_view.view_type
|
|
582
|
+
next_view_type = self.screen.next_view_type
|
|
582
583
|
has_views = self.screen.number_of_views > 1
|
|
583
584
|
if selected > 1:
|
|
584
585
|
name += '#%i' % selected
|
|
@@ -594,7 +595,9 @@ class Form(TabContent):
|
|
|
594
595
|
for b in self.screen.get_buttons())
|
|
595
596
|
set_sensitive(button_id, bool(position) and can_be_sensitive)
|
|
596
597
|
set_sensitive(
|
|
597
|
-
'switch',
|
|
598
|
+
'switch',
|
|
599
|
+
(position or view_type == 'form' or next_view_type != 'form')
|
|
600
|
+
and has_views)
|
|
598
601
|
set_sensitive('remove', self.screen.deletable)
|
|
599
602
|
set_sensitive('previous', self.screen.has_prev())
|
|
600
603
|
set_sensitive('next', self.screen.has_next())
|
tryton/gui/window/tabcontent.py
CHANGED
|
@@ -141,8 +141,8 @@ class TabContent(InfoBar):
|
|
|
141
141
|
accel_path='<tryton>/Form/Print'),
|
|
142
142
|
ToolbarItem(
|
|
143
143
|
id='email',
|
|
144
|
-
label=_("
|
|
145
|
-
tooltip=_("Send an
|
|
144
|
+
label=_("_Email..."),
|
|
145
|
+
tooltip=_("Send an email using the record"),
|
|
146
146
|
icon_name='tryton-email',
|
|
147
147
|
accel_path='<tryton>/Form/Email'),
|
|
148
148
|
None,
|
|
@@ -128,6 +128,7 @@ class Group(list):
|
|
|
128
128
|
self._group_list_changed('record-removed', record, idx)
|
|
129
129
|
super(Group, self).remove(record)
|
|
130
130
|
del self.__id2record[record.id]
|
|
131
|
+
record.destroy()
|
|
131
132
|
|
|
132
133
|
def clear(self):
|
|
133
134
|
# Use reversed order to minimize the cursor reposition as the cursor
|
|
@@ -408,7 +409,9 @@ class Group(list):
|
|
|
408
409
|
if record not in self.record_deleted:
|
|
409
410
|
self.record_deleted.append(record)
|
|
410
411
|
record.modified_fields.setdefault('id')
|
|
411
|
-
if record.id < 0
|
|
412
|
+
if (record.id < 0
|
|
413
|
+
or (self.parent and self.parent.id < 0)
|
|
414
|
+
or force_remove):
|
|
412
415
|
self._remove(record)
|
|
413
416
|
|
|
414
417
|
if len(self):
|
|
@@ -177,10 +177,10 @@ class Record:
|
|
|
177
177
|
for record in id2record.values():
|
|
178
178
|
record.exception = True
|
|
179
179
|
if process_exception:
|
|
180
|
-
values = [
|
|
180
|
+
values = []
|
|
181
181
|
default_values = {f: None for f in fnames if f != 'id'}
|
|
182
|
-
for
|
|
183
|
-
|
|
182
|
+
for id in id2record:
|
|
183
|
+
values.append({'id': id, **default_values})
|
|
184
184
|
else:
|
|
185
185
|
raise
|
|
186
186
|
id2value = dict((value['id'], value) for value in values)
|
|
@@ -299,20 +299,13 @@ class Record:
|
|
|
299
299
|
return self.group.fields
|
|
300
300
|
|
|
301
301
|
def _check_load(self, fields=None):
|
|
302
|
-
if
|
|
303
|
-
|
|
304
|
-
self.reload(fields)
|
|
305
|
-
return True
|
|
306
|
-
return False
|
|
307
|
-
if not self.loaded:
|
|
308
|
-
self.reload()
|
|
309
|
-
return True
|
|
310
|
-
return False
|
|
302
|
+
if not self.get_loaded(fields):
|
|
303
|
+
self.reload(fields)
|
|
311
304
|
|
|
312
305
|
def get_loaded(self, fields=None):
|
|
313
|
-
if fields:
|
|
314
|
-
|
|
315
|
-
return set(
|
|
306
|
+
if fields is None:
|
|
307
|
+
fields = self.group.fields.keys()
|
|
308
|
+
return set(fields) <= (self._loaded | set(self.modified_fields))
|
|
316
309
|
|
|
317
310
|
loaded = property(get_loaded)
|
|
318
311
|
|
|
@@ -444,10 +437,7 @@ class Record:
|
|
|
444
437
|
return ''
|
|
445
438
|
|
|
446
439
|
def validate(self, fields=None, softvalidation=False, pre_validate=None):
|
|
447
|
-
|
|
448
|
-
self._check_load(fields)
|
|
449
|
-
elif fields is None:
|
|
450
|
-
self._check_load()
|
|
440
|
+
self._check_load(fields)
|
|
451
441
|
res = True
|
|
452
442
|
for field_name, field in list(self.group.fields.items()):
|
|
453
443
|
if fields is not None and field_name not in fields:
|
|
@@ -323,6 +323,7 @@ class Screen:
|
|
|
323
323
|
if only_ids:
|
|
324
324
|
return ids
|
|
325
325
|
self.clear()
|
|
326
|
+
GLib.idle_add(self.screen_container.search_entry.grab_focus)
|
|
326
327
|
self.load(ids)
|
|
327
328
|
self.count_tab_domain()
|
|
328
329
|
return bool(ids)
|
|
@@ -541,6 +542,15 @@ class Screen:
|
|
|
541
542
|
def view_index(self):
|
|
542
543
|
return self.__current_view
|
|
543
544
|
|
|
545
|
+
@property
|
|
546
|
+
def next_view_type(self):
|
|
547
|
+
views = self.views + self.view_to_load
|
|
548
|
+
next_view_index = (self.view_index + 1) % len(views)
|
|
549
|
+
next_view = views[next_view_index]
|
|
550
|
+
if next_view and not isinstance(next_view, str):
|
|
551
|
+
next_view = next_view.view_type
|
|
552
|
+
return next_view
|
|
553
|
+
|
|
544
554
|
def switch_view(
|
|
545
555
|
self, view_type=None, view_id=None, creatable=None, display=True):
|
|
546
556
|
if view_id is not None:
|
|
@@ -792,6 +802,7 @@ class Screen:
|
|
|
792
802
|
records = records or self.selected_records
|
|
793
803
|
if not records:
|
|
794
804
|
return
|
|
805
|
+
current_record = self.current_record
|
|
795
806
|
if delete:
|
|
796
807
|
# Must delete children records before parent
|
|
797
808
|
records.sort(key=lambda r: r.depth, reverse=True)
|
|
@@ -822,7 +833,10 @@ class Screen:
|
|
|
822
833
|
record.parent.save(force_reload=False)
|
|
823
834
|
record.destroy()
|
|
824
835
|
|
|
825
|
-
|
|
836
|
+
if current_record and not current_record.destroyed:
|
|
837
|
+
self.current_record = current_record
|
|
838
|
+
else:
|
|
839
|
+
self.current_record = None
|
|
826
840
|
self.set_cursor()
|
|
827
841
|
self.display()
|
|
828
842
|
return True
|
|
@@ -224,17 +224,24 @@ class DictMultiSelectionEntry(DictEntry):
|
|
|
224
224
|
name = str(name)
|
|
225
225
|
model.append((value, name))
|
|
226
226
|
|
|
227
|
-
|
|
227
|
+
column = Gtk.TreeViewColumn()
|
|
228
|
+
select_cell = Gtk.CellRendererToggle()
|
|
229
|
+
select_cell.set_sensitive(False)
|
|
230
|
+
column.pack_start(select_cell, expand=False)
|
|
231
|
+
column.set_cell_data_func(select_cell, self._select_data_func)
|
|
228
232
|
name_cell = Gtk.CellRendererText()
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
self.tree.append_column(
|
|
233
|
+
column.pack_start(name_cell, expand=True)
|
|
234
|
+
column.add_attribute(name_cell, 'text', 1)
|
|
235
|
+
self.tree.append_column(column)
|
|
232
236
|
|
|
233
237
|
return widget
|
|
234
238
|
|
|
239
|
+
def _select_data_func(self, column, cell, model, iter_, selection):
|
|
240
|
+
cell.set_property('active', selection.iter_is_selected(iter_))
|
|
241
|
+
|
|
235
242
|
def get_value(self):
|
|
236
243
|
model, paths = self.tree.get_selection().get_selected_rows()
|
|
237
|
-
return [model[path][0] for path in paths]
|
|
244
|
+
return [model[path][0] for path in paths] or None
|
|
238
245
|
|
|
239
246
|
def set_value(self, value):
|
|
240
247
|
value2path = {v: idx for idx, (v, _) in enumerate(self.selection)}
|
|
@@ -1,5 +1,6 @@
|
|
|
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 logging
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
|
|
5
6
|
from gi.repository import Gdk, GLib, Gtk
|
|
@@ -15,6 +16,8 @@ from tryton.common import data2pixbuf, resize_pixbuf
|
|
|
15
16
|
from .binary import BinaryMixin
|
|
16
17
|
from .widget import Widget
|
|
17
18
|
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
18
21
|
|
|
19
22
|
class Document(BinaryMixin, Widget):
|
|
20
23
|
expand = True
|
|
@@ -90,5 +93,8 @@ class Document(BinaryMixin, Widget):
|
|
|
90
93
|
model.set_document(document)
|
|
91
94
|
self.evince_view.set_model(model)
|
|
92
95
|
except GLib.GError:
|
|
96
|
+
logger.warning(
|
|
97
|
+
f"Could not open document {filename}",
|
|
98
|
+
exc_info=True)
|
|
93
99
|
self.evince_view.set_model(EvinceView.DocumentModel())
|
|
94
100
|
self.evince_scroll.hide()
|
|
@@ -82,6 +82,14 @@ class Many2Many(Widget):
|
|
|
82
82
|
self.but_remove.set_relief(Gtk.ReliefStyle.NONE)
|
|
83
83
|
hbox.pack_start(self.but_remove, expand=False, fill=False, padding=0)
|
|
84
84
|
|
|
85
|
+
self.but_unremove = Gtk.Button(can_focus=False)
|
|
86
|
+
tooltips.set_tip(self.but_unremove, _("Restore selected record"))
|
|
87
|
+
self.but_unremove.connect('clicked', self._sig_unremove)
|
|
88
|
+
self.but_unremove.add(common.IconFactory.get_image(
|
|
89
|
+
'tryton-undo', Gtk.IconSize.SMALL_TOOLBAR))
|
|
90
|
+
self.but_unremove.set_relief(Gtk.ReliefStyle.NONE)
|
|
91
|
+
hbox.pack_start(self.but_unremove, expand=False, fill=False, padding=0)
|
|
92
|
+
|
|
85
93
|
tooltips.enable()
|
|
86
94
|
|
|
87
95
|
frame = Gtk.Frame()
|
|
@@ -130,6 +138,9 @@ class Many2Many(Widget):
|
|
|
130
138
|
elif event.keyval in remove_keys and editable:
|
|
131
139
|
self._sig_remove()
|
|
132
140
|
return True
|
|
141
|
+
elif event.keyval == Gdk.KEY_Insert:
|
|
142
|
+
self._sig_unremove()
|
|
143
|
+
return True
|
|
133
144
|
elif widget == self.wid_text:
|
|
134
145
|
if event.keyval == Gdk.KEY_F3:
|
|
135
146
|
self._sig_new()
|
|
@@ -166,6 +177,9 @@ class Many2Many(Widget):
|
|
|
166
177
|
add_remove = self.record.expr_eval(self.attrs.get('add_remove'))
|
|
167
178
|
if add_remove:
|
|
168
179
|
domain = [domain, add_remove]
|
|
180
|
+
existing_ids = self.field.get_eval(self.record)
|
|
181
|
+
if existing_ids:
|
|
182
|
+
domain = [domain, ('id', 'not in', existing_ids)]
|
|
169
183
|
context = self.field.get_search_context(self.record)
|
|
170
184
|
order = self.field.get_search_order(self.record)
|
|
171
185
|
value = self.wid_text.get_text()
|
|
@@ -194,6 +208,9 @@ class Many2Many(Widget):
|
|
|
194
208
|
def _sig_remove(self, *args):
|
|
195
209
|
self.screen.remove(remove=True)
|
|
196
210
|
|
|
211
|
+
def _sig_unremove(self, *args):
|
|
212
|
+
self.screen.unremove()
|
|
213
|
+
|
|
197
214
|
def _on_activate(self):
|
|
198
215
|
self._sig_edit()
|
|
199
216
|
|
|
@@ -284,11 +301,23 @@ class Many2Many(Widget):
|
|
|
284
301
|
else:
|
|
285
302
|
size_limit = False
|
|
286
303
|
|
|
304
|
+
removable = any(
|
|
305
|
+
not r.deleted and not r.removed
|
|
306
|
+
for r in self.screen.selected_records)
|
|
307
|
+
unremovable = any(
|
|
308
|
+
r.deleted or r.removed for r in self.screen.selected_records)
|
|
309
|
+
|
|
287
310
|
self.but_add.set_sensitive(bool(
|
|
288
311
|
not self._readonly
|
|
289
312
|
and not size_limit))
|
|
290
313
|
self.but_remove.set_sensitive(bool(
|
|
291
314
|
not self._readonly
|
|
315
|
+
and removable
|
|
316
|
+
and self._position))
|
|
317
|
+
self.but_unremove.set_sensitive(bool(
|
|
318
|
+
not self._readonly
|
|
319
|
+
and not size_limit
|
|
320
|
+
and unremovable
|
|
292
321
|
and self._position))
|
|
293
322
|
|
|
294
323
|
def record_message(self, position, size, *args):
|
|
@@ -343,6 +372,9 @@ class Many2Many(Widget):
|
|
|
343
372
|
add_remove = self.record.expr_eval(self.attrs.get('add_remove'))
|
|
344
373
|
if add_remove:
|
|
345
374
|
domain = [domain, add_remove]
|
|
375
|
+
existing_ids = self.field.get_eval(self.record)
|
|
376
|
+
if existing_ids:
|
|
377
|
+
domain = [domain, ('id', 'not in', existing_ids)]
|
|
346
378
|
update_completion(
|
|
347
379
|
self.wid_text, self.record, self.field, model, domain)
|
|
348
380
|
|
|
@@ -33,15 +33,23 @@ class MultiSelection(Widget, SelectionMixin):
|
|
|
33
33
|
selection.set_mode(Gtk.SelectionMode.MULTIPLE)
|
|
34
34
|
selection.connect('changed', self.changed)
|
|
35
35
|
self.widget.add(self.tree)
|
|
36
|
-
|
|
36
|
+
column = Gtk.TreeViewColumn()
|
|
37
|
+
select_cell = Gtk.CellRendererToggle()
|
|
38
|
+
select_cell.set_sensitive(False)
|
|
39
|
+
column.pack_start(select_cell, expand=False)
|
|
40
|
+
column.set_cell_data_func(
|
|
41
|
+
select_cell, self._select_data_func, selection)
|
|
37
42
|
name_cell = Gtk.CellRendererText()
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
self.tree.append_column(
|
|
43
|
+
column.pack_start(name_cell, expand=True)
|
|
44
|
+
column.add_attribute(name_cell, 'text', 1)
|
|
45
|
+
self.tree.append_column(column)
|
|
41
46
|
|
|
42
47
|
self.nullable_widget = False
|
|
43
48
|
self.init_selection()
|
|
44
49
|
|
|
50
|
+
def _select_data_func(self, column, cell, model, iter_, selection):
|
|
51
|
+
cell.set_property('active', selection.iter_is_selected(iter_))
|
|
52
|
+
|
|
45
53
|
def _readonly_set(self, readonly):
|
|
46
54
|
super(MultiSelection, self)._readonly_set(readonly)
|
|
47
55
|
selection = self.tree.get_selection()
|
|
@@ -70,6 +78,8 @@ class MultiSelection(Widget, SelectionMixin):
|
|
|
70
78
|
self.field.set_client(self.record, self.get_value())
|
|
71
79
|
|
|
72
80
|
def display(self):
|
|
81
|
+
def freeze(iter):
|
|
82
|
+
return list(map(tuple, iter))
|
|
73
83
|
selection = self.tree.get_selection()
|
|
74
84
|
selection.handler_block_by_func(self.changed)
|
|
75
85
|
try:
|
|
@@ -77,7 +87,7 @@ class MultiSelection(Widget, SelectionMixin):
|
|
|
77
87
|
# it will be set back in the super call
|
|
78
88
|
selection.set_select_function(lambda *a: True)
|
|
79
89
|
self.update_selection(self.record, self.field)
|
|
80
|
-
new_model = self.selection !=
|
|
90
|
+
new_model = freeze(self.selection) != freeze(self.model)
|
|
81
91
|
if new_model:
|
|
82
92
|
self.model.clear()
|
|
83
93
|
if not self.field:
|
|
@@ -141,7 +141,7 @@ class One2Many(Widget):
|
|
|
141
141
|
hbox.pack_start(self.but_del, expand=False, fill=False, padding=0)
|
|
142
142
|
|
|
143
143
|
self.but_undel = Gtk.Button(can_focus=False)
|
|
144
|
-
tooltips.set_tip(self.but_undel, _(
|
|
144
|
+
tooltips.set_tip(self.but_undel, _("Undelete selected record"))
|
|
145
145
|
self.but_undel.connect('clicked', self._sig_undelete)
|
|
146
146
|
self.but_undel.add(common.IconFactory.get_image(
|
|
147
147
|
'tryton-undo', Gtk.IconSize.SMALL_TOOLBAR))
|
|
@@ -295,7 +295,13 @@ class One2Many(Widget):
|
|
|
295
295
|
if isinstance(self._position, int):
|
|
296
296
|
first = self._position <= 1
|
|
297
297
|
last = self._position >= self._length
|
|
298
|
-
deletable =
|
|
298
|
+
deletable = (
|
|
299
|
+
self.screen.deletable
|
|
300
|
+
and any(
|
|
301
|
+
not r.deleted and not r.removed
|
|
302
|
+
for r in self.screen.selected_records))
|
|
303
|
+
undeletable = any(
|
|
304
|
+
r.deleted or r.removed for r in self.screen.selected_records)
|
|
299
305
|
view_type = self.screen.current_view.view_type
|
|
300
306
|
has_views = self.screen.number_of_views > 1
|
|
301
307
|
|
|
@@ -313,15 +319,16 @@ class One2Many(Widget):
|
|
|
313
319
|
self.but_undel.set_sensitive(bool(
|
|
314
320
|
not self._readonly
|
|
315
321
|
and not size_limit
|
|
322
|
+
and undeletable
|
|
316
323
|
and self._position))
|
|
317
324
|
self.but_open.set_sensitive(bool(
|
|
318
325
|
self._position
|
|
319
326
|
and self.read_access))
|
|
320
327
|
self.but_next.set_sensitive(bool(
|
|
321
|
-
self.
|
|
328
|
+
self._length
|
|
322
329
|
and not last))
|
|
323
330
|
self.but_pre.set_sensitive(bool(
|
|
324
|
-
self.
|
|
331
|
+
self._length
|
|
325
332
|
and not first))
|
|
326
333
|
if self.attrs.get('add_remove'):
|
|
327
334
|
self.but_add.set_sensitive(bool(
|
|
@@ -499,6 +506,9 @@ class One2Many(Widget):
|
|
|
499
506
|
domain = self.field.domain_get(self.record)
|
|
500
507
|
context = self.field.get_search_context(self.record)
|
|
501
508
|
domain = [domain, self.record.expr_eval(self.attrs.get('add_remove'))]
|
|
509
|
+
existing_ids = self.field.get_eval(self.record)
|
|
510
|
+
if existing_ids:
|
|
511
|
+
domain = [domain, ('id', 'not in', existing_ids)]
|
|
502
512
|
removed_ids = self.field.get_removed_ids(self.record)
|
|
503
513
|
domain = ['OR', domain, ('id', 'in', removed_ids)]
|
|
504
514
|
text = self.wid_text.get_text()
|
|
@@ -605,6 +615,9 @@ class One2Many(Widget):
|
|
|
605
615
|
model = self.attrs['relation']
|
|
606
616
|
domain = self.field.domain_get(self.record)
|
|
607
617
|
domain = [domain, self.record.expr_eval(self.attrs.get('add_remove'))]
|
|
618
|
+
existing_ids = self.field.get_eval(self.record)
|
|
619
|
+
if existing_ids:
|
|
620
|
+
domain = [domain, ('id', 'not in', existing_ids)]
|
|
608
621
|
removed_ids = self.field.get_removed_ids(self.record)
|
|
609
622
|
domain = ['OR', domain, ('id', 'in', removed_ids)]
|
|
610
623
|
update_completion(self.wid_text, self.record, self.field, model,
|
|
@@ -130,8 +130,10 @@ class HTML(Widget, TranslateMixin):
|
|
|
130
130
|
self.button, expand=False, fill=False, padding=0)
|
|
131
131
|
|
|
132
132
|
if attrs.get('translate'):
|
|
133
|
-
self.
|
|
134
|
-
|
|
133
|
+
self.button.set_image(common.IconFactory.get_image(
|
|
134
|
+
'tryton-translate', Gtk.IconSize.SMALL_TOOLBAR))
|
|
135
|
+
self.button.set_always_show_image(True)
|
|
136
|
+
self.button.connect('clicked', self.translate)
|
|
135
137
|
|
|
136
138
|
def uri(self, language=None):
|
|
137
139
|
if not self.record or self.record.id < 0 or CONNECTION.url is None:
|
|
@@ -149,7 +151,8 @@ class HTML(Widget, TranslateMixin):
|
|
|
149
151
|
|
|
150
152
|
def display(self):
|
|
151
153
|
super().display()
|
|
152
|
-
self.
|
|
154
|
+
if self.attrs.get('translate'):
|
|
155
|
+
self.button.set_uri(self.uri())
|
|
153
156
|
|
|
154
157
|
def _readonly_set(self, value):
|
|
155
158
|
super()._readonly_set(value)
|
|
@@ -158,6 +161,7 @@ class HTML(Widget, TranslateMixin):
|
|
|
158
161
|
|
|
159
162
|
def translate_dialog(self, languages):
|
|
160
163
|
languages = {l['name']: l['code'] for l in languages}
|
|
161
|
-
result = selection(
|
|
164
|
+
result = selection(
|
|
165
|
+
_('Choose a language'), languages, default=CONFIG['client.lang'])
|
|
162
166
|
if result:
|
|
163
167
|
webbrowser.open(self.uri(language=result[1]), new=2)
|