Codebase list lightdm-gtk-greeter-settings / 0f9cc4f
IconEntry updated Andrew P. 9 years ago
7 changed file(s) with 208 addition(s) and 184 deletion(s). Raw diff Collapse all Expand all
33 <requires lib="gtk+" version="3.8"/>
44 <requires lib="" version="3.8"/>
55 <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>
246 <object class="GtkListStore" id="greeter_indicators_model">
257 <columns>
268 <!-- column-name name -->
473455 <property name="can_focus">True</property>
474456 <property name="receives_default">True</property>
475457 <property name="halign">start</property>
476 <property name="popup">greeter_default-user-image_menu</property>
477458 <child>
478459 <object class="GtkImage" id="greeter_default-user-image_image">
479460 <property name="width_request">64</property>
2424 <mime-types>
2525 <mime-type>application/x-sharedlib</mime-type>
2626 </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>
5327 </object>
5428 <object class="GtkListStore" id="option_path_model">
5529 <columns>
267241 <property name="can_focus">True</property>
268242 <property name="receives_default">True</property>
269243 <property name="halign">start</property>
270 <property name="popup">option_image_menu</property>
271244 <child>
272245 <object class="GtkBox" id="box3">
273246 <property name="visible">True</property>
3232
3333 from lightdm_gtk_greeter_settings import (
3434 helpers,
35 IconEntry,
3536 IndicatorsEntry,
3637 OptionEntry,
3738 PositionEntry)
99100 'background': (OptionEntry.BackgroundEntry, '#000000'),
100101 'user-background': (OptionEntry.BooleanEntry, 'true'),
101102 'hide-user-image': (OptionEntry.InvertedBooleanEntry, 'false'),
102 'default-user-image': (OptionEntry.IconEntry, '#avatar-default'),
103 'default-user-image': (IconEntry.IconEntry, '#avatar-default'),
103104 # Panel
104105 'clock-format': (OptionEntry.ClockFormatEntry, '%a, %H:%M'),
105106 '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())
2222
2323 from gi.repository import Gtk
2424
25 from lightdm_gtk_greeter_settings import OptionEntry
25 from lightdm_gtk_greeter_settings import (
26 IconEntry,
27 OptionEntry)
2628 from lightdm_gtk_greeter_settings.helpers import (
2729 C_,
2830 bool2string,
5052 Icon = ()
5153
5254
53 class IndicatorIconEntry(OptionEntry.IconEntry):
55 class IndicatorIconEntry(IconEntry.IconEntry):
56
57 DefaultValue = ()
5458
5559 def __init__(self, widgets):
60 self._label = widgets['label']
5661 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)
6062
6163 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
8279 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
8585
8686
8787 class IndicatorTypeEntry(OptionEntry.BaseEntry):
1515 # with this program. If not, see <http://www.gnu.org/licenses/>.
1616
1717
18 import os
1918 import time
2019 from locale import gettext as _
2120
2928 C_,
3029 bool2string,
3130 string2bool, SimpleEnum)
32 from lightdm_gtk_greeter_settings.IconChooserDialog import IconChooserDialog
3331
3432
3533 __all__ = [
4038 'BooleanEntry',
4139 'ChoiceEntry',
4240 'ClockFormatEntry',
43 'IconEntry',
4441 'InvertedBooleanEntry',
4542 'StringEntry',
4643 'StringPathEntry']
383380 self._value.props.font_name = value or ''
384381
385382
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
493383 class AccessibilityStatesEntry(BaseEntry):
494384
495385 Options = ('keyboard', 'reader', 'contrast', 'font')
6969 'ModelRowEnum',
7070 'NC_',
7171 'pixbuf_from_file_scaled_down',
72 'set_image_from_path',
7273 'show_message',
7374 'SimpleEnum',
7475 'string2bool',
140141 pixbuf.props.height / scale,
141142 GdkPixbuf.InterpType.BILINEAR)
142143 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
143160
144161
145162 def check_path_accessibility(path, file=True, executable=False):
281298 def __iter__(self):
282299 return (self.__dict__[k] for k in self._dict)
283300
301 def __repr__(self):
302 return repr(tuple((k, self.__dict__[k]) for k in self._dict))
303
284304 @classmethod
285305 def _accept_member_(cls, name, value):
286306 return not name.startswith('_') and not name.endswith('_')