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