[conduit/gsoc09_alexandre] GSoC 2009
- From: Alexandre Rosenfeld <arosenfeld src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [conduit/gsoc09_alexandre] GSoC 2009
- Date: Tue, 10 Aug 2010 18:48:43 +0000 (UTC)
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"><b>Files and Folders</b>
+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"><b>Status:</b> 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"><b>Files and Folders</b>
+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]