tryton 7.0.6__py3-none-any.whl → 7.2.13__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 +137 -69
- tryton/common/completion.py +2 -2
- tryton/common/datetime_.py +3 -1
- tryton/common/domain_inversion.py +10 -7
- tryton/common/domain_parser.py +17 -7
- tryton/common/selection.py +6 -3
- tryton/common/tempfile.py +34 -0
- tryton/config.py +4 -5
- tryton/data/locale/bg/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/bg/LC_MESSAGES/tryton.po +42 -4
- tryton/data/locale/ca/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ca/LC_MESSAGES/tryton.po +47 -8
- tryton/data/locale/cs/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/cs/LC_MESSAGES/tryton.po +42 -4
- tryton/data/locale/de/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/de/LC_MESSAGES/tryton.po +45 -6
- tryton/data/locale/es/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/es/LC_MESSAGES/tryton.po +46 -7
- tryton/data/locale/es_419/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/es_419/LC_MESSAGES/tryton.po +43 -5
- tryton/data/locale/et/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/et/LC_MESSAGES/tryton.po +46 -6
- tryton/data/locale/fa/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fa/LC_MESSAGES/tryton.po +46 -7
- tryton/data/locale/fi/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fi/LC_MESSAGES/tryton.po +41 -4
- tryton/data/locale/fr/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fr/LC_MESSAGES/tryton.po +46 -7
- tryton/data/locale/hu/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/hu/LC_MESSAGES/tryton.po +46 -6
- tryton/data/locale/id/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/id/LC_MESSAGES/tryton.po +43 -4
- tryton/data/locale/it/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/it/LC_MESSAGES/tryton.po +45 -6
- 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 +45 -6
- tryton/data/locale/lt/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/lt/LC_MESSAGES/tryton.po +46 -6
- tryton/data/locale/nl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/nl/LC_MESSAGES/tryton.po +46 -7
- tryton/data/locale/pl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/pl/LC_MESSAGES/tryton.po +84 -60
- tryton/data/locale/pt/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/pt/LC_MESSAGES/tryton.po +45 -6
- tryton/data/locale/ro/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ro/LC_MESSAGES/tryton.po +57 -17
- tryton/data/locale/ru/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ru/LC_MESSAGES/tryton.po +43 -6
- tryton/data/locale/sl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/sl/LC_MESSAGES/tryton.po +46 -5
- tryton/data/locale/tr/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/tr/LC_MESSAGES/tryton.po +41 -4
- tryton/data/locale/uk/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/uk/LC_MESSAGES/tryton.po +46 -6
- tryton/data/locale/zh_CN/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/zh_CN/LC_MESSAGES/tryton.po +46 -5
- 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 +6 -4
- tryton/gui/window/log.py +24 -2
- tryton/gui/window/view_form/model/field.py +84 -34
- tryton/gui/window/view_form/model/group.py +3 -1
- tryton/gui/window/view_form/model/record.py +64 -15
- tryton/gui/window/view_form/screen/screen.py +83 -46
- tryton/gui/window/view_form/view/calendar_gtk/calendar_.py +15 -9
- tryton/gui/window/view_form/view/form.py +6 -12
- tryton/gui/window/view_form/view/form_gtk/char.py +5 -6
- tryton/gui/window/view_form/view/form_gtk/dictionary.py +37 -24
- tryton/gui/window/view_form/view/form_gtk/document.py +9 -10
- 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/graph_gtk/graph.py +3 -1
- tryton/gui/window/view_form/view/list.py +68 -35
- tryton/gui/window/view_form/view/list_gtk/editabletree.py +2 -1
- tryton/gui/window/view_form/view/list_gtk/widget.py +58 -23
- tryton/gui/window/view_form/view/screen_container.py +3 -5
- tryton/gui/window/win_export.py +1 -2
- tryton/gui/window/win_form.py +9 -7
- tryton/gui/window/win_import.py +9 -4
- tryton/gui/window/wizard.py +13 -10
- tryton/jsonrpc.py +46 -28
- tryton/plugins/__init__.py +5 -3
- tryton/pyson.py +55 -5
- tryton/rpc.py +18 -0
- tryton/tests/test_common_domain_parser.py +8 -0
- tryton/translate.py +5 -2
- {tryton-7.0.6.data → tryton-7.2.13.data}/scripts/tryton +8 -7
- {tryton-7.0.6.dist-info → tryton-7.2.13.dist-info}/METADATA +6 -6
- {tryton-7.0.6.dist-info → tryton-7.2.13.dist-info}/RECORD +100 -98
- {tryton-7.0.6.dist-info → tryton-7.2.13.dist-info}/WHEEL +1 -1
- {tryton-7.0.6.dist-info → tryton-7.2.13.dist-info}/LICENSE +0 -0
- {tryton-7.0.6.dist-info → tryton-7.2.13.dist-info}/top_level.txt +0 -0
|
@@ -75,7 +75,8 @@ class Screen:
|
|
|
75
75
|
self.__current_record = None
|
|
76
76
|
self.new_group(context or {})
|
|
77
77
|
self.current_record = None
|
|
78
|
-
self.screen_container = ScreenContainer(
|
|
78
|
+
self.screen_container = ScreenContainer(
|
|
79
|
+
self, attributes.get('tab_domain'))
|
|
79
80
|
self.screen_container.alternate_view = attributes.get(
|
|
80
81
|
'alternate_view', False)
|
|
81
82
|
self.widget = self.screen_container.widget_get()
|
|
@@ -116,11 +117,10 @@ class Screen:
|
|
|
116
117
|
return child
|
|
117
118
|
|
|
118
119
|
# Remove first level Viewport and ScrolledWindow to fill the Vbox
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
]
|
|
123
|
-
remove_bin(widget)
|
|
120
|
+
remove_bin(self.context_screen.screen_container.viewport)
|
|
121
|
+
if self.context_screen.current_view:
|
|
122
|
+
remove_bin(
|
|
123
|
+
self.context_screen.current_view.widget.get_children()[0])
|
|
124
124
|
|
|
125
125
|
self.screen_container.filter_vbox.pack_start(
|
|
126
126
|
context_widget, expand=False, fill=True, padding=0)
|
|
@@ -164,7 +164,6 @@ class Screen:
|
|
|
164
164
|
|
|
165
165
|
def search_active(self, active=True):
|
|
166
166
|
if active and not self.parent:
|
|
167
|
-
self.screen_container.set_screen(self)
|
|
168
167
|
self.screen_container.show_filter()
|
|
169
168
|
else:
|
|
170
169
|
self.screen_container.hide_filter()
|
|
@@ -308,7 +307,8 @@ class Screen:
|
|
|
308
307
|
try:
|
|
309
308
|
self.search_count = RPCExecute(
|
|
310
309
|
'model', self.model_name, 'search_count',
|
|
311
|
-
domain, 0, self.count_limit, context=context
|
|
310
|
+
domain, 0, self.count_limit, context=context,
|
|
311
|
+
process_exception=False)
|
|
312
312
|
except RPCException:
|
|
313
313
|
self.search_count = 0
|
|
314
314
|
else:
|
|
@@ -520,7 +520,8 @@ class Screen:
|
|
|
520
520
|
self.group.destroy()
|
|
521
521
|
|
|
522
522
|
def default_row_activate(self):
|
|
523
|
-
if (self.current_view
|
|
523
|
+
if (self.current_view
|
|
524
|
+
and self.current_view.view_type == 'tree'
|
|
524
525
|
and int(self.current_view.attributes.get('keyword_open', 0))):
|
|
525
526
|
return Action.exec_keyword('tree_open', {
|
|
526
527
|
'model': self.model_name,
|
|
@@ -536,6 +537,10 @@ class Screen:
|
|
|
536
537
|
def number_of_views(self):
|
|
537
538
|
return len(self.views) + len(self.view_to_load)
|
|
538
539
|
|
|
540
|
+
@property
|
|
541
|
+
def view_index(self):
|
|
542
|
+
return self.__current_view
|
|
543
|
+
|
|
539
544
|
def switch_view(
|
|
540
545
|
self, view_type=None, view_id=None, creatable=None, display=True):
|
|
541
546
|
if view_id is not None:
|
|
@@ -642,7 +647,7 @@ class Screen:
|
|
|
642
647
|
|
|
643
648
|
def new(self, default=True, defaults=None):
|
|
644
649
|
previous_view = self.current_view
|
|
645
|
-
if self.current_view.view_type == 'calendar':
|
|
650
|
+
if self.current_view and self.current_view.view_type == 'calendar':
|
|
646
651
|
selected_date = self.current_view.get_selected_date()
|
|
647
652
|
if self.current_view and not self.current_view.creatable:
|
|
648
653
|
self.switch_view(creatable=True)
|
|
@@ -695,16 +700,19 @@ class Screen:
|
|
|
695
700
|
|
|
696
701
|
def save_current(self):
|
|
697
702
|
if not self.current_record:
|
|
698
|
-
if self.current_view
|
|
703
|
+
if (self.current_view
|
|
704
|
+
and self.current_view.view_type == 'tree'
|
|
705
|
+
and len(self.group)):
|
|
699
706
|
self.current_record = self.group[0]
|
|
700
707
|
else:
|
|
701
708
|
return True
|
|
702
|
-
self.current_view.set_value()
|
|
703
709
|
saved = False
|
|
704
710
|
record_id = None
|
|
705
|
-
|
|
711
|
+
if self.current_view:
|
|
712
|
+
self.current_view.set_value()
|
|
713
|
+
fields = self.current_view.get_fields()
|
|
706
714
|
path = self.current_record.get_path(self.group)
|
|
707
|
-
if self.current_view.view_type == 'tree':
|
|
715
|
+
if self.current_view and self.current_view.view_type == 'tree':
|
|
708
716
|
# False value must be not saved
|
|
709
717
|
saved = all((
|
|
710
718
|
x is not False and x >= 0
|
|
@@ -714,7 +722,7 @@ class Screen:
|
|
|
714
722
|
record_id = self.current_record.save(force_reload=True)
|
|
715
723
|
# False value must be not saved
|
|
716
724
|
saved = record_id is not False and record_id >= 0
|
|
717
|
-
|
|
725
|
+
elif self.current_view:
|
|
718
726
|
self.set_cursor()
|
|
719
727
|
self.current_view.display()
|
|
720
728
|
return False
|
|
@@ -742,17 +750,19 @@ class Screen:
|
|
|
742
750
|
def get(self):
|
|
743
751
|
if not self.current_record:
|
|
744
752
|
return None
|
|
745
|
-
self.current_view
|
|
753
|
+
if self.current_view:
|
|
754
|
+
self.current_view.set_value()
|
|
746
755
|
return self.current_record.get()
|
|
747
756
|
|
|
748
757
|
def get_on_change_value(self):
|
|
749
758
|
if not self.current_record:
|
|
750
759
|
return None
|
|
751
|
-
self.current_view
|
|
760
|
+
if self.current_view:
|
|
761
|
+
self.current_view.set_value()
|
|
752
762
|
return self.current_record.get_on_change_value()
|
|
753
763
|
|
|
754
764
|
def modified(self):
|
|
755
|
-
if self.current_view.view_type != 'tree':
|
|
765
|
+
if self.current_view and self.current_view.view_type != 'tree':
|
|
756
766
|
if self.current_record:
|
|
757
767
|
if self.current_record.modified or self.current_record.id < 0:
|
|
758
768
|
return True
|
|
@@ -760,7 +770,7 @@ class Screen:
|
|
|
760
770
|
for record in self.group:
|
|
761
771
|
if record.modified or record.id < 0:
|
|
762
772
|
return True
|
|
763
|
-
if self.current_view.modified:
|
|
773
|
+
if self.current_view and self.current_view.modified:
|
|
764
774
|
return True
|
|
765
775
|
return False
|
|
766
776
|
|
|
@@ -795,6 +805,9 @@ class Screen:
|
|
|
795
805
|
record.group.remove(
|
|
796
806
|
record, remove=remove, modified=False,
|
|
797
807
|
force_remove=force_remove)
|
|
808
|
+
# set current_record to None to prevent __select_changed
|
|
809
|
+
# to set deleted record as current_record
|
|
810
|
+
self.current_record = None
|
|
798
811
|
# call only once
|
|
799
812
|
record.set_modified()
|
|
800
813
|
|
|
@@ -821,11 +834,16 @@ class Screen:
|
|
|
821
834
|
context=self.context)
|
|
822
835
|
except RPCException:
|
|
823
836
|
return False
|
|
824
|
-
self.load(new_ids, position=self.new_position)
|
|
837
|
+
self.group.load(new_ids, position=self.new_position)
|
|
838
|
+
if new_ids:
|
|
839
|
+
self.current_record = self.group.get(new_ids[0])
|
|
840
|
+
self.display(set_cursor=True)
|
|
825
841
|
return True
|
|
826
842
|
|
|
827
843
|
def set_tree_state(self):
|
|
828
844
|
view = self.current_view
|
|
845
|
+
if not view:
|
|
846
|
+
return
|
|
829
847
|
if view.view_type not in {'tree', 'form', 'list-form'}:
|
|
830
848
|
return
|
|
831
849
|
if id(view) in self.tree_states_done:
|
|
@@ -920,7 +938,7 @@ class Screen:
|
|
|
920
938
|
json_paths, json_selected_path,
|
|
921
939
|
process_exception=False)
|
|
922
940
|
clear_cache('model.ir.ui.view_tree_state.get')
|
|
923
|
-
except
|
|
941
|
+
except RPCException:
|
|
924
942
|
logger.warn(
|
|
925
943
|
_('Unable to set view tree state'), exc_info=True)
|
|
926
944
|
|
|
@@ -935,12 +953,13 @@ class Screen:
|
|
|
935
953
|
|
|
936
954
|
def load(self, ids, set_cursor=True, modified=False, position=-1):
|
|
937
955
|
self.group.load(ids, modified=modified, position=position)
|
|
938
|
-
self.current_view
|
|
956
|
+
if self.current_view:
|
|
957
|
+
self.current_view.reset()
|
|
939
958
|
self.current_record = None
|
|
940
959
|
self.display(set_cursor=set_cursor)
|
|
941
960
|
|
|
942
961
|
def display(self, set_cursor=False):
|
|
943
|
-
if self.views:
|
|
962
|
+
if self.views and self.current_view:
|
|
944
963
|
self.search_active(self.current_view.view_type
|
|
945
964
|
in ('tree', 'graph', 'calendar'))
|
|
946
965
|
for view in self.views:
|
|
@@ -972,7 +991,8 @@ class Screen:
|
|
|
972
991
|
|
|
973
992
|
def _get_next_record(self, test=False):
|
|
974
993
|
view = self.current_view
|
|
975
|
-
if (view
|
|
994
|
+
if (view
|
|
995
|
+
and view.view_type in {'tree', 'form'}
|
|
976
996
|
and self.current_record
|
|
977
997
|
and self.current_record.group):
|
|
978
998
|
group = self.current_record.group
|
|
@@ -1000,12 +1020,14 @@ class Screen:
|
|
|
1000
1020
|
record = next
|
|
1001
1021
|
break
|
|
1002
1022
|
return record
|
|
1003
|
-
elif (view
|
|
1023
|
+
elif (view
|
|
1024
|
+
and view.view_type == 'list-form'
|
|
1025
|
+
and len(self.group)
|
|
1004
1026
|
and self.current_record in self.group):
|
|
1005
1027
|
idx = self.group.index(self.current_record)
|
|
1006
1028
|
if 0 <= idx < len(self.group) - 1:
|
|
1007
1029
|
return self.group[idx + 1]
|
|
1008
|
-
elif view.view_type == 'calendar':
|
|
1030
|
+
elif view and view.view_type == 'calendar':
|
|
1009
1031
|
record = self.current_record
|
|
1010
1032
|
goocalendar = view.widgets.get('goocalendar')
|
|
1011
1033
|
if goocalendar:
|
|
@@ -1039,15 +1061,18 @@ class Screen:
|
|
|
1039
1061
|
|
|
1040
1062
|
def display_next(self):
|
|
1041
1063
|
view = self.current_view
|
|
1042
|
-
view
|
|
1064
|
+
if view:
|
|
1065
|
+
view.set_value()
|
|
1043
1066
|
self.set_cursor(reset_view=False)
|
|
1044
1067
|
self.current_record = self._get_next_record()
|
|
1045
1068
|
self.set_cursor(reset_view=False)
|
|
1046
|
-
view
|
|
1069
|
+
if view:
|
|
1070
|
+
view.display()
|
|
1047
1071
|
|
|
1048
1072
|
def _get_prev_record(self, test=False):
|
|
1049
1073
|
view = self.current_view
|
|
1050
|
-
if (view
|
|
1074
|
+
if (view
|
|
1075
|
+
and view.view_type in {'tree', 'form'}
|
|
1051
1076
|
and self.current_record
|
|
1052
1077
|
and self.current_record.group):
|
|
1053
1078
|
group = self.current_record.group
|
|
@@ -1065,7 +1090,7 @@ class Screen:
|
|
|
1065
1090
|
if parent and record.model_name == parent.model_name:
|
|
1066
1091
|
record = parent
|
|
1067
1092
|
return record
|
|
1068
|
-
elif view.view_type == 'calendar':
|
|
1093
|
+
elif view and view.view_type == 'calendar':
|
|
1069
1094
|
record = self.current_record
|
|
1070
1095
|
goocalendar = view.widgets.get('goocalendar')
|
|
1071
1096
|
if goocalendar:
|
|
@@ -1090,7 +1115,9 @@ class Screen:
|
|
|
1090
1115
|
if prev_id >= 0:
|
|
1091
1116
|
return events[prev_id].record
|
|
1092
1117
|
break
|
|
1093
|
-
elif (view
|
|
1118
|
+
elif (view
|
|
1119
|
+
and view.view_type == 'list-form'
|
|
1120
|
+
and len(self.group)
|
|
1094
1121
|
and self.current_record in self.group):
|
|
1095
1122
|
idx = self.group.index(self.current_record)
|
|
1096
1123
|
if 0 < idx <= len(self.group) - 1:
|
|
@@ -1104,11 +1131,13 @@ class Screen:
|
|
|
1104
1131
|
|
|
1105
1132
|
def display_prev(self):
|
|
1106
1133
|
view = self.current_view
|
|
1107
|
-
view
|
|
1134
|
+
if view:
|
|
1135
|
+
view.set_value()
|
|
1108
1136
|
self.set_cursor(reset_view=False)
|
|
1109
1137
|
self.current_record = self._get_prev_record()
|
|
1110
1138
|
self.set_cursor(reset_view=False)
|
|
1111
|
-
view
|
|
1139
|
+
if view:
|
|
1140
|
+
view.display()
|
|
1112
1141
|
|
|
1113
1142
|
def invalid_message(self, record=None):
|
|
1114
1143
|
if record is None:
|
|
@@ -1142,6 +1171,8 @@ class Screen:
|
|
|
1142
1171
|
def selected_paths(self):
|
|
1143
1172
|
if self.current_view and self.current_view.view_type == 'tree':
|
|
1144
1173
|
return self.current_view.get_selected_paths()
|
|
1174
|
+
else:
|
|
1175
|
+
return []
|
|
1145
1176
|
|
|
1146
1177
|
@property
|
|
1147
1178
|
def listed_records(self):
|
|
@@ -1181,7 +1212,7 @@ class Screen:
|
|
|
1181
1212
|
if not self.selected_records:
|
|
1182
1213
|
return []
|
|
1183
1214
|
|
|
1184
|
-
buttons = self.current_view.get_buttons()
|
|
1215
|
+
buttons = self.current_view.get_buttons() if self.current_view else []
|
|
1185
1216
|
|
|
1186
1217
|
for record in self.selected_records:
|
|
1187
1218
|
buttons = [b for b in buttons if is_active(record, b)]
|
|
@@ -1191,8 +1222,9 @@ class Screen:
|
|
|
1191
1222
|
|
|
1192
1223
|
def button(self, button):
|
|
1193
1224
|
'Execute button on the selected records'
|
|
1194
|
-
self.current_view
|
|
1195
|
-
|
|
1225
|
+
if self.current_view:
|
|
1226
|
+
self.current_view.set_value()
|
|
1227
|
+
fields = self.current_view.get_fields()
|
|
1196
1228
|
for record in self.selected_records:
|
|
1197
1229
|
domain = record.expr_eval(
|
|
1198
1230
|
button.get('states', {})).get('pre_validate', [])
|
|
@@ -1206,9 +1238,9 @@ class Screen:
|
|
|
1206
1238
|
if button.get('confirm', False) and not sur(button['confirm']):
|
|
1207
1239
|
return
|
|
1208
1240
|
if button.get('type', 'class') == 'class':
|
|
1209
|
-
|
|
1241
|
+
record_id = self.current_record.save(force_reload=False)
|
|
1242
|
+
if record_id is False or record_id < 0:
|
|
1210
1243
|
return
|
|
1211
|
-
if button.get('type', 'class') == 'class':
|
|
1212
1244
|
self._button_class(button)
|
|
1213
1245
|
else:
|
|
1214
1246
|
self._button_instance(button)
|
|
@@ -1217,6 +1249,7 @@ class Screen:
|
|
|
1217
1249
|
record = self.current_record
|
|
1218
1250
|
args = record.expr_eval(button.get('change', []))
|
|
1219
1251
|
values = record._get_on_change_args(args)
|
|
1252
|
+
values['id'] = record.id
|
|
1220
1253
|
try:
|
|
1221
1254
|
changes = RPCExecute('model', self.model_name, button['name'],
|
|
1222
1255
|
values, context=self.context)
|
|
@@ -1237,6 +1270,7 @@ class Screen:
|
|
|
1237
1270
|
except RPCException:
|
|
1238
1271
|
action = None
|
|
1239
1272
|
self.reload(ids, written=True)
|
|
1273
|
+
self.record_saved()
|
|
1240
1274
|
if isinstance(action, str):
|
|
1241
1275
|
self.client_action(action)
|
|
1242
1276
|
elif action:
|
|
@@ -1273,7 +1307,9 @@ class Screen:
|
|
|
1273
1307
|
elif action.startswith('switch'):
|
|
1274
1308
|
self.switch_view(*action.split(None, 2)[1:])
|
|
1275
1309
|
elif action == 'reload':
|
|
1276
|
-
if (self.current_view
|
|
1310
|
+
if (self.current_view
|
|
1311
|
+
and self.current_view.view_type in [
|
|
1312
|
+
'tree', 'graph', 'calendar']
|
|
1277
1313
|
and not self.parent):
|
|
1278
1314
|
self.search_filter()
|
|
1279
1315
|
elif action == 'reload menu':
|
|
@@ -1297,13 +1333,13 @@ class Screen:
|
|
|
1297
1333
|
if name:
|
|
1298
1334
|
query_string.append(
|
|
1299
1335
|
('name', json.dumps(name, separators=(',', ':'))))
|
|
1300
|
-
if self.screen_container.tab_domain:
|
|
1301
|
-
query_string.append(('tab_domain', json.dumps(
|
|
1302
|
-
self.screen_container.tab_domain,
|
|
1303
|
-
cls=JSONEncoder, separators=(',', ':'))))
|
|
1304
1336
|
path = [CONFIG['login.db'], 'model', self.model_name]
|
|
1305
1337
|
view_ids = [v.view_id for v in self.views] + self.view_ids
|
|
1306
|
-
if self.current_view.view_type != 'form':
|
|
1338
|
+
if self.current_view and self.current_view.view_type != 'form':
|
|
1339
|
+
if self.screen_container.tab_domain:
|
|
1340
|
+
query_string.append(('tab_domain', json.dumps(
|
|
1341
|
+
self.screen_container.tab_domain,
|
|
1342
|
+
cls=JSONEncoder, separators=(',', ':'))))
|
|
1307
1343
|
if self.search_value:
|
|
1308
1344
|
search_value = self.search_value
|
|
1309
1345
|
else:
|
|
@@ -1315,8 +1351,9 @@ class Screen:
|
|
|
1315
1351
|
separators=(',', ':'))))
|
|
1316
1352
|
elif self.current_record and self.current_record.id > -1:
|
|
1317
1353
|
path.append(str(self.current_record.id))
|
|
1318
|
-
|
|
1319
|
-
|
|
1354
|
+
if self.current_view:
|
|
1355
|
+
i = view_ids.index(self.current_view.view_id)
|
|
1356
|
+
view_ids = view_ids[i:] + view_ids[:i]
|
|
1320
1357
|
if view_ids:
|
|
1321
1358
|
query_string.append(('views', json.dumps(
|
|
1322
1359
|
view_ids, separators=(',', ':'))))
|
|
@@ -63,17 +63,23 @@ class Calendar_(goocalendar.Calendar):
|
|
|
63
63
|
return False
|
|
64
64
|
|
|
65
65
|
def current_domain(self):
|
|
66
|
-
|
|
67
|
-
self.current_domain_period.get_dates(True)
|
|
66
|
+
start, end = self.current_domain_period.get_dates(True)
|
|
68
67
|
dtstart = self.attrs['dtstart']
|
|
69
68
|
dtend = self.attrs.get('dtend') or dtstart
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
69
|
+
fields = self.view_calendar.screen.group.fields
|
|
70
|
+
if fields[dtstart].attrs['type'] == 'date':
|
|
71
|
+
start = start.date()
|
|
72
|
+
if fields[dtend].attrs['type'] == 'date':
|
|
73
|
+
end = end.date()
|
|
74
|
+
domain = [
|
|
75
|
+
(dtstart, '!=', None),
|
|
76
|
+
(dtend, '!=', None),
|
|
77
|
+
['OR',
|
|
78
|
+
['AND', (dtstart, '>=', start), (dtstart, '<', end)],
|
|
79
|
+
['AND', (dtend, '>=', start), (dtend, '<', end)],
|
|
80
|
+
['AND', (dtstart, '<', start), (dtend, '>', end)],
|
|
81
|
+
],
|
|
82
|
+
]
|
|
77
83
|
return domain
|
|
78
84
|
|
|
79
85
|
def get_colors(self, record):
|
|
@@ -230,6 +230,9 @@ class FormXMLViewParser(XMLViewParser):
|
|
|
230
230
|
self.container.add(None, attributes)
|
|
231
231
|
return
|
|
232
232
|
|
|
233
|
+
if int(attributes.get('visible', 0)):
|
|
234
|
+
self.field_attrs[name]['visible'] = True
|
|
235
|
+
|
|
233
236
|
widget = self.WIDGETS[attributes['widget']](self.view, attributes)
|
|
234
237
|
self.view.widgets[name].append(widget)
|
|
235
238
|
|
|
@@ -340,18 +343,7 @@ class FormXMLViewParser(XMLViewParser):
|
|
|
340
343
|
int(attributes.get('width', -1)),
|
|
341
344
|
int(attributes.get('height', -1)))
|
|
342
345
|
|
|
343
|
-
# Force to display the first time it switches on a page
|
|
344
|
-
# This avoids glitch in position of widgets
|
|
345
|
-
def switch(notebook, page, page_num):
|
|
346
|
-
if not self.view.widget:
|
|
347
|
-
# Not yet finish to parse
|
|
348
|
-
return
|
|
349
|
-
notebook.grab_focus()
|
|
350
|
-
self.view.display()
|
|
351
|
-
notebook.disconnect(handler_id)
|
|
352
|
-
handler_id = notebook.connect('switch-page', switch)
|
|
353
346
|
self.view.state_widgets.append(notebook)
|
|
354
|
-
|
|
355
347
|
self.view.notebooks.append(notebook)
|
|
356
348
|
self.container.add(notebook, attributes)
|
|
357
349
|
self.parse_child(node, notebook)
|
|
@@ -390,7 +382,6 @@ class FormXMLViewParser(XMLViewParser):
|
|
|
390
382
|
group = Container.constructor(
|
|
391
383
|
int(attributes.get('col', 4)),
|
|
392
384
|
attributes.get('homogeneous', False))
|
|
393
|
-
self.parse_child(node, group)
|
|
394
385
|
|
|
395
386
|
if 'name' in attributes and attributes['name'] == self.exclude_field:
|
|
396
387
|
self.container.add(None, attributes)
|
|
@@ -414,6 +405,9 @@ class FormXMLViewParser(XMLViewParser):
|
|
|
414
405
|
bool(attributes.get('yexpand'))))
|
|
415
406
|
self.view.state_widgets.append(widget)
|
|
416
407
|
self.container.add(widget, attributes)
|
|
408
|
+
# Parse the children at the end to preserve the order of the state
|
|
409
|
+
# widgets
|
|
410
|
+
self.parse_child(node, group)
|
|
417
411
|
|
|
418
412
|
def _parse_hpaned(self, node, attributes):
|
|
419
413
|
self._parse_paned(node, attributes, Gtk.HPaned)
|
|
@@ -171,10 +171,9 @@ class Password(Char):
|
|
|
171
171
|
self.widget.pack_start(
|
|
172
172
|
self.visibility_checkbox, expand=False, fill=True, padding=0)
|
|
173
173
|
|
|
174
|
-
def _readonly_set(self, value):
|
|
175
|
-
super(Char, self)._readonly_set(value)
|
|
176
|
-
self.entry.set_editable(not value)
|
|
177
|
-
self.visibility_checkbox.props.visible = not value
|
|
178
|
-
|
|
179
174
|
def toggle_visibility(self, button):
|
|
180
|
-
|
|
175
|
+
if self.autocomplete:
|
|
176
|
+
entry = self.entry.get_child()
|
|
177
|
+
else:
|
|
178
|
+
entry = self.entry
|
|
179
|
+
entry.props.visibility = not self.entry.props.visibility
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# This file is part of Tryton. The COPYRIGHT file at the top level of this
|
|
2
2
|
# repository contains the full copyright notices and license terms.
|
|
3
3
|
|
|
4
|
+
import datetime as dt
|
|
4
5
|
import decimal
|
|
5
6
|
import gettext
|
|
6
7
|
import locale
|
|
@@ -64,7 +65,7 @@ class DictEntry(object):
|
|
|
64
65
|
return self.widget.get_text()
|
|
65
66
|
|
|
66
67
|
def set_value(self, value):
|
|
67
|
-
self.widget.set_text(value or '')
|
|
68
|
+
self.widget.set_text(str(value or ''))
|
|
68
69
|
reset_position(self.widget)
|
|
69
70
|
|
|
70
71
|
def set_readonly(self, readonly):
|
|
@@ -241,9 +242,10 @@ class DictMultiSelectionEntry(DictEntry):
|
|
|
241
242
|
selection.handler_block_by_func(self._changed)
|
|
242
243
|
try:
|
|
243
244
|
selection.unselect_all()
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
245
|
+
if value:
|
|
246
|
+
for v in value:
|
|
247
|
+
if v in value2path:
|
|
248
|
+
selection.select_path(value2path[v])
|
|
247
249
|
finally:
|
|
248
250
|
selection.handler_unblock_by_func(self._changed)
|
|
249
251
|
|
|
@@ -278,7 +280,7 @@ class DictIntegerEntry(DictEntry):
|
|
|
278
280
|
return None
|
|
279
281
|
|
|
280
282
|
def set_value(self, value):
|
|
281
|
-
if value
|
|
283
|
+
if isinstance(value, (int, float, Decimal)):
|
|
282
284
|
txt_val = locale.format_string('%d', value, True)
|
|
283
285
|
else:
|
|
284
286
|
txt_val = ''
|
|
@@ -315,7 +317,7 @@ class DictFloatEntry(DictIntegerEntry):
|
|
|
315
317
|
else:
|
|
316
318
|
self.widget.digits = None
|
|
317
319
|
self.widget.set_width_chars(self.width)
|
|
318
|
-
if value
|
|
320
|
+
if isinstance(value, (int, float, Decimal)):
|
|
319
321
|
txt_val = locale.localize(
|
|
320
322
|
'{0:.{1}f}'.format(value, digits[1]), True)
|
|
321
323
|
else:
|
|
@@ -361,7 +363,8 @@ class DictDateTimeEntry(DictEntry):
|
|
|
361
363
|
return untimezoned_date(self.widget.props.value)
|
|
362
364
|
|
|
363
365
|
def set_value(self, value):
|
|
364
|
-
self.widget.props.value =
|
|
366
|
+
self.widget.props.value = (
|
|
367
|
+
timezoned_date(value) if isinstance(value, dt.datetime) else None)
|
|
365
368
|
|
|
366
369
|
def set_readonly(self, readonly):
|
|
367
370
|
for child in self.widget.get_children():
|
|
@@ -396,7 +399,7 @@ class DictDateEntry(DictEntry):
|
|
|
396
399
|
return self.widget.props.value
|
|
397
400
|
|
|
398
401
|
def set_value(self, value):
|
|
399
|
-
self.widget.props.value = value
|
|
402
|
+
self.widget.props.value = value if isinstance(value, dt.date) else None
|
|
400
403
|
|
|
401
404
|
def set_readonly(self, readonly):
|
|
402
405
|
super().set_readonly(readonly)
|
|
@@ -470,6 +473,7 @@ class DictWidget(Widget):
|
|
|
470
473
|
|
|
471
474
|
self._readonly = False
|
|
472
475
|
self._record_id = None
|
|
476
|
+
self._popup = False
|
|
473
477
|
|
|
474
478
|
@property
|
|
475
479
|
def _invalid_widget(self):
|
|
@@ -491,10 +495,16 @@ class DictWidget(Widget):
|
|
|
491
495
|
value = self.wid_text.get_text()
|
|
492
496
|
domain = self.field.domain_get(self.record)
|
|
493
497
|
|
|
498
|
+
if self._popup:
|
|
499
|
+
return
|
|
500
|
+
else:
|
|
501
|
+
self._popup = True
|
|
502
|
+
|
|
494
503
|
def callback(result):
|
|
495
504
|
if result:
|
|
496
505
|
self.add_new_keys([r[0] for r in result])
|
|
497
506
|
self.wid_text.set_text('')
|
|
507
|
+
self._popup = False
|
|
498
508
|
|
|
499
509
|
win = WinSearch(self.schema_model, callback, sel_multi=True,
|
|
500
510
|
context=context, domain=domain, new=False)
|
|
@@ -504,15 +514,14 @@ class DictWidget(Widget):
|
|
|
504
514
|
def add_new_keys(self, ids):
|
|
505
515
|
new_keys = self.field.add_new_keys(ids, self.record)
|
|
506
516
|
self.send_modified()
|
|
507
|
-
|
|
508
|
-
for
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
focus = True
|
|
517
|
+
value = self.field.get_client(self.record)
|
|
518
|
+
value.update({k: None for k in new_keys})
|
|
519
|
+
self.field.set_client(self.record, value)
|
|
520
|
+
self.display()
|
|
521
|
+
|
|
522
|
+
# Use idle add because it can be called from the callback
|
|
523
|
+
# of WinSearch while the popup is still there
|
|
524
|
+
GLib.idle_add(self.fields[new_keys[0]].widget.grab_focus)
|
|
516
525
|
|
|
517
526
|
def _sig_remove(self, button, key, modified=True):
|
|
518
527
|
self.fields[key].disconnect_signals()
|
|
@@ -558,7 +567,7 @@ class DictWidget(Widget):
|
|
|
558
567
|
not self._readonly
|
|
559
568
|
and self.attrs.get('delete', True)))
|
|
560
569
|
|
|
561
|
-
def add_line(self, key):
|
|
570
|
+
def add_line(self, key, position):
|
|
562
571
|
key_schema = self.field.keys[key]
|
|
563
572
|
self.fields[key] = DICT_ENTRIES[key_schema['type']](key, self)
|
|
564
573
|
field = self.fields[key]
|
|
@@ -566,8 +575,8 @@ class DictWidget(Widget):
|
|
|
566
575
|
label = Gtk.Label(
|
|
567
576
|
label=set_underline(text),
|
|
568
577
|
use_underline=True, halign=Gtk.Align.END)
|
|
569
|
-
self.grid.
|
|
570
|
-
|
|
578
|
+
self.grid.insert_row(position)
|
|
579
|
+
self.grid.attach(label, 0, position, 1, 1)
|
|
571
580
|
label.set_mnemonic_widget(field.widget)
|
|
572
581
|
label.show()
|
|
573
582
|
hbox = Gtk.HBox(hexpand=True)
|
|
@@ -604,6 +613,11 @@ class DictWidget(Widget):
|
|
|
604
613
|
self.field.add_keys(list(new_key_names), self.record)
|
|
605
614
|
decoder = PYSONDecoder()
|
|
606
615
|
|
|
616
|
+
# We remove first the old keys in order to keep the order when
|
|
617
|
+
# inserting the new ones
|
|
618
|
+
for key in set(self.fields.keys()) - set(value.keys()):
|
|
619
|
+
self._sig_remove(None, key, modified=False)
|
|
620
|
+
|
|
607
621
|
def filter_func(item):
|
|
608
622
|
key, value = item
|
|
609
623
|
return key in self.field.keys
|
|
@@ -612,9 +626,10 @@ class DictWidget(Widget):
|
|
|
612
626
|
key, value = item
|
|
613
627
|
return self.field.keys[key]['sequence'] or 0
|
|
614
628
|
|
|
615
|
-
for key, val in
|
|
629
|
+
for position, (key, val) in enumerate(
|
|
630
|
+
sorted(filter(filter_func, value.items()), key=key)):
|
|
616
631
|
if key not in self.fields:
|
|
617
|
-
self.add_line(key)
|
|
632
|
+
self.add_line(key, position)
|
|
618
633
|
widget = self.fields[key]
|
|
619
634
|
widget.set_value(val)
|
|
620
635
|
widget.set_readonly(self._readonly)
|
|
@@ -622,8 +637,6 @@ class DictWidget(Widget):
|
|
|
622
637
|
self.field.keys[key].get('domain') or '[]')
|
|
623
638
|
widget_class(
|
|
624
639
|
widget.widget, 'invalid', not eval_domain(key_domain, value))
|
|
625
|
-
for key in set(self.fields.keys()) - set(value.keys()):
|
|
626
|
-
self._sig_remove(None, key, modified=False)
|
|
627
640
|
|
|
628
641
|
self._set_button_sensitive()
|
|
629
642
|
|
|
@@ -1,7 +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
3
|
from pathlib import Path
|
|
4
|
-
from tempfile import NamedTemporaryFile
|
|
5
4
|
|
|
6
5
|
from gi.repository import Gdk, GLib, Gtk
|
|
7
6
|
|
|
@@ -76,17 +75,17 @@ class Document(BinaryMixin, Widget):
|
|
|
76
75
|
self.image.hide()
|
|
77
76
|
if self.evince_view:
|
|
78
77
|
self.evince_scroll.show()
|
|
78
|
+
suffix = None
|
|
79
79
|
if self.filename_field:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
filename = self.filename_field.get(self.record)
|
|
81
|
+
if filename:
|
|
82
|
+
suffix = Path(filename).suffix
|
|
83
|
+
filename = Path(self.field.get_filename(self.record, suffix))
|
|
83
84
|
try:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
EvinceDocument.Document.factory_get_document(
|
|
89
|
-
path.as_uri()))
|
|
85
|
+
document = (
|
|
86
|
+
EvinceDocument.Document.factory_get_document_full(
|
|
87
|
+
filename.as_uri(),
|
|
88
|
+
EvinceDocument.DocumentLoadFlags.NONE))
|
|
90
89
|
model = EvinceView.DocumentModel()
|
|
91
90
|
model.set_document(document)
|
|
92
91
|
self.evince_view.set_model(model)
|