1#!/usr/bin/env python3 2# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- 3# LightDM GTK Greeter Settings 4# Copyright (C) 2014 Andrew P. <pan.pav.7c5@gmail.com> 5# 6# This program is free software: you can redistribute it and/or modify it 7# under the terms of the GNU General Public License version 3, as published 8# by the Free Software Foundation. 9# 10# This program is distributed in the hope that it will be useful, but 11# WITHOUT ANY WARRANTY; without even the implied warranties of 12# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR 13# PURPOSE. See the GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License along 16# with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 19from gi.repository import GObject 20 21from lightdm_gtk_greeter_settings.helpers import WidgetsWrapper 22from lightdm_gtk_greeter_settings.OptionEntry import BaseEntry 23from lightdm_gtk_greeter_settings import helpers 24 25 26__all__ = [ 27 'BaseGroup', 28 'OneToManyEntryAdapter', 29 'SimpleGroup'] 30 31 32# Broken solution - too complex 33class BaseGroup(GObject.GObject): 34 35 def __init__(self, widgets): 36 super().__init__() 37 self.__entries_wrapper = helpers.SimpleDictWrapper(self._get_entry) 38 self.__defaults_wrapper = helpers.SimpleDictWrapper(self._get_default) 39 40 def read(self, config): 41 '''Read group content from specified GreeterConfig object''' 42 raise NotImplementedError(self.__class__) 43 44 def write(self, config, is_changed=None): 45 '''Writes content of this group to specified GreeterConfig object''' 46 raise NotImplementedError(self.__class__) 47 48 def clear(self): 49 '''Removes all entries''' 50 raise NotImplementedError(self.__class__) 51 52 @property 53 def entries(self): 54 '''entries["key"] - key => Entry mapping. Read only.''' 55 return self.__entries_wrapper 56 57 @property 58 def defaults(self): 59 '''defaults["key"] - default value for "key" entry. Read only.''' 60 return self.__defaults_wrapper 61 62 def _get_entry(self, key): 63 raise NotImplementedError(self.__class__) 64 65 def _get_default(self, key): 66 raise NotImplementedError(self.__class__) 67 68 @GObject.Signal 69 def entry_added(self, source: object, entry: BaseEntry, key: str): 70 '''New entry has been added to this group''' 71 pass 72 73 @GObject.Signal 74 def entry_removed(self, source: object, entry: BaseEntry, key: str): 75 '''Entry has been removed from this group''' 76 pass 77 78 79class SimpleGroup(BaseGroup): 80 81 def __init__(self, name, widgets, options=None): 82 super().__init__(widgets) 83 self._name = name 84 self._options_to_init = options 85 self._widgets = WidgetsWrapper(widgets) 86 self._entries = {} 87 self._defaults = {} 88 89 @property 90 def name(self): 91 return self._name 92 93 @name.setter 94 def name(self, value): 95 self._name = value 96 97 @property 98 def options(self): 99 pass 100 101 @options.setter 102 def options(self, options): 103 self.clear() 104 105 for key, (klass, default) in options.items(): 106 entry = klass(WidgetsWrapper(self._widgets, key)) 107 if default is not None: 108 entry.value = default 109 self._entries[key] = entry 110 self._defaults[key] = default 111 self.entry_added.emit(self, entry, key) 112 113 def read(self, config): 114 if self._options_to_init is not None: 115 self.options = self._options_to_init 116 self._options_to_init = None 117 118 for key, entry in self._entries.items(): 119 value = config[self._name, key] 120 entry.value = value if value is not None else self._defaults[key] 121 entry.enabled = value is not None 122 123 def write(self, config, is_changed=None): 124 for key, entry in self._entries.items(): 125 if is_changed and not is_changed(entry): 126 continue 127 config[self._name, key] = entry.value if entry.enabled else None, self._get_default(key) 128 129 def clear(self): 130 if not self._entries: 131 return 132 for key, entry in self._entries.items(): 133 self.entry_removed.emit(self, entry, key) 134 self._entries.clear() 135 136 def _get_entry(self, key): 137 return self._entries.get(key) 138 139 def _get_default(self, key): 140 return self._defaults.get(key) 141 142 143class OneToManyEntryAdapter: 144 145 class Error(Exception): 146 pass 147 148 class InvalidEntry(Error): 149 pass 150 151 class WrongAdapter(Error): 152 pass 153 154 class NoBaseEntryError(Error): 155 pass 156 157 class EntryWrapper(BaseEntry): 158 159 def __init__(self, adapter): 160 super().__init__(helpers.WidgetsWrapper(None)) 161 self._adapter = adapter 162 self._value = None 163 self._error = None 164 self._enabled = False 165 166 def _get_value(self): 167 return self._value 168 169 def _set_value(self, value): 170 self._value = value 171 if self._adapter._active == self and self._adapter._base_entry: 172 self._adapter._base_entry._set_value(value) 173 174 def _get_error(self): 175 return self._error 176 177 def _set_error(self, text): 178 self._error = text 179 if self._adapter._active == self and self._adapter._base_entry: 180 self._adapter._base_entry._set_error(text) 181 182 def _get_enabled(self): 183 return self._enabled 184 185 def _set_enabled(self, value): 186 self._enabled = value 187 if self._adapter._active == self and self._adapter._base_entry: 188 self._adapter._base_entry._set_enabled(value) 189 190 def __init__(self, entry=None): 191 self._base_entry = None 192 self._active = None 193 194 self._on_changed_id = None 195 self._on_menu_id = None 196 197 self.base_entry = entry 198 199 @property 200 def base_entry(self): 201 return self._base_entry 202 203 @base_entry.setter 204 def base_entry(self, entry): 205 if self._base_entry: 206 self._base_entry.disconnect(self._on_changed_id) 207 self._base_entry.disconnect(self._on_menu_id) 208 self._on_changed_id = None 209 self._on_menu_id = None 210 self._base_entry = entry 211 if entry: 212 self._on_changed_id = entry.changed.connect(self._on_changed) 213 self._on_menu_id = entry.show_menu.connect(self._on_show_menu) 214 215 def new_entry(self, widgets=None): 216 return OneToManyEntryAdapter.EntryWrapper(self) 217 218 def activate(self, entry): 219 if not isinstance(entry, OneToManyEntryAdapter.EntryWrapper): 220 raise OneToManyEntryAdapter.InvalidEntry() 221 if not entry._adapter == self: 222 raise OneToManyEntryAdapter.WrongAdapter() 223 if not self._base_entry: 224 raise OneToManyEntryAdapter.NoBaseEntryError() 225 226 self._active = entry 227 with self._base_entry.handler_block(self._on_changed_id): 228 self._base_entry._set_value(entry._value) 229 self._base_entry._set_enabled(entry._enabled) 230 self._base_entry._set_error(entry._error) 231 232 def _on_changed(self, entry): 233 if self._active: 234 self._active._enabled = entry._get_enabled() 235 self._active._value = entry._get_value() 236 self._active._emit_changed() 237 238 def _on_show_menu(self, base_entry): 239 if self._active: 240 self._active.show_menu.emit() 241