[conduit/gsoc09_alexandre] GSoC 2009



commit afe75300a6a6509043e35f7182613e9e6c1aacd6
Author: Alexandre Rosenfeld <alexandre rosenfeld gmail com>
Date:   Tue Aug 10 13:27:57 2010 -0300

    GSoC 2009

 conduit/Module.py         |  272 +++++++++++++++++++++-----------
 conduit/ModuleWrapper.py  |    2 +-
 conduit/gtkui/UI.py       |  172 ++++++++++++++++-----
 data/conduit_box_ui.glade |  377 ++++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 680 insertions(+), 143 deletions(-)
---
diff --git a/conduit/Module.py b/conduit/Module.py
index 1619dfc..fc42825 100644
--- a/conduit/Module.py
+++ b/conduit/Module.py
@@ -26,6 +26,163 @@ class Error(Exception):
     
 from gettext import gettext as _
 
+def is_factory(filename):
+    return filename.endswith("Factory.py")
+
+def is_module(filename):
+    return filename.endswith("Module.py")
+
+def is_module_dir(dirname):
+    return dirname.endswith("Module")
+    
+def is_valid_path(path):
+    return is_factory(path) or is_module(path)
+
+class ModuleInfo(object):
+
+    def __init__(self, path):
+        self.path = path
+        self.mtime = os.stat(path).st_mtime
+        self.internal_modules = []
+        self.factories = []
+        self.cached = False
+        self.cached_info = []
+
+    def _import_file(self, filename):
+        """
+        Tries to import the specified file. Returns the python module on succes.
+        Primarily for internal use. Note that the python module returned may actually
+        contain several more loadable modules.
+        """
+        mods = pydoc.importfile (filename)
+        try:
+            if (mods.MODULES): pass
+        except AttributeError:
+            log.warn("The file %s is not a valid module. Skipping." % (filename))
+            log.warn("A module must have the variable MODULES defined as a dictionary.")
+            raise
+        for modules, infos in mods.MODULES.items():
+            for i in ModuleWrapper.COMPULSORY_ATTRIBUTES:
+                if i not in infos:
+                    log.warn("Class %s in file %s does define a %s attribute. Skipping." % (modules, filename, i))
+                    raise Exception
+        return mods
+    
+    def _load_module(self, classname, *args, **kwargs):
+        mod = self._import_file(self.path)
+        for module, info in mod.MODULES.items():
+            klass = getattr(mod, module)
+            if getattr(klass, '__name__') == classname:
+                return klass(*args, **kwargs)
+        raise Error("Module %s not found" % classname)
+    
+    def load(self):
+        if self.cached and not self.is_factory():
+            for module_info in self.cached_info:
+                mod_wrapper = ModuleWrapper.ModuleWrapper(
+                    klass=self._load_module,
+                    initargs=(module_info['classname'],),
+                    category=module_info['category'],
+                    cached_info=module_info,
+                )
+                self.internal_modules.append((mod_wrapper, module_info['classname']))
+        else:
+            mod = self._import_file(self.path)
+            for name, info in mod.MODULES.items():
+                klass = getattr(mod, name)
+                if info["type"] == "dataprovider" or info["type"] == "converter":
+                    mod_wrapper = ModuleWrapper.ModuleWrapper(
+                        klass=klass,
+                        initargs=(),
+                        category=getattr(klass, "_category_", conduit.dataproviders.CATEGORY_TEST)
+                    )
+                    #Save the module (signal is emitted in _append_module)
+                    #self._append_module(
+                    #        mod_wrapper,
+                    #        klass
+                    #        )
+                    self.internal_modules.append((mod_wrapper, klass))
+                    self.cached_info.append(mod_wrapper.get_info())
+                    #modules_cache.append(mod_wrapper.get_info())
+                    #log.critical("Saving to cache %s" % mod_wrapper.get_info())                        
+                elif info["type"] == "dataprovider-factory":
+                    #log.critical("Creating factory %s" % self.path)
+                    # build a dict of kwargs to pass to factories
+                    kwargs = {
+                        "moduleManager": self,
+                    }
+                    #instantiate and store the factory
+                    instance = klass(**kwargs)
+                    self.factories.append(instance)
+                else:
+                    log.warn("Class is an unknown type: %s" % klass)
+            #except AttributeError:
+            #    log.warn("Could not find module %s in %s\n%s" % (modules,filename,traceback.format_exc()))
+
+    def is_factory(self):
+        return is_factory(self.path)
+
+    def is_module(self):
+        return is_module(self.path)
+        
+class CacheManager(object):
+
+    def __init__(self, cache_filename = "modules.list"):
+        self.cache_path = os.path.join(conduit.USER_CACHE_DIR, cache_filename)
+        self.modules = {}
+        self._load_cache()
+        
+    def _load_cache(self):
+        if not os.path.exists(self.cache_path):
+            log.info("No modules cache found (doing a cold start)")
+            return
+        cache_file = open(self.cache_path, "rb")
+        try:
+            cache_contents = pickle.load(cache_file)
+            #We check all the contents so we dont load an invalid cache
+            for path, module_info in cache_contents.iteritems():
+                
+                if not isinstance(path, basestring):
+                    raise Error("Error loading cache: key is not a string (clearing cache)")
+                self.modules[path] = ModuleInfo(path)
+                self.modules[path].mtime = module_info['mtime']
+                self.modules[path].cached_info = module_info['cached_info']
+        except Exception:
+            log.warn("Modules cache invalid (clearing cache)")
+            self.modules = {}
+        finally:
+            cache_file.close()
+
+    def _save_cache(self):
+        log.critical("Saving cache")
+        cache = {}
+        for module in self.modules.values():
+            cache[module.path] = {'mtime': module.mtime, 'cached_info': module.cached_info}
+        cache_file = open(self.cache_path, "wb")
+        try:
+            pickle.dump(cache, cache_file)
+        finally:
+            cache_file.close()       
+            
+    def add_module(self, path):
+        if path in self.modules and self.modules[path].mtime == os.stat(path).st_mtime:
+            self.modules[path].cached = True
+        elif os.path.basename(path) not in [os.path.basename(j) for j in self.modules.keys()]:
+            self.modules[path] = ModuleInfo(path)
+    
+    def load(self, blacklist, whitelist):
+        for module in self.modules.values():
+            name, ext = Vfs.uri_get_filename_and_extension(module.path)
+            if whitelist:
+                if name in whitelist:
+                    module.load()
+            elif blacklist:
+                if name not in blacklist: 
+                    module.load()
+            else:
+                module.load()
+        self._save_cache()
+
 class ModuleManager(gobject.GObject):
     """
     Generic dynamic module loader for conduit. Given a path
@@ -57,6 +214,8 @@ class ModuleManager(gobject.GObject):
         @type dirs: C{string[]}
         """
         gobject.GObject.__init__(self)
