Mercurial > libervia-backend
annotate sat_frontends/jp/xmlui_manager.py @ 3748:53a8b50d69ca
jp (xmlui): add `PasswordWidget`
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 22 Mar 2022 17:00:42 +0100 |
parents | 04283582966f |
children | 524856bd7b19 |
rev | line source |
---|---|
3137 | 1 #!/usr/bin/env python3 |
2 | |
2408 | 3 |
4 # JP: a SàT frontend | |
3479 | 5 # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org) |
2408 | 6 |
7 # This program is free software: you can redistribute it and/or modify | |
8 # it under the terms of the GNU Affero General Public License as published by | |
9 # the Free Software Foundation, either version 3 of the License, or | |
10 # (at your option) any later version. | |
11 | |
12 # This program is distributed in the hope that it will be useful, | |
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 # GNU Affero General Public License for more details. | |
16 | |
17 # You should have received a copy of the GNU Affero General Public License | |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | |
3040 | 20 from functools import partial |
2408 | 21 from sat.core.log import getLogger |
2669
bdb8276fd2da
frontends (xmlui): class_map is now an arg of create function:
Goffi <goffi@goffi.org>
parents:
2624
diff
changeset
|
22 from sat_frontends.tools import xmlui as xmlui_base |
2408 | 23 from sat_frontends.jp.constants import Const as C |
24 from sat.tools.common.ansi import ANSI as A | |
25 from sat.core.i18n import _ | |
3040 | 26 |
27 log = getLogger(__name__) | |
2408 | 28 |
29 # workflow constants | |
30 | |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
31 SUBMIT = "SUBMIT" # submit form |
2408 | 32 |
33 | |
34 ## Widgets ## | |
35 | |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
36 |
2408 | 37 class Base(object): |
38 """Base for Widget and Container""" | |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
39 |
2408 | 40 type = None |
41 _root = None | |
42 | |
43 def __init__(self, xmlui_parent): | |
44 self.xmlui_parent = xmlui_parent | |
45 self.host = self.xmlui_parent.host | |
46 | |
47 @property | |
48 def root(self): | |
49 """retrieve main XMLUI parent class""" | |
50 if self._root is not None: | |
51 return self._root | |
52 root = self | |
2669
bdb8276fd2da
frontends (xmlui): class_map is now an arg of create function:
Goffi <goffi@goffi.org>
parents:
2624
diff
changeset
|
53 while not isinstance(root, xmlui_base.XMLUIBase): |
2408 | 54 root = root.xmlui_parent |
55 self._root = root | |
56 return root | |
57 | |
58 def disp(self, *args, **kwargs): | |
59 self.host.disp(*args, **kwargs) | |
60 | |
61 | |
62 class Widget(Base): | |
3028 | 63 category = "widget" |
2408 | 64 enabled = True |
65 | |
66 @property | |
67 def name(self): | |
68 return self._xmlui_name | |
69 | |
3040 | 70 async def show(self): |
2408 | 71 """display current widget |
72 | |
73 must be overriden by subclasses | |
74 """ | |
75 raise NotImplementedError(self.__class__) | |
76 | |
77 def verboseName(self, elems=None, value=None): | |
78 """add name in color to the elements | |
79 | |
80 helper method to display name which can then be used to automate commands | |
81 elems is only modified if verbosity is > 0 | |
82 @param elems(list[unicode], None): elements to display | |
83 None to display name directly | |
84 @param value(unicode, None): value to show | |
85 use self.name if None | |
86 """ | |
87 if value is None: | |
88 value = self.name | |
89 if self.host.verbosity: | |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
90 to_disp = [ |
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
91 A.FG_MAGENTA, |
3028 | 92 " " if elems else "", |
93 "({})".format(value), | |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
94 A.RESET, |
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
95 ] |
2408 | 96 if elems is None: |
97 self.host.disp(A.color(*to_disp)) | |
98 else: | |
99 elems.extend(to_disp) | |
100 | |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
101 |
2408 | 102 class ValueWidget(Widget): |
103 def __init__(self, xmlui_parent, value): | |
104 super(ValueWidget, self).__init__(xmlui_parent) | |
105 self.value = value | |
106 | |
107 @property | |
108 def values(self): | |
109 return [self.value] | |
110 | |
111 | |
112 class InputWidget(ValueWidget): | |
113 def __init__(self, xmlui_parent, value, read_only=False): | |
114 super(InputWidget, self).__init__(xmlui_parent, value) | |
115 self.read_only = read_only | |
116 | |
117 def _xmluiGetValue(self): | |
118 return self.value | |
119 | |
120 | |
121 class OptionsWidget(Widget): | |
122 def __init__(self, xmlui_parent, options, selected, style): | |
123 super(OptionsWidget, self).__init__(xmlui_parent) | |
124 self.options = options | |
125 self.selected = selected | |
126 self.style = style | |
127 | |
128 @property | |
129 def values(self): | |
130 return self.selected | |
131 | |
132 @values.setter | |
133 def values(self, values): | |
134 self.selected = values | |
135 | |
136 @property | |
137 def value(self): | |
138 return self.selected[0] | |
139 | |
140 @value.setter | |
141 def value(self, value): | |
142 self.selected = [value] | |
143 | |
144 def _xmluiSelectValue(self, value): | |
145 self.value = value | |
146 | |
147 def _xmluiSelectValues(self, values): | |
148 self.values = values | |
149 | |
150 def _xmluiGetSelectedValues(self): | |
151 return self.values | |
152 | |
153 @property | |
154 def labels(self): | |
155 """return only labels from self.items""" | |
156 for value, label in self.items: | |
157 yield label | |
158 | |
159 @property | |
160 def items(self): | |
161 """return suitable items, according to style""" | |
162 no_select = self.no_select | |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
163 for value, label in self.options: |
2408 | 164 if no_select or value in self.selected: |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
165 yield value, label |
2408 | 166 |
167 @property | |
168 def inline(self): | |
3028 | 169 return "inline" in self.style |
2408 | 170 |
171 @property | |
172 def no_select(self): | |
3028 | 173 return "noselect" in self.style |
2408 | 174 |
175 | |
2669
bdb8276fd2da
frontends (xmlui): class_map is now an arg of create function:
Goffi <goffi@goffi.org>
parents:
2624
diff
changeset
|
176 class EmptyWidget(xmlui_base.EmptyWidget, Widget): |
2739
e8dc00f612fb
jp (xmlui): JidWidget + small improvments:
Goffi <goffi@goffi.org>
parents:
2669
diff
changeset
|
177 def __init__(self, xmlui_parent): |
e8dc00f612fb
jp (xmlui): JidWidget + small improvments:
Goffi <goffi@goffi.org>
parents:
2669
diff
changeset
|
178 Widget.__init__(self, xmlui_parent) |
e8dc00f612fb
jp (xmlui): JidWidget + small improvments:
Goffi <goffi@goffi.org>
parents:
2669
diff
changeset
|
179 |
3040 | 180 async def show(self): |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
181 self.host.disp("") |
2408 | 182 |
183 | |
2669
bdb8276fd2da
frontends (xmlui): class_map is now an arg of create function:
Goffi <goffi@goffi.org>
parents:
2624
diff
changeset
|
184 class TextWidget(xmlui_base.TextWidget, ValueWidget): |
3028 | 185 type = "text" |
2408 | 186 |
3040 | 187 async def show(self): |
2408 | 188 self.host.disp(self.value) |
189 | |
190 | |
2669
bdb8276fd2da
frontends (xmlui): class_map is now an arg of create function:
Goffi <goffi@goffi.org>
parents:
2624
diff
changeset
|
191 class LabelWidget(xmlui_base.LabelWidget, ValueWidget): |
3028 | 192 type = "label" |
2408 | 193 |
194 @property | |
195 def for_name(self): | |
196 try: | |
197 return self._xmlui_for_name | |
198 except AttributeError: | |
199 return None | |
200 | |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
201 async def show(self, end="\n", ansi=""): |
2408 | 202 """show label |
203 | |
3407
2f0be2b7de68
jp: replace `no_lf` argument by `end` in `disp` (same as in `print`)
Goffi <goffi@goffi.org>
parents:
3212
diff
changeset
|
204 @param end(str): same as for [JP.disp] |
2408 | 205 @param ansi(unicode): ansi escape code to print before label |
206 """ | |
3407
2f0be2b7de68
jp: replace `no_lf` argument by `end` in `disp` (same as in `print`)
Goffi <goffi@goffi.org>
parents:
3212
diff
changeset
|
207 self.disp(A.color(ansi, self.value), end=end) |
2408 | 208 |
209 | |
2739
e8dc00f612fb
jp (xmlui): JidWidget + small improvments:
Goffi <goffi@goffi.org>
parents:
2669
diff
changeset
|
210 class JidWidget(xmlui_base.JidWidget, TextWidget): |
3028 | 211 type = "jid" |
2739
e8dc00f612fb
jp (xmlui): JidWidget + small improvments:
Goffi <goffi@goffi.org>
parents:
2669
diff
changeset
|
212 |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
213 |
2669
bdb8276fd2da
frontends (xmlui): class_map is now an arg of create function:
Goffi <goffi@goffi.org>
parents:
2624
diff
changeset
|
214 class StringWidget(xmlui_base.StringWidget, InputWidget): |
3028 | 215 type = "string" |
2408 | 216 |
3040 | 217 async def show(self): |
2961
620bbcec884c
jp (xmlui): check root read_only status in addition to widget one
Goffi <goffi@goffi.org>
parents:
2960
diff
changeset
|
218 if self.read_only or self.root.read_only: |
2408 | 219 self.disp(self.value) |
220 else: | |
221 elems = [] | |
222 self.verboseName(elems) | |
223 if self.value: | |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
224 elems.append(_("(enter: {value})").format(value=self.value)) |
3028 | 225 elems.extend([C.A_HEADER, "> "]) |
3040 | 226 value = await self.host.ainput(A.color(*elems)) |
2408 | 227 if value: |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
228 # TODO: empty value should be possible |
2408 | 229 # an escape key should be used for default instead of enter with empty value |
230 self.value = value | |
231 | |
232 | |
2669
bdb8276fd2da
frontends (xmlui): class_map is now an arg of create function:
Goffi <goffi@goffi.org>
parents:
2624
diff
changeset
|
233 class JidInputWidget(xmlui_base.JidInputWidget, StringWidget): |
3028 | 234 type = "jid_input" |
2408 | 235 |
236 | |
3748 | 237 class PasswordWidget(xmlui_base.PasswordWidget, StringWidget): |
238 type = "password" | |
239 | |
240 | |
2669
bdb8276fd2da
frontends (xmlui): class_map is now an arg of create function:
Goffi <goffi@goffi.org>
parents:
2624
diff
changeset
|
241 class TextBoxWidget(xmlui_base.TextWidget, StringWidget): |
3028 | 242 type = "textbox" |
2939
18a98a541f7a
jp (xmlui manager): basic handling of multi-lines text in TextBoxWidget
Goffi <goffi@goffi.org>
parents:
2783
diff
changeset
|
243 # TODO: use a more advanced input method |
18a98a541f7a
jp (xmlui manager): basic handling of multi-lines text in TextBoxWidget
Goffi <goffi@goffi.org>
parents:
2783
diff
changeset
|
244 |
3040 | 245 async def show(self): |
2939
18a98a541f7a
jp (xmlui manager): basic handling of multi-lines text in TextBoxWidget
Goffi <goffi@goffi.org>
parents:
2783
diff
changeset
|
246 self.verboseName() |
2961
620bbcec884c
jp (xmlui): check root read_only status in addition to widget one
Goffi <goffi@goffi.org>
parents:
2960
diff
changeset
|
247 if self.read_only or self.root.read_only: |
2939
18a98a541f7a
jp (xmlui manager): basic handling of multi-lines text in TextBoxWidget
Goffi <goffi@goffi.org>
parents:
2783
diff
changeset
|
248 self.disp(self.value) |
18a98a541f7a
jp (xmlui manager): basic handling of multi-lines text in TextBoxWidget
Goffi <goffi@goffi.org>
parents:
2783
diff
changeset
|
249 else: |
18a98a541f7a
jp (xmlui manager): basic handling of multi-lines text in TextBoxWidget
Goffi <goffi@goffi.org>
parents:
2783
diff
changeset
|
250 if self.value: |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
251 self.disp( |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
252 A.color(C.A_HEADER, "↓ current value ↓\n", A.FG_CYAN, self.value, "") |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
253 ) |
2939
18a98a541f7a
jp (xmlui manager): basic handling of multi-lines text in TextBoxWidget
Goffi <goffi@goffi.org>
parents:
2783
diff
changeset
|
254 |
18a98a541f7a
jp (xmlui manager): basic handling of multi-lines text in TextBoxWidget
Goffi <goffi@goffi.org>
parents:
2783
diff
changeset
|
255 values = [] |
18a98a541f7a
jp (xmlui manager): basic handling of multi-lines text in TextBoxWidget
Goffi <goffi@goffi.org>
parents:
2783
diff
changeset
|
256 while True: |
18a98a541f7a
jp (xmlui manager): basic handling of multi-lines text in TextBoxWidget
Goffi <goffi@goffi.org>
parents:
2783
diff
changeset
|
257 try: |
18a98a541f7a
jp (xmlui manager): basic handling of multi-lines text in TextBoxWidget
Goffi <goffi@goffi.org>
parents:
2783
diff
changeset
|
258 if not values: |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
259 line = await self.host.ainput( |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
260 A.color(C.A_HEADER, "[Ctrl-D to finish]> ") |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
261 ) |
2939
18a98a541f7a
jp (xmlui manager): basic handling of multi-lines text in TextBoxWidget
Goffi <goffi@goffi.org>
parents:
2783
diff
changeset
|
262 else: |
3040 | 263 line = await self.host.ainput() |
2939
18a98a541f7a
jp (xmlui manager): basic handling of multi-lines text in TextBoxWidget
Goffi <goffi@goffi.org>
parents:
2783
diff
changeset
|
264 values.append(line) |
18a98a541f7a
jp (xmlui manager): basic handling of multi-lines text in TextBoxWidget
Goffi <goffi@goffi.org>
parents:
2783
diff
changeset
|
265 except EOFError: |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
266 break |
2939
18a98a541f7a
jp (xmlui manager): basic handling of multi-lines text in TextBoxWidget
Goffi <goffi@goffi.org>
parents:
2783
diff
changeset
|
267 |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
268 self.value = "\n".join(values).rstrip() |
2408 | 269 |
270 | |
2783
442ab697f831
frontends, jp, templates: added XHTMLBox widget:
Goffi <goffi@goffi.org>
parents:
2771
diff
changeset
|
271 class XHTMLBoxWidget(xmlui_base.XHTMLBoxWidget, StringWidget): |
3028 | 272 type = "xhtmlbox" |
2783
442ab697f831
frontends, jp, templates: added XHTMLBox widget:
Goffi <goffi@goffi.org>
parents:
2771
diff
changeset
|
273 |
3040 | 274 async def show(self): |
2783
442ab697f831
frontends, jp, templates: added XHTMLBox widget:
Goffi <goffi@goffi.org>
parents:
2771
diff
changeset
|
275 # FIXME: we use bridge in a blocking way as permitted by python-dbus |
442ab697f831
frontends, jp, templates: added XHTMLBox widget:
Goffi <goffi@goffi.org>
parents:
2771
diff
changeset
|
276 # this only for now to make it simpler, it must be refactored |
442ab697f831
frontends, jp, templates: added XHTMLBox widget:
Goffi <goffi@goffi.org>
parents:
2771
diff
changeset
|
277 # to use async when jp will be fully async (expected for 0.8) |
3040 | 278 self.value = await self.host.bridge.syntaxConvert( |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
279 self.value, C.SYNTAX_XHTML, "markdown", False, self.host.profile |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
280 ) |
3040 | 281 await super(XHTMLBoxWidget, self).show() |
2783
442ab697f831
frontends, jp, templates: added XHTMLBox widget:
Goffi <goffi@goffi.org>
parents:
2771
diff
changeset
|
282 |
442ab697f831
frontends, jp, templates: added XHTMLBox widget:
Goffi <goffi@goffi.org>
parents:
2771
diff
changeset
|
283 |
2669
bdb8276fd2da
frontends (xmlui): class_map is now an arg of create function:
Goffi <goffi@goffi.org>
parents:
2624
diff
changeset
|
284 class ListWidget(xmlui_base.ListWidget, OptionsWidget): |
3028 | 285 type = "list" |
2408 | 286 # TODO: handle flags, notably multi |
287 | |
3040 | 288 async def show(self): |
2541
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
289 if self.root.values_only: |
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
290 for value in self.values: |
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
291 self.disp(self.value) |
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
292 return |
2408 | 293 if not self.options: |
294 return | |
295 | |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
296 # list display |
2408 | 297 self.verboseName() |
298 | |
299 for idx, (value, label) in enumerate(self.options): | |
300 elems = [] | |
2541
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
301 if not self.root.read_only: |
3028 | 302 elems.extend([C.A_SUBHEADER, str(idx), A.RESET, ": "]) |
2408 | 303 elems.append(label) |
304 self.verboseName(elems, value) | |
305 self.disp(A.color(*elems)) | |
306 | |
2541
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
307 if self.root.read_only: |
2408 | 308 return |
309 | |
310 if len(self.options) == 1: | |
311 # we have only one option, no need to ask | |
312 self.value = self.options[0][0] | |
313 return | |
314 | |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
315 # we ask use to choose an option |
2408 | 316 choice = None |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
317 limit_max = len(self.options) - 1 |
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
318 while choice is None or choice < 0 or choice > limit_max: |
3040 | 319 choice = await self.host.ainput( |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
320 A.color( |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
321 C.A_HEADER, |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
322 _("your choice (0-{limit_max}): ").format(limit_max=limit_max), |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
323 ) |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
324 ) |
2408 | 325 try: |
326 choice = int(choice) | |
327 except ValueError: | |
328 choice = None | |
329 self.value = self.options[choice][0] | |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
330 self.disp("") |
2408 | 331 |
332 | |
2669
bdb8276fd2da
frontends (xmlui): class_map is now an arg of create function:
Goffi <goffi@goffi.org>
parents:
2624
diff
changeset
|
333 class BoolWidget(xmlui_base.BoolWidget, InputWidget): |
3028 | 334 type = "bool" |
2408 | 335 |
3040 | 336 async def show(self): |
3028 | 337 disp_true = A.color(A.FG_GREEN, "TRUE") |
338 disp_false = A.color(A.FG_RED, "FALSE") | |
2961
620bbcec884c
jp (xmlui): check root read_only status in addition to widget one
Goffi <goffi@goffi.org>
parents:
2960
diff
changeset
|
339 if self.read_only or self.root.read_only: |
2408 | 340 self.disp(disp_true if self.value else disp_false) |
341 else: | |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
342 self.disp( |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
343 A.color( |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
344 C.A_HEADER, "0: ", disp_false, A.RESET, " *" if not self.value else "" |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
345 ) |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
346 ) |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
347 self.disp( |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
348 A.color(C.A_HEADER, "1: ", disp_true, A.RESET, " *" if self.value else "") |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
349 ) |
2408 | 350 choice = None |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
351 while choice not in ("0", "1"): |
3028 | 352 elems = [C.A_HEADER, _("your choice (0,1): ")] |
2408 | 353 self.verboseName(elems) |
3040 | 354 choice = await self.host.ainput(A.color(*elems)) |
2408 | 355 self.value = bool(int(choice)) |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
356 self.disp("") |
2408 | 357 |
358 def _xmluiGetValue(self): | |
359 return C.boolConst(self.value) | |
360 | |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
361 ## Containers ## |
2408 | 362 |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
363 |
2408 | 364 class Container(Base): |
3028 | 365 category = "container" |
2408 | 366 |
367 def __init__(self, xmlui_parent): | |
368 super(Container, self).__init__(xmlui_parent) | |
369 self.children = [] | |
370 | |
371 def __iter__(self): | |
372 return iter(self.children) | |
373 | |
374 def _xmluiAppend(self, widget): | |
375 self.children.append(widget) | |
376 | |
2541
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
377 def _xmluiRemove(self, widget): |
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
378 self.children.remove(widget) |
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
379 |
3040 | 380 async def show(self): |
2408 | 381 for child in self.children: |
3040 | 382 await child.show() |
2408 | 383 |
384 | |
2669
bdb8276fd2da
frontends (xmlui): class_map is now an arg of create function:
Goffi <goffi@goffi.org>
parents:
2624
diff
changeset
|
385 class VerticalContainer(xmlui_base.VerticalContainer, Container): |
3028 | 386 type = "vertical" |
2408 | 387 |
388 | |
2669
bdb8276fd2da
frontends (xmlui): class_map is now an arg of create function:
Goffi <goffi@goffi.org>
parents:
2624
diff
changeset
|
389 class PairsContainer(xmlui_base.PairsContainer, Container): |
3028 | 390 type = "pairs" |
2408 | 391 |
392 | |
2669
bdb8276fd2da
frontends (xmlui): class_map is now an arg of create function:
Goffi <goffi@goffi.org>
parents:
2624
diff
changeset
|
393 class LabelContainer(xmlui_base.PairsContainer, Container): |
3028 | 394 type = "label" |
2408 | 395 |
3040 | 396 async def show(self): |
2408 | 397 for child in self.children: |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
398 end = "\n" |
2408 | 399 # we check linked widget type |
400 # to see if we want the label on the same line or not | |
3028 | 401 if child.type == "label": |
2408 | 402 for_name = child.for_name |
2739
e8dc00f612fb
jp (xmlui): JidWidget + small improvments:
Goffi <goffi@goffi.org>
parents:
2669
diff
changeset
|
403 if for_name: |
2408 | 404 for_widget = self.root.widgets[for_name] |
405 wid_type = for_widget.type | |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
406 if self.root.values_only or wid_type in ( |
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
407 "text", |
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
408 "string", |
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
409 "jid_input", |
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
410 ): |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
411 end = " " |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
412 elif wid_type == "bool" and for_widget.read_only: |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
413 end = " " |
3407
2f0be2b7de68
jp: replace `no_lf` argument by `end` in `disp` (same as in `print`)
Goffi <goffi@goffi.org>
parents:
3212
diff
changeset
|
414 await child.show(end=end, ansi=A.FG_CYAN) |
2408 | 415 else: |
3040 | 416 await child.show() |
2408 | 417 |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
418 ## Dialogs ## |
2408 | 419 |
420 | |
421 class Dialog(object): | |
422 def __init__(self, xmlui_parent): | |
423 self.xmlui_parent = xmlui_parent | |
424 self.host = self.xmlui_parent.host | |
425 | |
426 def disp(self, *args, **kwargs): | |
427 self.host.disp(*args, **kwargs) | |
428 | |
3040 | 429 async def show(self): |
2408 | 430 """display current dialog |
431 | |
432 must be overriden by subclasses | |
433 """ | |
434 raise NotImplementedError(self.__class__) | |
435 | |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
436 |
3040 | 437 class MessageDialog(xmlui_base.MessageDialog, Dialog): |
438 def __init__(self, xmlui_parent, title, message, level): | |
439 Dialog.__init__(self, xmlui_parent) | |
440 xmlui_base.MessageDialog.__init__(self, xmlui_parent) | |
441 self.title, self.message, self.level = title, message, level | |
442 | |
443 async def show(self): | |
444 # TODO: handle level | |
445 if self.title: | |
446 self.disp(A.color(C.A_HEADER, self.title)) | |
447 self.disp(self.message) | |
448 | |
2408 | 449 |
2669
bdb8276fd2da
frontends (xmlui): class_map is now an arg of create function:
Goffi <goffi@goffi.org>
parents:
2624
diff
changeset
|
450 class NoteDialog(xmlui_base.NoteDialog, Dialog): |
2739
e8dc00f612fb
jp (xmlui): JidWidget + small improvments:
Goffi <goffi@goffi.org>
parents:
2669
diff
changeset
|
451 def __init__(self, xmlui_parent, title, message, level): |
e8dc00f612fb
jp (xmlui): JidWidget + small improvments:
Goffi <goffi@goffi.org>
parents:
2669
diff
changeset
|
452 Dialog.__init__(self, xmlui_parent) |
e8dc00f612fb
jp (xmlui): JidWidget + small improvments:
Goffi <goffi@goffi.org>
parents:
2669
diff
changeset
|
453 xmlui_base.NoteDialog.__init__(self, xmlui_parent) |
2408 | 454 self.title, self.message, self.level = title, message, level |
455 | |
3040 | 456 async def show(self): |
3093
d909473a76cc
jp (xmlui_manager): use level for notes:
Goffi <goffi@goffi.org>
parents:
3040
diff
changeset
|
457 # TODO: handle title |
d909473a76cc
jp (xmlui_manager): use level for notes:
Goffi <goffi@goffi.org>
parents:
3040
diff
changeset
|
458 error = self.level in (C.XMLUI_DATA_LVL_WARNING, C.XMLUI_DATA_LVL_ERROR) |
d909473a76cc
jp (xmlui_manager): use level for notes:
Goffi <goffi@goffi.org>
parents:
3040
diff
changeset
|
459 if self.level == C.XMLUI_DATA_LVL_WARNING: |
d909473a76cc
jp (xmlui_manager): use level for notes:
Goffi <goffi@goffi.org>
parents:
3040
diff
changeset
|
460 msg = A.color(C.A_WARNING, self.message) |
d909473a76cc
jp (xmlui_manager): use level for notes:
Goffi <goffi@goffi.org>
parents:
3040
diff
changeset
|
461 elif self.level == C.XMLUI_DATA_LVL_ERROR: |
d909473a76cc
jp (xmlui_manager): use level for notes:
Goffi <goffi@goffi.org>
parents:
3040
diff
changeset
|
462 msg = A.color(C.A_FAILURE, self.message) |
d909473a76cc
jp (xmlui_manager): use level for notes:
Goffi <goffi@goffi.org>
parents:
3040
diff
changeset
|
463 else: |
d909473a76cc
jp (xmlui_manager): use level for notes:
Goffi <goffi@goffi.org>
parents:
3040
diff
changeset
|
464 msg = self.message |
d909473a76cc
jp (xmlui_manager): use level for notes:
Goffi <goffi@goffi.org>
parents:
3040
diff
changeset
|
465 self.disp(msg, error=error) |
3040 | 466 |
467 | |
468 class ConfirmDialog(xmlui_base.ConfirmDialog, Dialog): | |
469 def __init__(self, xmlui_parent, title, message, level, buttons_set): | |
470 Dialog.__init__(self, xmlui_parent) | |
471 xmlui_base.ConfirmDialog.__init__(self, xmlui_parent) | |
472 self.title, self.message, self.level, self.buttons_set = ( | |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
473 title, |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
474 message, |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
475 level, |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
476 buttons_set, |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
477 ) |
3040 | 478 |
479 async def show(self): | |
480 # TODO: handle buttons_set and level | |
481 self.disp(self.message) | |
482 if self.title: | |
483 self.disp(A.color(C.A_HEADER, self.title)) | |
484 input_ = None | |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
485 while input_ not in ("y", "n"): |
3040 | 486 input_ = await self.host.ainput(f"{self.message} (y/n)? ") |
487 input_ = input_.lower() | |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
488 if input_ == "y": |
3040 | 489 self._xmluiValidated() |
490 else: | |
491 self._xmluiCancelled() | |
492 | |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
493 ## Factory ## |
2408 | 494 |
495 | |
496 class WidgetFactory(object): | |
497 def __getattr__(self, attr): | |
498 if attr.startswith("create"): | |
499 cls = globals()[attr[6:]] | |
500 return cls | |
501 | |
502 | |
3040 | 503 class XMLUIPanel(xmlui_base.AIOXMLUIPanel): |
2408 | 504 widget_factory = WidgetFactory() |
505 _actions = 0 # use to keep track of bridge's launchAction calls | |
2541
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
506 read_only = False |
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
507 values_only = False |
2408 | 508 workflow = None |
509 _submit_cb = None | |
510 | |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
511 def __init__( |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
512 self, |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
513 host, |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
514 parsed_dom, |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
515 title=None, |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
516 flags=None, |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
517 callback=None, |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
518 ignore=None, |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
519 whitelist=None, |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
520 profile=None, |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
521 ): |
2669
bdb8276fd2da
frontends (xmlui): class_map is now an arg of create function:
Goffi <goffi@goffi.org>
parents:
2624
diff
changeset
|
522 xmlui_base.XMLUIPanel.__init__( |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
523 self, |
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
524 host, |
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
525 parsed_dom, |
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
526 title=title, |
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
527 flags=flags, |
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
528 ignore=ignore, |
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
529 whitelist=whitelist, |
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
530 profile=host.profile, |
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
531 ) |
2408 | 532 self.submitted = False |
533 | |
534 @property | |
535 def command(self): | |
536 return self.host.command | |
537 | |
3212
89d97776fd34
jp (xmlui): added missing `disp` method in XMLUIPanel
Goffi <goffi@goffi.org>
parents:
3137
diff
changeset
|
538 def disp(self, *args, **kwargs): |
89d97776fd34
jp (xmlui): added missing `disp` method in XMLUIPanel
Goffi <goffi@goffi.org>
parents:
3137
diff
changeset
|
539 self.host.disp(*args, **kwargs) |
89d97776fd34
jp (xmlui): added missing `disp` method in XMLUIPanel
Goffi <goffi@goffi.org>
parents:
3137
diff
changeset
|
540 |
3040 | 541 async def show(self, workflow=None, read_only=False, values_only=False): |
2408 | 542 """display the panel |
543 | |
544 @param workflow(list, None): command to execute if not None | |
545 put here for convenience, the main workflow is the class attribute | |
546 (because workflow can continue in subclasses) | |
547 command are a list of consts or lists: | |
548 - SUBMIT is the only constant so far, it submits the XMLUI | |
549 - list must contain widget name/widget value to fill | |
2541
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
550 @param read_only(bool): if True, don't request values |
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
551 @param values_only(bool): if True, only show select values (imply read_only) |
2408 | 552 """ |
2541
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
553 self.read_only = read_only |
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
554 self.values_only = values_only |
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
555 if self.values_only: |
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
556 self.read_only = True |
2408 | 557 if workflow: |
558 XMLUIPanel.workflow = workflow | |
559 if XMLUIPanel.workflow: | |
3040 | 560 await self.runWorkflow() |
2408 | 561 else: |
3040 | 562 await self.main_cont.show() |
2408 | 563 |
3040 | 564 async def runWorkflow(self): |
2408 | 565 """loop into workflow commands and execute commands |
566 | |
567 SUBMIT will interrupt workflow (which will be continue on callback) | |
568 @param workflow(list): same as [show] | |
569 """ | |
570 workflow = XMLUIPanel.workflow | |
571 while True: | |
572 try: | |
573 cmd = workflow.pop(0) | |
574 except IndexError: | |
575 break | |
576 if cmd == SUBMIT: | |
3040 | 577 await self.onFormSubmitted() |
2408 | 578 self.submit_id = None # avoid double submit |
579 return | |
580 elif isinstance(cmd, list): | |
581 name, value = cmd | |
2412
7641bef56dcd
jp (xmlui): fixed workflow when value is for a BoolWidget
Goffi <goffi@goffi.org>
parents:
2410
diff
changeset
|
582 widget = self.widgets[name] |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
583 if widget.type == "bool": |
2412
7641bef56dcd
jp (xmlui): fixed workflow when value is for a BoolWidget
Goffi <goffi@goffi.org>
parents:
2410
diff
changeset
|
584 value = C.bool(value) |
7641bef56dcd
jp (xmlui): fixed workflow when value is for a BoolWidget
Goffi <goffi@goffi.org>
parents:
2410
diff
changeset
|
585 widget.value = value |
3040 | 586 await self.show() |
2408 | 587 |
3040 | 588 async def submitForm(self, callback=None): |
2408 | 589 XMLUIPanel._submit_cb = callback |
3040 | 590 await self.onFormSubmitted() |
2408 | 591 |
3040 | 592 async def onFormSubmitted(self, ignore=None): |
593 # self.submitted is a Q&D workaround to avoid | |
2408 | 594 # double submit when a workflow is set |
595 if self.submitted: | |
596 return | |
597 self.submitted = True | |
3040 | 598 await super(XMLUIPanel, self).onFormSubmitted(ignore) |
2408 | 599 |
600 def _xmluiClose(self): | |
601 pass | |
602 | |
3040 | 603 async def _launchActionCb(self, data): |
2408 | 604 XMLUIPanel._actions -= 1 |
605 assert XMLUIPanel._actions >= 0 | |
3028 | 606 if "xmlui" in data: |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
607 xmlui_raw = data["xmlui"] |
2669
bdb8276fd2da
frontends (xmlui): class_map is now an arg of create function:
Goffi <goffi@goffi.org>
parents:
2624
diff
changeset
|
608 xmlui = create(self.host, xmlui_raw) |
3040 | 609 await xmlui.show() |
2408 | 610 if xmlui.submit_id: |
3040 | 611 await xmlui.onFormSubmitted() |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
612 # TODO: handle data other than XMLUI |
2408 | 613 if not XMLUIPanel._actions: |
614 if self._submit_cb is None: | |
615 self.host.quit() | |
616 else: | |
617 self._submit_cb() | |
618 | |
3040 | 619 async def _xmluiLaunchAction(self, action_id, data): |
2408 | 620 XMLUIPanel._actions += 1 |
3040 | 621 try: |
622 data = await self.host.bridge.launchAction( | |
623 action_id, | |
624 data, | |
625 self.profile, | |
626 ) | |
627 except Exception as e: | |
628 self.disp(f"can't launch XMLUI action: {e}", error=True) | |
629 self.host.quit(C.EXIT_BRIDGE_ERRBACK) | |
630 else: | |
631 await self._launchActionCb(data) | |
2408 | 632 |
633 | |
2669
bdb8276fd2da
frontends (xmlui): class_map is now an arg of create function:
Goffi <goffi@goffi.org>
parents:
2624
diff
changeset
|
634 class XMLUIDialog(xmlui_base.XMLUIDialog): |
2624
56f94936df1e
code style reformatting using black
Goffi <goffi@goffi.org>
parents:
2562
diff
changeset
|
635 type = "dialog" |
2408 | 636 dialog_factory = WidgetFactory() |
2541
65695b9343d3
jp (xmlui): added whitelist, read_only and values_only options:
Goffi <goffi@goffi.org>
parents:
2483
diff
changeset
|
637 read_only = False |
2408 | 638 |
3040 | 639 async def show(self, __=None): |
640 await self.dlg.show() | |
2408 | 641 |
642 def _xmluiClose(self): | |
643 pass | |
644 | |
645 | |
3568
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
646 create = partial( |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
647 xmlui_base.create, |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
648 class_map={xmlui_base.CLASS_PANEL: XMLUIPanel, xmlui_base.CLASS_DIALOG: XMLUIDialog}, |
04283582966f
core, frontends: fix invalid translatable strings.
Goffi <goffi@goffi.org>
parents:
3479
diff
changeset
|
649 ) |