screenlets-dev team mailing list archive
-
screenlets-dev team
-
Mailing list archive
-
Message #00682
[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