+        #Initialize our cache so we dont import uneeded modules
+        self.cacheManager = CacheManager()
         #Dict of loaded modulewrappers. key is wrapper.get_key()
         #Stored seperate to the classes because dynamic dataproviders may
         #use the same class but with different initargs (diff keys)
@@ -65,29 +224,9 @@ class ModuleManager(gobject.GObject):
         self.invalidFiles = []
         #Keep a ref to dataprovider factories so they are not collected
         self.dataproviderFactories = []
-        self.cache_path = os.path.join(conduit.USER_CACHE_DIR, "modules.list")
-        self.modules_cache = {}
-        if os.path.exists(self.cache_path):
-            cache_file = open(self.cache_path, "rb")
-            try:
-                self.modules_cache = pickle.load(cache_file)
-                #We check all the contents so we dont load an invalid cache
-                if not isinstance(self.modules_cache, dict):
-                    raise Exception()
-                for key, value in self.modules_cache.iteritems():
-                    if not isinstance(key, basestring):
-                        raise Error("%s not a string" % key)
-                    if not isinstance(value, dict):
-                        raise Error("%s not a dict" % value)
-            except Error:
-                log.warn("Modules cache invalid")
-                self.modules_cache = {}
-            finally:
-                cache_file.close()
-        else:
-            log.critical("No modules cache found")
         #scan all dirs for files in the right format (*Module/*Module.py)
