1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 import screenlets
30 import utils
31
32 import os
33 import gtk, gobject
34 import xml.dom.minidom
35 from xml.dom.minidom import Node
36
37
38 import gettext
39 gettext.textdomain('screenlets')
40 gettext.bindtextdomain('screenlets', screenlets.INSTALL_PREFIX + '/share/locale')
41
43 return gettext.gettext(s)
44
45
46
47
48
50 """An Option stores information about a certain object-attribute. It doesn't
51 carry information about the value or the object it belongs to - it is only a
52 one-way data-storage for describing how to handle attributes."""
53
54 __gsignals__ = dict(option_changed=(gobject.SIGNAL_RUN_FIRST,
55 gobject.TYPE_NONE, (gobject.TYPE_OBJECT,)))
56
57 - def __init__ (self, group, name, default, label, desc,
58 disabled=False, hidden=False, callback=None, protected=False):
59 """Creates a new Option with the given information."""
60 super(Option, self).__init__()
61 self.name = name
62 self.label = label
63 self.desc = desc
64 self.default = default
65 self.disabled = disabled
66 self.hidden = hidden
67
68 self.group= group
69
70 self.callback = callback
71
72 self.realtime = True
73
74 self.protected = protected
75
77 """Callback - called when an option gets imported from a string.
78 This function MUST return the string-value converted to the required
79 type!"""
80 return strvalue.replace("\\n", "\n")
81
83 """Callback - called when an option gets exported to a string. The
84 value-argument needs to be converted to a string that can be imported
85 by the on_import-handler. This handler MUST return the value
86 converted to a string!"""
87 return str(value).replace("\n", "\\n")
88
89
91 """An Option-subclass for string-values that contain filenames. Adds
92 a patterns-attribute that can contain a list of patterns to be shown
93 in the assigned file selection dialog. The show_pixmaps-attribute
94 can be set to True to make the filedialog show all image-types
95 supported by gtk.Pixmap. If the directory-attributue is true, the
96 dialog will ony allow directories."""
97
98 - def __init__ (self, group, name, default, label, desc,
99 patterns=['*'], image=False, directory=False, **keyword_args):
100 Option.__init__(self, group, name, default,label, desc, **keyword_args)
101 self.patterns = patterns
102 self.image = image
103 self.directory = False
104
105
107 """An Option-subclass for string-values that contain filenames of
108 image-files."""
109
110
112 """An Option-subclass for filename-strings that contain directories."""
113
114
116 """An Option for boolean values."""
117
119 if strvalue == "True":
120 return True
121 return False
122
123
125 """An Option for values of type string."""
126
127 - def __init__ (self, group, name, default, label, desc,
128 choices=None, password=False, **keyword_args):
132
133
135 """An Option for values of type number (can be int or float)."""
136
137 - def __init__ (self, group, name, default, label, desc, min=-100000, max=100000,
138 increment=1, **keyword_args):
139 Option.__init__(self, group, name, default, label, desc, **keyword_args)
140 self.min = min
141 self.max = max
142 self.increment = increment
143
145 """Called when IntOption gets imported. Converts str to int."""
146 try:
147 if strvalue[0]=='-':
148 return int(strvalue[1:]) * -1
149 return int(strvalue)
150 except:
151 print "Error during on_import - option: %s." % self.name
152 return 0
153
154
156 """An Option for values of type float."""
157
158 - def __init__ (self, group, name, default, label, desc, digits=1,
159 **keyword_args):
160 IntOption.__init__(self, group, name, default, label, desc,
161 **keyword_args)
162 self.digits = digits
163
165 """Called when FloatOption gets imported. Converts str to float."""
166 if strvalue[0]=='-':
167 return float(strvalue[1:]) * -1.0
168 return float(strvalue)
169
170
172 """An Option for fonts (a simple StringOption)."""
173
174
176 """An Option for colors. Stored as a list with 4 values (r, g, b, a)."""
177
179 """Import (r, g, b, a) from comma-separated string."""
180
181 strvalue = strvalue.lstrip('(')
182 strvalue = strvalue.rstrip(')')
183 strvalue = strvalue.strip()
184
185 tmpval = strvalue.split(',')
186 outval = []
187 for f in tmpval:
188
189 outval.append(float(f.strip()))
190 return outval
191
193 """Export r, g, b, a to comma-separated string."""
194 l = len(value)
195 outval = ''
196 for i in xrange(l):
197 outval += str(value[i])
198 if i < l-1:
199 outval += ','
200 return outval
201
202
204 """An Option-type for list of strings."""
205
207 """Import python-style list from a string (like [1, 2, 'test'])"""
208 lst = eval(strvalue)
209 return lst
210
212 """Export list as string."""
213 return str(value)
214
215
216 import gnomekeyring
218 """An Option-type for username/password combos. Stores the password in
219 the gnome-keyring (if available) and only saves username and auth_token
220 through the screenlets-backend.
221 TODO:
222 - not create new token for any change (use "set" instead of "create" if
223 the given item already exists)
224 - use usual storage if no keyring is available but output warning
225 - on_delete-function for removing the data from keyring when the
226 Screenlet holding the option gets deleted"""
227
228 - def __init__ (self, group, name, default, label, desc, **keyword_args):
229 Option.__init__ (self, group, name, default, label, desc,
230 protected=True, **keyword_args)
231
232 if not gnomekeyring.is_available():
233 raise Exception('GnomeKeyring is not available!!')
234
235
236
237 self.keyring_list = gnomekeyring.list_keyring_names_sync()
238 if len(self.keyring_list) == 0:
239 raise Exception('No keyrings found. Please create one first!')
240 else:
241
242 try:
243 self.keyring = gnomekeyring.get_default_keyring_sync()
244 except:
245 if "session" in self.keyring_list:
246 print "Warning: No default keyring found, using session keyring. Storage is not permanent!"
247 self.keyring = "session"
248 else:
249 print "Warning: Neither default nor session keyring found, assuming keyring %s!" % self.keyring_list[0]
250 self.keyring = self.keyring_list[0]
251
252
254 """Import account info from a string (like 'username:auth_token'),
255 retrieve the password from the storage and return a tuple containing
256 username and password."""
257
258
259 (name, auth_token) = strvalue.split(':', 1)
260 if name and auth_token:
261
262 try:
263 pw = gnomekeyring.item_get_info_sync(self.keyring,
264 int(auth_token)).get_secret()
265 except Exception, ex:
266 print "ERROR: Unable to read password from keyring: %s" % ex
267 pw = ''
268
269 return (name, pw)
270 else:
271 raise Exception('Illegal value in AccountOption.on_import.')
272
274 """Export the given tuple/list containing a username and a password. The
275 function stores the password in the gnomekeyring and returns a
276 string in form 'username:auth_token'."""
277
278 attribs = dict(name=value[0])
279 auth_token = gnomekeyring.item_create_sync(self.keyring,
280 gnomekeyring.ITEM_GENERIC_SECRET, value[0], attribs, value[1], True)
281
282 return value[0] + ':' + str(auth_token)
283
284 """#TEST:
285 o = AccountOption('None', 'pop3_account', ('',''), 'Username/Password', 'Enter username/password here ...')
286 # save option to keyring
287 exported_account = o.on_export(('RYX', 'mysecretpassword'))
288 print exported_account
289 # and read option back from keyring
290 print o.on_import(exported_account)
291
292
293 import sys
294 sys.exit(0)"""
295
297 """An Option-subclass for string-values that contain dates."""
298
299
300
301
302
303
305 """Create an Option from an XML-node with option-metadata."""
306
307 otype = node.getAttribute("type")
308 oname = node.getAttribute("name")
309 ohidden = node.getAttribute("hidden")
310 odefault = None
311 oinfo = ''
312 olabel = ''
313 omin = None
314 omax = None
315 oincrement = 1
316 ochoices = ''
317 odigits = None
318 if otype and oname:
319
320 for attr in node.childNodes:
321 if attr.nodeType == Node.ELEMENT_NODE:
322 if attr.nodeName == 'label':
323 olabel = attr.firstChild.nodeValue
324 elif attr.nodeName == 'info':
325 oinfo = attr.firstChild.nodeValue
326 elif attr.nodeName == 'default':
327 odefault = attr.firstChild.nodeValue
328 elif attr.nodeName == 'min':
329 omin = attr.firstChild.nodeValue
330 elif attr.nodeName == 'max':
331 omax = attr.firstChild.nodeValue
332 elif attr.nodeName == 'increment':
333 oincrement = attr.firstChild.nodeValue
334 elif attr.nodeName == 'choices':
335 ochoices = attr.firstChild.nodeValue
336 elif attr.nodeName == 'digits':
337 odigits = attr.firstChild.nodeValue
338
339 if odefault:
340
341 cls = otype[0].upper() + otype.lower()[1:] + 'Option'
342
343
344 clsobj = getattr(__import__(__name__), cls)
345 opt = clsobj(groupname, oname, None, olabel, oinfo)
346 opt.default = opt.on_import(odefault)
347
348 if cls == 'IntOption':
349 if omin:
350 opt.min = int(omin)
351 if omax:
352 opt.max = int(omax)
353 if oincrement:
354 opt.increment = int(oincrement)
355 elif cls == 'FloatOption':
356 if odigits:
357 opt.digits = int(odigits)
358 if omin:
359 opt.min = float(omin)
360 if omax:
361 opt.max = float(omax)
362 if oincrement:
363 opt.increment = float(oincrement)
364 elif cls == 'StringOption':
365 if ochoices:
366 opt.choices = ochoices
367 return opt
368 return None
369
370
372 """The EditableOptions can be inherited from to allow objects to export
373 editable options for editing them with the OptionsEditor-class.
374 NOTE: This could use some improvement and is very poorly coded :) ..."""
375
377 self.__options__ = []
378 self.__options_groups__ = {}
379
380 self.__options_groups_ordered__ = []
381
382 - def add_option (self, option, callback=None, realtime=True):
383 """Add an editable option to this object. Editable Options can be edited
384 and configured using the OptionsDialog. The optional callback-arg can be
385 used to set a callback that gets notified when the option changes its
386 value."""
387
388
389 for o in self.__options__:
390 if o.name == option.name:
391 return False
392 self.__dict__[option.name] = option.default
393
394 option.realtime = realtime
395
396 try:
397 self.__options_groups__[option.group]['options'].append(option)
398 except:
399 print "Options: Error - group %s not defined." % option.group
400 return False
401
402 self.__options__.append(option)
403
404 if callback:
405 option.connect("option_changed", callback)
406 return True
407
408
410 """Add a new options-group to this Options-object"""
411 self.__options_groups__[name] = {'label':name,
412 'info':group_info, 'options':[]}
413 self.__options_groups_ordered__.append(name)
414
415
417 """Disable the inputs for a certain Option."""
418 for o in self.__options__:
419 if o.name == name:
420 o.disabled = True
421 return True
422 return False
423
425 """Enable the inputs for a certain Option."""
426 for o in self.__options__:
427 if o.name == name:
428 o.disabled = False
429 return True
430 return False
431
433 """Returns all editable options within a list (without groups)
434 as key/value tuples."""
435 lst = []
436 for o in self.__options__:
437 lst.append((o.name, getattr(self, o.name)))
438 return lst
439
441 """Returns an option in this Options by it's name (or None).
442 TODO: this gives wrong results in childclasses ... maybe access
443 as class-attribute??"""
444 for o in self.__options__:
445 if o.name == name:
446 return o
447 return None
448
450 """Remove an option from this Options."""
451 for o in self.__options__:
452 if o.name == name:
453 del o
454 return True
455 return True
456
458 """This function creates options from an XML-file with option-metadata.
459 TODO: make this more reusable and place it into module (once the groups
460 are own objects)"""
461
462 try:
463 doc = xml.dom.minidom.parse(filename)
464 except:
465 raise Exception('Invalid XML in metadata-file (or file missing): "%s".' % filename)
466
467 root = doc.firstChild
468 if not root or root.nodeName != 'screenlet':
469 raise Exception('Missing or invalid rootnode in metadata-file: "%s".' % filename)
470
471 groups = []
472 for node in root.childNodes:
473
474 if node.nodeType == Node.ELEMENT_NODE:
475
476 if node.nodeName != 'group' or not node.hasChildNodes():
477
478 raise Exception('Error in metadata-file "%s" - only <group>-tags allowed in first level. Groups must contain at least one <info>-element.' % filename)
479 else:
480
481 group = {}
482 group['name'] = node.getAttribute("name")
483 if not group['name']:
484 raise Exception('No name for group defined in "%s".' % filename)
485 group['info'] = ''
486 group['options'] = []
487
488 for on in node.childNodes:
489 if on.nodeType == Node.ELEMENT_NODE:
490 if on.nodeName == 'info':
491
492 group['info'] = on.firstChild.nodeValue
493 elif on.nodeName == 'option':
494
495 opt = create_option_from_node (on, group['name'])
496
497 if opt:
498 group['options'].append(opt)
499 else:
500 raise Exception('Invalid option-node found in "%s".' % filename)
501
502
503 if len(group['options']):
504 self.add_options_group(group['name'], group['info'])
505 for o in group['options']:
506 self.add_option(o)
507
508
509
510
511
512
513
515 """An editing dialog used for editing options of the ListOption-type."""
516
517 model = None
518 tree = None
519 buttonbox = None
520
521
523 super(ListOptionDialog, self).__init__("Edit List",
524 flags=gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR,
525 buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
526 gtk.STOCK_OK, gtk.RESPONSE_OK))
527
528 self.resize(300, 370)
529 self.set_keep_above(True)
530
531 self.model = gtk.ListStore(str)
532
533 self.create_ui()
534
536 """Create the user-interface for this dialog."""
537
538 hbox = gtk.HBox()
539 hbox.set_border_width(10)
540 hbox.set_spacing(10)
541
542 self.tree = gtk.TreeView(model=self.model)
543 self.tree.set_headers_visible(False)
544 self.tree.set_reorderable(True)
545
546 col = gtk.TreeViewColumn('')
547 cell = gtk.CellRendererText()
548
549 cell.set_property('foreground', 'black')
550 col.pack_start(cell, False)
551 col.set_attributes(cell, text=0)
552 self.tree.append_column(col)
553 self.tree.show()
554 hbox.pack_start(self.tree, True, True)
555
556
557
558
559 self.buttonbox = bb = gtk.VButtonBox()
560 self.buttonbox.set_layout(gtk.BUTTONBOX_START)
561 b1 = gtk.Button(stock=gtk.STOCK_ADD)
562 b2 = gtk.Button(stock=gtk.STOCK_EDIT)
563 b3 = gtk.Button(stock=gtk.STOCK_REMOVE)
564 b1.connect('clicked', self.button_callback, 'add')
565 b2.connect('clicked', self.button_callback, 'edit')
566 b3.connect('clicked', self.button_callback, 'remove')
567 bb.add(b1)
568 bb.add(b2)
569 bb.add(b3)
570 self.buttonbox.show_all()
571
572 hbox.pack_end(self.buttonbox, False)
573
574 hbox.show()
575 self.vbox.add(hbox)
576
578 """Set the list to be edited in this editor."""
579 for el in lst:
580 self.model.append([el])
581
583 """Return the list that is currently being edited in this editor."""
584 lst = []
585 for i in self.model:
586 lst.append(i[0])
587 return lst
588
590 """Remove the currently selected item."""
591 sel = self.tree.get_selection()
592 if sel:
593 it = sel.get_selected()[1]
594 if it:
595 print self.model.get_value(it, 0)
596 self.model.remove(it)
597
598 - def entry_dialog (self, default = ''):
599 """Show entry-dialog and return string."""
600 entry = gtk.Entry()
601 entry.set_text(default)
602 entry.show()
603 dlg = gtk.Dialog("Add/Edit Item", flags=gtk.DIALOG_DESTROY_WITH_PARENT,
604 buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK,
605 gtk.RESPONSE_OK))
606 dlg.set_keep_above(True)
607 dlg.vbox.add(entry)
608 resp = dlg.run()
609 ret = None
610 if resp == gtk.RESPONSE_OK:
611 ret = entry.get_text()
612 dlg.destroy()
613 return ret
614
632
633
634
635 """dlg = ListOptionDialog()
636 dlg.set_list(['test1', 'afarew34s', 'fhjh23faj', 'yxcdfs58df', 'hsdf7jsdfh'])
637 dlg.run()
638 print "RESULT: " + str(dlg.get_list())
639 dlg.destroy()
640 import sys
641 sys.exit(1)"""
642
643
645 """A dynamic options-editor for editing Screenlets which are implementing
646 the EditableOptions-class."""
647
648 __shown_object = None
649
651
652 super(OptionsDialog, self).__init__(
653 _("Edit Options"), flags=gtk.DIALOG_DESTROY_WITH_PARENT |
654 gtk.DIALOG_NO_SEPARATOR,
655 buttons = (
656 gtk.STOCK_CLOSE, gtk.RESPONSE_OK))
657
658 self.resize(width, height)
659 self.set_keep_above(True)
660 self.set_border_width(10)
661
662 self.page_about = None
663 self.page_options = None
664 self.page_themes = None
665 self.vbox_editor = None
666 self.hbox_about = None
667 self.infotext = None
668 self.infoicon = None
669
670 self.liststore = gtk.ListStore(object)
671 self.tree = gtk.TreeView(model=self.liststore)
672
673 self.main_notebook = gtk.Notebook()
674 self.main_notebook.show()
675 self.vbox.add(self.main_notebook)
676
677 self.create_about_page()
678 self.create_themes_page()
679 self.create_options_page()
680
681
682
684 """Reset all entries for the currently shown object to their default
685 values (the values the object has when it is first created).
686 NOTE: This function resets ALL options, so BE CARFEUL!"""
687 if self.__shown_object:
688 for o in self.__shown_object.__options__:
689
690 setattr(self.__shown_object, o.name, o.default)
691
692 - def set_info (self, name, info, copyright='', version='', icon=None):
693 """Update the "About"-page with the given information."""
694
695 info = info.replace("\n", "")
696 info = info.replace("\t", " ")
697
698 markup = '\n<b><span size="xx-large">' + name + '</span></b>'
699 if version:
700 markup += ' <span size="large"><b>' + version + '</b></span>'
701 markup += '\n\n'+info+'\n<span size="small">\n'+copyright+'</span>'
702 self.infotext.set_markup(markup)
703
704 if icon:
705
706 if self.infoicon:
707 self.infoicon.destroy()
708
709 self.infoicon = icon
710 self.infoicon.set_alignment(0.0, 0.10)
711 self.infoicon.show()
712 self.hbox_about.pack_start(self.infoicon, 0, 1, 10)
713 else:
714 self.infoicon.hide()
715
717 """Update the OptionsEditor to show the options for the given Object.
718 The Object needs to be an EditableOptions-subclass.
719 NOTE: This needs heavy improvement and should use OptionGroups once
720 they exist"""
721 self.__shown_object = obj
722
723 notebook = gtk.Notebook()
724 self.vbox_editor.add(notebook)
725 for group in obj.__options_groups_ordered__:
726 group_data = obj.__options_groups__[group]
727
728 page = gtk.VBox()
729 page.set_border_width(10)
730 if group_data['info'] != '':
731 info = gtk.Label(group_data['info'])
732 info.show()
733 info.set_alignment(0, 0)
734 page.pack_start(info, 0, 0, 7)
735 sep = gtk.HSeparator()
736 sep.show()
737
738
739 box = gtk.VBox()
740 box.show()
741 box.set_border_width(5)
742
743 page.add(box)
744 page.show()
745
746 label = gtk.Label(group_data['label'])
747 label.show()
748 notebook.append_page(page, label)
749
750 for option in group_data['options']:
751 if option.hidden == False:
752 val = getattr(obj, option.name)
753 w = self.get_widget_for_option(option, val)
754 if w:
755 box.pack_start(w, 0, 0)
756 w.show()
757 notebook.show()
758
759 if obj.uses_theme and obj.theme_name != '':
760 self.show_themes_for_screenlet(obj)
761 else:
762 self.page_themes.hide()
763
765 """Update the Themes-page to display the available themes for the
766 given Screenlet-object."""
767
768
769 dircontent = []
770 screenlets.utils.refresh_available_screenlet_paths()
771
772 for path in screenlets.SCREENLETS_PATH:
773 p = path + '/' + obj.get_short_name() + '/themes'
774 print p
775
776 try:
777 dc = os.listdir(p)
778 for d in dc:
779 dircontent.append({'name':d, 'path':p+'/'})
780 except:
781 print "Path %s not found." % p
782
783
784 found_themes = []
785
786
787 for elem in dircontent:
788
789 if found_themes.count(elem['name']):
790 continue
791 found_themes.append(elem['name'])
792
793 theme_conf = elem['path'] + elem['name'] + '/theme.conf'
794
795 if os.access(theme_conf, os.F_OK):
796
797 ini = screenlets.utils.IniReader()
798 if ini.load(theme_conf):
799
800 if ini.has_section('Theme'):
801
802 th_fullname = ini.get_option('name',
803 section='Theme')
804 th_info = ini.get_option('info',
805 section='Theme')
806 th_version = ini.get_option('version',
807 section='Theme')
808 th_author = ini.get_option('author',
809 section='Theme')
810
811 info = [elem['name'], th_fullname, th_info, th_author,
812 th_version]
813 self.liststore.append([info])
814 else:
815
816 self.liststore.append([[elem['name'], '-', '-', '-', '-']])
817 else:
818
819 self.liststore.append([[elem['name'], '-', '-', '-', '-']])
820
821 if elem['name'] == obj.theme_name:
822
823 print "active theme is: %s" % elem['name']
824 sel = self.tree.get_selection()
825 if sel:
826 it = self.liststore.get_iter_from_string(\
827 str(len(self.liststore)-1))
828 if it:
829 sel.select_iter(it)
830
831
832
834 """Create the "About"-tab."""
835 self.page_about = gtk.HBox()
836
837 self.hbox_about = gtk.HBox()
838 self.hbox_about.show()
839 self.page_about.add(self.hbox_about)
840
841 self.infoicon = gtk.Image()
842 self.infoicon.show()
843 self.page_about.pack_start(self.infoicon, 0, 1, 10)
844
845 self.infotext = gtk.Label()
846 self.infotext.use_markup = True
847 self.infotext.set_line_wrap(True)
848 self.infotext.set_alignment(0.0, 0.0)
849 self.infotext.show()
850 self.page_about.pack_start(self.infotext, 1, 1, 5)
851
852 self.page_about.show()
853 self.main_notebook.append_page(self.page_about, gtk.Label(_('About ')))
854
856 """Create the "Options"-tab."""
857 self.page_options = gtk.HBox()
858
859 self.vbox_editor = gtk.VBox(spacing=3)
860 self.vbox_editor.set_border_width(5)
861 self.vbox_editor.show()
862 self.page_options.add(self.vbox_editor)
863
864 self.page_options.show()
865 self.main_notebook.append_page(self.page_options, gtk.Label(_('Options ')))
866
868 """Create the "Themes"-tab."""
869 self.page_themes = gtk.VBox(spacing=5)
870 self.page_themes.set_border_width(10)
871
872 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.'))
873 txt.set_size_request(450, -1)
874 txt.set_line_wrap(True)
875 txt.set_alignment(0.0, 0.0)
876 txt.show()
877 self.page_themes.pack_start(txt, False, True)
878
879 self.tree.set_headers_visible(False)
880 self.tree.connect('cursor-changed', self.__tree_cursor_changed)
881 self.tree.show()
882 col = gtk.TreeViewColumn('')
883 cell = gtk.CellRendererText()
884 col.pack_start(cell, True)
885
886 col.set_cell_data_func(cell, self.__render_cell)
887 self.tree.append_column(col)
888
889 sw = gtk.ScrolledWindow()
890 sw.set_shadow_type(gtk.SHADOW_IN)
891 sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
892 sw.add(self.tree)
893 sw.show()
894
895 vbox = gtk.VBox()
896 vbox.pack_start(sw, True, True)
897 vbox.show()
898
899 self.page_themes.add(vbox)
900 self.page_themes.show()
901 self.main_notebook.append_page(self.page_themes, gtk.Label(_('Themes ')))
902
904 """Callback for rendering the cells in the theme-treeview."""
905
906 attrib = model.get_value(iter, 0)
907
908
909 col = '555555'
910 name_uc = attrib[0][0].upper() + attrib[0][1:]
911
912 if attrib[1] == '-' and attrib[2] == '-':
913 mu = '<b><span weight="ultrabold" size="large">' + name_uc + \
914 '</span></b> (' + _('no info available') + ')'
915 else:
916 if attrib[1] == None : attrib[1] = '-'
917 if attrib[2] == None : attrib[2] = '-'
918 if attrib[3] == None : attrib[3] = '-'
919 if attrib[4] == None : attrib[4] = '-'
920 mu = '<b><span weight="ultrabold" size="large">' + name_uc + \
921 '</span></b> v' + attrib[4] + '\n<small><span color="#555555' +\
922 '">' + attrib[2].replace('\\n', '\n') + \
923 '</span></small>\n<i><small>by '+str(attrib[3])+'</small></i>'
924
925 cell.set_property('markup', mu)
926
927
928
930 """Callback for handling selection changes in the Themes-treeview."""
931 sel = self.tree.get_selection()
932 if sel:
933 s = sel.get_selected()
934 if s:
935 it = s[1]
936 if it:
937 attribs = self.liststore.get_value(it, 0)
938 if attribs and self.__shown_object:
939
940
941 if self.__shown_object.theme_name != attribs[0]:
942 self.__shown_object.theme_name = attribs[0]
943
944
945
1036
1037 def but_callback (widget):
1038 dlg = gtk.FileChooserDialog(buttons=(gtk.STOCK_CANCEL,
1039 gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
1040 dlg.set_title(_("Choose Image"))
1041 dlg.set_keep_above(True)
1042 dlg.set_filename(entry.get_text())
1043 flt = gtk.FileFilter()
1044 flt.add_pixbuf_formats()
1045 dlg.set_filter(flt)
1046 prev = gtk.Image()
1047 box = gtk.VBox()
1048 box.set_size_request(150, -1)
1049 box.add(prev)
1050 prev.show()
1051
1052 def preview_callback(widget):
1053 fname = dlg.get_preview_filename()
1054 if fname and os.path.isfile(fname):
1055 pb = gtk.gdk.pixbuf_new_from_file_at_size(fname, 150, -1)
1056 if pb:
1057 prev.set_from_pixbuf(pb)
1058 dlg.set_preview_widget_active(True)
1059 else:
1060 dlg.set_preview_widget_active(False)
1061 dlg.set_preview_widget_active(True)
1062 dlg.connect('selection-changed', preview_callback)
1063 dlg.set_preview_widget(box)
1064
1065 response = dlg.run()
1066 if response == gtk.RESPONSE_OK:
1067 entry.set_text(dlg.get_filename())
1068 but.set_image(create_preview(dlg.get_filename()))
1069 self.options_callback(dlg, option)
1070 dlg.destroy()
1071
1072 but.set_image(create_preview(value))
1073 but.connect('clicked', but_callback)
1074
1075 widget = gtk.HBox()
1076 widget.add(entry)
1077 widget.add(but)
1078 but.show()
1079 widget.show()
1080
1081
1082 but.set_tooltip_text(option.desc)
1083
1084 elif t == ListOption:
1085 entry= gtk.Entry()
1086 entry.set_editable(False)
1087 entry.set_text(str(value))
1088 entry.show()
1089 img = gtk.Image()
1090 img.set_from_stock(gtk.STOCK_EDIT, 1)
1091 but = gtk.Button()
1092 but.set_image(img)
1093 def open_listeditor(event):
1094
1095 dlg = ListOptionDialog()
1096
1097
1098 dlg.set_list(option.on_import(entry.get_text()))
1099 resp = dlg.run()
1100 if resp == gtk.RESPONSE_OK:
1101
1102 entry.set_text(str(dlg.get_list()))
1103
1104 self.options_callback(dlg, option)
1105 dlg.destroy()
1106 but.show()
1107 but.connect("clicked", open_listeditor)
1108 but.set_tooltip_text(_('Open List-Editor ...'))
1109 entry.set_tooltip_text(option.desc)
1110 widget = gtk.HBox()
1111 widget.add(entry)
1112 widget.add(but)
1113 elif t == AccountOption:
1114 widget = gtk.HBox()
1115 vb = gtk.VBox()
1116 input_name = gtk.Entry()
1117 input_name.set_text(value[0])
1118 input_name.show()
1119 input_pass = gtk.Entry()
1120 input_pass.set_visibility(False)
1121 input_pass.set_text(value[1])
1122 input_pass.show()
1123 but = gtk.Button(_('Apply'), gtk.STOCK_APPLY)
1124 but.show()
1125 but.connect("clicked", self.apply_options_callback, option, widget)
1126 vb.add(input_name)
1127 vb.add(input_pass)
1128 vb.show()
1129 but.set_tooltip_text(_('Apply username/password ...'))
1130 input_name.set_tooltip_text(_('Enter username here ...'))
1131 input_pass.set_tooltip_text(_('Enter password here ...'))
1132 widget.add(vb)
1133 widget.add(but)
1134 elif t == TimeOption:
1135 widget = gtk.HBox()
1136 input_hour = gtk.SpinButton()
1137 input_minute = gtk.SpinButton()
1138 input_second = gtk.SpinButton()
1139 input_hour.set_range(0, 23)
1140 input_hour.set_max_length(2)
1141 input_hour.set_increments(1, 1)
1142 input_hour.set_numeric(True)
1143 input_hour.set_value(value[0])
1144 input_minute.set_range(0, 59)
1145 input_minute.set_max_length(2)
1146 input_minute.set_increments(1, 1)
1147 input_minute.set_numeric(True)
1148 input_minute.set_value(value[1])
1149 input_second.set_range(0, 59)
1150 input_second.set_max_length(2)
1151 input_second.set_increments(1, 1)
1152 input_second.set_numeric(True)
1153 input_second.set_value(value[2])
1154 input_hour.connect('value-changed', self.options_callback, option)
1155 input_minute.connect('value-changed', self.options_callback, option)
1156 input_second.connect('value-changed', self.options_callback, option)
1157 input_hour.set_tooltip_text(option.desc)
1158 input_minute.set_tooltip_text(option.desc)
1159 input_second.set_tooltip_text(option.desc)
1160 widget.add(input_hour)
1161 widget.add(gtk.Label(':'))
1162 widget.add(input_minute)
1163 widget.add(gtk.Label(':'))
1164 widget.add(input_second)
1165 widget.add(gtk.Label('h'))
1166 widget.show_all()
1167 else:
1168 widget = gtk.Entry()
1169 print "unsupported type ''" % str(t)
1170 hbox = gtk.HBox()
1171 label = gtk.Label()
1172 label.set_alignment(0.0, 0.0)
1173 label.set_label(option.label)
1174 label.set_size_request(180, 28)
1175 label.show()
1176 hbox.pack_start(label, 0, 1)
1177 if widget:
1178 if option.disabled:
1179 widget.set_sensitive(False)
1180 label.set_sensitive(False)
1181
1182 widget.set_tooltip_text(option.desc)
1183 widget.show()
1184
1185 if option.realtime == False:
1186 but = gtk.Button(_('Apply'), gtk.STOCK_APPLY)
1187 but.show()
1188 but.connect("clicked", self.apply_options_callback,
1189 option, widget)
1190 b = gtk.HBox()
1191 b.show()
1192 b.pack_start(widget, 0, 0)
1193 b.pack_start(but, 0, 0)
1194 hbox.pack_start(b, 0, 0)
1195 else:
1196
1197 hbox.pack_start(widget, 0, 0)
1198 return hbox
1199
1251
1252
1253
1254
1256 """Callback for handling changed-events on entries."""
1257 print "Changed: %s" % optionobj.name
1258 if self.__shown_object:
1259
1260 if optionobj.realtime == False:
1261 return False
1262
1263 val = self.read_option_from_widget(widget, optionobj)
1264 if val != None:
1265
1266
1267 setattr(self.__shown_object, optionobj.name, val)
1268
1269 optionobj.emit("option_changed", optionobj)
1270 return False
1271
1273 """Callback for handling Apply-button presses."""
1274 if self.__shown_object:
1275
1276 val = self.read_option_from_widget(entry, optionobj)
1277 if val != None:
1278
1279
1280 setattr(self.__shown_object, optionobj.name, val)
1281
1282 optionobj.emit("option_changed", optionobj)
1283 return False
1284
1285
1286
1287
1288 if __name__ == "__main__":
1289
1290 import os
1291
1292
1294
1295 testlist = ['test1', 'test2', 3, 5, 'Noch ein Test']
1296 pop3_account = ('Username', '')
1297
1298
1299 pin_x = 100
1300 pin_y = 6
1301 text_x = 19
1302 text_y = 35
1303 font_name = 'Sans 12'
1304 rgba_color = (0.0, 0.0, 1.0, 1.0)
1305 text_prefix = '<b>'
1306 text_suffix = '</b>'
1307 note_text = ""
1308 random_pin_pos = True
1309 opt1 = 'testval 1'
1310 opt2 = 'testval 2'
1311 filename2 = ''
1312 filename = ''
1313 dirname = ''
1314 font = 'Sans 12'
1315 color = (0.1, 0.5, 0.9, 0.9)
1316 name = 'a name'
1317 name2 = 'another name'
1318 combo_test = 'el2'
1319 flt = 0.5
1320 x = 10
1321 y = 25
1322 width = 30
1323 height = 50
1324 is_sticky = False
1325 is_widget = False
1326 time = (12, 32, 49)
1327
1329 EditableOptions.__init__(self)
1330
1331 self.add_options_group('General',
1332 'The general options for this Object ...')
1333 self.add_options_group('Window',
1334 'The Window-related options for this Object ...')
1335 self.add_options_group('Test', 'A Test-group ...')
1336
1337 self.add_option(ListOption('Test', 'testlist', self.testlist,
1338 'ListOption-Test', 'Testing a ListOption-type ...'))
1339 self.add_option(StringOption('Window', 'name', 'TESTNAME',
1340 'Testname', 'The name/id of this Screenlet-instance ...'),
1341 realtime=False)
1342 self.add_option(AccountOption('Test', 'pop3_account',
1343 self.pop3_account, 'Username/Password',
1344 'Enter username/password here ...'))
1345 self.add_option(StringOption('Window', 'name2', 'TESTNAME2',
1346 'String2', 'Another string-test ...'))
1347 self.add_option(StringOption('Test', 'combo_test', "el1", 'Combo',
1348 'A StringOption displaying a drop-down-list with choices...',
1349 choices=['el1', 'el2', 'element 3']))
1350 self.add_option(FloatOption('General', 'flt', 30,
1351 'A Float', 'Testing a FLOAT-type ...',
1352 min=0, max=gtk.gdk.screen_width(), increment=0.01, digits=4))
1353 self.add_option(IntOption('General', 'x', 30,
1354 'X-Position', 'The X-position of this Screenlet ...',
1355 min=0, max=gtk.gdk.screen_width()))
1356 self.add_option(IntOption('General', 'y', 30,
1357 'Y-Position', 'The Y-position of this Screenlet ...',
1358 min=0, max=gtk.gdk.screen_height()))
1359 self.add_option(IntOption('Test', 'width', 300,
1360 'Width', 'The width of this Screenlet ...', min=100, max=1000))
1361 self.add_option(IntOption('Test', 'height', 150,
1362 'Height', 'The height of this Screenlet ...',
1363 min=100, max=1000))
1364 self.add_option(BoolOption('General', 'is_sticky', True,
1365 'Stick to Desktop', 'Show this Screenlet always ...'))
1366 self.add_option(BoolOption('General', 'is_widget', False,
1367 'Treat as Widget', 'Treat this Screenlet as a "Widget" ...'))
1368 self.add_option(FontOption('Test', 'font', 'Sans 14',
1369 'Font', 'The font for whatever ...'))
1370 self.add_option(ColorOption('Test', 'color', (1, 0.35, 0.35, 0.7),
1371 'Color', 'The color for whatever ...'))
1372 self.add_option(FileOption('Test', 'filename', os.environ['HOME'],
1373 'Filename-Test', 'Testing a FileOption-type ...',
1374 patterns=['*.py', '*.pyc']))
1375 self.add_option(ImageOption('Test', 'filename2', os.environ['HOME'],
1376 'Image-Test', 'Testing the ImageOption-type ...'))
1377 self.add_option(DirectoryOption('Test', 'dirname', os.environ['HOME'],
1378 'Directory-Test', 'Testing a FileOption-type ...'))
1379 self.add_option(TimeOption('Test','time', self.time,
1380 'TimeOption-Test', 'Testing a TimeOption-type ...'))
1381
1382 self.disable_option('width')
1383 self.disable_option('height')
1384
1385
1386
1388 self.__dict__[name] = value
1389 print name + "=" + str(value)
1390
1392 return self.__class__.__name__[:-6]
1393
1394
1395
1396
1398
1399 uses_theme = True
1400 theme_name = 'test'
1401
1403 TestObject.__init__(self)
1404 self.add_option(StringOption('Test', 'anothertest', 'ksjhsjgd',
1405 'Another Test', 'An attribute in the subclass ...'))
1406 self.add_option(StringOption('Test', 'theme_name', self.theme_name,
1407 'Theme', 'The theme for this Screenelt ...',
1408 choices=['test1', 'test2', 'mytheme', 'blue', 'test']))
1409
1410
1411
1412
1413 to = TestChildObject()
1414
1415 se = OptionsDialog(500, 380)
1416
1417 img = gtk.Image()
1418 img.set_from_file('../share/screenlets/Notes/icon.svg')
1419 se.set_info('TestOptions',
1420 'A test for an extended options-dialog with embedded about-info.' +
1421 ' Can be used for the Screenlets to have all in one ...\nNOTE:' +
1422 '<span color="red"> ONLY A TEST!</span>',
1423 '(c) RYX 2007', version='v0.0.1', icon=img)
1424 se.show_options_for_object(to)
1425 resp = se.run()
1426 if resp == gtk.RESPONSE_OK:
1427 print "OK"
1428 else:
1429 print "Cancelled."
1430 se.destroy()
1431 print to.export_options_as_list()
1432