← Back to team overview

screenlets-dev team mailing list archive

[Merge] lp:~doctormo/screenlets/options-rework into lp:screenlets

 

Martin Owens has proposed merging lp:~doctormo/screenlets/options-rework into lp:screenlets.

Requested reviews:
  Screenlets Dev Team (screenlets-dev)

For more details, see:
https://code.launchpad.net/~doctormo/screenlets/options-rework/+merge/55179

Reattempt to get merged in updated options. These are API compatible and offer a new colour array option.
-- 
https://code.launchpad.net/~doctormo/screenlets/options-rework/+merge/55179
Your team Screenlets Dev Team is requested to review the proposed merge of lp:~doctormo/screenlets/options-rework into lp:screenlets.
=== modified file 'setup.py'
--- setup.py	2010-11-10 23:53:53 +0000
+++ setup.py	2011-03-28 16:19:24 +0000
@@ -119,7 +119,7 @@
 			os.system (buildcmd % (dname, name, name, dname))
 			files_list.append ((destpath % dname, [mopath % (dname,name.replace('-'+dname,''))]))
 				
-PACKAGES = ['screenlets','screenlets.plugins']
+PACKAGES = ['screenlets','screenlets.plugins','screenlets.options']
 
 # ----------------------------
 # Call setup()

=== modified file 'src/lib/__init__.py'
--- src/lib/__init__.py	2011-02-09 20:45:08 +0000
+++ src/lib/__init__.py	2011-03-28 16:19:24 +0000
@@ -136,15 +136,15 @@
 
 DEBIAN = True
 try:
-    subprocess.call(["dpkg"], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT)
+	subprocess.call(["dpkg"], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT)
 except OSError:
-    DEBIAN = False
+	DEBIAN = False
 
 UBUNTU = True
 try:
-    subprocess.call(["apt-add-repository"], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT)
+	subprocess.call(["apt-add-repository"], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT)
 except OSError:
-    UBUNTU = False
+	UBUNTU = False
 
 #-------------------------------------------------------------------------------
 # CLASSES
@@ -318,8 +318,8 @@
 		"""@DEPRECATED Moved to Screenlets class: Draws a circule"""
 		ctx.save()
 		ctx.translate(x, y)
-       		ctx.arc(width/2,height/2,min(height,width)/2,0,2*math.pi)
-		if fill:ctx.fill()
+		ctx.arc(width/2,height/2,min(height,width)/2,0,2*math.pi)
+		if fill: ctx.fill()
 		else: ctx.stroke()
 		ctx.restore()
 
@@ -328,7 +328,7 @@
 		ctx.save()
 		ctx.move_to(start_x, start_y)
 		ctx.set_line_width(line_width)
-        	ctx.rel_line_to(end_x, end_y)
+		ctx.rel_line_to(end_x, end_y)
 		if close : ctx.close_path()
 		if preserve: ctx.stroke_preserve()
 		else: ctx.stroke()
@@ -348,30 +348,30 @@
 		ctx.save()
 		ctx.translate(x, y)
 		padding=0 # Padding from the edges of the window
-        	rounded=rounded_angle # How round to make the edges 20 is ok
-        	w = width
+		rounded=rounded_angle # How round to make the edges 20 is ok
+		w = width
 		h = height
 
-        	# Move to top corner
-        	ctx.move_to(0+padding+rounded, 0+padding)
-        	
-        	# Top right corner and round the edge
-        	ctx.line_to(w-padding-rounded, 0+padding)
-        	ctx.arc(w-padding-rounded, 0+padding+rounded, rounded, (math.pi/2 )+(math.pi) , 0)
-	
-        	# Bottom right corner and round the edge
-        	ctx.line_to(w-padding, h-padding-rounded)
-        	ctx.arc(w-padding-rounded, h-padding-rounded, rounded, 0, math.pi/2)
-       	
-        	# Bottom left corner and round the edge.
-        	ctx.line_to(0+padding+rounded, h-padding)
-        	ctx.arc(0+padding+rounded, h-padding-rounded, rounded,math.pi/2, math.pi)
-	
-        	# Top left corner and round the edge
-        	ctx.line_to(0+padding, 0+padding+rounded)
-        	ctx.arc(0+padding+rounded, 0+padding+rounded, rounded, math.pi, (math.pi/2 )+(math.pi))
-        	
-        	# Fill in the shape.
+		# Move to top corner
+		ctx.move_to(0+padding+rounded, 0+padding)
+			
+		# Top right corner and round the edge
+		ctx.line_to(w-padding-rounded, 0+padding)
+		ctx.arc(w-padding-rounded, 0+padding+rounded, rounded, (math.pi/2 )+(math.pi) , 0)
+	
+		# Bottom right corner and round the edge
+		ctx.line_to(w-padding, h-padding-rounded)
+		ctx.arc(w-padding-rounded, h-padding-rounded, rounded, 0, math.pi/2)
+	   	
+		# Bottom left corner and round the edge.
+		ctx.line_to(0+padding+rounded, h-padding)
+		ctx.arc(0+padding+rounded, h-padding-rounded, rounded,math.pi/2, math.pi)
+	
+		# Top left corner and round the edge
+		ctx.line_to(0+padding, 0+padding+rounded)
+		ctx.arc(0+padding+rounded, 0+padding+rounded, rounded, math.pi, (math.pi/2 )+(math.pi))
+			
+		# Fill in the shape.
 		if fill:ctx.fill()
 		else: ctx.stroke()
 		ctx.restore()
@@ -430,29 +430,29 @@
 		ctx.restore()
 
 	def show_notification (self,text):
-	        """@DEPRECATED Moved to Screenlets class: Show notification window at current mouse position."""
+		"""@DEPRECATED Moved to Screenlets class: Show notification window at current mouse position."""
 		if self.notify == None:
-	      		self.notify = Notify()
-	        	self.notify.text = text
-	        	self.notify.show()
+		 	self.notify = Notify()
+			self.notify.text = text
+		self.notify.show()
 
 	def hide_notification (self):
-	        """@DEPRECATED Moved to Screenlets class: hide notification window"""
+		"""@DEPRECATED Moved to Screenlets class: hide notification window"""
 		if self.notify != None:
 			self.notify.hide()
 			self.notify = None
 
 	def show_tooltip (self,text,tooltipx,tooltipy):
-	        """@DEPRECATED: Moved to Screenlets class: Show tooltip window at current mouse position."""
+		"""@DEPRECATED: Moved to Screenlets class: Show tooltip window at current mouse position."""
 		if self.tooltip == None:
-      			self.tooltip = Tooltip(300, 400)
-        		self.tooltip.text = text
-        		self.tooltip.x    = tooltipx
-        		self.tooltip.y    = tooltipy
-			self.tooltip.show()
+	  		self.tooltip = Tooltip(300, 400)
+			self.tooltip.text = text
+			self.tooltip.x	= tooltipx
+			self.tooltip.y	= tooltipy
+		self.tooltip.show()
 
 	def hide_tooltip (self):
-	        """@DEPRECATED Moved to Screenlets class: hide tooltip window"""
+		"""@DEPRECATED Moved to Screenlets class: hide tooltip window"""
 		if self.tooltip != None:
 			self.tooltip.hide()
 			self.tooltip = None		
@@ -742,70 +742,80 @@
 		else: self.draw_buttons = False
 		if uses_theme:
 			self.uses_theme = True
-			self.add_option(StringOption('Screenlet', 'theme_name', 
-				'default', '', '', hidden=True))
+			self.add_option(StringOption('Screenlet', 'theme_name',
+				default='default', hidden=True))
 		# create/add options