-        self.filelist = self._build_filelist_from_directories(dirs)
+        self._fill_cache_from_directories(dirs)
+        #self.filelist = self._build_filelist_from_directories(dirs)
 
     def _on_dynamic_dataprovider_added(self, monitor, dpw, klass):
         """
@@ -109,7 +248,7 @@ class ModuleManager(gobject.GObject):
         if dataproviderWrapper.module_type in ["source", "sink", "twoway"]:
             self.emit("dataprovider-unavailable", dataproviderWrapper)
 
-    def _build_filelist_from_directories(self, directories=None):
+    def _fill_cache_from_directories(self, directories=None):
         """
         Converts a given array of directories into a list 
         containing the filenames of all qualified modules. Recurses into
@@ -117,9 +256,9 @@ class ModuleManager(gobject.GObject):
         directory in which they reside.
         This method is automatically invoked by the constructor.
         """
-        res = {}
+        #res = {}
         if not directories:
-            return res
+            return
             
         #convert to abs path    
         directories = [os.path.abspath(os.path.expanduser(s)) for s in directories]
@@ -131,23 +270,12 @@ class ModuleManager(gobject.GObject):
                     continue
                 for i in os.listdir(d):
                     f = os.path.join(d,i)
-                    if os.path.isfile(f) and (self._is_module(f) or self._is_factory(f)):
-                        if os.path.basename(f) not in [os.path.basename(j) for j in res.keys()]:
-                            res[f] = {'mtime': os.stat(f).st_mtime}
-                    elif os.path.isdir(f) and self._is_module_dir(f):
+                    if os.path.isfile(f) and is_valid_path(f):
+                        self.cacheManager.add_module(f)
+                    elif os.path.isdir(f) and is_module_dir(f):
                         directories.append(f)
             except OSError, err:
                 log.warn("Error reading directory %s, skipping." % (d))
-        return res
-       
-    def _is_factory(self, filename):
-        return filename.endswith("Factory.py")
-       
-    def _is_module(self, filename):
-        return filename.endswith("Module.py")
-
-    def _is_module_dir(self, dirname):
-        return dirname.endswith("Module")
         
     def _append_module(self, wrapper, klass):
         #Check if the wrapper is unique
@@ -178,35 +306,7 @@ class ModuleManager(gobject.GObject):
         # notify everything that dp is no longer available
         self._emit_unavailable(dpw)
 
-    def _import_file(self, filename):
-        """
-        Tries to import the specified file. Returns the python module on succes.
-        Primarily for internal use. Note that the python module returned may actually
-        contain several more loadable modules.
-        """
-        mods = pydoc.importfile (filename)
-        try:
-            if (mods.MODULES): pass
-        except AttributeError:
-            log.warn("The file %s is not a valid module. Skipping." % (filename))
-            log.warn("A module must have the variable MODULES defined as a dictionary.")
-            raise
-        for modules, infos in mods.MODULES.items():
-            for i in ModuleWrapper.COMPULSORY_ATTRIBUTES:
-                if i not in infos:
-                    log.warn("Class %s in file %s does define a %s attribute. Skipping." % (modules, filename, i))
-                    raise Exception
-        return mods
     
-    def _load_module(self, info, *args, **kwargs):
-        mod = self._import_file(info['filename'])
-        #log.critical("Cached imported file %s: %s" % (info['filename'], mod.MODULES.items()))
-        #log.critical(" Looking for %s" % (info,))
-        for modules, infos in mod.MODULES.items():
-            klass = getattr(mod, modules)
-            if getattr(klass, '__name__') == info['classname']:
-                return klass(*args, **kwargs)
-        raise Error("Module %s not found" % info['classname'])
 
     def _load_modules_in_file(self, filename, f_data):
         """
@@ -234,7 +334,7 @@ class ModuleManager(gobject.GObject):
                             modules_cache.append(mod_wrapper.get_info())
                             #log.critical("Saving to cache %s" % mod_wrapper.get_info())                        
                         elif infos["type"] == "dataprovider-factory":
