[conduit/gsoc09_alexandre] Changed config dialog to "Restore/Close" from "Cancel/Apply".



commit aa8b9e1eed637fde56b1aef5812b8961d2498447
Author: Alexandre Rosenfeld <airmind gmail com>
Date:   Fri Jun 12 21:52:32 2009 -0300

    Changed config dialog to "Restore/Close" from "Cancel/Apply".
    
    Fixed YouTube (Google module) behaviour to work with Restore.
    Added getter for ConfigContainer and changed Flickr config to use it.

 conduit/gtkui/ConfigContainer.py             |   89 ++++++++++++++++++--------
 conduit/gtkui/ConfigItems.py                 |   23 ++++---
 conduit/gtkui/WindowConfigurator.py          |   46 +++++++++----
 conduit/modules/FlickrModule/FlickrModule.py |   17 +++--
 conduit/modules/GoogleModule/GoogleModule.py |    3 +
 5 files changed, 119 insertions(+), 59 deletions(-)
---
diff --git a/conduit/gtkui/ConfigContainer.py b/conduit/gtkui/ConfigContainer.py
index a6a5fec..a92f75f 100644
--- a/conduit/gtkui/ConfigContainer.py
+++ b/conduit/gtkui/ConfigContainer.py
@@ -39,10 +39,11 @@ class ConfigContainer(Configurator.BaseConfigContainer):
         self.section = None
         self.sections = []
         self.items = []
+        self.item_names = {}
         self.built_items = False
         self.config_values = None
         
-        self._reset_modified_items()
+        self.__modified_items = None
         
         #the child widget to contain the custom settings
         self.widgetTable = gtk.Table(rows=1, columns=2)        
@@ -52,11 +53,19 @@ class ConfigContainer(Configurator.BaseConfigContainer):
         self.config_widget = self.widgetTable
         
         self.firstRow = True
+        
+    def __len__(self):
+        return len(self.item_names)
+        
+    def __getitem__(self, key):
+        return self.item_names[key]
 
     def _reset_modified_items(self, empty = True):
         '''
-        Reset the list of modified items. If empty is true, just create a new
-        empty list of modified items (so that no item is modified).
+        Reset the list of modified items. 
+        
+        If empty is True, just create a new empty list of modified items (so 
+        that no item is modified).
         If empty is False, set the list to None, so that it will be recreated
         next time get_modified_items is called.
         '''
@@ -67,8 +76,6 @@ class ConfigContainer(Configurator.BaseConfigContainer):
         
     def _item_changed(self, item, initial_state, value):
         self.emit("item-changed", item)
-        if self.modified_items is None:
-            self.get_modified_items()
         if not initial_state:
             self.modified_items.add(item)
         elif item in self.modified_items:
@@ -77,7 +84,7 @@ class ConfigContainer(Configurator.BaseConfigContainer):
         
     def _rebuild_widgets(self):
         '''
-        Rebuild widgets if needed
+        Rebuild widgets if needed.
         '''
         self.modified_items = None
         if self.showing:
@@ -85,7 +92,7 @@ class ConfigContainer(Configurator.BaseConfigContainer):
     
     def _build_widgets(self):
         '''
-        Creates all necessary widgets
+        Creates all necessary widgets.
         '''
         table = self.widgetTable
         if self.showing:
@@ -130,36 +137,58 @@ class ConfigContainer(Configurator.BaseConfigContainer):
         self._rebuild_widgets()
         return self.section
     
-    def add_item(self, title, kind, order = 0, **kwargs):
+    def add_item(self, title, kind, order = 0, name = None, **kwargs):
         '''
         Add a configuration item. Returns the Item object.
         
-        You can pass properties to the configuration item in kwargs.
+        Title is used for the label next to this item.
+        Kind is the type of item used for the widget. See the ConfigItems file
+        for a list of default kinds provided. Descendants of ItemBase are auto-
+        matically registered and can be used here.
+        Name is used to access this item via __getitem__ or with config[name].
+        If name is not specified here it will use the config_name defined in
+        kwargs, if availiable.
+        
+        You can pass other properties to the configuration item in kwargs. 
+        For documentation on which properties to use, check the ConfigItems file.
         '''
+        # Check if this kind of item is registered
+        if kind not in ConfigItems.ItemBase.items:
+            raise Error("Config kind %s not found" % kind)
+        # We only add a Section for this item if no previous section is defined.
         if not self.section:
             self.add_section()