-		self.add_option(IntOption('Screenlet', 'x', 
-			0, _('X-Position'), _('The X-position of this Screenlet ...'), 
+		self.add_option(IntOption('Screenlet', 'x',
+			default=0, label=_('X-Position'),
+			desc=_('The X-position of this Screenlet ...'),
 			min=0, max=gtk.gdk.screen_width()))
-		self.add_option(IntOption('Screenlet', 'y', 
-			0, _('Y-Position'), _('The Y-position of this Screenlet ...'), 
+		self.add_option(IntOption('Screenlet', 'y',
+			default=0, label=_('Y-Position'),
+			desc=_('The Y-position of this Screenlet ...'),
 			min=0, max=gtk.gdk.screen_height()))
-		self.add_option(IntOption('Screenlet', 'width', 
-			width, _('Width'), _('The width of this Screenlet ...'), 
-			min=16, max=1000, hidden=True))
-		self.add_option(IntOption('Screenlet', 'height', 
-			height, _('Height'), _('The height of this Screenlet ...'), 
-			min=16, max=1000, hidden=True))
-		self.add_option(FloatOption('Screenlet', 'scale', 
-			self.scale, _('Scale'), _('The scale-factor of this Screenlet ...'), 
+		self.add_option(IntOption('Screenlet', 'width',
+			default=width, label=_('Width'),
+			desc=_('The width of this Screenlet ...'),
+			min=16, max=1000, hidden=True))
+		self.add_option(IntOption('Screenlet', 'height',
+			default=height, label=_('Height'),
+			desc=_('The height of this Screenlet ...'),
+			min=16, max=1000, hidden=True))
+		self.add_option(FloatOption('Screenlet', 'scale',
+			default=self.scale, label=_('Scale'),
+			desc=_('The scale-factor of this Screenlet ...'),
 			min=0.1, max=10.0, digits=2, increment=0.1))
-		self.add_option(FloatOption('Screenlet', 'opacity', 
-			self.opacity, _('Opacity'), _('The opacity of the Screenlet window ...'), 
+		self.add_option(FloatOption('Screenlet', 'opacity',
+			default=self.opacity, label=_('Opacity'),
+			desc=_('The opacity of the Screenlet window ...'),
 			min=0.1, max=1.0, digits=2, increment=0.1))
-		self.add_option(BoolOption('Screenlet', 'is_sticky', 
-			is_sticky, _('Stick to Desktop'), 
-			_('Show this Screenlet on all workspaces ...')))
-		self.add_option(BoolOption('Screenlet', 'is_widget', 
-			is_widget, _('Treat as Widget'), 
-			_('Treat this Screenlet as a "Widget" ...')))
-		self.add_option(BoolOption('Screenlet', 'is_dragged', 
-			self.is_dragged, "Is the screenlet dragged","Is the screenlet dragged", hidden=True))
-		self.add_option(BoolOption('Screenlet', 'is_sizable', 
-			is_sizable, "Can the screenlet be resized","is_sizable", hidden=True))
-		self.add_option(BoolOption('Screenlet', 'is_visible', 
-			self.is_visible, "Usefull to use screenlets as gnome panel applets","is_visible", hidden=True))
-		self.add_option(BoolOption('Screenlet', 'lock_position', 
-			self.lock_position, _('Lock position'), 
-			_('Stop the screenlet from being moved...')))
-		self.add_option(BoolOption('Screenlet', 'keep_above', 
-			self.keep_above, _('Keep above'), 
-			_('Keep this Screenlet above other windows ...')))
-		self.add_option(BoolOption('Screenlet', 'keep_below', 
-			self.keep_below, _('Keep below'), 
-			_('Keep this Screenlet below other windows ...')))
-		self.add_option(BoolOption('Screenlet', 'draw_buttons', 
-			self.draw_buttons, _('Draw button controls'), 
-			_('Draw buttons in top right corner')))
-		self.add_option(BoolOption('Screenlet', 'skip_pager', 
-			self.skip_pager, _('Skip Pager'), 
-			_('Set this Screenlet to show/hide in pagers ...')))
-		self.add_option(BoolOption('Screenlet', 'skip_taskbar', 
-			self.skip_pager, _('Skip Taskbar'), 
-			_('Set this Screenlet to show/hide in taskbars ...')))
-		self.add_option(BoolOption('Screenlet', 'resize_on_scroll', 
-			self.resize_on_scroll, _("Resize on mouse scroll"),"resize_on_scroll"))
+		self.add_option(BoolOption('Screenlet', 'is_sticky',
+			default=is_sticky, label=_('Stick to Desktop'),
+			desc=_('Show this Screenlet on all workspaces ...')))
+		self.add_option(BoolOption('Screenlet', 'is_widget',
+			default=is_widget, label=_('Treat as Widget'),
+			desc=_('Treat this Screenlet as a "Widget" ...')))
+		self.add_option(BoolOption('Screenlet', 'is_dragged',
+			default=self.is_dragged, label="Is the screenlet dragged",
+			desc="Is the screenlet dragged", hidden=True))
+		self.add_option(BoolOption('Screenlet', 'is_sizable',
+			default=is_sizable, label="Can the screenlet be resized",
+			desc="is_sizable", hidden=True))
+		self.add_option(BoolOption('Screenlet', 'is_visible',
+			default=self.is_visible, label="Usefull to use screenlets as gnome panel applets",
+			desc="is_visible", hidden=True))
+		self.add_option(BoolOption('Screenlet', 'lock_position',
+			default=self.lock_position, label=_('Lock position'),
+			desc=_('Stop the screenlet from being moved...')))
+		self.add_option(BoolOption('Screenlet', 'keep_above',
+			default=self.keep_above, label=_('Keep above'),
+			desc=_('Keep this Screenlet above other windows ...')))
+		self.add_option(BoolOption('Screenlet', 'keep_below',
+			default=self.keep_below, label=_('Keep below'),
+			desc=_('Keep this Screenlet below other windows ...')))
+		self.add_option(BoolOption('Screenlet', 'draw_buttons',
+			default=self.draw_buttons, label=_('Draw button controls'),
+			desc=_('Draw buttons in top right corner')))
+		self.add_option(BoolOption('Screenlet', 'skip_pager',
+			default=self.skip_pager, label=_('Skip Pager'),
+			desc=_('Set this Screenlet to show/hide in pagers ...')))
+		self.add_option(BoolOption('Screenlet', 'skip_taskbar',
+			default=self.skip_pager, label=_('Skip Taskbar'),
+			desc=_('Set this Screenlet to show/hide in taskbars ...')))
+		self.add_option(BoolOption('Screenlet', 'resize_on_scroll',
+			default=self.resize_on_scroll, label=_("Resize on mouse scroll"),
+			desc="resize_on_scroll"))
 		self.add_option(BoolOption('Screenlet', 'ignore_requirements', 
 			self.ignore_requirements, _('Ignore requirements'), 
 			_('Set this Screenlet to ignore/demand DEB requirements ...')))
 		if uses_theme:
 			self.ask_on_option_override = ask_on_option_override
-			self.add_option(BoolOption('Screenlet', 'allow_option_override', 
-			self.allow_option_override, _('Allow overriding Options'), 
-				_('Allow themes to override options in this screenlet ...')))
-			self.add_option(BoolOption('Screenlet', 'ask_on_option_override', 
-				self.ask_on_option_override, _('Ask on Override'), 
-				_('Show a confirmation-dialog when a theme wants to override ')+\
+			self.add_option(BoolOption('Screenlet', 'allow_option_override',
+				default=self.allow_option_override, label=_('Allow overriding Options'),
+				desc=_('Allow themes to override options in this screenlet ...')))
+			self.add_option(BoolOption('Screenlet', 'ask_on_option_override',
+				default=self.ask_on_option_override, label=_('Ask on Override'),
+				desc=_('Show a confirmation-dialog when a theme wants to override ')+\
 				_('the current options of this Screenlet ...')))
 		# disable width/height
 		self.disable_option('width')
@@ -959,7 +969,7 @@
 			if self.window.window:
 				self.window.window.set_skip_taskbar_hint(bool(value))
 		# NOTE: This is the new recommended way of storing options in real-time
-		#       (we access the backend through the session here)
+		#	   (we access the backend through the session here)
 		if self.saving_enabled:
 			o = self.get_option_by_name(name)
 			if o != None:
@@ -2084,35 +2094,35 @@
 
 
 	def show_notification (self,text):
-	        """Show notification window at current mouse position."""
+		"""Show notification window at current mouse position."""
 		if self.notify == None:
-	      		self.notify = Notify()
-	        	self.notify.text = text
-	        	self.notify.show()
+			self.notify = Notify()
+			self.notify.text = text
+			self.notify.show()
 
 	def hide_notification (self):
-	        """hide notification window"""
+		"""hide notification window"""
 		if self.notify != None:
 			self.notify.hide()
 			self.notify = None
 
 	def show_tooltip (self,text,tooltipx,tooltipy):
-	        """Show tooltip window at current mouse position."""
+		"""Show tooltip window at current mouse position."""
 		if self.tooltip == None:
-      			self.tooltip = Tooltip(300, 400)
-        		self.tooltip.text = text
-        		self.tooltip.x    = tooltipx
-        		self.tooltip.y    = tooltipy
+	  		self.tooltip = Tooltip(300, 400)
+			self.tooltip.text = text
+			self.tooltip.x	= tooltipx
+			self.tooltip.y	= tooltipy
 			self.tooltip.show()
 		else:
-     			#self.tooltip = Tooltip(300, 400)
-        		self.tooltip.text = text
-        		self.tooltip.x    = tooltipx
-        		self.tooltip.y    = tooltipy
+	 		#self.tooltip = Tooltip(300, 400)
+			self.tooltip.text = text
+			self.tooltip.x	= tooltipx
+			self.tooltip.y	= tooltipy
 			#self.tooltip.show()
 
 	def hide_tooltip (self):
-	        """hide tooltip window"""
+		"""hide tooltip window"""
 		if self.tooltip != None:
 			self.tooltip.hide()
 			self.tooltip = None		
@@ -2204,21 +2214,21 @@
 	"""A window that displays a text and serves as Tooltip (very basic yet)."""
 	
 	# internals
-	__timeout    = None
-    
+	__timeout	= None
+	
 	# attribs
-	text        = ''
-	font_name    = 'FreeSans 9'
-	width        = 100
-	height        = 20
-	x             = 0
-	y             = 0
-    
+	text		= ''
+	font_name	= 'FreeSans 9'
+	width		= 100
+	height		= 20
+	x			 = 0
+	y			 = 0
+	
 	def __init__ (self, width, height):
 		object.__init__(self)
 		# init
-		self.__dict__['width']    = width
-		self.__dict__['height']    = height
+		self.__dict__['width']	= width
+		self.__dict__['height']	= height
 		self.window = gtk.Window()
 		self.window.set_app_paintable(True)
 		self.window.set_size_request(width, height)
@@ -2237,7 +2247,7 @@
 		pango.FontDescription(self.font_name))
 		#self.p_layout.set_width(-1)
 		self.p_layout.set_width(width * pango.SCALE - 6)
-    
+	
 	def __setattr__ (self, name, value):
 		self.__dict__[name] = value
 		if name in ('width', 'height', 'text'):
@@ -2253,7 +2263,7 @@
 			self.window.move(int(value), int(self.y))
 		elif name == 'y':
 			self.window.move(int(self.x), int(value))
-    
+	
 	def show (self):
 		"""Show the Tooltip window."""
 		self.cancel_show()
@@ -2264,22 +2274,22 @@
 		"""Show the Tooltip window after a given delay."""
 		self.cancel_show()
 		self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
-    
+	
 	def hide (self):
 		"""Hide the Tooltip window."""
 		self.cancel_show()
 		self.window.destroy()
-    
+	
 	def cancel_show (self):
 		"""Cancel showing of the Tooltip."""
 		if self.__timeout:
 			gobject.source_remove(self.__timeout)
 			self.p_context = None
 			self.p_layout = None
-    
+	
 	def __show_timeout (self):
 		self.show()
-    
+	
 	def screen_changed (self, window, screen=None):
 		if screen == None:
 			screen = window.get_screen()
@@ -2287,10 +2297,10 @@
 		if not map:
 			map = screen.get_rgb_colormap()
 		window.set_colormap(map)
-    
+	
 	def expose (self, widget, event):
 		ctx = self.window.window.cairo_create()
-		ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL)    # ?
+		ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL)	# ?
 		# set a clip region for the expose event
 		ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
 		ctx.clip()
@@ -2317,17 +2327,17 @@
 	"""A window that displays a text and serves as Notification (very basic yet)."""
 	
 	# internals
-	__timeout    = None
-    
+	__timeout	= None
+	
 	# attribs
-	text        = ''
-	font_name    = 'FreeSans 9'
-	width        = 200
-	height        = 100
-	x             = 0
-	y             = 0
+	text		= ''
+	font_name	= 'FreeSans 9'
+	width		= 200
+	height		= 100
+	x			 = 0
+	y			 = 0
 	gradient = cairo.LinearGradient(0, 100,0, 0)
-    
+	
 	def __init__ (self):
 		object.__init__(self)
 		# init
@@ -2349,7 +2359,7 @@
 		pango.FontDescription(self.font_name))
 		#self.p_layout.set_width(-1)
 		self.p_layout.set_width(self.width * pango.SCALE - 6)
-    
+	
 	def __setattr__ (self, name, value):
 		self.__dict__[name] = value
 		if name in ('text'):
@@ -2369,22 +2379,22 @@
 		"""Show the Notify window after a given delay."""
 		self.cancel_show()
 		self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
-    
+	
 	def hide (self):
 		"""Hide the Notify window."""
 		self.cancel_show()
 		self.window.destroy()
-    
+	
 	def cancel_show (self):
 		"""Cancel showing of the Notify."""
 		if self.__timeout:
 			gobject.source_remove(self.__timeout)
 			self.p_context = None
 			self.p_layout = None
-    
+	
 	def __show_timeout (self):
 		self.show()
-    
+	
 	def screen_changed (self, window, screen=None):
 		if screen == None:
 			screen = window.get_screen()
@@ -2392,10 +2402,10 @@
 		if not map:
 			map = screen.get_rgb_colormap()
 		window.set_colormap(map)
-    
+	
 	def expose (self, widget, event):
 		ctx = self.window.window.cairo_create()
-		ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL)    # ?
+		ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL)	# ?
 		# set a clip region for the expose event
 		ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
 		ctx.clip()

=== added directory 'src/lib/options'
=== removed file 'src/lib/options.py'
--- src/lib/options.py	2011-02-20 05:06:16 +0000
+++ src/lib/options.py	1970-01-01 00:00:00 +0000
@@ -1,1432 +0,0 @@
-# This application is released under the GNU General Public License 
-# v3 (or, at your option, any later version). You can find the full 
-# text of the license under http://www.gnu.org/licenses/gpl.txt. 
-# By using, editing and/or distributing this software you agree to 
-# the terms and conditions of this license. 
-# Thank you for using free software!
-
-# Options-system (c) RYX (aka Rico Pfaus) 2007 <ryx@xxxxxxxxxxxxxxx>
-#
-# INFO:
-# - a dynamic Options-system that allows very easy creation of
-#   objects with embedded configuration-system.
-#   NOTE: The Dialog is not very nice yet - it is not good OOP-practice
-#   because too big functions and bad class-layout ... but it works
-#   for now ... :)
-#
-# TODO:
-# - option-widgets for all option-types (e.g. ListOptionWidget, ColorOptionWidget)
-# - OptionGroup-class instead of (or behind) add_options_group
-# - TimeOption, DateOption
-# - FileOption needs filter/limit-attribute
-# - allow options to disable/enable other options
-# - support for EditableOptions-subclasses as options
-# - separate OptionEditorWidget from Editor-Dialog
-# - place ui-code into screenlets.options.ui-module
-# - create own widgets for each Option-subclass
-#
-
-import screenlets
-import utils
-
-import os		
-import gtk, gobject
-import xml.dom.minidom
-from xml.dom.minidom import Node
-
-# translation stuff
-import gettext
-gettext.textdomain('screenlets')
-gettext.bindtextdomain('screenlets', screenlets.INSTALL_PREFIX +  '/share/locale')
-
-def _(s):
-	return gettext.gettext(s)
-
-# -----------------------------------------------------------------------
-# Option-classes and subclasses
-# -----------------------------------------------------------------------
-
-class Option(gobject.GObject):
-	"""An Option stores information about a certain object-attribute. It doesn't
-	carry information about the value or the object it belongs to - it is only a
-	one-way data-storage for describing how to handle attributes."""
-	
-	__gsignals__ = dict(option_changed=(gobject.SIGNAL_RUN_FIRST,
-		gobject.TYPE_NONE, (gobject.TYPE_OBJECT,)))
-	
-	def __init__ (self, group, name, default, label, desc,  
-		disabled=False, hidden=False, callback=None, protected=False):
-		"""Creates a new Option with the given information."""
-		super(Option, self).__init__()
-		self.name = name
-		self.label = label
-		self.desc = desc
-		self.default = default
-		self.disabled = disabled
-		self.hidden = hidden
-		# for groups (TODO: OptionGroup)
-		self.group= group
-		# callback to be notified when this option changes
-		self.callback = callback
-		# real-time update?
-		self.realtime = True
-		# protected from get/set through service
-		self.protected = protected
-		
-	def on_import (self, strvalue):
-		"""Callback - called when an option gets imported from a string. 
-		This function MUST return the string-value converted to the required 
-		type!"""
-		return strvalue.replace("\\n", "\n")
-	
-	def on_export (self, value):
-		"""Callback - called when an option gets exported to a string. The
-		value-argument needs to be converted to a string that can be imported 
-		by the on_import-handler. This handler MUST return the value 
-		converted to a string!"""
-		return str(value).replace("\n", "\\n")
-	
-
-class FileOption (Option):
-	"""An Option-subclass for string-values that contain filenames. Adds
-	a patterns-attribute that can contain a list of patterns to be shown
-	in the assigned file selection dialog. The show_pixmaps-attribute
-	can be set to True to make the filedialog show all image-types 
-	supported by gtk.Pixmap. If the directory-attributue is true, the
-	dialog will ony allow directories."""
-
-	def __init__ (self, group, name, default, label, desc, 
-		patterns=['*'], image=False, directory=False, **keyword_args):
-		Option.__init__(self, group, name, default,label, desc, **keyword_args)
-		self.patterns	= patterns
-		self.image		= image
-		self.directory	= False
-
-
-class ImageOption (Option):
-	"""An Option-subclass for string-values that contain filenames of
-	image-files."""
-
-
-class DirectoryOption (Option):
-	"""An Option-subclass for filename-strings that contain directories."""
-
-
-class BoolOption (Option):
-	"""An Option for boolean values."""
-	
-	def on_import (self, strvalue):
-		if strvalue == "True":
-			return True
-		return False
-
-
-class StringOption (Option):
-	"""An Option for values of type string."""
-	
-	def __init__ (self, group, name, default, label, desc, 
-		choices=None, password=False, **keyword_args):
-		Option.__init__(self, group, name, default,label, desc, **keyword_args)
-		self.choices	= choices
-		self.password	= password
-
-
-class IntOption (Option):
-	"""An Option for values of type number (can be int or float)."""
-	
-	def __init__ (self, group, name, default, label, desc,  min=-100000, max=100000, 
-		increment=1, **keyword_args):
-		Option.__init__(self, group, name, default, label, desc, **keyword_args)
-		self.min = min
-		self.max = max
-		self.increment = increment
-	
-	def on_import (self, strvalue):
-		"""Called when IntOption gets imported. Converts str to int."""
-		try:
-			if strvalue[0]=='-':
-				return int(strvalue[1:]) * -1
-			return int(strvalue)
-		except:
-			print "Error during on_import - option: %s." % self.name
-			return 0
-
-
-class FloatOption (IntOption):
-	"""An Option for values of type float."""
-	
-	def __init__ (self, group, name, default, label, desc, digits=1, 
-		**keyword_args):
-		IntOption.__init__(self, group, name, default, label, desc,
-			**keyword_args)
-		self.digits = digits
-	
-	def on_import (self, strvalue):
-		"""Called when FloatOption gets imported. Converts str to float."""
-		if strvalue[0]=='-':
-			return float(strvalue[1:]) * -1.0
-		return float(strvalue)
-
-
-class FontOption (Option):
-	"""An Option for fonts (a simple StringOption)."""
-
-
-class ColorOption (Option):
-	"""An Option for colors. Stored as a list with 4 values (r, g, b, a)."""
-	
-	def on_import (self, strvalue):
-		"""Import (r, g, b, a) from comma-separated string."""
-		# strip braces and spaces
-		strvalue = strvalue.lstrip('(')
-		strvalue = strvalue.rstrip(')')
-		strvalue = strvalue.strip()
-		# split value on commas
-		tmpval = strvalue.split(',')
-		outval = []
-		for f in tmpval:
-			# create list and again remove spaces
-			outval.append(float(f.strip()))
-		return outval
-	
-	def on_export (self, value):
-		"""Export r, g, b, a to comma-separated string."""
-		l = len(value)
-		outval = ''
-		for i in xrange(l):
-			outval += str(value[i])
-			if i < l-1:
-				outval += ','
-		return outval
-
-
-class ListOption (Option):
-	"""An Option-type for list of strings."""
-
-	def on_import (self, strvalue):
-		"""Import python-style list from a string (like [1, 2, 'test'])"""
-		lst = eval(strvalue)
-		return lst
-	
-	def on_export (self, value):
-		"""Export list as string."""
-		return str(value)
-
-
-import gnomekeyring
-class AccountOption (Option):
-	"""An Option-type for username/password combos. Stores the password in
-	the gnome-keyring (if available) and only saves username and auth_token
-	through the screenlets-backend.
-	TODO:
-	- not create new token for any change (use "set" instead of "create" if
-	  the given item already exists)
-	- use usual storage if no keyring is available but output warning
-	- on_delete-function for removing the data from keyring when the
-	  Screenlet holding the option gets deleted"""
-	
-	def __init__ (self, group, name, default, label, desc, **keyword_args):
-		Option.__init__ (self, group, name, default, label, desc, 
-			protected=True, **keyword_args)
-		# check for availability of keyring
-		if not gnomekeyring.is_available():
-			raise Exception('GnomeKeyring is not available!!')	# TEMP!!!
-		# THIS IS A WORKAROUND FOR A BUG IN KEYRING (usually we would use
-		# gnomekeyring.get_default_keyring_sync() here):
-		# find first available keyring
-		self.keyring_list = gnomekeyring.list_keyring_names_sync()
-		if len(self.keyring_list) == 0:
-			raise Exception('No keyrings found. Please create one first!')
-		else:
-			# we prefer the default keyring
- 			try:
- 				self.keyring = gnomekeyring.get_default_keyring_sync()
- 			except:
-				if "session" in self.keyring_list:
-					print "Warning: No default keyring found, using session keyring. Storage is not permanent!"
-					self.keyring = "session"
-				else:
-					print "Warning: Neither default nor session keyring found, assuming keyring %s!" % self.keyring_list[0]
-					self.keyring = self.keyring_list[0]
-
-	
-	def on_import (self, strvalue):
-		"""Import account info from a string (like 'username:auth_token'), 
-		retrieve the password from the storage and return a tuple containing
-		username and password."""
-		# split string into username/auth_token
-		#data = strvalue.split(':', 1)
-		(name, auth_token) = strvalue.split(':', 1)
- 		if name and auth_token:
- 			# read pass from storage
- 			try:
-				pw = gnomekeyring.item_get_info_sync(self.keyring, 
-					int(auth_token)).get_secret()
-			except Exception, ex:
-				print "ERROR: Unable to read password from keyring: %s" % ex
-				pw = ''
-			# return
-			return (name, pw)
-		else:
-			raise Exception('Illegal value in AccountOption.on_import.')
-	
-	def on_export (self, value):
-		"""Export the given tuple/list containing a username and a password. The
-		function stores the password in the gnomekeyring and returns a
- 		string in form 'username:auth_token'."""
- 		# store password in storage
- 		attribs = dict(name=value[0])
-		auth_token = gnomekeyring.item_create_sync(self.keyring, 
-			gnomekeyring.ITEM_GENERIC_SECRET, value[0], attribs, value[1], True)
- 		# build value from username and auth_token
- 		return value[0] + ':' + str(auth_token)
-
-"""#TEST:
-o = AccountOption('None', 'pop3_account', ('',''), 'Username/Password', 'Enter username/password here ...')
-# save option to keyring
-exported_account = o.on_export(('RYX', 'mysecretpassword'))
-print exported_account
-# and read option back from keyring
-print o.on_import(exported_account)
-
-
-import sys
-sys.exit(0)"""
-
-class TimeOption (ColorOption):
-	"""An Option-subclass for string-values that contain dates."""
-
-
-# -----------------------------------------------------------------------
-# EditableOptions-class and needed functions
-# -----------------------------------------------------------------------
-
-def create_option_from_node (node, groupname):
-	"""Create an Option from an XML-node with option-metadata."""
-	#print "TODO OPTION: " + str(cn)
-	otype = node.getAttribute("type")
-	oname = node.getAttribute("name")
-	ohidden = node.getAttribute("hidden")
-	odefault	= None
-	oinfo		= ''
-	olabel		= ''
-	omin		= None
-	omax		= None
-	oincrement	= 1
-	ochoices	= ''
-	odigits		= None
-	if otype and oname:
-		# parse children of option-node and save all useful attributes
-		for attr in node.childNodes:
-			if attr.nodeType == Node.ELEMENT_NODE:
-				if attr.nodeName == 'label':
-					olabel = attr.firstChild.nodeValue
-				elif attr.nodeName == 'info':
-					oinfo = attr.firstChild.nodeValue
-				elif attr.nodeName == 'default':
-					odefault = attr.firstChild.nodeValue
-				elif attr.nodeName == 'min':
-					omin = attr.firstChild.nodeValue
-				elif attr.nodeName == 'max':
-					omax = attr.firstChild.nodeValue
-				elif attr.nodeName == 'increment':
-					oincrement = attr.firstChild.nodeValue
-				elif attr.nodeName == 'choices':
-					ochoices = attr.firstChild.nodeValue
-				elif attr.nodeName == 'digits':
-					odigits = attr.firstChild.nodeValue
-		# if we have all needed values, create the Option
-		if odefault:
-			# create correct classname here
-			cls = otype[0].upper() + otype.lower()[1:] + 'Option'
-			#print 'Create: ' +cls +' / ' + oname + ' ('+otype+')'
-			# and build new instance (we use on_import for setting default val)
-			clsobj = getattr(__import__(__name__), cls)
-			opt = clsobj(groupname, oname, None, olabel, oinfo)
-			opt.default = opt.on_import(odefault)
-			# set values to the correct types
-			if cls == 'IntOption':
-				if omin:
-					opt.min = int(omin)
-				if omax:
-					opt.max = int(omax)
-				if oincrement:
-					opt.increment = int(oincrement)
-			elif cls == 'FloatOption':
-				if odigits:
-					opt.digits = int(odigits)
-				if omin:
-					opt.min = float(omin)
-				if omax:
-					opt.max = float(omax)
-				if oincrement:
-					opt.increment = float(oincrement)
-			elif cls == 'StringOption':
-				if ochoices:
-					opt.choices = ochoices
-			return opt
-	return None
-	
-
-class EditableOptions(object):
-	"""The EditableOptions can be inherited from to allow objects to export 
-	editable options for editing them with the OptionsEditor-class.
-	NOTE: This could use some improvement and is very poorly coded :) ..."""
-	
-	def __init__ (self):
-		self.__options__ = []
-		self.__options_groups__ = {}
-		# This is a workaround to remember the order of groups
-		self.__options_groups_ordered__ = []
-	
-	def add_option (self, option, callback=None, realtime=True):
-		"""Add an editable option to this object. Editable Options can be edited
-		and configured using the OptionsDialog. The optional callback-arg can be
-		used to set a callback that gets notified when the option changes its 
-		value."""
-		#print "Add option: "+option.name
-		# if option already editable (i.e. initialized), return
-		for o in self.__options__:
-			if o.name == option.name:
-				return False
-		self.__dict__[option.name] = option.default
-		# set auto-update (TEMPORARY?)
-		option.realtime = realtime
-		# add option to group (output error if group is undefined)
-		try:
-			self.__options_groups__[option.group]['options'].append(option)
-		except:
-			print "Options: Error - group %s not defined." % option.group
-			return False
-		# now add the option
-		self.__options__.append(option)
-		# if callback is set, add callback
-		if callback:
-			option.connect("option_changed", callback)
-		return True
-		
-		
-	def add_options_group (self, name, group_info):
-		"""Add a new options-group to this Options-object"""
-		self.__options_groups__[name] = {'label':name, 
-			'info':group_info, 'options':[]}
-		self.__options_groups_ordered__.append(name)
-		#print self.options_groups
-	
-	def disable_option (self, name):
-		"""Disable the inputs for a certain Option."""
-		for o in self.__options__:
-			if o.name == name:
-				o.disabled = True
-				return True
-		return False
-
-	def enable_option(self, name):
-		"""Enable the inputs for a certain Option."""
-		for o in self.__options__:
-			if o.name == name:
-				o.disabled = False
-				return True
-		return False
-	
-	def export_options_as_list (self):
-		"""Returns all editable options within a list (without groups)
-		as key/value tuples."""
-		lst = []
-		for o in self.__options__:
-			lst.append((o.name, getattr(self, o.name)))
-		return lst
-	
-	def get_option_by_name (self, name):
-		"""Returns an option in this Options by it's name (or None).
-		TODO: this gives wrong results in childclasses ... maybe access
-		as class-attribute??"""
-		for o in self.__options__:
-			if o.name == name:
-				return o
-		return None
-	
-	def remove_option (self, name):
-		"""Remove an option from this Options."""
-		for o in self.__options__:
-			if o.name == name:
-				del o
-				return True
-		return True
-	
-	def add_options_from_file (self, filename):
-		"""This function creates options from an XML-file with option-metadata.
-		TODO: make this more reusable and place it into module (once the groups
-		are own objects)"""
-		# create xml document
-		try:
-			doc = xml.dom.minidom.parse(filename)
-		except:
-			raise Exception('Invalid XML in metadata-file (or file missing): "%s".' % filename)
-		# get rootnode
-		root = doc.firstChild
-		if not root or root.nodeName != 'screenlet':
-			raise Exception('Missing or invalid rootnode in metadata-file: "%s".' % filename)
-		# ok, let's check the nodes: this one should contain option-groups
-		groups = []
-		for node in root.childNodes:
-			# we only want element-nodes
-			if node.nodeType == Node.ELEMENT_NODE:
-				#print node
-				if node.nodeName != 'group' or not node.hasChildNodes():
-					# we only allow groups in the first level (groups need children)
-					raise Exception('Error in metadata-file "%s" - only <group>-tags allowed in first level. Groups must contain at least one <info>-element.' % filename)
-				else:
-					# ok, create a new group and parse its elements
-					group = {}
-					group['name']		= node.getAttribute("name")
-					if not group['name']:
-						raise Exception('No name for group defined in "%s".' % filename)
-					group['info']		= ''
-					group['options']	= []
-					# check all children in group
-					for on in node.childNodes:
-						if on.nodeType == Node.ELEMENT_NODE:
-							if on.nodeName == 'info':
-								# info-node? set group-info
-								group['info'] = on.firstChild.nodeValue
-							elif on.nodeName == 'option':
-								# option node? parse option node
-								opt = create_option_from_node (on, group['name'])
-								# ok? add it to list
-								if opt:
-									group['options'].append(opt)
-								else:
-									raise Exception('Invalid option-node found in "%s".' % filename)
-					
-					# create new group
-					if len(group['options']):
-						self.add_options_group(group['name'], group['info'])
-						for o in group['options']:
-							self.add_option(o)
-					# add group to list
-					#groups.append(group)
-
-# -----------------------------------------------------------------------
-# OptionsDialog and UI-classes
-# -----------------------------------------------------------------------
-
-class ListOptionDialog (gtk.Dialog):
-	"""An editing dialog used for editing options of the ListOption-type."""
-	
-	model = None
-	tree = None
-	buttonbox = None
-	
-	# call gtk.Dialog.__init__
-	def __init__ (self):
-		super(ListOptionDialog, self).__init__("Edit List", 
-			flags=gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR,
-			buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, 
-				gtk.STOCK_OK, gtk.RESPONSE_OK))
-		# set size
-		self.resize(300, 370)
-		self.set_keep_above(True)	# to avoid confusion
-		# init vars
-		self.model = gtk.ListStore(str)
-		# create UI
-		self.create_ui()
-	
-	def create_ui (self):
-		"""Create the user-interface for this dialog."""
-		# create outer hbox (tree|buttons)
-		hbox = gtk.HBox()
-		hbox.set_border_width(10)
-		hbox.set_spacing(10)
-		# create tree
-		self.tree = gtk.TreeView(model=self.model)
-		self.tree.set_headers_visible(False)
-		self.tree.set_reorderable(True)
-		#self.tree.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_HORIZONTAL)
-		col = gtk.TreeViewColumn('')
-		cell = gtk.CellRendererText()
-		#cell.set_property('cell-background', 'cyan')
-		cell.set_property('foreground', 'black')
-		col.pack_start(cell, False)
-		col.set_attributes(cell, text=0)
-		self.tree.append_column(col)
-		self.tree.show()
-		hbox.pack_start(self.tree, True, True)
-		#sep = gtk.VSeparator()
-		#sep.show()
-		#hbox.add(sep)
-		# create  buttons
-		self.buttonbox = bb = gtk.VButtonBox()
-		self.buttonbox.set_layout(gtk.BUTTONBOX_START)
-		b1 = gtk.Button(stock=gtk.STOCK_ADD)
-		b2 = gtk.Button(stock=gtk.STOCK_EDIT)
-		b3 = gtk.Button(stock=gtk.STOCK_REMOVE)
-		b1.connect('clicked', self.button_callback, 'add')
-		b2.connect('clicked', self.button_callback, 'edit')
-		b3.connect('clicked', self.button_callback, 'remove')
-		bb.add(b1)
-		bb.add(b2)
-		bb.add(b3)
-		self.buttonbox.show_all()
-		#hbox.add(self.buttonbox)
-		hbox.pack_end(self.buttonbox, False)
-		# add everything to outer hbox and show it
-		hbox.show()
-		self.vbox.add(hbox)
-		
-	def set_list (self, lst):
-		"""Set the list to be edited in this editor."""
-		for el in lst:
-			self.model.append([el])
-	
-	def get_list (self):
-		"""Return the list that is currently being edited in this editor."""
-		lst = []
-		for i in self.model:
-			lst.append(i[0])
-		return lst
-	
-	def remove_selected_item (self):
-		"""Remove the currently selected item."""
-		sel = self.tree.get_selection()
-		if sel:
-			it = sel.get_selected()[1]
-			if it:
-				print self.model.get_value(it, 0)
-				self.model.remove(it)
-	
-	def entry_dialog (self, default = ''):
-		"""Show entry-dialog and return string."""
-		entry = gtk.Entry()
-		entry.set_text(default)
-		entry.show()
-		dlg = gtk.Dialog("Add/Edit Item", flags=gtk.DIALOG_DESTROY_WITH_PARENT,
-			buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, 
-			gtk.RESPONSE_OK))
-		dlg.set_keep_above(True)
-		dlg.vbox.add(entry)
-		resp = dlg.run()
-		ret = None
-		if resp == gtk.RESPONSE_OK:
-			ret = entry.get_text()
-		dlg.destroy()
-		return ret
-				
-	def button_callback (self, widget, id):
-		print "PRESS: %s" % id
-		if id == 'remove':
-			self.remove_selected_item()
-		if id == 'add':
-			new = self.entry_dialog()
-			if new != None:
-				self.model.append([new])
-		if id == 'edit':
-			sel = self.tree.get_selection()
-			if sel:
-				it = sel.get_selected()[1]
-				if it:
-					new = self.entry_dialog(self.model.get_value(it, 0))
-					if new != None:
-						#self.model.append([new])
-						self.model.set_value(it, 0, new)
-	
-	
-# TEST	
-"""dlg = ListOptionDialog()
-dlg.set_list(['test1', 'afarew34s', 'fhjh23faj', 'yxcdfs58df', 'hsdf7jsdfh'])
-dlg.run()
-print "RESULT: " + str(dlg.get_list())
-dlg.destroy()
-import sys
-sys.exit(1)"""
-# /TEST
-
-class OptionsDialog (gtk.Dialog):
-	"""A dynamic options-editor for editing Screenlets which are implementing 
-	the EditableOptions-class."""
-	
-	__shown_object = None
-	
-	def __init__ (self, width, height):
-		# call gtk.Dialog.__init__
-		super(OptionsDialog, self).__init__(
-			_("Edit Options"), flags=gtk.DIALOG_DESTROY_WITH_PARENT |
-			gtk.DIALOG_NO_SEPARATOR,
-			buttons = (#gtk.STOCK_REVERT_TO_SAVED, gtk.RESPONSE_APPLY,
-				gtk.STOCK_CLOSE, gtk.RESPONSE_OK))
-		# set size
-		self.resize(width, height)
-		self.set_keep_above(True)	# to avoid confusion
-		self.set_border_width(10)
-		# create attribs
-		self.page_about		= None
-		self.page_options	= None
-		self.page_themes	= None
-		self.vbox_editor	= None
-		self.hbox_about		= None
-		self.infotext		= None
-		self.infoicon		= None
-		# create theme-list
-		self.liststore	= gtk.ListStore(object)
-		self.tree		= gtk.TreeView(model=self.liststore)
-		# create/add outer notebook
-		self.main_notebook = gtk.Notebook()
-		self.main_notebook.show()
-		self.vbox.add(self.main_notebook)
-		# create/init notebook pages
-		self.create_about_page()
-		self.create_themes_page()
-		self.create_options_page()
-	
-	# "public" functions
-	
-	def reset_to_defaults (self):
-		"""Reset all entries for the currently shown object to their default 
-		values (the values the object has when it is first created).
-		NOTE: This function resets ALL options, so BE CARFEUL!"""
-		if self.__shown_object:
-			for o in self.__shown_object.__options__:
-				# set default value
-				setattr(self.__shown_object, o.name, o.default)
-
-	def set_info (self, name, info, copyright='', version='', icon=None):
-		"""Update the "About"-page with the given information."""
-		# convert infotext (remove EOLs and TABs)
-		info = info.replace("\n", "")
-		info = info.replace("\t", " ")
-		# create markup
-		markup = '\n<b><span size="xx-large">' + name + '</span></b>'
-		if version:
-			markup += '  <span size="large"><b>' + version + '</b></span>'
-		markup += '\n\n'+info+'\n<span size="small">\n'+copyright+'</span>'
-		self.infotext.set_markup(markup)
-		# icon?
-		if icon:
-			# remove old icon
-			if self.infoicon:
-				self.infoicon.destroy()
-			# set new icon
-			self.infoicon = icon
-			self.infoicon.set_alignment(0.0, 0.10)
-			self.infoicon.show()
-			self.hbox_about.pack_start(self.infoicon, 0, 1, 10)
-		else:
-			self.infoicon.hide()
-
-	def show_options_for_object (self, obj):
-		"""Update the OptionsEditor to show the options for the given Object.
-		The Object needs to be an EditableOptions-subclass.
-		NOTE: This needs heavy improvement and should use OptionGroups once
-		      they exist"""
-		self.__shown_object = obj
-		# create notebook for groups
-		notebook = gtk.Notebook()
-		self.vbox_editor.add(notebook)
-		for group in obj.__options_groups_ordered__:
-			group_data = obj.__options_groups__[group]
-			# create box for tab-page
-			page = gtk.VBox()
-			page.set_border_width(10)
-			if group_data['info'] != '':
-				info = gtk.Label(group_data['info'])
-				info.show()
-				info.set_alignment(0, 0)
-				page.pack_start(info, 0, 0, 7)
-				sep = gtk.HSeparator()
-				sep.show()
-				#page.pack_start(sep, 0, 0, 5)
-			# create VBox for inputs
-			box = gtk.VBox()
-			box.show()
-			box.set_border_width(5)
-			# add box to page
-			page.add(box)
-			page.show()
-			# add new notebook-page
-			label = gtk.Label(group_data['label'])
-			label.show()
-			notebook.append_page(page, label)
-			# and create inputs
-			for option in group_data['options']:
-				if option.hidden == False:
-					val = getattr(obj, option.name)#obj.__dict__[option.name]
-					w = self.get_widget_for_option(option, val)
-					if w:
-						box.pack_start(w, 0, 0)
-						w.show()
-		notebook.show()	
-		# show/hide themes tab, depending on whether the screenlet uses themes
-		if obj.uses_theme and obj.theme_name != '':
-			self.show_themes_for_screenlet(obj)
-		else:
-			self.page_themes.hide()
-	
-	def show_themes_for_screenlet (self, obj):
-		"""Update the Themes-page to display the available themes for the
-		given Screenlet-object."""
-
-
-		dircontent = []
-		screenlets.utils.refresh_available_screenlet_paths()
-
-		for path in screenlets.SCREENLETS_PATH:
-			p = path + '/' + obj.get_short_name() + '/themes'
-			print p
-			#p = '/usr/local/share/screenlets/Clock/themes'	# TEMP!!!
-			try:
-				dc = os.listdir(p)
-				for d in dc:
-					dircontent.append({'name':d, 'path':p+'/'})
-			except:
-				print "Path %s not found." % p
-
-		# list with found themes
-		found_themes = []
-			
-		# check all themes in path
-		for elem in dircontent:
-			# load themes with the same name only once
-			if found_themes.count(elem['name']):
-				continue
-			found_themes.append(elem['name'])
-			# build full path of theme.conf
-			theme_conf = elem['path'] + elem['name'] + '/theme.conf'
-			# if dir contains a theme.conf
-			if os.access(theme_conf, os.F_OK):
-				# load it and create new list entry
-				ini = screenlets.utils.IniReader()
-				if ini.load(theme_conf):
-					# check for section
-					if ini.has_section('Theme'):
-						# get metainfo from theme
-						th_fullname	= ini.get_option('name', 
-							section='Theme')
-						th_info		= ini.get_option('info', 
-							section='Theme')
-						th_version	= ini.get_option('version', 
-							section='Theme')
-						th_author	= ini.get_option('author', 
-							section='Theme')
-						# create array from metainfo and add it to liststore
-						info = [elem['name'], th_fullname, th_info, th_author, 
-							th_version]
-						self.liststore.append([info])
-					else:
-						# no theme section in theme.conf just add theme-name
-						self.liststore.append([[elem['name'], '-', '-', '-', '-']])
-			else:
-				# no theme.conf in dir? just add theme-name
-				self.liststore.append([[elem['name'], '-', '-', '-', '-']])
-			# is it the active theme?
-			if elem['name'] == obj.theme_name:
-				# select it in tree
-				print "active theme is: %s" % elem['name']
-				sel = self.tree.get_selection()
-				if sel:
-					it = self.liststore.get_iter_from_string(\
-						str(len(self.liststore)-1))
-					if it:
-						sel.select_iter(it)
-
-	# UI-creation
-	
-	def create_about_page (self):
-		"""Create the "About"-tab."""
-		self.page_about = gtk.HBox()
-		# create about box
-		self.hbox_about = gtk.HBox()
-		self.hbox_about.show()
-		self.page_about.add(self.hbox_about)
-		# create icon
-		self.infoicon = gtk.Image()
-		self.infoicon.show()
-		self.page_about.pack_start(self.infoicon, 0, 1, 10)
-		# create infotext
-		self.infotext = gtk.Label()
-		self.infotext.use_markup = True
-		self.infotext.set_line_wrap(True)
-		self.infotext.set_alignment(0.0, 0.0)
-		self.infotext.show()
-		self.page_about.pack_start(self.infotext, 1, 1, 5)
-		# add page
-		self.page_about.show()
-		self.main_notebook.append_page(self.page_about, gtk.Label(_('About ')))	
-	
-	def create_options_page (self):
-		"""Create the "Options"-tab."""
-		self.page_options = gtk.HBox()
-		# create vbox for options-editor
-		self.vbox_editor	= gtk.VBox(spacing=3)
-		self.vbox_editor.set_border_width(5)
-		self.vbox_editor.show()
-		self.page_options.add(self.vbox_editor)
-		# show/add page
-		self.page_options.show()
-		self.main_notebook.append_page(self.page_options, gtk.Label(_('Options ')))
-
-	def create_themes_page (self):
-		"""Create the "Themes"-tab."""
-		self.page_themes = gtk.VBox(spacing=5)
-		self.page_themes.set_border_width(10)
-		# create info-text list
-		txt = gtk.Label(_('Themes allow you to easily switch the appearance of your Screenlets. On this page you find a list of all available themes for this Screenlet.'))
-		txt.set_size_request(450, -1)
-		txt.set_line_wrap(True)
-		txt.set_alignment(0.0, 0.0)
-		txt.show()
-		self.page_themes.pack_start(txt, False, True)
-		# create theme-selector list
-		self.tree.set_headers_visible(False)
-		self.tree.connect('cursor-changed', self.__tree_cursor_changed)
-		self.tree.show()
-		col = gtk.TreeViewColumn('')
-		cell = gtk.CellRendererText()
-		col.pack_start(cell, True)
-		#cell.set_property('foreground', 'black')
-		col.set_cell_data_func(cell, self.__render_cell)
-		self.tree.append_column(col)
-		# wrap tree in scrollwin
-		sw = gtk.ScrolledWindow()
-		sw.set_shadow_type(gtk.SHADOW_IN)
-		sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
-		sw.add(self.tree)
-		sw.show()
-		# add vbox and add tree/buttons
-		vbox = gtk.VBox()
-		vbox.pack_start(sw, True, True)
-		vbox.show()
-		# show/add page
-		self.page_themes.add(vbox)
-		self.page_themes.show()
-		self.main_notebook.append_page(self.page_themes, gtk.Label(_('Themes ')))
-		
-	def __render_cell(self, tvcolumn, cell, model, iter):
-		"""Callback for rendering the cells in the theme-treeview."""
-		# get attributes-list from Treemodel
-		attrib = model.get_value(iter, 0)
-
-		# set colors depending on state
-		col = '555555'
-		name_uc = attrib[0][0].upper() + attrib[0][1:]
-		# create markup depending on info
-		if attrib[1] == '-' and attrib[2] == '-':
-			mu = '<b><span weight="ultrabold" size="large">' + name_uc + \
-			'</span></b> (' + _('no info available') + ')'
-		else:
-			if attrib[1] == None : attrib[1] = '-'
-			if attrib[2] == None : attrib[2] = '-'
-			if attrib[3] == None : attrib[3] = '-'
-			if attrib[4] == None : attrib[4] = '-'
-			mu = '<b><span weight="ultrabold" size="large">' + name_uc + \
-				'</span></b> v' + attrib[4] + '\n<small><span color="#555555' +\
-				'">' + attrib[2].replace('\\n', '\n') + \
-				'</span></small>\n<i><small>by '+str(attrib[3])+'</small></i>'
-		# set markup
-		cell.set_property('markup', mu)
-	
-	# UI-callbacks
-	
-	def __tree_cursor_changed (self, treeview):
-		"""Callback for handling selection changes in the Themes-treeview."""
-		sel = self.tree.get_selection()
-		if sel:
-			s = sel.get_selected()
-			if s:
-				it = s[1]
-				if it:
-					attribs = self.liststore.get_value(it, 0)
-					if attribs and self.__shown_object:
-						#print attribs
-						# set theme in Screenlet (if not already active)
-						if self.__shown_object.theme_name != attribs[0]:
-							self.__shown_object.theme_name = attribs[0]
-	
-	# option-widget creation (should be split in several classes)
-	
-	def get_widget_for_option (self, option, value=None):
-		"""Return a gtk.*Widget with Label within a HBox for a given option.
-		NOTE: This is incredibly ugly, ideally all Option-subclasses should
-		have their own widgets - like StringOptionWidget, ColorOptionWidget, 
-		... and then be simply created dynamically"""
-		t = option.__class__
-		widget = None
-		if t == BoolOption:
-			widget = gtk.CheckButton()
-			widget.set_active(value)
-			widget.connect("toggled", self.options_callback, option)
-		elif t == StringOption:
-			if option.choices:
-				# if a list of values is defined, show combobox
-				widget = gtk.combo_box_new_text()
-				p = -1
-				i = 0
-				for s in option.choices:
-					widget.append_text(s)
-					if s==value:
-						p = i
-					i+=1
-				widget.set_active(p)
-				#widget.connect("changed", self.options_callback, option)
-			else:
-				widget = gtk.Entry()
-				widget.set_text(value)
-				# if it is a password, set text to be invisible
-				if option.password:
-					widget.set_visibility(False)
-			#widget.connect("key-press-event", self.options_callback, option)
-			widget.connect("changed", self.options_callback, option)
-			#widget.set_size_request(180, 28)
-		elif t == IntOption or t == FloatOption:
-			widget = gtk.SpinButton()
-			#widget.set_size_request(50, 22)
-			#widget.set_text(str(value))
-			if t == FloatOption:
-				widget.set_digits(option.digits)
-				widget.set_increments(option.increment, int(option.max/option.increment))
-			else:
-				widget.set_increments(option.increment, int(option.max/option.increment))
-			if option.min!=None and option.max!=None:
-				#print "Setting range for input to: %f, %f" % (option.min, option.max)
-				widget.set_range(option.min, option.max)
-			widget.set_value(value)
-			widget.connect("value-changed", self.options_callback, option)
-		elif t == ColorOption:
-			widget = gtk.ColorButton(gtk.gdk.Color(int(value[0]*65535), int(value[1]*65535), int(value[2]*65535)))
-			widget.set_use_alpha(True)
-#			print value
-#			print value[3]
-			widget.set_alpha(int(value[3]*65535))
-			widget.connect("color-set", self.options_callback, option)
-		elif t == FontOption:
-			widget = gtk.FontButton()
-			widget.set_font_name(value)
-			widget.connect("font-set", self.options_callback, option)		
-		elif t == FileOption:
-			widget = gtk.FileChooserButton(_("Choose File"))
-			widget.set_filename(value)
-			widget.set_size_request(180, 28)
-			widget.connect("selection-changed", self.options_callback, option)
-		elif t == DirectoryOption:
-			dlg = gtk.FileChooserDialog(buttons=(gtk.STOCK_CANCEL, 
-				gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK),
-				action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER)
-			widget = gtk.FileChooserButton(dlg)
-			widget.set_title(_("Choose Directory"))
-			widget.set_filename(value)
-			widget.set_size_request(180, 28)
-			widget.connect("selection-changed", self.options_callback, option)
-		elif t == ImageOption:
-			# create entry and button (entry is hidden)
-			entry = gtk.Entry()
-			entry.set_text(value)
-			entry.set_editable(False)
-			but = gtk.Button()
-			# util to reload preview image
-			def create_preview (filename):
-				if filename and os.path.isfile(filename):
-					pb = gtk.gdk.pixbuf_new_from_file_at_size(filename, 64, -1)
-					if pb:
-						img = gtk.Image()
-						img.set_from_pixbuf(pb)
-						return img
-				img = gtk.image_new_from_stock(gtk.STOCK_MISSING_IMAGE, 
-					gtk.ICON_SIZE_LARGE_TOOLBAR)
-				img.set_size_request(64, 64)
-				return img
-			# create button
-			def but_callback (widget):
-				dlg = gtk.FileChooserDialog(buttons=(gtk.STOCK_CANCEL, 
-					gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
-				dlg.set_title(_("Choose Image"))
-				dlg.set_keep_above(True)
-				dlg.set_filename(entry.get_text())
-				flt = gtk.FileFilter()
-				flt.add_pixbuf_formats()
-				dlg.set_filter(flt) 
-				prev = gtk.Image()
-				box = gtk.VBox()
-				box.set_size_request(150, -1)
-				box.add(prev)
-				prev.show()
-				# add preview widget to filechooser
-				def preview_callback(widget):
-					fname = dlg.get_preview_filename()
-					if fname and os.path.isfile(fname):
-						pb = gtk.gdk.pixbuf_new_from_file_at_size(fname, 150, -1)
-						if pb:
-							prev.set_from_pixbuf(pb)
-							dlg.set_preview_widget_active(True)
-						else:
-							dlg.set_preview_widget_active(False)
-				dlg.set_preview_widget_active(True)
-				dlg.connect('selection-changed', preview_callback)
-				dlg.set_preview_widget(box)
-				# run 
-				response = dlg.run()
-				if response == gtk.RESPONSE_OK:
-					entry.set_text(dlg.get_filename())
-					but.set_image(create_preview(dlg.get_filename()))
-					self.options_callback(dlg, option)
-				dlg.destroy()
-			# load preview image
-			but.set_image(create_preview(value))
-			but.connect('clicked', but_callback)
-			# create widget
-			widget = gtk.HBox()
-			widget.add(entry)
-			widget.add(but)
-			but.show()
-			widget.show()
-			# add tooltips
-			#but.set_tooltip_text(_('Select Image ...'))
-			but.set_tooltip_text(option.desc)
-			
-		elif t == ListOption:
-			entry= gtk.Entry()
-			entry.set_editable(False)
-			entry.set_text(str(value))
-			entry.show()
-			img = gtk.Image()
-			img.set_from_stock(gtk.STOCK_EDIT, 1)
-			but = gtk.Button()
-			but.set_image(img)
-			def open_listeditor(event):
-				# open dialog
-				dlg = ListOptionDialog()
-				# read string from entry and import it through option-class
-				# (this is needed to always have an up-to-date value)
-				dlg.set_list(option.on_import(entry.get_text()))
-				resp = dlg.run()
-				if resp == gtk.RESPONSE_OK:
-					# set text in entry
-					entry.set_text(str(dlg.get_list()))
-					# manually call the options-callback
-					self.options_callback(dlg, option)
-				dlg.destroy()
-			but.show()
-			but.connect("clicked", open_listeditor)
-			but.set_tooltip_text(_('Open List-Editor ...'))
-			entry.set_tooltip_text(option.desc)
-			widget = gtk.HBox()
-			widget.add(entry)
-			widget.add(but)
-		elif t == AccountOption:
-			widget = gtk.HBox()
-			vb = gtk.VBox()
-			input_name = gtk.Entry()
-			input_name.set_text(value[0])
-			input_name.show()
-			input_pass = gtk.Entry()
-			input_pass.set_visibility(False)	# password
-			input_pass.set_text(value[1])
-			input_pass.show()
-			but = gtk.Button(_('Apply'), gtk.STOCK_APPLY)
-			but.show()
-			but.connect("clicked", self.apply_options_callback, option, widget)
-			vb.add(input_name)
-			vb.add(input_pass)
-			vb.show()
-			but.set_tooltip_text(_('Apply username/password ...'))
-			input_name.set_tooltip_text(_('Enter username here ...'))
-			input_pass.set_tooltip_text(_('Enter password here ...'))
-			widget.add(vb)
-			widget.add(but)
-		elif t == TimeOption:
-			widget = gtk.HBox()
-			input_hour		= gtk.SpinButton()#climb_rate=1.0)
-			input_minute	= gtk.SpinButton()
-			input_second	= gtk.SpinButton()
-			input_hour.set_range(0, 23)
-			input_hour.set_max_length(2)
-			input_hour.set_increments(1, 1)
-			input_hour.set_numeric(True)
-			input_hour.set_value(value[0])
-			input_minute.set_range(0, 59)
-			input_minute.set_max_length(2)
-			input_minute.set_increments(1, 1)
-			input_minute.set_numeric(True)
-			input_minute.set_value(value[1])
-			input_second.set_range(0, 59)
-			input_second.set_max_length(2)
-			input_second.set_increments(1, 1)
-			input_second.set_numeric(True)
-			input_second.set_value(value[2])
-			input_hour.connect('value-changed', self.options_callback, option)
-			input_minute.connect('value-changed', self.options_callback, option)
-			input_second.connect('value-changed', self.options_callback, option)
-			input_hour.set_tooltip_text(option.desc)
-			input_minute.set_tooltip_text(option.desc)
-			input_second.set_tooltip_text(option.desc)
-			widget.add(input_hour)
-			widget.add(gtk.Label(':'))
-			widget.add(input_minute)
-			widget.add(gtk.Label(':'))
-			widget.add(input_second)
-			widget.add(gtk.Label('h'))
-			widget.show_all()
-		else:
-			widget = gtk.Entry()
-			print "unsupported type ''" % str(t)
-		hbox = gtk.HBox()
-		label = gtk.Label()
-		label.set_alignment(0.0, 0.0)
-		label.set_label(option.label)
-		label.set_size_request(180, 28)
-		label.show()
-		hbox.pack_start(label, 0, 1)
-		if widget:
-			if option.disabled:		# option disabled?
-				widget.set_sensitive(False)
-				label.set_sensitive(False)
-			#label.set_mnemonic_widget(widget)
-			widget.set_tooltip_text(option.desc)
-			widget.show()
-			# check if needs Apply-button
-			if option.realtime == False:
-				but = gtk.Button(_('Apply'), gtk.STOCK_APPLY)
-				but.show()
-				but.connect("clicked", self.apply_options_callback, 
-					option, widget)
-				b = gtk.HBox()
-				b.show()
-				b.pack_start(widget, 0, 0)
-				b.pack_start(but, 0, 0)
-				hbox.pack_start(b, 0, 0)
-			else:
-				#hbox.pack_start(widget, -1, 1)
-				hbox.pack_start(widget, 0, 0)
-		return hbox
-	
-	def read_option_from_widget (self, widget, option):
-		"""Read an option's value from the widget and return it."""
-		if not widget.window:
-			return False
-		# get type of option and read the widget's value
-		val	= None
-		t	= option.__class__
-		if t == IntOption:
-			val = int(widget.get_value())
-		elif t == FloatOption:
-			val = widget.get_value()
-		elif t == StringOption:
-			if option.choices:
-				# if default is a list, handle combobox
-				val = widget.get_active_text()
-			else:
-				val = widget.get_text()
-		elif t == BoolOption:
-			val = widget.get_active()
-		elif t == ColorOption:
-			col = widget.get_color()
-			al = widget.get_alpha()
-			val = (col.red/65535.0, col.green/65535.0, 
-				col.blue/65535.0, al/65535.0)
-		elif t == FontOption:
-			val = widget.get_font_name()
-		elif t == FileOption or t == DirectoryOption or t == ImageOption:
-			val = widget.get_filename()
-			#print widget
-		#elif t == ImageOption:
-		#	val = widget.get_text()
-		elif t == ListOption:	
-			# the widget is a ListOptionDialog here
-			val = widget.get_list()
-		elif t == AccountOption:
-			# the widget is a HBox containing a VBox containing two Entries
-			# (ideally we should have a custom widget for the AccountOption)
-			for c in widget.get_children():
-				if c.__class__ == gtk.VBox:
-					c2 = c.get_children()
-					val = (c2[0].get_text(), c2[1].get_text())
-		elif t == TimeOption:
-			box = widget.get_parent()
-			inputs = box.get_children()
-			val = (int(inputs[0].get_value()), int(inputs[2].get_value()), 
-				int(inputs[4].get_value()))
-		else:
-			print "OptionsDialog: Unknown option type: %s" % str(t)
-			return None
-		# return the value
-		return val
-	
-	# option-widget event-handling
-	
-	# TODO: custom callback/signal for each option?
-	def options_callback (self, widget, optionobj):
-		"""Callback for handling changed-events on entries."""
-		print "Changed: %s" % optionobj.name
-		if self.__shown_object:
-			# if the option is not real-time updated,
-			if optionobj.realtime == False:
-				return False
-			# read option
-			val = self.read_option_from_widget(widget, optionobj)
-			if val != None:
-				#print "SetOption: "+optionobj.name+"="+str(val)
-				# set option
-				setattr(self.__shown_object, optionobj.name, val)
-				# notify option-object's on_changed-handler
-				optionobj.emit("option_changed", optionobj)
-		return False
-
-	def apply_options_callback (self, widget, optionobj, entry):
-		"""Callback for handling Apply-button presses."""
-		if self.__shown_object:
-			# read option
-			val = self.read_option_from_widget(entry, optionobj)
-			if val != None:
-				#print "SetOption: "+optionobj.name+"="+str(val)
-				# set option
-				setattr(self.__shown_object, optionobj.name, val)
-				# notify option-object's on_changed-handler
-				optionobj.emit("option_changed", optionobj)
-		return False
-
-
-
-# ------ ONLY FOR TESTING ------------------:
-if __name__ == "__main__":
-
-	import os
-	
-	# this is only for testing - should be a Screenlet
-	class TestObject (EditableOptions):
-		
-		testlist = ['test1', 'test2', 3, 5, 'Noch ein Test']
-		pop3_account = ('Username', '')
-		
-		# TEST
-		pin_x		= 100
-		pin_y		= 6
-		text_x		= 19
-		text_y		= 35
-		font_name	= 'Sans 12'
-		rgba_color	= (0.0, 0.0, 1.0, 1.0)
-		text_prefix	= '<b>'
-		text_suffix	= '</b>'
-		note_text	= ""	# hidden option because val has its own editing-dialog
-		random_pin_pos	= True
-		opt1 = 'testval 1'
-		opt2 = 'testval 2'
-		filename2	= ''
-		filename	= ''
-		dirname		= ''
-		font = 'Sans 12'
-		color = (0.1, 0.5, 0.9, 0.9)
-		name = 'a name'
-		name2 = 'another name'
-		combo_test = 'el2'
-		flt = 0.5
-		x = 10
-		y = 25
-		width = 30
-		height = 50
-		is_sticky = False
-		is_widget = False
-		time	= (12, 32, 49)		# a time-value (tuple with ints)
-
-		def __init__ (self):
-			EditableOptions.__init__(self)
-			# Add group
-			self.add_options_group('General', 
-				'The general options for this Object ...')
-			self.add_options_group('Window', 
-				'The Window-related options for this Object ...')
-			self.add_options_group('Test', 'A Test-group ...')
-			# Add editable options
-			self.add_option(ListOption('Test', 'testlist', self.testlist,
-				'ListOption-Test', 'Testing a ListOption-type ...'))
-			self.add_option(StringOption('Window', 'name', 'TESTNAME',
-				'Testname', 'The name/id of this Screenlet-instance ...'), 
-				realtime=False)
-			self.add_option(AccountOption('Test', 'pop3_account', 
-				self.pop3_account, 'Username/Password', 
-				'Enter username/password here ...'))
-			self.add_option(StringOption('Window', 'name2', 'TESTNAME2',
-				'String2', 'Another string-test ...'))
-			self.add_option(StringOption('Test', 'combo_test', "el1", 'Combo', 
-				'A StringOption displaying a drop-down-list with choices...', 
-				choices=['el1', 'el2', 'element 3']))
-			self.add_option(FloatOption('General', 'flt', 30, 
-				'A Float', 'Testing a FLOAT-type ...', 
-				min=0, max=gtk.gdk.screen_width(), increment=0.01, digits=4))
-			self.add_option(IntOption('General', 'x', 30, 
-				'X-Position', 'The X-position of this Screenlet ...', 
-				min=0, max=gtk.gdk.screen_width()))
-			self.add_option(IntOption('General', 'y', 30, 
-				'Y-Position', 'The Y-position of this Screenlet ...',
-				min=0, max=gtk.gdk.screen_height()))
-			self.add_option(IntOption('Test', 'width', 300, 
-				'Width', 'The width of this Screenlet ...', min=100, max=1000))
-			self.add_option(IntOption('Test', 'height', 150, 
-				'Height', 'The height of this Screenlet ...', 
-				min=100, max=1000))
-			self.add_option(BoolOption('General', 'is_sticky', True, 
-				'Stick to Desktop', 'Show this Screenlet always ...'))
-			self.add_option(BoolOption('General', 'is_widget', False,  
-				'Treat as Widget', 'Treat this Screenlet as a "Widget" ...'))
-			self.add_option(FontOption('Test', 'font', 'Sans 14', 
-				'Font', 'The font for whatever ...'))
-			self.add_option(ColorOption('Test', 'color', (1, 0.35, 0.35, 0.7), 
-				'Color', 'The color for whatever ...'))
-			self.add_option(FileOption('Test', 'filename', os.environ['HOME'],
-				'Filename-Test', 'Testing a FileOption-type ...',
-				patterns=['*.py', '*.pyc']))
-			self.add_option(ImageOption('Test', 'filename2', os.environ['HOME'],
-				'Image-Test', 'Testing the ImageOption-type ...'))
-			self.add_option(DirectoryOption('Test', 'dirname', os.environ['HOME'],
-				'Directory-Test', 'Testing a FileOption-type ...'))
-			self.add_option(TimeOption('Test','time', self.time,
-				'TimeOption-Test', 'Testing a TimeOption-type ...'))
-			# TEST
-			self.disable_option('width')
-			self.disable_option('height')
-			# TEST: load options from file
-			#self.add_options_from_file('/home/ryx/Desktop/python/screenlets/screenlets-0.0.9/src/share/screenlets/Notes/options.xml')
-		
-		def __setattr__(self, name, value):
-			self.__dict__[name] = value
-			print name + "=" + str(value)
-		
-		def get_short_name(self):
-			return self.__class__.__name__[:-6]
-
-			
-		
-	# this is only for testing - should be a Screenlet
-	class TestChildObject (TestObject):
-		
-		uses_theme = True
-		theme_name = 'test'
-		
-		def __init__ (self):
-			TestObject.__init__(self)
-			self.add_option(StringOption('Test', 'anothertest', 'ksjhsjgd',
-				'Another Test', 'An attribute in the subclass  ...'))
-			self.add_option(StringOption('Test', 'theme_name', self.theme_name,
-				'Theme', 'The theme for this Screenelt  ...',
-				choices=['test1', 'test2', 'mytheme', 'blue', 'test']))
-	
-	
-	# TEST: load/save
-	# TEST: option-editing
-	to = TestChildObject()
-	#print to.export_options_as_list()
-	se = OptionsDialog(500, 380)#, treeview=True)
-	#img = gtk.image_new_from_stock(gtk.STOCK_ABOUT, 5)
-	img = gtk.Image()
-	img.set_from_file('../share/screenlets/Notes/icon.svg')
-	se.set_info('TestOptions', 
-		'A test for an extended options-dialog with embedded about-info.' + 
-		' Can be used for the Screenlets to have all in one ...\nNOTE:' + 
-		'<span color="red"> ONLY A TEST!</span>',
-		'(c) RYX 2007', version='v0.0.1', icon=img)
-	se.show_options_for_object(to)
-	resp = se.run()
-	if resp == gtk.RESPONSE_OK:
-		print "OK"
-	else:
-		print "Cancelled."
-	se.destroy()
-	print to.export_options_as_list()
-

=== added file 'src/lib/options/__init__.py'
--- src/lib/options/__init__.py	1970-01-01 00:00:00 +0000
+++ src/lib/options/__init__.py	2011-03-28 16:19:24 +0000
@@ -0,0 +1,202 @@
+# This application is released under the GNU General Public License
+# v3 (or, at your option, any later version). You can find the full
+# text of the license under http://www.gnu.org/licenses/gpl.txt.
+# By using, editing and/or distributing this software you agree to
+# the terms and conditions of this license.
+# Thank you for using free software!
+
+# Options-system (c) RYX (aka Rico Pfaus) 2007 <ryx@xxxxxxxxxxxxxxx>
+# Heavily Refactored by Martin Owens (c) 2009
+#
+# INFO:
+# - a dynamic Options-system that allows very easy creation of
+#   objects with embedded configuration-system.
+#   NOTE: The Dialog is not very nice yet - it is not good OOP-practice
+#   because too big functions and bad class-layout ... but it works
+#   for now ... :)
+#
+# TODO:
+# - option-widgets for all option-types (e.g. ListOptionWidget, ColorOptionWidget)
+# - OptionGroup-class instead of (or behind) add_options_group
+# - TimeOption, DateOption
+# - FileOption needs filter/limit-attribute
+# - allow options to disable/enable other options
+# - support for EditableOptions-subclasses as options
+# - separate OptionEditorWidget from Editor-Dialog
+# - place ui-code into screenlets.options.ui-module
+# - create own widgets for each Option-subclass
+#
+
+import screenlets
+
+import os
+import gtk, gobject
+
+# translation stuff
+import gettext
+gettext.textdomain('screenlets')
+gettext.bindtextdomain('screenlets', screenlets.INSTALL_PREFIX +  '/share/locale')
+
+def _(s):
+    return gettext.gettext(s)
+
+from boolean_option import BoolOption
+from string_option import StringOption
+from number_option import IntOption, FloatOption
+from list_option import ListOption
+from account_option import AccountOption
+from font_option import FontOption
+from file_option import FileOption, DirectoryOption, ImageOption
+from colour_option import ColorOption, ColorsOption
+from time_option import TimeOption
+from base import EditableOptions, OptionsDialog
+
+# ------ ONLY FOR TESTING ------------------:
+if __name__ == "__main__":
+
+    import os
+
+    # this is only for testing - should be a Screenlet
+    class TestObject (EditableOptions):
+
+        testlist = ['test1', 'test2', 3, 5, 'Noch ein Test']
+        pop3_account = ('Username', '')
+
+        # TEST
+        pin_x        = 100
+        pin_y        = 6
+        text_x        = 19
+        text_y        = 35
+        font_name    = 'Sans 12'
+        rgba_color    = (0.0, 0.0, 1.0, 1.0)
+        text_prefix    = '<b>'
+        text_suffix    = '</b>'
+        note_text    = ""    # hidden option because val has its own editing-dialog
+        random_pin_pos    = True
+        opt1 = 'testval 1'
+        opt2 = 'testval 2'
+        filename2    = ''
+        filename    = ''
+        dirname        = ''
+        font = 'Sans 12'
+        color = (0.1, 0.5, 0.9, 0.9)
+        name = 'a name'
+        name2 = 'another name'
+        combo_test = 'el2'
+        flt = 0.5
+        x = 10
+        y = 25
+        width = 30
+        height = 50
+        is_sticky = False
+        is_widget = False
+        time    = (12, 32, 49)        # a time-value (tuple with ints)
+
+        def __init__ (self):
+            EditableOptions.__init__(self)
+            # Add group
+            self.add_options_group('General',
+                'The general options for this Object ...')
+            self.add_options_group('Window',
+                'The Window-related options for this Object ...')
+            self.add_options_group('Test', 'A Test-group ...')
+            # Add editable options
+            self.add_option(ListOption('Test', 'testlist', default=self.testlist,
+                label='ListOption-Test', desc='Testing a ListOption-type ...'))
+            self.add_option(StringOption('Window', 'name', default='TESTNAME',
+                label='Testname', desc='The name/id of this Screenlet-instance ...'),
+                realtime=False)
+            self.add_option(AccountOption('Test', 'pop3_account',
+                default=self.pop3_account, label='Username/Password',
+                desc='Enter username/password here ...'))
+            self.add_option(StringOption('Window', 'name2', default='TESTNAME2',
+                label='String2', desc='Another string-test ...'))
+            self.add_option(StringOption('Test', 'combo_test', default="el1",
+                label='Combo', desc='A StringOption for a drop-down-list.',
+                choices=['el1', 'el2', 'element 3']))
+            self.add_option(FloatOption('General', 'flt', default=30.4,
+                label='A Float', desc='Testing a FLOAT-type ...',
+                min=0, max=gtk.gdk.screen_width(), increment=0.01, digits=4))
+            self.add_option(IntOption('General', 'x', default=30,
+                label='X-Position', desc='The X-position of this Screenlet ...',
+                min=0, max=gtk.gdk.screen_width()))
+            self.add_option(IntOption('General', 'y', default=30,
+                label='Y-Position', desc='The Y-position of this Screenlet ...',
+                min=0, max=gtk.gdk.screen_height()))
+            self.add_option(IntOption('Test', 'width', default=300,
+                label='Width', desc='The width of this Screenlet ...',
+                min=100, max=1000, increment=12))
+            self.add_option(IntOption('Test', 'height', default=150,
+                label='Height', desc='The height of this Screenlet ...',
+                min=100, max=1000))
+            self.add_option(BoolOption('General', 'is_sticky', default=True,
+                label='Stick to Desktop', desc='Show this Screenlet always ...'))
+            self.add_option(BoolOption('General', 'is_widget', default=False,
+                label='Treat as Widget', desc='Treat this Screenlet as a "Widget" ...'))
+            self.add_option(FontOption('Test', 'font', default='Sans 14',
+                label='Font', desc='The font for whatever ...'))
+            self.add_option(ColorOption('Test', 'color', default=(1, 0.35, 0.35, 0.7),
+                label='Color', desc='The color for whatever ...'))
+            self.add_option(ColorsOption('Test', 'rainbows', default=[(1, 0.35, 0.35, 0.7), (0.1, 0.8, 0.2, 0.2), (1, 0.35, 0.6, 0.7)],
+                label='Multi-Colours', desc='The colors for whatever ...'))
+            self.add_option(ColorsOption('Test', 'rainbow2', default=(1, 0.35, 0.35, 0.7),
+                label='Colours-Up', desc='The colors for whatever ...'))
+            self.add_option(FileOption('Test', 'filename', default=os.environ['HOME'],
+                label='Filename-Test', desc='Testing a FileOption-type ...',
+                patterns=[ ( 'Python Files', ['*.py', '*.pyc'] ) ]))
+            self.add_option(ImageOption('Test', 'filename2', default=os.environ['HOME'],
+                label='Image-Test', desc='Testing the ImageOption-type ...'))
+            self.add_option(DirectoryOption('Test', 'dirname', default=os.environ['HOME'],
+                label='Directory-Test', desc='Testing a FileOption-type ...'))
+            self.add_option(TimeOption('Test','time', default=self.time,
+                label='TimeOption-Test', desc='Testing a TimeOption-type ...'))
+            # TEST
+            self.disable_option('width')
+            self.disable_option('height')
+            # TEST: load options from file
+            #self.add_options_from_file('/home/ryx/Desktop/python/screenlets/screenlets-0.0.9/src/share/screenlets/Notes/options.xml')
+
+        def __setattr__(self, name, value):
+            self.__dict__[name] = value
+            print name + "=" + str(value)
+
+        def get_short_name(self):
+            return self.__class__.__name__[:-6]
+
+
+    # this is only for testing - should be a Screenlet
+    class TestChildObject (TestObject):
+
+        uses_theme = True
+        theme_name = 'test'
+
+        def __init__ (self):
+            TestObject.__init__(self)
+            self.add_option(StringOption('Test', 'anothertest', default='ksjhsjgd',
+                label='Another Test', desc='An attribute in the subclass  ...'))
+            self.add_option(StringOption('Test', 'theme_name', default=self.theme_name,
+                label='Theme', desc='The theme for this Screenelt  ...',
+                choices=['test1', 'test2', 'mytheme', 'blue', 'test']))
+
+    # TEST: load/save
+    # TEST: option-editing
+    to = TestChildObject()
+    #print to.export_options_as_list()
+    se = OptionsDialog(500, 380)#, treeview=True)
+    #img = gtk.image_new_from_stock(gtk.STOCK_ABOUT, 5)
+    img = gtk.Image()
+    img.set_from_file('../share/screenlets/Notes/icon.svg')
+    se.set_info('TestOptions',
+        'A test for an extended options-dialog with embedded about-info.' +
+        ' Can be used for the Screenlets to have all in one ...\nNOTE:' +
+        '<span color="red"> ONLY A TEST!</span>',
+        '(c) RYX 2007', version='v0.0.1', icon=img)
+    se.show_options_for_object(to)
+    resp = se.run()
+    if resp == gtk.RESPONSE_OK:
+        print "OK"
+    else:
+        print "Cancelled."
+    se.destroy()
+    print to.export_options_as_list()
+

=== added file 'src/lib/options/account_option.py'
--- src/lib/options/account_option.py	1970-01-01 00:00:00 +0000
+++ src/lib/options/account_option.py	2011-03-28 16:19:24 +0000
@@ -0,0 +1,143 @@
+# 
+# Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@xxxxxxxxx>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# 
+"""
+Account options, these classes will display a text box.
+"""
+
+import gtk
+import gnomekeyring
+
+from screenlets.options import _
+from base import Option
+
+class AccountOption(Option):
+    """
+    An Option-type for username/password combos. Stores the password in
+    the gnome-keyring (if available) and only saves username and auth_token
+    through the screenlets-backend.
+    TODO:
+    - not create new token for any change (use "set" instead of "create" if
+      the given item already exists)
+    - use usual storage if no keyring is available but output warning
+    - on_delete-function for removing the data from keyring when the
+      Screenlet holding the option gets deleted
+    """
+    protected = True
+
+    def __init__(self, group, name, *attr, **args):
+        super(AccountOption, self).__init__ (group, name, *attr, **args)
+        # check for availability of keyring
+        if not gnomekeyring.is_available():
+            raise Exception('GnomeKeyring is not available!!')
+        # THIS IS A WORKAROUND FOR A BUG IN KEYRING (usually we would use
+        # gnomekeyring.get_default_keyring_sync() here):
+        # find first available keyring
+        self.keyring_list = gnomekeyring.list_keyring_names_sync()
+        if len(self.keyring_list) == 0:
+            raise Exception('No keyrings found. Please create one first!')
+        else:
+            # we prefer the default keyring
+            try:
+                self.keyring = gnomekeyring.get_default_keyring_sync()
+            except:
+                if "session" in self.keyring_list:
+                    print "Warning: No default keyring found, using session keyring. Storage is not permanent!"
+                    self.keyring = "session"
+                else:
+                    print "Warning: Neither default nor session keyring found, assuming keyring %s!" % self.keyring_list[0]
+                    self.keyring = self.keyring_list[0]
+
+
+    def on_import(self, strvalue):
+        """Import account info from a string (like 'username:auth_token'),.
+        retrieve the password from the storage and return a tuple containing
+        username and password."""
+        # split string into username/auth_token
+        #data = strvalue.split(':', 1)
+        (name, auth_token) = strvalue.split(':', 1)
+        if name and auth_token:
+            # read pass from storage
+            try:
+                pw = gnomekeyring.item_get_info_sync(self.keyring, 
+                    int(auth_token)).get_secret()
+            except Exception, ex:
+                print "ERROR: Unable to read password from keyring: %s" % ex
+                pw = ''
+            # return
+            return (name, pw)
+        else:
+            raise Exception('Illegal value in AccountOption.on_import.')
+
+    def on_export(self, value):
+        """Export the given tuple/list containing a username and a password. The
+        function stores the password in the gnomekeyring and returns a
+        string in form 'username:auth_token'."""
+        # store password in storage
+        attribs = dict(name=value[0])
+        auth_token = gnomekeyring.item_create_sync(self.keyring, 
+            gnomekeyring.ITEM_GENERIC_SECRET, value[0], attribs, value[1], True)
+        # build value from username and auth_token
+        return value[0] + ':' + str(auth_token)
+
+    def generate_widget(self, value):
+        """Generate a textbox for a account options"""
+        self.widget = gtk.HBox()
+        vb = gtk.VBox()
+        input_name = gtk.Entry()
+        input_name.set_text(value[0])
+        input_name.show()
+        input_pass = gtk.Entry()
+        input_pass.set_visibility(False)    # password
+        input_pass.set_text(value[1])
+        input_pass.show()
+        but = gtk.Button(_('Apply'), gtk.STOCK_APPLY)
+        but.show()
+        but.connect("clicked", self.has_changed)
+        vb.add(input_name)
+        vb.add(input_pass)
+        vb.show()
+        but.set_tooltip_text(_('Apply username/password ...'))
+        input_name.set_tooltip_text(_('Enter username here ...'))
+        input_pass.set_tooltip_text(_('Enter password here ...'))
+        self.widget.add(vb)
+        self.widget.add(but)
+        return self.widget
+
+    def set_value(self, value):
+        """Set the account value as required."""
+        self.value = value
+
+    def has_changed(self, widget):
+        """Executed when the widget event kicks off."""
+        # the widget is a HBox containing a VBox containing two Entries
+        # (ideally we should have a custom widget for the AccountOption)
+        for c in self.widget.get_children():
+            if c.__class__ == gtk.VBox:
+               c2 = c.get_children()
+               self.value = (c2[0].get_text(), c2[1].get_text())
+        super(AccountOption, self).has_changed()
+
+"""#TEST:
+o = AccountOption('None', 'pop3_account', ('',''), 'Username/Password', 'Enter username/password here ...')
+# save option to keyring
+exported_account = o.on_export(('RYX', 'mysecretpassword'))
+print exported_account
+# and read option back from keyring
+print o.on_import(exported_account)
+"""
+

=== added file 'src/lib/options/base.py'
--- src/lib/options/base.py	1970-01-01 00:00:00 +0000
+++ src/lib/options/base.py	2011-03-28 16:19:24 +0000
@@ -0,0 +1,624 @@
+#
+# Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@xxxxxxxxx>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+"""
+Base classes and basic mechanics for all screenlet options.
+"""
+
+import screenlets
+from screenlets.options import _
+
+import os
+import gtk, gobject
+import xml.dom.minidom
+from xml.dom.minidom import Node
+
+# -----------------------------------------------------------------------
+# Option-classes and subclasses
+# -----------------------------------------------------------------------
+
+class Option(gobject.GObject):
+    """An Option stores information about a certain object-attribute. It doesn't
+    carry information about the value or the object it belongs to - it is only a
+    one-way data-storage for describing how to handle attributes."""
+    __gsignals__ = dict(option_changed=(gobject.SIGNAL_RUN_FIRST,
+        gobject.TYPE_NONE, (gobject.TYPE_OBJECT,)))
+    default = None
+    label = None
+    desc = None
+    hidden = False
+    disabled = False
+    realtime = True
+    protected = False
+    widget = None
+
+    def __init__ (self, group, name, *attr, **args):
+        """Creates a new Option with the given information."""
+        super(Option, self).__init__()
+        self.name = name
+        self.group = group
+        # To maintain compatability, we parse out the 3 attributes and
+        # Move into known arguments.
+        if len(attr) == 3:
+            args.setdefault('default', attr[0])
+            args.setdefault('label', attr[1])
+            args.setdefault('desc', attr[2])
+        # This should allow any of the class options to be set on init.
+        for name in args.keys():
+            if hasattr(self, name):
+                # Replaces class variables (defaults) with object vars
+                setattr(self, name, args[name])
+
+        # XXX for groups (TODO: OptionGroup)
+        # XXX callback to be notified when this option changes
+        # XXX real-time update?
+        # XXX protected from get/set through service
+
+    def on_import(self, strvalue):
+        """Callback - called when an option gets imported from a string.
+        This function MUST return the string-value converted to the required
+        type!"""
+        return strvalue.replace("\\n", "\n")
+
+    def on_export(self, value):
+        """Callback - called when an option gets exported to a string. The
+        value-argument needs to be converted to a string that can be imported
+        by the on_import-handler. This handler MUST return the value
+        converted to a string!"""
+        return str(value).replace("\n", "\\n")
+
+    def generate_widget(self):
+        """This should generate all the required widgets for display."""
+        raise NotImplementedError, "Generating Widget should be done in child"
+
+    def set_value(self, value):
+        """Set the true/false value to the checkbox widget"""
+        raise NotImplementedError, "Can't update the widget and local value"
+
+    def has_changed(self):
+        """Executed when the widget event kicks off."""
+        return self.emit("option_changed", self)
+
+
+def create_option_from_node (node, groupname):
+    """Create an Option from an XML-node with option-metadata."""
+    #print "TODO OPTION: " + str(cn)
+    otype = node.getAttribute("type")
+    oname = node.getAttribute("name")
+    ohidden = node.getAttribute("hidden")
+    options = {
+        'default'   : None,
+        'info'      : '',
+        'label'     : '',
+        'min'       : None,
+        'max'       : None,
+        'increment' : 1,
+        'choices'   : '',
+        'digits'    : None,
+    }
+    if otype and oname:
+        # parse children of option-node and save all useful attributes
+        for attr in node.childNodes:
+            if attr.nodeType == Node.ELEMENT_NODE and attr.nodeName in options.keys():
+                options[attr.nodeName] = attr.firstChild.nodeValue
+        # if we have all needed values, create the Option
+        if options['default']:
+            # create correct classname here
+            cls = otype[0].upper() + otype.lower()[1:] + 'Option'
+            #print 'Create: ' +cls +' / ' + oname + ' ('+otype+')'
+            # and build new instance (we use on_import for setting default val)
+            clsobj = getattr(__import__(__name__), cls)
+            opt = clsobj(groupname, oname, default=None,
+                label=options['label'], desc=options['info'])
+            opt.default = opt.on_import(options['default'])
+            # set values to the correct types
+            if cls == 'IntOption':
+                if options['min']:
+                    opt.min = int(options['min'])
+                if options['max']:
+                    opt.max = int(options['max'])
+                if options['increment']:
+                    opt.increment = int(options['increment'])
+            elif cls == 'FloatOption':
+                if options['digits']:
+                    opt.digits = int(options['digits'])
+                if options['min']:
+                    opt.min = float(options['min'])
+                if options['min']:
+                    opt.max = float(options['max'])
+                if options['increment']:
+                    opt.increment = float(options['increment'])
+            elif cls == 'StringOption':
+                if options['choices']:
+                    opt.choices = options['choices']
+            return opt
+    return None
+
+
+class EditableOptions(object):
+    """The EditableOptions can be inherited from to allow objects to export
+    editable options for editing them with the OptionsEditor-class.
+    NOTE: This could use some improvement and is very poorly coded :) ..."""
+
+    def __init__ (self):
+        self.__options__ = []
+        self.__options_groups__ = {}
+        # This is a workaround to remember the order of groups
+        self.__options_groups_ordered__ = []
+
+    def add_option (self, option, callback=None, realtime=True):
+        """Add an editable option to this object. Editable Options can be edited
+        and configured using the OptionsDialog. The optional callback-arg can be
+        used to set a callback that gets notified when the option changes its
+        value."""
+        #print "Add option: "+option.name
+        # if option already editable (i.e. initialized), return
+        for o in self.__options__:
+            if o.name == option.name:
+                return False
+        self.__dict__[option.name] = option.default
+        # set auto-update (TEMPORARY?)
+        option.realtime = realtime
+        # add option to group (output error if group is undefined)
+        try:
+            self.__options_groups__[option.group]['options'].append(option)
+        except:
+            print "Options: Error - group %s not defined." % option.group
+            return False
+        # now add the option
+        self.__options__.append(option)
+        # if callback is set, add callback
+        if callback:
+            option.connect("option_changed", callback)
+        option.connect("option_changed", self.callback_value_changed)
+        return True
+
+
+    def add_options_group (self, name, group_info):
+        """Add a new options-group to this Options-object"""
+        self.__options_groups__[name] = {'label':name,
+            'info':group_info, 'options':[]}
+        self.__options_groups_ordered__.append(name)
+        #print self.options_groups
+
+    def disable_option(self, name):
+        """Disable the inputs for a certain Option."""
+        for o in self.__options__:
+            if o.name == name:
+                o.disabled = True
+                return True
+        return False
+
+    def enable_option(self, name):
+        """Enable the inputs for a certain Option."""
+        for o in self.__options__:
+            if o.name == name:
+                o.disabled = False
+                return True
+        return False
+
+    def export_options_as_list(self):
+        """Returns all editable options within a list (without groups)
+        as key/value tuples."""
+        lst = []
+        for o in self.__options__:
+            lst.append((o.name, o.on_export(getattr(self, o.name))))
+        return lst
+
+    def callback_value_changed(self, sender, option):
+        """Called when a widget has updated and this needs calling."""
+        if hasattr(self, option.name):
+            return setattr(self, option.name, option.value)
+        raise KeyError, "Callback tried to set an option that wasn't defined."
+
+    def get_option_by_name (self, name):
+        """Returns an option in this Options by it's name (or None).
+        TODO: this gives wrong results in childclasses ... maybe access
+        as class-attribute??"""
+        for o in self.__options__:
+            if o.name == name:
+                return o
+        return None
+
+    def remove_option (self, name):
+        """Remove an option from this Options."""
+        for o in self.__options__:
+            if o.name == name:
+                del o
+                return True
+        return True
+
+    def add_options_from_file (self, filename):
+        """This function creates options from an XML-file with option-metadata.
+        TODO: make this more reusable and place it into module (once the groups
+        are own objects)"""
+        # create xml document
+        try:
+            doc = xml.dom.minidom.parse(filename)
+        except:
+            raise Exception('Invalid XML in metadata-file (or file missing): "%s".' % filename)
+        # get rootnode
+        root = doc.firstChild
+        if not root or root.nodeName != 'screenlet':
+            raise Exception('Missing or invalid rootnode in metadata-file: "%s".' % filename)
+        # ok, let's check the nodes: this one should contain option-groups
+        groups = []
+        for node in root.childNodes:
+            # we only want element-nodes
+            if node.nodeType == Node.ELEMENT_NODE:
+                #print node
+                if node.nodeName != 'group' or not node.hasChildNodes():
+                    # we only allow groups in the first level (groups need children)
+                    raise Exception('Error in metadata-file "%s" - only <group>-tags allowed in first level. Groups must contain at least one <info>-element.' % filename)
+                else:
+                    # ok, create a new group and parse its elements
+                    group = {}
+                    group['name']        = node.getAttribute("name")
+                    if not group['name']:
+                        raise Exception('No name for group defined in "%s".' % filename)
+                    group['info']        = ''
+                    group['options']    = []
+                    # check all children in group
+                    for on in node.childNodes:
+                        if on.nodeType == Node.ELEMENT_NODE:
+                            if on.nodeName == 'info':
+                                # info-node? set group-info
+                                group['info'] = on.firstChild.nodeValue
+                            elif on.nodeName == 'option':
+                                # option node? parse option node
+                                opt = create_option_from_node (on, group['name'])
+                                # ok? add it to list
+                                if opt:
+                                    group['options'].append(opt)
+                                else:
+                                    raise Exception('Invalid option-node found in "%s".' % filename)
+
+                    # create new group
+                    if len(group['options']):
+                        self.add_options_group(group['name'], group['info'])
+                        for o in group['options']:
+                            self.add_option(o)
+                    # add group to list
+                    #groups.append(group)
+
+
+class OptionsDialog(gtk.Dialog):
+    """A dynamic options-editor for editing Screenlets which are implementing
+    the EditableOptions-class."""
+
+    __shown_object = None
+
+    def __init__ (self, width, height):
+        # call gtk.Dialog.__init__
+        super(OptionsDialog, self).__init__(
+            _("Edit Options"), flags=gtk.DIALOG_DESTROY_WITH_PARENT |
+            gtk.DIALOG_NO_SEPARATOR,
+            buttons = (#gtk.STOCK_REVERT_TO_SAVED, gtk.RESPONSE_APPLY,
+                gtk.STOCK_CLOSE, gtk.RESPONSE_OK))
+        # set size
+        self.resize(width, height)
+        self.set_keep_above(True)    # to avoid confusion
+        self.set_border_width(10)
+        # create attribs
+        self.page_about   = None
+        self.page_options = None
+        self.page_themes  = None
+        self.vbox_editor  = None
+        self.hbox_about   = None
+        self.infotext     = None
+        self.infoicon     = None
+        # create theme-list
+        self.liststore    = gtk.ListStore(object)
+        self.tree         = gtk.TreeView(model=self.liststore)
+        # create/add outer notebook
+        self.main_notebook = gtk.Notebook()
+        self.main_notebook.show()
+        self.vbox.add(self.main_notebook)
+        # create/init notebook pages
+        self.create_about_page()
+        self.create_themes_page()
+        self.create_options_page()
+
+    # "public" functions
+
+    def reset_to_defaults(self):
+        """Reset all entries for the currently shown object to their default
+        values (the values the object has when it is first created).
+        NOTE: This function resets ALL options, so BE CARFEUL!"""
+        if self.__shown_object:
+            for o in self.__shown_object.__options__:
+                # set default value
+                setattr(self.__shown_object, o.name, o.default)
+
+    def set_info(self, name, info, copyright='', version='', icon=None):
+        """Update the "About"-page with the given information."""
+        # convert infotext (remove EOLs and TABs)
+        info = info.replace("\n", "")
+        info = info.replace("\t", " ")
+        # create markup
+        markup = '\n<b><span size="xx-large">' + name + '</span></b>'
+        if version:
+            markup += '  <span size="large"><b>' + version + '</b></span>'
+        markup += '\n\n'+info+'\n<span size="small">\n'+copyright+'</span>'
+        self.infotext.set_markup(markup)
+        # icon?
+        if icon:
+            # remove old icon
+            if self.infoicon:
+                self.infoicon.destroy()
+            # set new icon
+            self.infoicon = icon
+            self.infoicon.set_alignment(0.0, 0.10)
+            self.infoicon.show()
+            self.hbox_about.pack_start(self.infoicon, 0, 1, 10)
+        else:
+            self.infoicon.hide()
+
+    def show_options_for_object (self, obj):
+        """Update the OptionsEditor to show the options for the given Object.
+        The Object needs to be an EditableOptions-subclass.
+        NOTE: This needs heavy improvement and should use OptionGroups once
+              they exist"""
+        self.__shown_object = obj
+        # create notebook for groups
+        notebook = gtk.Notebook()
+        self.vbox_editor.add(notebook)
+        for group in obj.__options_groups_ordered__:
+            group_data = obj.__options_groups__[group]
+            # create box for tab-page
+            page = gtk.VBox()
+            page.set_border_width(10)
+            if group_data['info'] != '':
+                info = gtk.Label(group_data['info'])
+                info.show()
+                info.set_alignment(0, 0)
+                page.pack_start(info, 0, 0, 7)
+                sep = gtk.HSeparator()
+                sep.show()
+                #page.pack_start(sep, 0, 0, 5)
+            # create VBox for inputs
+            box = gtk.VBox()
+            box.show()
+            box.set_border_width(5)
+            # add box to page
+            page.add(box)
+            page.show()
+            # add new notebook-page
+            label = gtk.Label(group_data['label'])
+            label.show()
+            notebook.append_page(page, label)
+            # and create inputs
+            for option in group_data['options']:
+                if option.hidden == False:
+                    val = getattr(obj, option.name)
+                    w = self.generate_widget( option, val )
+                    if w:
+                        box.pack_start(w, 0, 0)
+                        w.show()
+        notebook.show()
+        # show/hide themes tab, depending on whether the screenlet uses themes
+        if obj.uses_theme and obj.theme_name != '':
+            self.show_themes_for_screenlet(obj)
+        else:
+            self.page_themes.hide()
+
+    def generate_widget(self, option, value):
+        """Generate the required widgets and add the label."""
+        widget = option.generate_widget(value)
+        hbox = gtk.HBox()
+        label = gtk.Label()
+        label.set_alignment(0.0, 0.0)
+        label.set_label(option.label)
+        label.set_size_request(180, 28)
+        label.show()
+        hbox.pack_start(label, 0, 1)
+        if widget:
+            if option.disabled:
+                widget.set_sensitive(False)
+                label.set_sensitive(False)
+            widget.set_tooltip_text(option.desc)
+            widget.show()
+            # check if needs Apply-button
+            if option.realtime == False:
+                but = gtk.Button(_('Apply'), gtk.STOCK_APPLY)
+                but.show()
+                but.connect("clicked", option.has_changed)
+                b = gtk.HBox()
+                b.show()
+                b.pack_start(widget, 0, 0)
+                b.pack_start(but, 0, 0)
+                hbox.pack_start(b, 0, 0)
+            else:
+                hbox.pack_start(widget, 0, 0)
+        return hbox
+
+
+    def show_themes_for_screenlet (self, obj):
+        """Update the Themes-page to display the available themes for the
+        given Screenlet-object."""
+        dircontent = []
+        screenlets.utils.refresh_available_screenlet_paths()
+        # now check all paths for themes
+        for path in screenlets.SCREENLETS_PATH:
+            p = path + '/' + obj.get_short_name() + '/themes'
+            print p
+            #p = '/usr/local/share/screenlets/Clock/themes'    # TEMP!!!
+            try:
+                dc = os.listdir(p)
+                for d in dc:
+                    dircontent.append({'name':d, 'path':p+'/'})
+            except:
+                print "Path %s not found." % p
+        # list with found themes
+        found_themes = []
+        # check all themes in path
+        for elem in dircontent:
+            # load themes with the same name only once
+            if found_themes.count(elem['name']):
+                continue
+            found_themes.append(elem['name'])
+            # build full path of theme.conf
+            theme_conf = elem['path'] + elem['name'] + '/theme.conf'
+            # if dir contains a theme.conf
+            if os.access(theme_conf, os.F_OK):
+                # load it and create new list entry
+                ini = screenlets.utils.IniReader()
+                if ini.load(theme_conf):
+                    # check for section
+                    if ini.has_section('Theme'):
+                        # get metainfo from theme
+                        th_fullname = ini.get_option('name', 
+                            section='Theme')
+                        th_info     = ini.get_option('info', 
+                            section='Theme')
+                        th_version  = ini.get_option('version', 
+                            section='Theme')
+                        th_author   = ini.get_option('author', 
+                            section='Theme')
+                        # create array from metainfo and add it to liststore
+                        info = [elem['name'], th_fullname, th_info, th_author, 
+                            th_version]
+                        self.liststore.append([info])
+                    else:
+                        # no theme section in theme.conf just add theme-name
+                        self.liststore.append([[elem['name'], '-', '-', '-', '-']])
+            else:
+                # no theme.conf in dir? just add theme-name
+                self.liststore.append([[elem['name'], '-', '-', '-', '-']])
+            # is it the active theme?
+            if elem['name'] == obj.theme_name:
+                # select it in tree
+                print "active theme is: %s" % elem['name']
+                sel = self.tree.get_selection()
+                if sel:
+                    it = self.liststore.get_iter_from_string(\
+                        str(len(self.liststore)-1))
+                    if it:
+                        sel.select_iter(it)
+    # UI-creation
+
+    def create_about_page (self):
+        """Create the "About"-tab."""
+        self.page_about = gtk.HBox()
+        # create about box
+        self.hbox_about = gtk.HBox()
+        self.hbox_about.show()
+        self.page_about.add(self.hbox_about)
+        # create icon
+        self.infoicon = gtk.Image()
+        self.infoicon.show()
+        self.page_about.pack_start(self.infoicon, 0, 1, 10)
+        # create infotext
+        self.infotext = gtk.Label()
+        self.infotext.use_markup = True
+        self.infotext.set_line_wrap(True)
+        self.infotext.set_alignment(0.0, 0.0)
+        self.infotext.show()
+        self.page_about.pack_start(self.infotext, 1, 1, 5)
+        # add page
+        self.page_about.show()
+        self.main_notebook.append_page(self.page_about, gtk.Label(_('About ')))
+
+    def create_options_page (self):
+        """Create the "Options"-tab."""
+        self.page_options = gtk.HBox()
+        # create vbox for options-editor
+        self.vbox_editor    = gtk.VBox(spacing=3)
+        self.vbox_editor.set_border_width(5)
+        self.vbox_editor.show()
+        self.page_options.add(self.vbox_editor)
+        # show/add page
+        self.page_options.show()
+        self.main_notebook.append_page(self.page_options, gtk.Label(_('Options ')))
+
+    def create_themes_page (self):
+        """Create the "Themes"-tab."""
+        self.page_themes = gtk.VBox(spacing=5)
+        self.page_themes.set_border_width(10)
+        # create info-text list
+        txt = gtk.Label(_('Themes allow you to easily switch the appearance of your Screenlets. On this page you find a list of all available themes for this Screenlet.'))
+        txt.set_size_request(450, -1)
+        txt.set_line_wrap(True)
+        txt.set_alignment(0.0, 0.0)
+        txt.show()
+        self.page_themes.pack_start(txt, False, True)
+        # create theme-selector list
+        self.tree.set_headers_visible(False)
+        self.tree.connect('cursor-changed', self.__tree_cursor_changed)
+        self.tree.show()
+        col = gtk.TreeViewColumn('')
+        cell = gtk.CellRendererText()
+        col.pack_start(cell, True)
+        #cell.set_property('foreground', 'black')
+        col.set_cell_data_func(cell, self.__render_cell)
+        self.tree.append_column(col)
+        # wrap tree in scrollwin
+        sw = gtk.ScrolledWindow()
+        sw.set_shadow_type(gtk.SHADOW_IN)
+        sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+        sw.add(self.tree)
+        sw.show()
+        # add vbox and add tree/buttons
+        vbox = gtk.VBox()
+        vbox.pack_start(sw, True, True)
+        vbox.show()
+        # show/add page
+        self.page_themes.add(vbox)
+        self.page_themes.show()
+        self.main_notebook.append_page(self.page_themes, gtk.Label(_('Themes ')))
+
+    def __render_cell(self, tvcolumn, cell, model, iter):
+        """Callback for rendering the cells in the theme-treeview."""
+        # get attributes-list from Treemodel
+        attrib = model.get_value(iter, 0)
+
+        # set colors depending on state
+        col = '555555'
+        name_uc = attrib[0][0].upper() + attrib[0][1:]
+        # create markup depending on info
+        if attrib[1] == '-' and attrib[2] == '-':
+            mu = '<b><span weight="ultrabold" size="large">' + name_uc + \
+            '</span></b> (' + _('no info available') + ')'
+        else:
+            if attrib[1] == None : attrib[1] = '-'
+            if attrib[2] == None : attrib[2] = '-'
+            if attrib[3] == None : attrib[3] = '-'
+            if attrib[4] == None : attrib[4] = '-'
+            mu = '<b><span weight="ultrabold" size="large">' + name_uc + \
+                '</span></b> v' + attrib[4] + '\n<small><span color="#555555' +\
+                '">' + attrib[2].replace('\\n', '\n') + \
+                '</span></small>\n<i><small>by '+str(attrib[3])+'</small></i>'
+        # set markup
+        cell.set_property('markup', mu)
+
+    # UI-callbacks
+
+    def __tree_cursor_changed (self, treeview):
+        """Callback for handling selection changes in the Themes-treeview."""
+        sel = self.tree.get_selection()
+        if sel:
+            s = sel.get_selected()
+            if s:
+                it = s[1]
+                if it:
+                    attribs = self.liststore.get_value(it, 0)
+                    if attribs and self.__shown_object:
+                        #print attribs
+                        # set theme in Screenlet (if not already active)
+                        if self.__shown_object.theme_name != attribs[0]:
+                            self.__shown_object.theme_name = attribs[0]
+

=== added file 'src/lib/options/boolean_option.py'
--- src/lib/options/boolean_option.py	1970-01-01 00:00:00 +0000
+++ src/lib/options/boolean_option.py	2011-03-28 16:19:24 +0000
@@ -0,0 +1,54 @@
+# 
+# Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@xxxxxxxxx>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# 
+"""
+Boolean options, these classes will display a boolean
+Checkbox as required and control the formating of data.
+"""
+
+import gtk
+
+from screenlets.options import _
+from base import Option
+
+class BoolOption(Option):
+    """An Option for boolean values."""
+    def on_import(self, strvalue):
+        """When a boolean is imported from the config."""
+        return strvalue.lower() == "true"
+
+    def on_export(self, value):
+        """When a boolean is exported to the config."""
+        return str(value)
+
+    def generate_widget(self, value):
+        """Generate a checkbox for a boolean option."""
+        if not self.widget:
+            self.widget = gtk.CheckButton()
+            self.set_value(value)
+            self.widget.connect("toggled", self.has_changed)
+        return self.widget
+
+    def set_value(self, value):
+        """Set the true/false value to the checkbox widget"""
+        self.value = value
+        self.widget.set_active(self.value)
+
+    def has_changed(self, widget):
+        """Executed when the widget event kicks off."""
+        self.set_value( self.widget.get_active() )
+        super(BoolOption, self).has_changed()

=== added file 'src/lib/options/colour_option.py'
--- src/lib/options/colour_option.py	1970-01-01 00:00:00 +0000
+++ src/lib/options/colour_option.py	2011-03-28 16:19:24 +0000
@@ -0,0 +1,149 @@
+# 
+# Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@xxxxxxxxx>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# 
+"""
+Color options, these classes will display a text box.
+"""
+
+import gtk
+
+from screenlets.options import _
+from base import Option
+
+class ColorOption(Option):
+    """An Option for color options."""
+    def on_import(self, strvalue):
+        """Import (r, g, b, a) from comma-separated string."""
+        # strip braces and spaces
+        strvalue = strvalue.lstrip('(')
+        strvalue = strvalue.rstrip(')')
+        strvalue = strvalue.strip()
+        # split value on commas
+        tmpval = strvalue.split(',')
+        outval = []
+        for f in tmpval:
+            # create list and again remove spaces
+            outval.append(float(f.strip()))
+        return outval
+
+    def on_export(self, value):
+        """Export r, g, b, a to comma-separated string."""
+        l = len(value)
+        outval = ''
+        for i in xrange(l):
+            if type(value[i]) == float:
+                outval += "%0.5f" % value[i]
+            else:
+                outval += str(value[i])
+            if i < l-1:
+                outval += ','
+        return outval
+
+    def generate_widget(self, value):
+        """Generate a textbox for a color options"""
+        self.widget = self.get_box_from_colour( value )
+        self.set_value(value)
+        return self.widget
+
+    def set_value(self, value):
+        """Set the color value as required."""
+        self.value = value
+
+    def has_changed(self, widget):
+        """Executed when the widget event kicks off."""
+        self.value = self.get_colour_from_box(self.widget)
+        super(ColorOption, self).has_changed()
+
+    def get_box_from_colour(self, colour):
+        """Turn a colour array into a colour widget"""
+        result = gtk.ColorButton(gtk.gdk.Color(
+            int(colour[0]*65535), int(colour[1]*65535), int(colour[2]*65535)))
+        result.set_use_alpha(True)
+        result.set_alpha(int(colour[3]*65535))
+        result.connect("color-set", self.has_changed)
+        return result
+
+    def get_colour_from_box(self, widget):
+        """Turn a colour widget into a colour array"""
+        colour = widget.get_color()
+        return (
+            colour.red/65535.0,
+            colour.green/65535.0,
+            colour.blue/65535.0,
+            widget.get_alpha()/65535.0
+        )
+
+
+class ColorsOption(ColorOption):
+    """Allow a list of colours to be created"""
+    def on_import(self, value):
+        """Importing multiple colours"""
+        result = []
+        for col in value.split(';'):
+            if col:
+               result.append(super(ColorsOption, self).on_import(col))
+        return result
+
+    def on_export(self, value):
+        """Exporting multiple colours"""
+        result = ""
+        for col in value:
+            result += super(ColorsOption, self).on_export(col)+';'
+        return result
+
+    def generate_widget(self, value):
+        """Generate a textbox for a color options"""
+        self.widget = gtk.HBox()
+        if type(value[0]) in [int, float]:
+            value = [value]
+        for col in value:
+            self.add_colour_box(self.widget, col, False)
+
+        but = gtk.Button('Add', gtk.STOCK_ADD)
+        but.show()
+        but.connect("clicked", self.add_colour_box)
+        self.widget.pack_end(but)
+
+        self.set_value(value)
+        return self.widget
+
+    def del_colour_box(self, widget, event):
+        """Remove a colour box from the array when right clicked"""
+        if event.button == 3:
+            if len(self.widget.get_children()) > 2:
+                self.widget.remove(widget)
+                self.has_changed(widget)
+
+    def add_colour_box(self, widget, col=None, update=True):
+        """Add a new box for colours"""
+        if not col:
+            col = self.value[-1]
+        new_box = self.get_box_from_colour( col )
+        new_box.connect("button_press_event", self.del_colour_box)
+        self.widget.pack_start(new_box, padding=1)
+        new_box.show()
+        if update:
+            self.has_changed(widget)
+
+    def has_changed(self, widget):
+        """The colour widgets have changed!"""
+        self.value = []
+        for c in self.widget.get_children():
+            if type(c) == gtk.ColorButton:
+                self.value.append(self.get_colour_from_box( c ))
+        super(ColorOption, self).has_changed()
+

=== added file 'src/lib/options/file_option.py'
--- src/lib/options/file_option.py	1970-01-01 00:00:00 +0000
+++ src/lib/options/file_option.py	2011-03-28 16:19:24 +0000
@@ -0,0 +1,179 @@
+# 
+# Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@xxxxxxxxx>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# 
+"""
+File options, these classes will display a file select widget.
+"""
+
+import gtk
+import os
+
+from screenlets.options import _
+from base import Option
+
+class FileOption(Option):
+    """
+    An Option-subclass for string-values that contain filenames. Adds
+    a patterns-attribute that can contain a list of patterns to be shown
+    in the assigned file selection dialog. The show_pixmaps-attribute
+    can be set to True to make the filedialog show all image-types.
+    supported by gtk.Pixmap. If the directory-attributue is true, the
+    dialog will ony allow directories.
+
+    XXX - Some of this doen't yet work, unknown reason.
+    """
+    patterns = [ ( 'All Files', ['*'] ) ]
+    image = False
+    _gtk_file_mode = gtk.FILE_CHOOSER_ACTION_OPEN
+    _opener_title = _("Choose File")
+
+    def on_import(self, strvalue):
+        """When a file is imported from the config."""
+        return strvalue
+
+    def on_export(self, value):
+        """When a file is exported to the config."""
+        return str(value)
+
+    def generate_widget(self, value):
+        """Generate a special widget for file options"""
+        dlg = self.generate_file_opener()
+        self.widget = gtk.FileChooserButton(dlg)
+        self.widget.set_title(self._opener_title)
+        self.widget.set_size_request(180, 28)
+        self.set_value(value)
+        self.widget.connect("selection-changed", self.has_changed)
+        return self.widget
+
+    def generate_file_opener(self):
+        """Generate a file opener widget"""
+        dlg = gtk.FileChooserDialog(action=self._gtk_file_mode,
+            buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+                     gtk.STOCK_OPEN, gtk.RESPONSE_OK),
+        )
+        dlg.set_keep_above(True)
+        self.set_filters(dlg)
+        return dlg
+
+    def set_filters(self, dlg):
+        """Add file filters to the dialog widget"""
+        if self.patterns:
+            for filter in self.patterns:
+                fil = gtk.FileFilter()
+                fil.set_name("%s (%s)" % (filter[0], ','.join(filter[1])))
+                for pattern in filter[1]:
+                    fil.add_pattern(pattern)
+                dlg.add_filter(fil)
+
+    def set_value(self, value):
+        """Set the file value as required."""
+        self.widget.set_filename(value)
+        self.value = value
+
+    def has_changed(self, widget):
+        """Executed when the widget event kicks off."""
+        self.value = self.widget.get_filename()
+        super(FileOption, self).has_changed()
+
+
+class DirectoryOption(FileOption):
+    """Directory is based on file widgets"""
+    _gtk_file_mode = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER
+    _opener_title = _("Choose Directory")
+
+
+class ImageOption(FileOption):
+    """Image is based on file widgets"""
+    _opener_title = _("Choose Image")
+
+    def set_filters(self, dlg):
+        """Add the standard pixbug formats"""
+        flt = gtk.FileFilter()
+        flt.add_pixbuf_formats()
+        dlg.set_filter(flt)
+
+    def generate_widget(self, value):
+        """Crazy image opener widget generation."""
+        # create entry and button (entry is hidden)
+        self._entry = gtk.Entry()
+        self._entry.set_text(value)
+        self._entry.set_editable(False)
+        but = gtk.Button()
+        # load preview image
+        but.set_image(self.create_preview(value))
+        but.connect('clicked', self.but_callback)
+        # create widget
+        self.widget = gtk.HBox()
+        self.widget.add(self._entry)
+        self.widget.add(but)
+        but.show()
+        self.widget.show()
+        # add tooltips
+        but.set_tooltip_text(_('Select Image ...'))
+        but.set_tooltip_text(self.desc)
+        return self.widget
+
+    def create_preview(self, filename):
+        """Utililty method to reload preview image"""
+        if filename and os.path.isfile(filename):
+            pb = gtk.gdk.pixbuf_new_from_file_at_size(filename, 64, -1)
+            if pb:
+                img = gtk.Image()
+                img.set_from_pixbuf(pb)
+                return img
+        img = gtk.image_new_from_stock(gtk.STOCK_MISSING_IMAGE,
+            gtk.ICON_SIZE_LARGE_TOOLBAR)
+        img.set_size_request(64, 64)
+        return img
+
+    def but_callback(self, widget):
+        """Create button"""
+        dlg = gtk.FileChooserDialog(buttons=(gtk.STOCK_CANCEL,
+            gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
+        dlg.set_keep_above(True)
+        dlg.set_filename(self._entry.get_text())
+        prev = gtk.Image()
+        box = gtk.VBox()
+        box.set_size_request(150, -1)
+        box.add(prev)
+        prev.show()
+        dlg.set_preview_widget_active(True)
+        dlg.connect('selection-changed', self.preview_callback, dlg, prev)
+        dlg.set_preview_widget(box)
+        response = dlg.run()
+        if response == gtk.RESPONSE_OK:
+            self._entry.set_text(dlg.get_filename())
+            widget.set_image(self.create_preview(dlg.get_filename()))
+            self.has_changed(self.widget)
+        dlg.destroy()
+
+    def preview_callback(self, widget, dlg, prev):
+        """add preview widget to filechooser"""
+        fname = dlg.get_preview_filename()
+        if fname and os.path.isfile(fname):
+            pb = gtk.gdk.pixbuf_new_from_file_at_size(fname, 150, -1)
+            if pb:
+                prev.set_from_pixbuf(pb)
+                dlg.set_preview_widget_active(True)
+            else:
+                dlg.set_preview_widget_active(False)
+
+    def has_changed(self, widget):
+        """Executed when the widget event kicks off."""
+        self.value = self._entry.get_text()
+        super(FileOption, self).has_changed()
+

=== added file 'src/lib/options/font_option.py'
--- src/lib/options/font_option.py	1970-01-01 00:00:00 +0000
+++ src/lib/options/font_option.py	2011-03-28 16:19:24 +0000
@@ -0,0 +1,52 @@
+# 
+# Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@xxxxxxxxx>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# 
+"""
+Font options, these classes will display a text box.
+"""
+
+import gtk
+
+from screenlets.options import _
+from base import Option
+
+class FontOption(Option):
+    """An class for font options."""
+    def on_import(self, strvalue):
+        """When a font is imported from the config."""
+        return strvalue
+
+    def on_export(self, value):
+        """When a font is exported to the config."""
+        return str(value)
+
+    def generate_widget(self, value):
+        """Generate a special widget for font options"""
+        self.widget = gtk.FontButton()
+        self.set_value(value)
+        self.widget.connect("font-set", self.has_changed)
+        return self.widget
+
+    def set_value(self, value):
+        """Set the font value as required."""
+        self.widget.set_font_name(value)
+        self.value = value
+
+    def has_changed(self, widget):
+        """Executed when the widget event kicks off."""
+        self.value = widget.get_font_name()
+        super(FontOption, self).has_changed()

=== added file 'src/lib/options/list_option.py'
--- src/lib/options/list_option.py	1970-01-01 00:00:00 +0000
+++ src/lib/options/list_option.py	2011-03-28 16:19:24 +0000
@@ -0,0 +1,210 @@
+# 
+# Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@xxxxxxxxx>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# 
+"""
+List options, these classes will display all sorts of crazy shit.
+"""
+
+import gtk
+
+from screenlets.options import _
+from base import Option
+
+class ListOption(Option):
+    """An Option for string options."""
+    def on_import(self, strvalue):
+        """When a list is imported from the config."""
+        return eval(strvalue)
+
+    def on_export(self, value):
+        """When a list is exported to the config."""
+        return str(value)
+
+    def generate_widget(self, value):
+        """Generate some widgets for a list."""
+        self._entry = gtk.Entry()
+        self._entry.set_editable(False)
+        self.set_value(value)
+        self._entry.show()
+        img = gtk.Image()
+        img.set_from_stock(gtk.STOCK_EDIT, 1)
+        but = gtk.Button()
+        but.set_image(img)
+        but.show()
+        but.connect("clicked", self.open_listeditor)
+        but.set_tooltip_text(_('Open List-Editor ...'))
+        self._entry.set_tooltip_text(self.desc)
+        self.widget = gtk.HBox()
+        self.widget.add(self._entry)
+        self.widget.add(but)
+        return self.widget
+
+    def open_listeditor(self, event):
+        # open dialog
+        dlg = ListOptionDialog()
+        # read string from entry and import it through option-class
+        # (this is needed to always have an up-to-date value)
+        dlg.set_list(self.on_import(self._entry.get_text()))
+        resp = dlg.run()
+        if resp == gtk.RESPONSE_OK:
+            # set text in entry
+            self._entry.set_text(str(dlg.get_list()))
+            # manually call the options-callback
+            self.has_changed(dlg)
+        dlg.destroy()
+
+    def set_value(self, value):
+        """Set the list string value as required."""
+        self._entry.set_text(str(value))
+        self.value = value
+
+    def has_changed(self, widget):
+        """Executed when the widget event kicks off."""
+        self.value = widget.get_list()
+        super(ListOption, self).has_changed()
+
+
+class ListOptionDialog(gtk.Dialog):
+    """An editing dialog used for editing options of the ListOption-type."""
+    model = None
+    tree = None
+    buttonbox = None
+
+    # call gtk.Dialog.__init__
+    def __init__ (self):
+        super(ListOptionDialog, self).__init__(
+            "Edit List",
+            flags=gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR,
+            buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
+            gtk.STOCK_OK, gtk.RESPONSE_OK)
+        )
+        # set size
+        self.resize(300, 370)
+        self.set_keep_above(True)
+        # init vars
+        self.model = gtk.ListStore(str)
+        # create UI
+        self.create_ui()
+
+    def create_ui (self):
+        """Create the user-interface for this dialog."""
+        # create outer hbox (tree|buttons)
+        hbox = gtk.HBox()
+        hbox.set_border_width(10)
+        hbox.set_spacing(10)
+        # create tree
+        self.tree = gtk.TreeView(model=self.model)
+        self.tree.set_headers_visible(False)
+        self.tree.set_reorderable(True)
+        #self.tree.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_HORIZONTAL)
+        col = gtk.TreeViewColumn('')
+        cell = gtk.CellRendererText()
+        #cell.set_property('cell-background', 'cyan')
+        cell.set_property('foreground', 'black')
+        col.pack_start(cell, False)
+        col.set_attributes(cell, text=0)
+        self.tree.append_column(col)
+        self.tree.show()
+        hbox.pack_start(self.tree, True, True)
+        #sep = gtk.VSeparator()
+        #sep.show()
+        #hbox.add(sep)
+        # create  buttons
+        self.buttonbox = bb = gtk.VButtonBox()
+        self.buttonbox.set_layout(gtk.BUTTONBOX_START)
+        b1 = gtk.Button(stock=gtk.STOCK_ADD)
+        b2 = gtk.Button(stock=gtk.STOCK_EDIT)
+        b3 = gtk.Button(stock=gtk.STOCK_REMOVE)
+        b1.connect('clicked', self.button_callback, 'add')
+        b2.connect('clicked', self.button_callback, 'edit')
+        b3.connect('clicked', self.button_callback, 'remove')
+        bb.add(b1)
+        bb.add(b2)
+        bb.add(b3)
+        self.buttonbox.show_all()
+        #hbox.add(self.buttonbox)
+        hbox.pack_end(self.buttonbox, False)
+        # add everything to outer hbox and show it
+        hbox.show()
+        self.vbox.add(hbox)
+
+    def set_list (self, lst):
+        """Set the list to be edited in this editor."""
+        for el in lst:
+            self.model.append([el])
+
+    def get_list (self):
+        """Return the list that is currently being edited in this editor."""
+        lst = []
+        for i in self.model:
+            lst.append(i[0])
+        return lst
+
+    def remove_selected_item (self):
+        """Remove the currently selected item."""
+        sel = self.tree.get_selection()
+        if sel:
+            it = sel.get_selected()[1]
+            if it:
+                print self.model.get_value(it, 0)
+                self.model.remove(it)
+
+    def entry_dialog (self, default = ''):
+        """Show entry-dialog and return string."""
+        entry = gtk.Entry()
+        entry.set_text(default)
+        entry.show()
+        dlg = gtk.Dialog("Add/Edit Item", flags=gtk.DIALOG_DESTROY_WITH_PARENT,
+            buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK,
+            gtk.RESPONSE_OK))
+        dlg.set_keep_above(True)
+        dlg.vbox.add(entry)
+        resp = dlg.run()
+        ret = None
+        if resp == gtk.RESPONSE_OK:
+            ret = entry.get_text()
+        dlg.destroy()
+        return ret
+
+    def button_callback (self, widget, id):
+        print "PRESS: %s" % id
+        if id == 'remove':
+            self.remove_selected_item()
+        if id == 'add':
+            new = self.entry_dialog()
+            if new != None:
+                self.model.append([new])
+        if id == 'edit':
+            sel = self.tree.get_selection()
+            if sel:
+                it = sel.get_selected()[1]
+                if it:
+                    new = self.entry_dialog(self.model.get_value(it, 0))
+                    if new != None:
+                        #self.model.append([new])
+                        self.model.set_value(it, 0, new)
+
+# TEST>-
+"""dlg = ListOptionDialog()
+dlg.set_list(['test1', 'afarew34s', 'fhjh23faj', 'yxcdfs58df', 'hsdf7jsdfh'])
+dlg.run()
+print "RESULT: " + str(dlg.get_list())
+dlg.destroy()
+import sys
+sys.exit(1)"""
+# /TEST
+

=== added file 'src/lib/options/number_option.py'
--- src/lib/options/number_option.py	1970-01-01 00:00:00 +0000
+++ src/lib/options/number_option.py	2011-03-28 16:19:24 +0000
@@ -0,0 +1,90 @@
+# 
+# Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@xxxxxxxxx>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# 
+"""
+Integer and Float options, these classes will display a spin box.
+"""
+
+import gtk
+
+from screenlets.options import _
+from base import Option
+
+class IntOption(Option):
+    """An Option for integer options."""
+    min = -100000
+    max = 100000
+    increment = 1
+
+    def on_import(self, strvalue):
+        """When a integer is imported from the config."""
+        try:
+            if strvalue[0]=='-':
+                return int(strvalue[1:]) * -1
+            return int(strvalue)
+        except:
+            sys.stderr.write(_("Error during on_import - option: %s.\n") % self.name)
+            return 0
+
+        return int(strvalue)
+
+    def on_export(self, value):
+        """When a string is exported to the config."""
+        return str(value)
+
+    def generate_widget(self, value):
+        """Generate a spin button for integer options"""
+        self.widget = gtk.SpinButton()
+        self.widget.set_increments(self.increment, int(self.max / self.increment))
+        if self.min != None and self.max != None:
+            self.widget.set_range(self.min, self.max)
+        self.set_value(value)
+        self.widget.connect("value-changed", self.has_changed)
+        return self.widget
+
+    def set_value(self, value):
+        """Set the int value, including the value of the widget."""
+        self.value = value
+        self.widget.set_value(value)
+
+    def has_changed(self, widget):
+        """Executed when the widget event kicks off."""
+        self.value = int(widget.get_value())
+        super(IntOption, self).has_changed()
+
+
+class FloatOption(IntOption):
+    """An option for float numbers."""
+    digits = 0
+
+    def on_import (self, strvalue):
+        """Called when FloatOption gets imported. Converts str to float."""
+        if strvalue[0]=='-':
+            return float(strvalue[1:]) * -1.0
+        return float(strvalue)
+
+    def generate_widget(self, value):
+        """Do the same as int but add the number of ditgits"""
+        super(FloatOption, self).generate_widget(value)
+        self.widget.set_digits(self.digits)
+        return self.widget
+
+    def has_changed(self, widget):
+        """Executed when the widget event kicks off."""
+        self.value = float(widget.get_value())
+        super(IntOption, self).has_changed()
+

=== added file 'src/lib/options/string_option.py'
--- src/lib/options/string_option.py	1970-01-01 00:00:00 +0000
+++ src/lib/options/string_option.py	2011-03-28 16:19:24 +0000
@@ -0,0 +1,79 @@
+# 
+# Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@xxxxxxxxx>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# 
+"""
+String options, these classes will display a text box.
+"""
+
+import gtk
+
+from screenlets.options import _
+from base import Option
+
+class StringOption(Option):
+    """An Option for string options."""
+    choices = None
+    password = False
+
+    def on_import(self, strvalue):
+        """When a string is imported from the config."""
+        return strvalue
+
+    def on_export(self, value):
+        """When a string is exported to the config."""
+        return str(value)
+
+    def generate_widget(self, value):
+        """Generate a textbox for a string options"""
+        if self.choices:
+            # if a list of values is defined, show combobox
+            self.widget = gtk.combo_box_new_text()
+            p = -1
+            i = 0
+            for s in self.choices:
+                self.widget.append_text(s)
+                if s==value:
+                    p = i
+                i+=1
+            self.widget.set_active(p)
+        else:
+            self.widget = gtk.Entry()
+            # if it is a password, set text to be invisible
+            if self.password:
+                self.widget.set_visibility(False)
+
+        self.set_value(value)
+        self.widget.connect("changed", self.has_changed)
+        #self.widget.set_size_request(180, 28)
+        return self.widget
+
+    def set_value(self, value):
+        """Set the string value as required."""
+        self.value = value
+        if self.choices:
+            # TODO self.widget.set_active(p)
+            pass
+        else:
+            self.widget.set_text(value)
+
+    def has_changed(self, widget):
+        """Executed when the widget event kicks off."""
+        if self.choices:
+            self.set_value( widget.get_active_text() )
+        else:
+            self.set_value( widget.get_text() )
+        super(StringOption, self).has_changed()

=== added file 'src/lib/options/time_option.py'
--- src/lib/options/time_option.py	1970-01-01 00:00:00 +0000
+++ src/lib/options/time_option.py	2011-03-28 16:19:24 +0000
@@ -0,0 +1,80 @@
+# 
+# Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@xxxxxxxxx>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+# 
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+# 
+"""
+Time options, these classes will display a text box.
+"""
+
+import gtk
+
+from screenlets.options import _
+from colour_option import ColorOption
+
+class TimeOption(ColorOption):
+    """An class for time options."""
+
+    def generate_widget(self, value):
+        """Generate a textbox for a time options"""
+        self.widget = gtk.HBox()
+        input_hour = gtk.SpinButton()
+        input_minute = gtk.SpinButton()
+        input_second = gtk.SpinButton()
+        input_hour.set_range(0, 23)
+        input_hour.set_max_length(2)
+        input_hour.set_increments(1, 1)
+        input_hour.set_numeric(True)
+        input_hour.set_value(value[0])
+        input_minute.set_range(0, 59)
+        input_minute.set_max_length(2)
+        input_minute.set_increments(1, 1)
+        input_minute.set_numeric(True)
+        input_minute.set_value(value[1])
+        input_second.set_range(0, 59)
+        input_second.set_max_length(2)
+        input_second.set_increments(1, 1)
+        input_second.set_numeric(True)
+        input_second.set_value(value[2])
+        input_hour.connect('value-changed', self.has_changed)
+        input_minute.connect('value-changed', self.has_changed)
+        input_second.connect('value-changed', self.has_changed)
+        input_hour.set_tooltip_text(self.desc)
+        input_minute.set_tooltip_text(self.desc)
+        input_second.set_tooltip_text(self.desc)
+        self.widget.add(input_hour)
+        self.widget.add(gtk.Label(':'))
+        self.widget.add(input_minute)
+        self.widget.add(gtk.Label(':'))
+        self.widget.add(input_second)
+        self.widget.add(gtk.Label('h'))
+        self.widget.show_all()
+        return self.widget
+
+    def set_value(self, value):
+        """Set the time value as required."""
+        self.value = value
+
+    def has_changed(self, widget):
+        """Executed when the widget event kicks off."""
+        box = widget.get_parent()
+        inputs = box.get_children()
+        self.value = (
+            int(inputs[0].get_value()),
+            int(inputs[2].get_value()),
+            int(inputs[4].get_value()),
+        )
+        super(ColorOption, self).has_changed()
+


Follow ups