IconEntry updated
Andrew P.
9 years ago
3 | 3 | <requires lib="gtk+" version="3.8"/> |
4 | 4 | <requires lib="" version="3.8"/> |
5 | 5 | <requires lib="gtk_greeter_settings" version="1.0"/> |
6 | <object class="GtkMenu" id="greeter_default-user-image_menu"> | |
7 | <property name="visible">True</property> | |
8 | <property name="can_focus">False</property> | |
9 | <child> | |
10 | <object class="GtkMenuItem" id="greeter_default-user-image_icon_item"> | |
11 | <property name="visible">True</property> | |
12 | <property name="can_focus">False</property> | |
13 | <property name="label" translatable="yes" context="option|greeter|default-user-image">Icon</property> | |
14 | </object> | |
15 | </child> | |
16 | <child> | |
17 | <object class="GtkMenuItem" id="greeter_default-user-image_path_item"> | |
18 | <property name="visible">True</property> | |
19 | <property name="can_focus">False</property> | |
20 | <property name="label" translatable="yes" context="option|greeter|default-user-image">Path</property> | |
21 | </object> | |
22 | </child> | |
23 | </object> | |
24 | 6 | <object class="GtkListStore" id="greeter_indicators_model"> |
25 | 7 | <columns> |
26 | 8 | <!-- column-name name --> |
473 | 455 | <property name="can_focus">True</property> |
474 | 456 | <property name="receives_default">True</property> |
475 | 457 | <property name="halign">start</property> |
476 | <property name="popup">greeter_default-user-image_menu</property> | |
477 | 458 | <child> |
478 | 459 | <object class="GtkImage" id="greeter_default-user-image_image"> |
479 | 460 | <property name="width_request">64</property> |
24 | 24 | <mime-types> |
25 | 25 | <mime-type>application/x-sharedlib</mime-type> |
26 | 26 | </mime-types> |
27 | </object> | |
28 | <object class="GtkMenu" id="option_image_menu"> | |
29 | <property name="visible">True</property> | |
30 | <property name="can_focus">False</property> | |
31 | <child> | |
32 | <object class="GtkMenuItem" id="option_image_default_item"> | |
33 | <property name="visible">True</property> | |
34 | <property name="can_focus">False</property> | |
35 | <property name="label">[default]</property> | |
36 | <property name="use_underline">True</property> | |
37 | </object> | |
38 | </child> | |
39 | <child> | |
40 | <object class="GtkMenuItem" id="option_image_icon_item"> | |
41 | <property name="visible">True</property> | |
42 | <property name="can_focus">False</property> | |
43 | <property name="label">[icon]</property> | |
44 | </object> | |
45 | </child> | |
46 | <child> | |
47 | <object class="GtkMenuItem" id="option_image_path_item"> | |
48 | <property name="visible">True</property> | |
49 | <property name="can_focus">False</property> | |
50 | <property name="label">[path]</property> | |
51 | </object> | |
52 | </child> | |
53 | 27 | </object> |
54 | 28 | <object class="GtkListStore" id="option_path_model"> |
55 | 29 | <columns> |
267 | 241 | <property name="can_focus">True</property> |
268 | 242 | <property name="receives_default">True</property> |
269 | 243 | <property name="halign">start</property> |
270 | <property name="popup">option_image_menu</property> | |
271 | 244 | <child> |
272 | 245 | <object class="GtkBox" id="box3"> |
273 | 246 | <property name="visible">True</property> |
32 | 32 | |
33 | 33 | from lightdm_gtk_greeter_settings import ( |
34 | 34 | helpers, |
35 | IconEntry, | |
35 | 36 | IndicatorsEntry, |
36 | 37 | OptionEntry, |
37 | 38 | PositionEntry) |
99 | 100 | 'background': (OptionEntry.BackgroundEntry, '#000000'), |
100 | 101 | 'user-background': (OptionEntry.BooleanEntry, 'true'), |
101 | 102 | 'hide-user-image': (OptionEntry.InvertedBooleanEntry, 'false'), |
102 | 'default-user-image': (OptionEntry.IconEntry, '#avatar-default'), | |
103 | 'default-user-image': (IconEntry.IconEntry, '#avatar-default'), | |
103 | 104 | # Panel |
104 | 105 | 'clock-format': (OptionEntry.ClockFormatEntry, '%a, %H:%M'), |
105 | 106 | 'indicators': (IndicatorsEntry.IndicatorsEntry, |
0 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | |
1 | # LightDM GTK Greeter Settings | |
2 | # Copyright (C) 2015 Andrew P. <pan.pav.7c5@gmail.com> | |
3 | # | |
4 | # This program is free software: you can redistribute it and/or modify it | |
5 | # under the terms of the GNU General Public License version 3, as published | |
6 | # by the Free Software Foundation. | |
7 | # | |
8 | # This program is distributed in the hope that it will be useful, but | |
9 | # WITHOUT ANY WARRANTY; without even the implied warranties of | |
10 | # MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR | |
11 | # PURPOSE. See the GNU General Public License for more details. | |
12 | # | |
13 | # You should have received a copy of the GNU General Public License along | |
14 | # with this program. If not, see <http://www.gnu.org/licenses/>. | |
15 | ||
16 | ||
17 | import os | |
18 | ||
19 | from gi.repository import Gtk | |
20 | ||
21 | from lightdm_gtk_greeter_settings.IconChooserDialog import IconChooserDialog | |
22 | from lightdm_gtk_greeter_settings.OptionEntry import BaseEntry | |
23 | from lightdm_gtk_greeter_settings.helpers import ( | |
24 | C_, | |
25 | get_data_path, | |
26 | set_image_from_path, | |
27 | SimpleEnum) | |
28 | ||
29 | ||
30 | __all__ = ['IconEntry'] | |
31 | ||
32 | ||
33 | class IconEntry(BaseEntry): | |
34 | ||
35 | class Item(SimpleEnum): | |
36 | priority = 0 | |
37 | # (value, just_label) => (label, tooltip) | |
38 | update = None | |
39 | # (old_value) => str or None | |
40 | ask = None | |
41 | # Associated menu item | |
42 | menuitem = None | |
43 | ||
44 | def __init__(self, widgets): | |
45 | super().__init__(widgets) | |
46 | self._value = None | |
47 | self._image = widgets['image'] | |
48 | self._button = widgets['button'] | |
49 | self._button.props.popup = Gtk.Menu() | |
50 | self._widgets_to_disable.append(self._button) | |
51 | self._icon_dialog = None | |
52 | self._path_dialog = None | |
53 | self._current_item = None | |
54 | ||
55 | self._items = [] | |
56 | for priority, (update, ask) in self._get_items(): | |
57 | item = self.Item(priority=priority, update=update, ask=ask) | |
58 | item.menuitem = Gtk.MenuItem() | |
59 | item.menuitem.props.visible = True | |
60 | item.menuitem.props.label = item.update(None, True)[0] | |
61 | item.menuitem.connect('activate', self._on_item_clicked, item) | |
62 | self._button.props.popup.append(item.menuitem) | |
63 | self._items.append(item) | |
64 | ||
65 | self._items.sort(key=lambda i: i.priority) | |
66 | ||
67 | def _get_value(self): | |
68 | return self._value | |
69 | ||
70 | def _set_value(self, value): | |
71 | applied_item = None | |
72 | tooltip = None | |
73 | for item in self._items: | |
74 | if applied_item: | |
75 | label, __ = item.update(None, True) | |
76 | else: | |
77 | label, tooltip = item.update(value, False) | |
78 | if tooltip: | |
79 | applied_item = item | |
80 | item.menuitem.get_child().set_markup(label) | |
81 | ||
82 | if not applied_item: | |
83 | tooltip = C_('option-entry|icon', 'Unrecognized value: {value}').format(value=value) | |
84 | self._button.set_tooltip_markup(tooltip) | |
85 | ||
86 | self._value = value | |
87 | self._current_item = applied_item | |
88 | ||
89 | self._emit_changed() | |
90 | ||
91 | def _get_items(self): | |
92 | return ((0, (self._update_icon, self._ask_icon)), | |
93 | (100, (self._update_image, self._ask_image))) | |
94 | ||
95 | def _on_item_clicked(self, menuitem, item): | |
96 | value = item.ask(self._value if item == self._current_item else None) | |
97 | if value is not None: | |
98 | self._set_value(value) | |
99 | ||
100 | def _update_icon(self, value, just_label=False): | |
101 | if just_label or value is None or not value.startswith('#'): | |
102 | return C_('option-entry|icon', 'Select icon name...'), None | |
103 | name = value[1:] | |
104 | label = C_('option-entry|icon', '<b>Icon: {icon}</b>').format(icon=name) | |
105 | tooltip = label | |
106 | self._image.set_from_icon_name(name, Gtk.IconSize.DIALOG) | |
107 | return label, tooltip | |
108 | ||
109 | def _ask_icon(self, oldvalue): | |
110 | if not self._icon_dialog: | |
111 | self._icon_dialog = IconChooserDialog() | |
112 | self._icon_dialog.props.transient_for = self._image.get_toplevel() | |
113 | if oldvalue: | |
114 | self._icon_dialog.select_icon(oldvalue[1:]) | |
115 | ||
116 | value = None | |
117 | if self._icon_dialog.run() == Gtk.ResponseType.OK: | |
118 | value = '#' + self._icon_dialog.get_selected_icon() | |
119 | self._icon_dialog.hide() | |
120 | return value | |
121 | ||
122 | def _update_image(self, value, just_label=False): | |
123 | if just_label or value is None: | |
124 | return C_('option-entry|icon', 'Select file...'), None | |
125 | ||
126 | if set_image_from_path(self._image, value): | |
127 | label = C_('option-entry|icon', '<b>File: {path}</b>') | |
128 | else: | |
129 | label = C_('option-entry|icon', '<b>File: {path}</b> (failed to load)') | |
130 | ||
131 | return (label.format(path=os.path.basename(value)), | |
132 | label.format(path=value)) | |
133 | ||
134 | def _ask_image(self, oldvalue): | |
135 | if not self._path_dialog: | |
136 | builder = Gtk.Builder() | |
137 | builder.add_from_file(get_data_path('ImageChooserDialog.ui')) | |
138 | ||
139 | self._path_dialog = builder.get_object('dialog') | |
140 | self._path_dialog.props.transient_for = self._image.get_toplevel() | |
141 | self._path_dialog.connect('update-preview', self._on_update_path_preview) | |
142 | ||
143 | preview_size = self._image.props.pixel_size | |
144 | preview = self._path_dialog.props.preview_widget | |
145 | preview.props.pixel_size = preview_size | |
146 | preview.set_size_request(preview_size, preview_size) | |
147 | ||
148 | if oldvalue is not None: | |
149 | self._path_dialog.select_filename(self._value) | |
150 | ||
151 | value = None | |
152 | if self._path_dialog.run() == Gtk.ResponseType.OK: | |
153 | value = self._path_dialog.get_filename() | |
154 | self._path_dialog.hide() | |
155 | return value | |
156 | ||
157 | def _on_update_path_preview(self, chooser): | |
158 | set_image_from_path(chooser.props.preview_widget, chooser.get_filename()) |
22 | 22 | |
23 | 23 | from gi.repository import Gtk |
24 | 24 | |
25 | from lightdm_gtk_greeter_settings import OptionEntry | |
25 | from lightdm_gtk_greeter_settings import ( | |
26 | IconEntry, | |
27 | OptionEntry) | |
26 | 28 | from lightdm_gtk_greeter_settings.helpers import ( |
27 | 29 | C_, |
28 | 30 | bool2string, |
50 | 52 | Icon = () |
51 | 53 | |
52 | 54 | |
53 | class IndicatorIconEntry(OptionEntry.IconEntry): | |
55 | class IndicatorIconEntry(IconEntry.IconEntry): | |
56 | ||
57 | DefaultValue = () | |
54 | 58 | |
55 | 59 | def __init__(self, widgets): |
60 | self._label = widgets['label'] | |
56 | 61 | super().__init__(widgets) |
57 | self._label = widgets['label'] | |
58 | self._default_item = widgets['default_item'] | |
59 | self._default_item.connect('activate', self._on_select_default) | |
60 | 62 | |
61 | 63 | def _set_value(self, value): |
62 | if value is None: | |
63 | self._on_select_default() | |
64 | else: | |
65 | super()._set_value(value) | |
66 | ||
67 | def _update(self, icon=None, path=None, failed=False, default=None): | |
68 | super()._update(icon, path, failed) | |
69 | if default: | |
70 | markup = C_('option-entry|indicators', '<b>Using default value</b>') | |
71 | self._default_item.get_child().set_markup(markup) | |
72 | self._button.set_tooltip_markup(markup) | |
73 | self._image.props.visible = False | |
74 | else: | |
75 | self._default_item.get_child().set_markup( | |
76 | C_('option-entry|indicators', 'Use default value...')) | |
77 | self._image.props.visible = True | |
78 | self._label.props.label = self._button.get_tooltip_text() | |
79 | ||
80 | def _on_select_default(self, item=None): | |
81 | self._value = None | |
64 | super()._set_value(self.DefaultValue if value is None else value) | |
65 | self._label.set_markup(self._current_item.menuitem.get_label()) | |
66 | self._image.props.visible = value not in (None, self.DefaultValue) | |
67 | ||
68 | def _get_value(self): | |
69 | return super()._get_value() or None | |
70 | ||
71 | def _get_items(self): | |
72 | for item in super()._get_items(): | |
73 | yield item | |
74 | yield -1, (self._update_default, self._ask_default) | |
75 | ||
76 | def _update_default(self, value, just_label): | |
77 | if just_label or value is not self.DefaultValue: | |
78 | return C_('option-entry|indicators', 'Use default value...'), None | |
82 | 79 | self._image.props.icon_name = '' |
83 | self._update(default=True) | |
84 | self._emit_changed() | |
80 | label = C_('option-entry|indicators', '<b>Using default value</b>') | |
81 | return label, label | |
82 | ||
83 | def _ask_default(self, oldvalue): | |
84 | return self.DefaultValue | |
85 | 85 | |
86 | 86 | |
87 | 87 | class IndicatorTypeEntry(OptionEntry.BaseEntry): |
15 | 15 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
16 | 16 | |
17 | 17 | |
18 | import os | |
19 | 18 | import time |
20 | 19 | from locale import gettext as _ |
21 | 20 | |
29 | 28 | C_, |
30 | 29 | bool2string, |
31 | 30 | string2bool, SimpleEnum) |
32 | from lightdm_gtk_greeter_settings.IconChooserDialog import IconChooserDialog | |
33 | 31 | |
34 | 32 | |
35 | 33 | __all__ = [ |
40 | 38 | 'BooleanEntry', |
41 | 39 | 'ChoiceEntry', |
42 | 40 | 'ClockFormatEntry', |
43 | 'IconEntry', | |
44 | 41 | 'InvertedBooleanEntry', |
45 | 42 | 'StringEntry', |
46 | 43 | 'StringPathEntry'] |
383 | 380 | self._value.props.font_name = value or '' |
384 | 381 | |
385 | 382 | |
386 | class IconEntry(BaseEntry): | |
387 | ||
388 | def __init__(self, widgets): | |
389 | super().__init__(widgets) | |
390 | self._value = None | |
391 | self._button = widgets['button'] | |
392 | self._image = widgets['image'] | |
393 | self._icon_item = widgets['icon_item'] | |
394 | self._path_item = widgets['path_item'] | |
395 | self._widgets_to_disable.append(self._button) | |
396 | self._icon_dialog = None | |
397 | self._path_dialog = None | |
398 | ||
399 | self._icon_item.connect('activate', self._on_select_icon) | |
400 | self._path_item.connect('activate', self._on_select_path) | |
401 | ||
402 | def _get_value(self): | |
403 | return self._value | |
404 | ||
405 | def _set_value(self, value): | |
406 | if value.startswith('#'): | |
407 | self._set_icon(value[1:]) | |
408 | else: | |
409 | self._set_path(value) | |
410 | ||
411 | def _set_icon(self, icon): | |
412 | self._value = '#' + icon | |
413 | self._image.set_from_icon_name(icon, Gtk.IconSize.DIALOG) | |
414 | self._update(icon=icon) | |
415 | self._emit_changed() | |
416 | ||
417 | def _set_path(self, path): | |
418 | self._value = path | |
419 | failed = not self._set_image_from_path(self._image, path) | |
420 | self._update(path=path, failed=failed) | |
421 | self._emit_changed() | |
422 | ||
423 | def _update(self, icon=None, path=None, failed=False): | |
424 | if icon: | |
425 | markup = C_('option-entry|icon', '<b>Icon: {icon}</b>').format(icon=icon) | |
426 | self._icon_item.get_child().set_markup(markup) | |
427 | self._button.set_tooltip_markup(markup) | |
428 | else: | |
429 | self._icon_item.get_child().set_markup( | |
430 | C_('option-entry|icon', 'Select icon name...')) | |
431 | ||
432 | if path: | |
433 | if failed: | |
434 | markup = C_('option-entry|icon', '<b>File: {path}</b> (failed to load)') | |
435 | else: | |
436 | markup = C_('option-entry|icon', '<b>File: {path}</b>') | |
437 | markup = markup.format(path=os.path.basename(path)) | |
438 | self._path_item.get_child().set_markup(markup) | |
439 | self._button.set_tooltip_markup(markup) | |
440 | else: | |
441 | self._path_item.get_child().set_markup( | |
442 | C_('option-entry|icon', 'Select file...')) | |
443 | ||
444 | def _set_image_from_path(self, image, path): | |
445 | if not path or not os.path.isfile(path): | |
446 | image.props.icon_name = 'unknown' | |
447 | else: | |
448 | try: | |
449 | width, height = image.get_size_request() | |
450 | if -1 in (width, height): | |
451 | width, height = 64, 64 | |
452 | pixbuf = helpers.pixbuf_from_file_scaled_down(path, width, height) | |
453 | image.set_from_pixbuf(pixbuf) | |
454 | return True | |
455 | except GLib.Error: | |
456 | image.props.icon_name = 'file-broken' | |
457 | return False | |
458 | ||
459 | def _on_select_icon(self, item): | |
460 | if not self._icon_dialog: | |
461 | self._icon_dialog = IconChooserDialog() | |
462 | self._icon_dialog.props.transient_for = self._image.get_toplevel() | |
463 | if self._value and self._value.startswith('#'): | |
464 | self._icon_dialog.select_icon(self._value[1:]) | |
465 | if self._icon_dialog.run() == Gtk.ResponseType.OK: | |
466 | self._set_icon(self._icon_dialog.get_selected_icon()) | |
467 | self._icon_dialog.hide() | |
468 | ||
469 | def _on_select_path(self, item): | |
470 | if not self._path_dialog: | |
471 | builder = Gtk.Builder() | |
472 | builder.add_from_file(helpers.get_data_path('ImageChooserDialog.ui')) | |
473 | ||
474 | self._path_dialog = builder.get_object('dialog') | |
475 | self._path_dialog.props.transient_for = self._image.get_toplevel() | |
476 | self._path_dialog.connect('update-preview', self._on_update_path_preview) | |
477 | ||
478 | preview_size = self._image.props.pixel_size | |
479 | preview = self._path_dialog.props.preview_widget | |
480 | preview.props.pixel_size = preview_size | |
481 | preview.set_size_request(preview_size, preview_size) | |
482 | ||
483 | if self._value: | |
484 | self._path_dialog.select_filename(self._value) | |
485 | if self._path_dialog.run() == Gtk.ResponseType.OK: | |
486 | self._set_path(self._path_dialog.get_filename()) | |
487 | self._path_dialog.hide() | |
488 | ||
489 | def _on_update_path_preview(self, chooser): | |
490 | self._set_image_from_path(chooser.props.preview_widget, chooser.get_filename()) | |
491 | ||
492 | ||
493 | 383 | class AccessibilityStatesEntry(BaseEntry): |
494 | 384 | |
495 | 385 | Options = ('keyboard', 'reader', 'contrast', 'font') |
69 | 69 | 'ModelRowEnum', |
70 | 70 | 'NC_', |
71 | 71 | 'pixbuf_from_file_scaled_down', |
72 | 'set_image_from_path', | |
72 | 73 | 'show_message', |
73 | 74 | 'SimpleEnum', |
74 | 75 | 'string2bool', |
140 | 141 | pixbuf.props.height / scale, |
141 | 142 | GdkPixbuf.InterpType.BILINEAR) |
142 | 143 | return pixbuf |
144 | ||
145 | ||
146 | def set_image_from_path(image, path): | |
147 | if not path or not os.path.isfile(path): | |
148 | image.props.icon_name = 'unknown' | |
149 | else: | |
150 | try: | |
151 | width, height = image.get_size_request() | |
152 | if -1 in (width, height): | |
153 | width, height = 64, 64 | |
154 | pixbuf = pixbuf_from_file_scaled_down(path, width, height) | |
155 | image.set_from_pixbuf(pixbuf) | |
156 | return True | |
157 | except GLib.Error: | |
158 | image.props.icon_name = 'file-broken' | |
159 | return False | |
143 | 160 | |
144 | 161 | |
145 | 162 | def check_path_accessibility(path, file=True, executable=False): |
281 | 298 | def __iter__(self): |
282 | 299 | return (self.__dict__[k] for k in self._dict) |
283 | 300 | |
301 | def __repr__(self): | |
302 | return repr(tuple((k, self.__dict__[k]) for k in self._dict)) | |
303 | ||
284 | 304 | @classmethod |
285 | 305 | def _accept_member_(cls, name, value): |
286 | 306 | return not name.startswith('_') and not name.endswith('_') |