-        # If we have a saved configuration in the config dict from the dp, 
-        # use it as initial value.
+        # If we have a saved configuration in the config dict from the 
+        # dataprovider use it as initial value.
+        # Note that we only get the configuration dict once for this 
+        # dataprovider, so that next items will get this cached version.
+        # Because dataproviders usually add several items in a row, this is 
+        # usually faster then retrieving it every time.
         if self.config_values is None:
             if self.dataprovider:
                 self.config_values = self.dataprovider.get_configuration()
             else:
-                self.config_values = {}        
-        if kwargs.get('config_name', None):
-            if kwargs['config_name'] in self.config_values:
-                kwargs['initial_value'] = self.config_values[kwargs['config_name']]
+                self.config_values = {}
+        config_name = kwargs.get('config_name', None)
+        if config_name:
+            if config_name in self.config_values:
+                kwargs['initial_value'] = self.config_values[config_name]
             else:
-                raise Error("Value for %s (configuration item %s) not found in dataprovider" % (kwargs['config_name'], title))
+                raise Error("Value for %s (configuration item %s) not found in dataprovider" % (config_name, title))
+        # The name of this item will be either the explicitely defined name or
+        # the config_name value.
+        name = name or config_name
         if 'enabled' not in kwargs:
             kwargs['enabled'] = self.section.enabled
-        try:
-            item_cls = ConfigItems.ItemBase.items[kind]
-        except KeyError:
-            raise Error("Config kind %s not found" % kind)
+        item_cls = ConfigItems.ItemBase.items[kind]
         item = item_cls(container = self, title = title, order = order, **kwargs)
         item.connect("value-changed", self._item_changed)
         self.items.append(item)
+        if name:
+            self.item_names[name] = item
         self.section.add_item(item)
+        # If we are already showing the configuration dialog, we need to 
+        # recreate the table of items.
         self._rebuild_widgets()
         return item
     
@@ -167,15 +196,20 @@ class ConfigContainer(Configurator.BaseConfigContainer):
         '''
         Return a list of items that has been modified
         '''
-        if self.modified_items is None:
-            self.modified_items = set([item for item in self.items if not item.is_initial_value()])
-        return self.modified_items
+        if self.__modified_items is None:
+            self.__modified_items = set([item for item in self.items if not item.is_initial_value()])
+        return self.__modified_items
+        
+    def set_modified_items(self, value):
+        self.__modified_items = value
+        
+    modified_items = property(get_modified_items, set_modified_items)
     
     def is_modified(self):
         '''
         Returns true if any item has been modified
         '''
-        return len(self.get_modified_items()) != 0        
+        return len(self.modified_items) != 0
         
     def get_config_values(self, items):
         '''
@@ -231,10 +265,11 @@ class ConfigContainer(Configurator.BaseConfigContainer):
             self.dataprovider.set_configuration(config_values)
         for item in items:
             item.save_state()
-        if not items and not sections:
-            self._reset_modified_items()
+        if items or sections:
+            self._reset_modified_items(empty = False)
         else:
-            self._reset_modified_items(False)
+            self._reset_modified_items(empty = True)
+        self.emit('changed', self.is_modified())
     
     def cancel_config(self):
         '''
diff --git a/conduit/gtkui/ConfigItems.py b/conduit/gtkui/ConfigItems.py
index be56032..5fc5ce6 100644
--- a/conduit/gtkui/ConfigItems.py
+++ b/conduit/gtkui/ConfigItems.py
@@ -275,6 +275,10 @@ class ItemBase(gobject.GObject):
             table.attach(align, 0, 2, row - 1, row, xoptions = gtk.FILL | gtk.EXPAND, yoptions = yoptions)
         return row        
 
+    def _enabled_check_toggled(self, widget):
+        self.set_enabled(widget.get_active())
+        self.emit('value-changed')
+
     def get_label(self):
         '''
         Returns the gtk.Label to this item (if needed)
@@ -334,6 +338,12 @@ class ItemBase(gobject.GObject):
         heavy processing in this method. 
         It is called every time the user changes the value.
         '''
