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