-                            log.critical("Creating factory %s" % filename)
+                            #log.critical("Creating factory %s" % filename)
                             # build a dict of kwargs to pass to factories
                             kwargs = {
                                 "moduleManager": self,
@@ -249,7 +349,6 @@ class ModuleManager(gobject.GObject):
                 self.filelist[filename]['modules'] = modules_cache
                 self.filelist[filename]['mtime'] = os.stat(filename).st_mtime
             else:
-                #log.critical("File %s in cache" % filename)
                 self.filelist[filename] = self.modules_cache[filename]
                 for module in self.modules_cache[filename]['modules']:
                     module['filename'] = filename
@@ -277,32 +376,17 @@ class ModuleManager(gobject.GObject):
         If whitelist and blacklist are supplied then the name of the file
         is tested against them. Default policy is to load all modules unless
         """
-        for f, f_data in self.filelist.iteritems():
-            name, ext = Vfs.uri_get_filename_and_extension(f)
-            if whitelist:
-                if name in whitelist:
-                    self._load_modules_in_file(f, f_data)
-            elif blacklist:
-                if name not in blacklist: 
-                    self._load_modules_in_file(f, f_data)
-            else:            
-                self._load_modules_in_file(f, f_data)
+        self.cacheManager.load(whitelist, blacklist)
         
-        # Save the modules cache if it's different then the file contents
-        if self.modules_cache != self.filelist:
-            log.critical("Saving cache")
-            self.modules_cache = self.filelist
-            cache_file = open(self.cache_path, "wb")
-            try:
-                pickle.dump(self.filelist, cache_file)
-            finally:
-                cache_file.close()
-
-        for i in self.dataproviderFactories:
-            log.critical("Probing %s" % i)
-            i.connect("dataprovider-removed", self._on_dynamic_dataprovider_removed)
-            i.connect("dataprovider-added", self._on_dynamic_dataprovider_added)
-            i.probe()
+        for module in self.cacheManager.modules.values():
+            for wrapper, klass in module.internal_modules:
+                self._append_module(wrapper, klass)
+            for factory in module.factories:
+                self.dataproviderFactories.append(factory)
+                #log.critical("Probing %s" % factory)
+                factory.connect("dataprovider-removed", self._on_dynamic_dataprovider_removed)
+                factory.connect("dataprovider-added", self._on_dynamic_dataprovider_added)
+                factory.probe()
 
         self.emit('all-modules-loaded')
             
diff --git a/conduit/ModuleWrapper.py b/conduit/ModuleWrapper.py
index 1360e92..640e8d3 100644
--- a/conduit/ModuleWrapper.py
+++ b/conduit/ModuleWrapper.py
@@ -305,7 +305,7 @@ class ModuleWrapper:
         #    self.module = self.klass(self.cached_info, *self.initargs)
         #else:
         self.module = self.klass(*self.initargs)
-        log.critical("Module instantiated: %s" % (self.module))
+        #log.critical("Module instantiated: %s" % (self.module))
         
     def is_pending(self):
         return self.module == None
diff --git a/conduit/gtkui/UI.py b/conduit/gtkui/UI.py
index 03f1c55..a4cd58b 100644
--- a/conduit/gtkui/UI.py
+++ b/conduit/gtkui/UI.py
@@ -52,7 +52,7 @@ class Error(Exception):
 
 PIX_COLUMN, STR_COLUMN, CONDUIT_COLUMN = range(3)
 
-class ConduitWidget(object):
+class ConduitView(object):
     def __init__(self, cond, main_window):
         self.conduit = cond
         self.main_window = main_window
@@ -70,8 +70,31 @@ class ConduitWidget(object):
         self.status_label = builder.get_object("status_label")
         #builder.get_object("conduit_name_label").set_text(cond.name)
         self.sizegroup = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
-        self.source_widget = None
-        self.sink_widgets = []
+        
+        self.dps_buttons_box = builder.get_object('dps_buttons_box')
+        self.no_sink_label = builder.get_object('no_sink_label')
+        
+        self.dp_notebook = builder.get_object("dp_notebook")
+        
+        self.status_button = builder.get_object('status_button')
+        self.status_button.connect('clicked', self.on_dp_button_clicked)
+        
+        self.status_widget = builder.get_object('status_widget')
+        
+        self.source_button = builder.get_object('source_button')
+        self.source_button.connect('clicked', self.on_dp_button_clicked)
+        
+        self.dp_buttons = [self.status_button]
+        self.db_widgets = []
+        self.button_widgets = {self.status_button: self.status_widget}
+        
+        self.updating_buttons = False
+        
+        self.views = []
+        
+        self.source_view = None
+        self.sink_views = []
+        
         self.source_box = builder.get_object("source_vbox")
         if cond.datasource:
             self.add_dataprovider(cond.datasource)
@@ -91,6 +114,7 @@ class ConduitWidget(object):
         
         self.refresh_status()
         
+        
     def on_sync_started(self, cond):
         self.syncing = True
         self.refresh_status()
@@ -124,75 +148,109 @@ class ConduitWidget(object):
         self.main_window.open_add_sink_window(self.conduit)
         
     def on_revert_action_activate(self, action):
-        for dp_widget in [self.source_widget] + self.sink_widgets:
-            if not dp_widget:
+        for dp_view in [self.source_view] + self.sink_views:
+            if not dp_view:
                 continue
-            modified = dp_widget.config.cancel_config()
+            modified = dp_view.config.cancel_config()
             
     def on_apply_action_activate(self, action):
-        for dp_widget in [self.source_widget] + self.sink_widgets:
-            if not dp_widget:
+        for dp_view in [self.source_view] + self.sink_views:
+            if not dp_view:
                 continue
-            modified = dp_widget.config.apply_config()
+            modified = dp_view.config.apply_config()
 
     def on_dataprovider_added(self, cond, dp):
         self.add_dataprovider(dp)
         
     def on_dataprovider_removed(self, cond, dp):
+        #for widget in self.dp_notebook.get_children():
+        #    if widget
         if dp == cond.datasource:
-            self.remove_dataprovider_widget(self.source_widget)
+            self.remove_dataprovider(self.source_view)
         else:
-            for dp_widget in self.sink_widgets:
-                if dp_widget.dp == dp:
-                    self.remove_dataprovider_widget(dp_widget)
+            for dp_view in self.sink_views:
+                if dp_view.dp == dp:
+                    self.remove_dataprovider(dp_view)
                     break
         
     def on_config_changed(self, config, item):
         modified = True
-        for dp_widget in [self.source_widget] + self.sink_widgets:
-            if not dp_widget:
+        for dp_view in [self.source_view] + self.sink_views:
+            if not dp_view:
                 continue
-            modified = dp_widget.config.is_modified()
+            modified = dp_view.config.is_modified()
             if modified:
                 break
         self.revert_action.set_sensitive(modified)
         self.apply_action.set_sensitive(modified)        
+        
+    def on_dp_button_clicked(self, button):
+        if self.updating_buttons:
+            return
+        self.updating_buttons = True
+        for dp_button in self.dp_buttons:
+            if dp_button.get_active():
+                dp_button.set_active(False)
+        button.set_active(True)
+        if button in self.button_widgets:
+            self.dp_notebook.set_current_page(self.dp_notebook.page_num(self.button_widgets[button]))
+        self.updating_buttons = False
     
     def add_dataprovider(self, dp):
+        dp_view = DataproviderView(self, dp, source = self.conduit.datasource == dp)        
+        self.dp_notebook.append_page(dp_view.widget, gtk.Label(dp.get_name()))
         if self.conduit.datasource == dp:
-            self.source_widget = DataproviderWidget(self, dp, source = True)
-            self.source_box.add(self.source_widget.widget)
+            button = self.source_button
+            self.source_view = dp_view
+        else:
+            self.no_sink_label.hide()
+            button = gtk.ToggleButton()
+            button.show_all()
+            self.dps_buttons_box.pack_start(button, False, False)
+            self.sink_views.append(dp_view)
+        button.add(self.make_dp_button(dp))
+        button.connect('clicked', self.on_dp_button_clicked)
+        self.dp_buttons.append(button)
+        self.button_widgets[button] = dp_view.widget
+        return
+        if self.conduit.datasource == dp:
+            self.source_view = DataproviderView(self, dp, source = True)
+            self.source_box.add(self.source_view.widget)
             dp.module.connect("status-changed", self.on_status_changed)
         else:
-            sink_widget = DataproviderWidget(self, dp, source = False)
-            self.sink_widgets.append(sink_widget)
+            sink_view = DataproviderView(self, dp, source = False)
+            self.sink_views.append(sink_widget)
             self.sink_box.add(sink_widget.widget)
             
-    def remove_dataprovider_widget(self, dp_widget):
-        if self.source_widget == dp_widget:
-            if not self.source_widget:
+    def remove_dataprovider(self, dp_view):
+        dp_view.remove_dp()
+        #return
+        if self.source_view == dp_view:
+            if not self.source_view:
                 return
-            self.source_box.remove(self.source_widget.widget)
+            #self.source_box.remove(self.source_widget.widget)
             self.source_widget = None
-        elif dp_widget in self.sink_widgets:
-            self.sink_widgets.remove(dp_widget)
-            self.sink_box.remove(dp_widget.widget)
+        elif dp_view in self.sink_views:
+            self.sink_views.remove(dp_view)
+            #self.sink_box.remove(dp_widget.widget)
         else:
             raise Error("Dataprovider not found on this conduit")
             
         
-class DataproviderWidget(object):
-    def __init__(self, conduit_widget, dp, source = True):
-        self.conduit_widget = conduit_widget
+class DataproviderView(object):
+    def __init__(self, conduit_view, dp, source = True):
+        self.conduit_view = conduit_view
         self.dp = dp
         self.source = source
         
         self.widget = gtk.VBox()
+        self.widget.set_spacing(8)
         hbox = gtk.HBox()
         hbox.set_spacing(8)
         icon = gtk.image_new_from_pixbuf(dp.get_icon(48))
         icon.set_property("pixel-size", 48)
-        label = gtk.Label(dp.get_name())
+        label = gtk.Label("<b>%s</b>" % dp.get_name())
+        label.set_use_markup(True)
         label.props.xalign = 0
         button_box = gtk.HButtonBox()
         #config_button = gtk.Button(stock="gtk-preferences")
@@ -200,23 +258,53 @@ class DataproviderWidget(object):
         if not source:
             remove_button = gtk.Button(stock="gtk-delete")
             remove_button.connect("clicked", self.on_remove_button_activate)
-            button_box.add(remove_button)
+            #button_box.add(remove_button)
+            hbox.pack_end(remove_button, expand=False, fill=False)
         hbox.pack_start(icon, expand=False, fill=False)
         hbox.pack_start(label, expand=True, fill=True)
-        hbox.pack_start(button_box, expand=False, fill=False)
+        #hbox.pack_start(button_box, expand=False, fill=False)
         self.widget.pack_start(hbox, False, False)
-        self.config = dp.module.get_config_container(ConfigContainer, dp.name, dp.get_icon(), self)
-        self.config.connect("changed", conduit_widget.on_config_changed)
-        config_widget = self.config.get_config_widget()
+        try:
+            self.config = dp.module.get_config_container(ConfigContainer, dp.name, dp.get_icon(), self)
+            self.config.connect("changed", conduit_view.on_config_changed)
+            config_widget = self.config.get_config_widget()
+        except Exception:
+            self.config = None
+            config_widget = gtk.Label("Ouch, something went wrong!")
         if config_widget:
-            align = gtk.Alignment(xalign=0.5, yalign=0.5)
+            align = gtk.Alignment(xalign=0.0, yalign=0.0, xscale=1.0, yscale=1.0)
             align.add(config_widget)
-            conduit_widget.sizegroup.add_widget(config_widget)
-            self.widget.pack_start(align, False, False)
+            conduit_view.sizegroup.add_widget(config_widget)
+            #self.widget.pack_start(align, False, False)
+            self.widget.pack_start(align, True, True)
         self.widget.show_all()
+    
+    def make_dp_button(self, button):
+        hbox = gtk.HBox()
+        hbox.set_spacing(4)
+        icon = gtk.image_new_from_pixbuf(self.dp.get_icon())
+        #icon.set_property("pixel-size", 48)
+        label = gtk.Label(self.dp.get_name())
+        #label.props.xalign = 0
+        label.set_alignment(xalign = 0, yalign = 0.5)
+        
+        hbox.pack_start(icon)
+        hbox.pack_start(label)
+        hbox.show_all()
+        
+        button.add(hbox)
+        
+        self.button = button
+        return button
+        
+    def remove_dp(self):
+        parent = self.widget.get_parent()
+        if parent:
+            parent.remove(self.widget)
+            self.widget = None
         
     def on_remove_button_activate(self, button):
-        self.conduit_widget.conduit.delete_dataprovider(self.dp)
+        self.conduit_view.conduit.delete_dataprovider(self.dp)
 
 class MainWindow:
     """
@@ -417,7 +505,7 @@ class MainWindow:
         pass
         
     def _make_conduit_page(self, cond):
-        return ConduitWidget(cond, self)
+        return ConduitView(cond, self)
         
     def on_main_window_destroy(self, window):
         self.conduitApplication.Quit()
@@ -436,7 +524,7 @@ class MainWindow:
         cond.connect("sync-started", self.on_sync_started)
         cond.connect("sync-completed", self.on_sync_completed)
         iter_ = self.conduits_treemodel.append((cond.get_icon(), cond.get_name(), cond))
-        self.conduits_widgets[cond] = ConduitWidget(cond, self)
+        self.conduits_widgets[cond] = ConduitView(cond, self)
         tab_label = gtk.HBox()
         tab_label.pack_start(gtk.image_new_from_pixbuf(cond.get_icon()))
         tab_label.pack_start(gtk.Label(cond.get_name()))
diff --git a/data/conduit_box_ui.glade b/data/conduit_box_ui.glade
index e9f8f76..544614d 100644
--- a/data/conduit_box_ui.glade
+++ b/data/conduit_box_ui.glade
@@ -8,8 +8,114 @@
     <property name="orientation">vertical</property>
     <property name="spacing">8</property>
     <child>
-      <object class="GtkHBox" id="hbox2">
+      <object class="GtkHBox" id="hbox3">
         <property name="visible">True</property>
+        <child>
+          <object class="GtkHBox" id="dps_buttons_box">
+            <property name="visible">True</property>
+            <property name="spacing">4</property>
+            <child>
+              <object class="GtkToggleButton" id="status_button">
+                <property name="label" translatable="yes">Status</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="image">status_image</property>
+                <property name="active">True</property>
+                <property name="draw_indicator">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkVSeparator" id="vseparator1">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="padding">4</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToggleButton" id="source_button">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkImage" id="image5">
+                <property name="visible">True</property>
+                <property name="stock">gtk-go-forward</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="padding">4</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="no_sink_label">
+                <property name="visible">True</property>
+                <property name="label" translatable="yes">No sink configured</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="padding">8</property>
+                <property name="position">4</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="padding">2</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkButton" id="add_sink_button">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="is_focus">True</property>
+            <property name="can_default">True</property>
+            <property name="has_default">True</property>
+            <property name="receives_default">True</property>
+            <property name="related_action">add_sink_action</property>
+            <property name="use_action_appearance">True</property>
+            <property name="use_stock">True</property>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">False</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkHBox" id="hbox2">
         <property name="spacing">8</property>
         <child>
           <object class="GtkImage" id="image16">
@@ -76,15 +182,265 @@
       <packing>
         <property name="expand">False</property>
         <property name="fill">False</property>
-        <property name="position">0</property>
+        <property name="position">1</property>
       </packing>
     </child>
     <child>
-      <placeholder/>
+      <object class="GtkNotebook" id="dp_notebook">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="border_width">10</property>
+        <property name="show_tabs">False</property>
+        <property name="show_border">False</property>
+        <child>
+          <object class="GtkVBox" id="status_widget">
+            <property name="visible">True</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">20</property>
+            <child>
+              <object class="GtkHBox" id="hbox9">
+                <property name="visible">True</property>
+                <property name="spacing">8</property>
+                <child>
+                  <object class="GtkImage" id="image8">
+                    <property name="visible">True</property>
+                    <property name="stock">gtk-go-down</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label7">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Get data from:</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkVBox" id="vbox3">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">8</property>
+                <child>
+                  <object class="GtkHBox" id="hbox5">
+                    <property name="visible">True</property>
+                    <property name="spacing">10</property>
+                    <child>
+                      <object class="GtkImage" id="image4">
+                        <property name="visible">True</property>
+                        <property name="stock">gtk-directory</property>
+                        <property name="icon-size">6</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label2">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Files and Folders&lt;/b&gt;
+33 directories and 5 files</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="hbox6">
+                    <property name="visible">True</property>
+                    <child>
+                      <object class="GtkLabel" id="label4">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Status:&lt;/b&gt; Idle</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                      <packing>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkHBox" id="hbox10">
+                <property name="visible">True</property>
+                <property name="spacing">8</property>
+                <child>
+                  <object class="GtkImage" id="image9">
+                    <property name="visible">True</property>
+                    <property name="stock">gtk-go-up</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label8">
+                    <property name="visible">True</property>
+                    <property name="xalign">0</property>
+                    <property name="label" translatable="yes">Send data to:</property>
+                    <attributes>
+                      <attribute name="weight" value="bold"/>
+                    </attributes>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkVBox" id="vbox4">
+                <property name="visible">True</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">8</property>
+                <child>
+                  <object class="GtkHBox" id="hbox7">
+                    <property name="visible">True</property>
+                    <property name="spacing">10</property>
+                    <child>
+                      <object class="GtkImage" id="image6">
+                        <property name="visible">True</property>
+                        <property name="stock">gtk-directory</property>
+                        <property name="icon-size">6</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label5">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Files and Folders&lt;/b&gt;
+33 directories and 5 files</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                      <packing>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="label6">
+                        <property name="visible">True</property>
+                        <property name="xalign">0</property>
+                        <property name="label" translatable="yes">Idle</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="fill">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="hbox8">
+                    <property name="visible">True</property>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">False</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+            <child>
+              <placeholder/>
+            </child>
+          </object>
+        </child>
+        <child type="tab">
+          <placeholder/>
+        </child>
+        <child>
+          <placeholder/>
+        </child>
+        <child type="tab">
+          <placeholder/>
+        </child>
+        <child>
+          <placeholder/>
+        </child>
+        <child type="tab">
+          <placeholder/>
+        </child>
+      </object>
+      <packing>
+        <property name="position">2</property>
+      </packing>
     </child>
     <child>
       <object class="GtkScrolledWindow" id="scrolledwindow1">
-        <property name="visible">True</property>
         <property name="can_focus">True</property>
         <property name="hadjustment">adjustment1</property>
         <property name="hscrollbar_policy">never</property>
@@ -236,7 +592,7 @@
         </child>
       </object>
       <packing>
-        <property name="position">2</property>
+        <property name="position">3</property>
       </packing>
     </child>
     <child>
@@ -277,7 +633,7 @@
       </object>
       <packing>
         <property name="expand">False</property>
-        <property name="position">3</property>
+        <property name="position">4</property>
       </packing>
     </child>
   </object>
@@ -320,4 +676,13 @@
     <property name="stock_id">gtk-delete</property>
     <signal name="activate" handler="on_remove_sink_action_activate"/>
   </object>
+  <object class="GtkImage" id="status_image">
+    <property name="visible">True</property>
+    <property name="stock">gtk-info</property>
+  </object>
+  <object class="GtkSizeGroup" id="dp_buttons_sizegroup">
+    <widgets>
+      <widget name="status_button"/>
+    </widgets>
+  </object>
 </interface>



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