+        #FIXME: This is a hack to allow the Youtube configuration to work.
+        # The way this should be implemented is adding a callback to this function
+        # or something similar. But because we already have too much callbacks
+        # this way is simpler
+        if (not self.enabled) and (self.disabled_value is not None):
+            return self.disabled_value
         if not self.__widget:
             return self.initial_value
         return self._get_value()
@@ -368,14 +378,7 @@ class ItemBase(gobject.GObject):
         '''
         if not self.config_name:
             return None
-        #FIXME: This is a hack to allow the Youtube configuration to work.
-        # The way this should be implemented is adding a callback to this function
-        # or something similar. But because we already have too much callbacks
-        # this way is simpler
-        if (not self.enabled) and (self.disabled_value is not None):
-            value = self.disabled_value
-        else:
-            value = self.get_value()
+        value = self.get_value()
         try:
             if self.config_type:
                 self.config_type(value)
@@ -406,7 +409,9 @@ class ItemBase(gobject.GObject):
         self.__enabled = enabled
         if self.__widget:
             self._set_enabled(enabled)
-            #self.widget.set_sensitive(enabled)
+            if self.disabled_value is not None:
+                log.critical("Toggling enabled")
+                self._value_changed()
     
     enabled = property(lambda self: self.__enabled, lambda self, enabled: self.set_enabled(enabled))
         
diff --git a/conduit/gtkui/WindowConfigurator.py b/conduit/gtkui/WindowConfigurator.py
index d140381..c67ddf8 100644
--- a/conduit/gtkui/WindowConfigurator.py
+++ b/conduit/gtkui/WindowConfigurator.py
@@ -28,27 +28,36 @@ class WindowConfigurator:
         @type window: C{gtk.Window}
         """        
         self.showing = False
-        self.built_configs = False
+        self.built_containers = False
         
         self.dialog = gtk.Dialog(self.CONFIG_WINDOW_TITLE_TEXT,
                           window,
                           gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,
-                          (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
-                           gtk.STOCK_OK, gtk.RESPONSE_OK,
-                           gtk.STOCK_HELP, gtk.RESPONSE_HELP))
+                          (gtk.STOCK_REVERT_TO_SAVED, gtk.RESPONSE_CANCEL,
+                           gtk.STOCK_CLOSE, gtk.RESPONSE_OK))
         #TODO: Unless we actually have a help to show, make the help button 
         #disabled.
         #BTW, modules should be able to define their own help
         self.dialog.set_has_separator(False)
-        self.dialog.set_response_sensitive(gtk.RESPONSE_HELP, False)
-        self.dialog.set_default_size(300, -1)
+        #self.dialog.set_response_sensitive(gtk.RESPONSE_HELP, False)
+        self.dialog.set_response_sensitive(gtk.RESPONSE_CANCEL, False)
+        self.dialog.set_default_size(-1, -1)
+        #self.dialog.set_border_width(12)
+        
+        self.dialog_box = self.dialog.get_content_area()
+        align = gtk.Alignment()
+        align.set_padding(0, 8, 0, 0)
+        align.add(self._make_config_widget())
         
-        self.dialog_box = self.dialog.vbox
-        self.dialog_box.pack_start(self._make_config_widget())
+        self.dialog_box.pack_start(align)
         self.dialog_box.show_all()
         
+        self.container_signals = {}
         self.container_widgets = {}
         
+    def _container_changed(self, container, changed):
+        self.dialog.set_response_sensitive(gtk.RESPONSE_CANCEL, changed)
+        
     def _make_config_widget(self):
         if self.NOTEBOOK:
             self.tabs = {}
@@ -57,24 +66,26 @@ class WindowConfigurator:
             self.notebook.set_show_tabs(self.MULTIPLE_VIEW)
             return self.notebook
         else:
-            self.containers_box = gtk.VBox(spacing = 8)
-            self.containers_box.set_border_width(6)        
+            self.containers_box = gtk.VBox(spacing = 6)
+            self.containers_box.set_border_width(12)
             return self.containers_box
         
     def set_containers(self, containers):
         self._clear_containers()
         self.built_containers = False
-        
         self.containers = containers
         
     def _clear_containers(self):
+        for container, signal_id in self.container_signals.iteritems():
+            container.disconnect(signal_id)
+        self.container_signals = {}
         if self.NOTEBOOK:
             while self.notebook.get_n_pages() > 0:
                 self.notebook.remove_page(0)
             self.tabs = {}
         else:
             for container, widget in self.container_widgets.iteritems():
-                self.containers_box.remove(widget)
+                self.containers_box.remove(widget)                
                 container_widget = container.get_config_widget()
                 container_widget.get_parent().remove(container_widget)
         self.container_widgets = {}
@@ -102,9 +113,10 @@ class WindowConfigurator:
                 title_box.show_all()
                 container_box.pack_start(title_box, False, False)
             container_box.pack_start(container_widget, True, True)
-            container_box.pack_start(gtk.HSeparator(), False, False)
+            #container_box.pack_start(gtk.HSeparator(), False, False)
             self.containers_box.pack_start(container_box)
             self.containers_box.show_all()
+        self.container_signals[container] = container.connect('changed', self._container_changed)
         self.container_widgets[container] = widget
     
     def build_containers(self):
@@ -133,8 +145,8 @@ class WindowConfigurator:
         if busy:
             self.window.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
         else:
-            self.window.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.ARROW))            
-        
+            self.window.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.LEFT_PTR))            
+    
     @property
     def window(self):
         return self.dialog
@@ -166,6 +178,10 @@ class WindowConfigurator:
             self.notebook.set_current_page(self.tabs[config_container])
         self.dialog.reshow_with_initial_size()
         resp = self.dialog.run()
+        while resp == gtk.RESPONSE_CANCEL:
+            for container in containers:
+                container.cancel_config()
+            resp = self.dialog.run()
         for container in containers:
             container.hide()
         self.dialog.hide()
diff --git a/conduit/modules/FlickrModule/FlickrModule.py b/conduit/modules/FlickrModule/FlickrModule.py
index a106370..7fd5d0d 100644
--- a/conduit/modules/FlickrModule/FlickrModule.py
+++ b/conduit/modules/FlickrModule/FlickrModule.py
@@ -279,28 +279,28 @@ class FlickrTwoWay(Image.ImageTwoWay):
         def _login_finished(*args):
             try:
                 if self.logged_in:
-                    status_label.value = _('Loading album list...')
+                    config['status'].value = _('Loading album list...')
                     try:
                         #FIXME: Blocks and brings the whole UI with it.
                         photosets = self._get_photosets()
                     except:
-                        status_label.value = _('Failed to connect.')
+                        config['status'].value = _('Failed to connect.')
                     else:
-                        photoset_config.choices = [name for name, photoSetId in photosets]
-                        status_label.value = _('Album names loaded.')
+                        config['photoSetName'].choices = [name for name, photoSetId in photosets]
+                        config['status'].value = _('Album names loaded.')
                 else:
-                    status_label.value = _('Failed to login.')
+                    config['status'].value = _('Failed to login.')
             finally:
-                load_photosets_config.enabled = True
+                config['photoSetName'].enabled = True
                 account_section.enabled = True
                 config.set_busy(False)
                 
         def _load_photosets(button):
             config.set_busy(True)
-            load_photosets_config.enabled = False
+            config['photoSetName'].enabled = False
             account_section.enabled = False
             username_config.apply()
-            status_label.value = _('Logging in, please wait...')
+            config['status'].value = _('Logging in, please wait...')
             conduit.GLOBALS.syncManager.run_blocking_dataprovider_function_calls(
                 self, _login_finished, self._login)
 
@@ -311,6 +311,7 @@ class FlickrTwoWay(Image.ImageTwoWay):
         username_config.connect('value-changed',
             lambda item, initial, value: load_photosets_config.set_enabled(bool(value)))
         status_label = config.add_item(None, 'label',
+            name = 'status',
             initial_value = self.status,
             use_markup = True,
             xalignment = 0.5,
diff --git a/conduit/modules/GoogleModule/GoogleModule.py b/conduit/modules/GoogleModule/GoogleModule.py
index 10931e4..fbd2d5f 100644
--- a/conduit/modules/GoogleModule/GoogleModule.py
+++ b/conduit/modules/GoogleModule/GoogleModule.py
@@ -1373,6 +1373,9 @@ class YouTubeTwoWay(_GoogleBase, DataProvider.TwoWay):
             disable_check = True, 
             disabled_value = 0,
             enabled = self.max_downloads > 0)
+    
+    def config_cancel(self, config):
+        config['max_downloads'].enabled = (self.max_downloads > 0)
 
     def _get_video_info (self, id):
         if self.entries.has_key(id):



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]