Merge lp:~doctormo/screenlets/options-rework into lp:screenlets
- options-rework
- Merge into trunk
Proposed by
Martin Owens
Status: | Merged |
---|---|
Merged at revision: | 632 |
Proposed branch: | lp:~doctormo/screenlets/options-rework |
Merge into: | lp:screenlets |
Diff against target: |
3868 lines (+2019/-1579) 14 files modified
setup.py (+1/-1) src/lib/__init__.py (+156/-146) src/lib/options.py (+0/-1432) src/lib/options/__init__.py (+202/-0) src/lib/options/account_option.py (+143/-0) src/lib/options/base.py (+624/-0) src/lib/options/boolean_option.py (+54/-0) src/lib/options/colour_option.py (+149/-0) src/lib/options/file_option.py (+179/-0) src/lib/options/font_option.py (+52/-0) src/lib/options/list_option.py (+210/-0) src/lib/options/number_option.py (+90/-0) src/lib/options/string_option.py (+79/-0) src/lib/options/time_option.py (+80/-0) |
To merge this branch: | bzr merge lp:~doctormo/screenlets/options-rework |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Screenlets Dev Team | Pending | ||
Review via email: mp+55179@code.launchpad.net |
Commit message
Description of the change
Reattempt to get merged in updated options. These are API compatible and offer a new colour array option.
To post a comment you must log in.
Revision history for this message
Märt Põder (boamaod) wrote : | # |
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'setup.py' | |||
2 | --- setup.py 2010-11-10 23:53:53 +0000 | |||
3 | +++ setup.py 2011-03-28 16:19:24 +0000 | |||
4 | @@ -119,7 +119,7 @@ | |||
5 | 119 | os.system (buildcmd % (dname, name, name, dname)) | 119 | os.system (buildcmd % (dname, name, name, dname)) |
6 | 120 | files_list.append ((destpath % dname, [mopath % (dname,name.replace('-'+dname,''))])) | 120 | files_list.append ((destpath % dname, [mopath % (dname,name.replace('-'+dname,''))])) |
7 | 121 | 121 | ||
9 | 122 | PACKAGES = ['screenlets','screenlets.plugins'] | 122 | PACKAGES = ['screenlets','screenlets.plugins','screenlets.options'] |
10 | 123 | 123 | ||
11 | 124 | # ---------------------------- | 124 | # ---------------------------- |
12 | 125 | # Call setup() | 125 | # Call setup() |
13 | 126 | 126 | ||
14 | === modified file 'src/lib/__init__.py' | |||
15 | --- src/lib/__init__.py 2011-02-09 20:45:08 +0000 | |||
16 | +++ src/lib/__init__.py 2011-03-28 16:19:24 +0000 | |||
17 | @@ -136,15 +136,15 @@ | |||
18 | 136 | 136 | ||
19 | 137 | DEBIAN = True | 137 | DEBIAN = True |
20 | 138 | try: | 138 | try: |
22 | 139 | subprocess.call(["dpkg"], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT) | 139 | subprocess.call(["dpkg"], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT) |
23 | 140 | except OSError: | 140 | except OSError: |
25 | 141 | DEBIAN = False | 141 | DEBIAN = False |
26 | 142 | 142 | ||
27 | 143 | UBUNTU = True | 143 | UBUNTU = True |
28 | 144 | try: | 144 | try: |
30 | 145 | subprocess.call(["apt-add-repository"], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT) | 145 | subprocess.call(["apt-add-repository"], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT) |
31 | 146 | except OSError: | 146 | except OSError: |
33 | 147 | UBUNTU = False | 147 | UBUNTU = False |
34 | 148 | 148 | ||
35 | 149 | #------------------------------------------------------------------------------- | 149 | #------------------------------------------------------------------------------- |
36 | 150 | # CLASSES | 150 | # CLASSES |
37 | @@ -318,8 +318,8 @@ | |||
38 | 318 | """@DEPRECATED Moved to Screenlets class: Draws a circule""" | 318 | """@DEPRECATED Moved to Screenlets class: Draws a circule""" |
39 | 319 | ctx.save() | 319 | ctx.save() |
40 | 320 | ctx.translate(x, y) | 320 | ctx.translate(x, y) |
43 | 321 | ctx.arc(width/2,height/2,min(height,width)/2,0,2*math.pi) | 321 | ctx.arc(width/2,height/2,min(height,width)/2,0,2*math.pi) |
44 | 322 | if fill:ctx.fill() | 322 | if fill: ctx.fill() |
45 | 323 | else: ctx.stroke() | 323 | else: ctx.stroke() |
46 | 324 | ctx.restore() | 324 | ctx.restore() |
47 | 325 | 325 | ||
48 | @@ -328,7 +328,7 @@ | |||
49 | 328 | ctx.save() | 328 | ctx.save() |
50 | 329 | ctx.move_to(start_x, start_y) | 329 | ctx.move_to(start_x, start_y) |
51 | 330 | ctx.set_line_width(line_width) | 330 | ctx.set_line_width(line_width) |
53 | 331 | ctx.rel_line_to(end_x, end_y) | 331 | ctx.rel_line_to(end_x, end_y) |
54 | 332 | if close : ctx.close_path() | 332 | if close : ctx.close_path() |
55 | 333 | if preserve: ctx.stroke_preserve() | 333 | if preserve: ctx.stroke_preserve() |
56 | 334 | else: ctx.stroke() | 334 | else: ctx.stroke() |
57 | @@ -348,30 +348,30 @@ | |||
58 | 348 | ctx.save() | 348 | ctx.save() |
59 | 349 | ctx.translate(x, y) | 349 | ctx.translate(x, y) |
60 | 350 | padding=0 # Padding from the edges of the window | 350 | padding=0 # Padding from the edges of the window |
63 | 351 | rounded=rounded_angle # How round to make the edges 20 is ok | 351 | rounded=rounded_angle # How round to make the edges 20 is ok |
64 | 352 | w = width | 352 | w = width |
65 | 353 | h = height | 353 | h = height |
66 | 354 | 354 | ||
87 | 355 | # Move to top corner | 355 | # Move to top corner |
88 | 356 | ctx.move_to(0+padding+rounded, 0+padding) | 356 | ctx.move_to(0+padding+rounded, 0+padding) |
89 | 357 | 357 | ||
90 | 358 | # Top right corner and round the edge | 358 | # Top right corner and round the edge |
91 | 359 | ctx.line_to(w-padding-rounded, 0+padding) | 359 | ctx.line_to(w-padding-rounded, 0+padding) |
92 | 360 | ctx.arc(w-padding-rounded, 0+padding+rounded, rounded, (math.pi/2 )+(math.pi) , 0) | 360 | ctx.arc(w-padding-rounded, 0+padding+rounded, rounded, (math.pi/2 )+(math.pi) , 0) |
93 | 361 | 361 | ||
94 | 362 | # Bottom right corner and round the edge | 362 | # Bottom right corner and round the edge |
95 | 363 | ctx.line_to(w-padding, h-padding-rounded) | 363 | ctx.line_to(w-padding, h-padding-rounded) |
96 | 364 | ctx.arc(w-padding-rounded, h-padding-rounded, rounded, 0, math.pi/2) | 364 | ctx.arc(w-padding-rounded, h-padding-rounded, rounded, 0, math.pi/2) |
97 | 365 | 365 | ||
98 | 366 | # Bottom left corner and round the edge. | 366 | # Bottom left corner and round the edge. |
99 | 367 | ctx.line_to(0+padding+rounded, h-padding) | 367 | ctx.line_to(0+padding+rounded, h-padding) |
100 | 368 | ctx.arc(0+padding+rounded, h-padding-rounded, rounded,math.pi/2, math.pi) | 368 | ctx.arc(0+padding+rounded, h-padding-rounded, rounded,math.pi/2, math.pi) |
101 | 369 | 369 | ||
102 | 370 | # Top left corner and round the edge | 370 | # Top left corner and round the edge |
103 | 371 | ctx.line_to(0+padding, 0+padding+rounded) | 371 | ctx.line_to(0+padding, 0+padding+rounded) |
104 | 372 | ctx.arc(0+padding+rounded, 0+padding+rounded, rounded, math.pi, (math.pi/2 )+(math.pi)) | 372 | ctx.arc(0+padding+rounded, 0+padding+rounded, rounded, math.pi, (math.pi/2 )+(math.pi)) |
105 | 373 | 373 | ||
106 | 374 | # Fill in the shape. | 374 | # Fill in the shape. |
107 | 375 | if fill:ctx.fill() | 375 | if fill:ctx.fill() |
108 | 376 | else: ctx.stroke() | 376 | else: ctx.stroke() |
109 | 377 | ctx.restore() | 377 | ctx.restore() |
110 | @@ -430,29 +430,29 @@ | |||
111 | 430 | ctx.restore() | 430 | ctx.restore() |
112 | 431 | 431 | ||
113 | 432 | def show_notification (self,text): | 432 | def show_notification (self,text): |
115 | 433 | """@DEPRECATED Moved to Screenlets class: Show notification window at current mouse position.""" | 433 | """@DEPRECATED Moved to Screenlets class: Show notification window at current mouse position.""" |
116 | 434 | if self.notify == None: | 434 | if self.notify == None: |
120 | 435 | self.notify = Notify() | 435 | self.notify = Notify() |
121 | 436 | self.notify.text = text | 436 | self.notify.text = text |
122 | 437 | self.notify.show() | 437 | self.notify.show() |
123 | 438 | 438 | ||
124 | 439 | def hide_notification (self): | 439 | def hide_notification (self): |
126 | 440 | """@DEPRECATED Moved to Screenlets class: hide notification window""" | 440 | """@DEPRECATED Moved to Screenlets class: hide notification window""" |
127 | 441 | if self.notify != None: | 441 | if self.notify != None: |
128 | 442 | self.notify.hide() | 442 | self.notify.hide() |
129 | 443 | self.notify = None | 443 | self.notify = None |
130 | 444 | 444 | ||
131 | 445 | def show_tooltip (self,text,tooltipx,tooltipy): | 445 | def show_tooltip (self,text,tooltipx,tooltipy): |
133 | 446 | """@DEPRECATED: Moved to Screenlets class: Show tooltip window at current mouse position.""" | 446 | """@DEPRECATED: Moved to Screenlets class: Show tooltip window at current mouse position.""" |
134 | 447 | if self.tooltip == None: | 447 | if self.tooltip == None: |
140 | 448 | self.tooltip = Tooltip(300, 400) | 448 | self.tooltip = Tooltip(300, 400) |
141 | 449 | self.tooltip.text = text | 449 | self.tooltip.text = text |
142 | 450 | self.tooltip.x = tooltipx | 450 | self.tooltip.x = tooltipx |
143 | 451 | self.tooltip.y = tooltipy | 451 | self.tooltip.y = tooltipy |
144 | 452 | self.tooltip.show() | 452 | self.tooltip.show() |
145 | 453 | 453 | ||
146 | 454 | def hide_tooltip (self): | 454 | def hide_tooltip (self): |
148 | 455 | """@DEPRECATED Moved to Screenlets class: hide tooltip window""" | 455 | """@DEPRECATED Moved to Screenlets class: hide tooltip window""" |
149 | 456 | if self.tooltip != None: | 456 | if self.tooltip != None: |
150 | 457 | self.tooltip.hide() | 457 | self.tooltip.hide() |
151 | 458 | self.tooltip = None | 458 | self.tooltip = None |
152 | @@ -742,70 +742,80 @@ | |||
153 | 742 | else: self.draw_buttons = False | 742 | else: self.draw_buttons = False |
154 | 743 | if uses_theme: | 743 | if uses_theme: |
155 | 744 | self.uses_theme = True | 744 | self.uses_theme = True |
158 | 745 | self.add_option(StringOption('Screenlet', 'theme_name', | 745 | self.add_option(StringOption('Screenlet', 'theme_name', |
159 | 746 | 'default', '', '', hidden=True)) | 746 | default='default', hidden=True)) |
160 | 747 | # create/add options | 747 | # create/add options |
163 | 748 | self.add_option(IntOption('Screenlet', 'x', | 748 | self.add_option(IntOption('Screenlet', 'x', |
164 | 749 | 0, _('X-Position'), _('The X-position of this Screenlet ...'), | 749 | default=0, label=_('X-Position'), |
165 | 750 | desc=_('The X-position of this Screenlet ...'), | ||
166 | 750 | min=0, max=gtk.gdk.screen_width())) | 751 | min=0, max=gtk.gdk.screen_width())) |
169 | 751 | self.add_option(IntOption('Screenlet', 'y', | 752 | self.add_option(IntOption('Screenlet', 'y', |
170 | 752 | 0, _('Y-Position'), _('The Y-position of this Screenlet ...'), | 753 | default=0, label=_('Y-Position'), |
171 | 754 | desc=_('The Y-position of this Screenlet ...'), | ||
172 | 753 | min=0, max=gtk.gdk.screen_height())) | 755 | min=0, max=gtk.gdk.screen_height())) |
181 | 754 | self.add_option(IntOption('Screenlet', 'width', | 756 | self.add_option(IntOption('Screenlet', 'width', |
182 | 755 | width, _('Width'), _('The width of this Screenlet ...'), | 757 | default=width, label=_('Width'), |
183 | 756 | min=16, max=1000, hidden=True)) | 758 | desc=_('The width of this Screenlet ...'), |
184 | 757 | self.add_option(IntOption('Screenlet', 'height', | 759 | min=16, max=1000, hidden=True)) |
185 | 758 | height, _('Height'), _('The height of this Screenlet ...'), | 760 | self.add_option(IntOption('Screenlet', 'height', |
186 | 759 | min=16, max=1000, hidden=True)) | 761 | default=height, label=_('Height'), |
187 | 760 | self.add_option(FloatOption('Screenlet', 'scale', | 762 | desc=_('The height of this Screenlet ...'), |
188 | 761 | self.scale, _('Scale'), _('The scale-factor of this Screenlet ...'), | 763 | min=16, max=1000, hidden=True)) |
189 | 764 | self.add_option(FloatOption('Screenlet', 'scale', | ||
190 | 765 | default=self.scale, label=_('Scale'), | ||
191 | 766 | desc=_('The scale-factor of this Screenlet ...'), | ||
192 | 762 | min=0.1, max=10.0, digits=2, increment=0.1)) | 767 | min=0.1, max=10.0, digits=2, increment=0.1)) |
195 | 763 | self.add_option(FloatOption('Screenlet', 'opacity', | 768 | self.add_option(FloatOption('Screenlet', 'opacity', |
196 | 764 | self.opacity, _('Opacity'), _('The opacity of the Screenlet window ...'), | 769 | default=self.opacity, label=_('Opacity'), |
197 | 770 | desc=_('The opacity of the Screenlet window ...'), | ||
198 | 765 | min=0.1, max=1.0, digits=2, increment=0.1)) | 771 | min=0.1, max=1.0, digits=2, increment=0.1)) |
231 | 766 | self.add_option(BoolOption('Screenlet', 'is_sticky', | 772 | self.add_option(BoolOption('Screenlet', 'is_sticky', |
232 | 767 | is_sticky, _('Stick to Desktop'), | 773 | default=is_sticky, label=_('Stick to Desktop'), |
233 | 768 | _('Show this Screenlet on all workspaces ...'))) | 774 | desc=_('Show this Screenlet on all workspaces ...'))) |
234 | 769 | self.add_option(BoolOption('Screenlet', 'is_widget', | 775 | self.add_option(BoolOption('Screenlet', 'is_widget', |
235 | 770 | is_widget, _('Treat as Widget'), | 776 | default=is_widget, label=_('Treat as Widget'), |
236 | 771 | _('Treat this Screenlet as a "Widget" ...'))) | 777 | desc=_('Treat this Screenlet as a "Widget" ...'))) |
237 | 772 | self.add_option(BoolOption('Screenlet', 'is_dragged', | 778 | self.add_option(BoolOption('Screenlet', 'is_dragged', |
238 | 773 | self.is_dragged, "Is the screenlet dragged","Is the screenlet dragged", hidden=True)) | 779 | default=self.is_dragged, label="Is the screenlet dragged", |
239 | 774 | self.add_option(BoolOption('Screenlet', 'is_sizable', | 780 | desc="Is the screenlet dragged", hidden=True)) |
240 | 775 | is_sizable, "Can the screenlet be resized","is_sizable", hidden=True)) | 781 | self.add_option(BoolOption('Screenlet', 'is_sizable', |
241 | 776 | self.add_option(BoolOption('Screenlet', 'is_visible', | 782 | default=is_sizable, label="Can the screenlet be resized", |
242 | 777 | self.is_visible, "Usefull to use screenlets as gnome panel applets","is_visible", hidden=True)) | 783 | desc="is_sizable", hidden=True)) |
243 | 778 | self.add_option(BoolOption('Screenlet', 'lock_position', | 784 | self.add_option(BoolOption('Screenlet', 'is_visible', |
244 | 779 | self.lock_position, _('Lock position'), | 785 | default=self.is_visible, label="Usefull to use screenlets as gnome panel applets", |
245 | 780 | _('Stop the screenlet from being moved...'))) | 786 | desc="is_visible", hidden=True)) |
246 | 781 | self.add_option(BoolOption('Screenlet', 'keep_above', | 787 | self.add_option(BoolOption('Screenlet', 'lock_position', |
247 | 782 | self.keep_above, _('Keep above'), | 788 | default=self.lock_position, label=_('Lock position'), |
248 | 783 | _('Keep this Screenlet above other windows ...'))) | 789 | desc=_('Stop the screenlet from being moved...'))) |
249 | 784 | self.add_option(BoolOption('Screenlet', 'keep_below', | 790 | self.add_option(BoolOption('Screenlet', 'keep_above', |
250 | 785 | self.keep_below, _('Keep below'), | 791 | default=self.keep_above, label=_('Keep above'), |
251 | 786 | _('Keep this Screenlet below other windows ...'))) | 792 | desc=_('Keep this Screenlet above other windows ...'))) |
252 | 787 | self.add_option(BoolOption('Screenlet', 'draw_buttons', | 793 | self.add_option(BoolOption('Screenlet', 'keep_below', |
253 | 788 | self.draw_buttons, _('Draw button controls'), | 794 | default=self.keep_below, label=_('Keep below'), |
254 | 789 | _('Draw buttons in top right corner'))) | 795 | desc=_('Keep this Screenlet below other windows ...'))) |
255 | 790 | self.add_option(BoolOption('Screenlet', 'skip_pager', | 796 | self.add_option(BoolOption('Screenlet', 'draw_buttons', |
256 | 791 | self.skip_pager, _('Skip Pager'), | 797 | default=self.draw_buttons, label=_('Draw button controls'), |
257 | 792 | _('Set this Screenlet to show/hide in pagers ...'))) | 798 | desc=_('Draw buttons in top right corner'))) |
258 | 793 | self.add_option(BoolOption('Screenlet', 'skip_taskbar', | 799 | self.add_option(BoolOption('Screenlet', 'skip_pager', |
259 | 794 | self.skip_pager, _('Skip Taskbar'), | 800 | default=self.skip_pager, label=_('Skip Pager'), |
260 | 795 | _('Set this Screenlet to show/hide in taskbars ...'))) | 801 | desc=_('Set this Screenlet to show/hide in pagers ...'))) |
261 | 796 | self.add_option(BoolOption('Screenlet', 'resize_on_scroll', | 802 | self.add_option(BoolOption('Screenlet', 'skip_taskbar', |
262 | 797 | self.resize_on_scroll, _("Resize on mouse scroll"),"resize_on_scroll")) | 803 | default=self.skip_pager, label=_('Skip Taskbar'), |
263 | 804 | desc=_('Set this Screenlet to show/hide in taskbars ...'))) | ||
264 | 805 | self.add_option(BoolOption('Screenlet', 'resize_on_scroll', | ||
265 | 806 | default=self.resize_on_scroll, label=_("Resize on mouse scroll"), | ||
266 | 807 | desc="resize_on_scroll")) | ||
267 | 798 | self.add_option(BoolOption('Screenlet', 'ignore_requirements', | 808 | self.add_option(BoolOption('Screenlet', 'ignore_requirements', |
268 | 799 | self.ignore_requirements, _('Ignore requirements'), | 809 | self.ignore_requirements, _('Ignore requirements'), |
269 | 800 | _('Set this Screenlet to ignore/demand DEB requirements ...'))) | 810 | _('Set this Screenlet to ignore/demand DEB requirements ...'))) |
270 | 801 | if uses_theme: | 811 | if uses_theme: |
271 | 802 | self.ask_on_option_override = ask_on_option_override | 812 | self.ask_on_option_override = ask_on_option_override |
278 | 803 | self.add_option(BoolOption('Screenlet', 'allow_option_override', | 813 | self.add_option(BoolOption('Screenlet', 'allow_option_override', |
279 | 804 | self.allow_option_override, _('Allow overriding Options'), | 814 | default=self.allow_option_override, label=_('Allow overriding Options'), |
280 | 805 | _('Allow themes to override options in this screenlet ...'))) | 815 | desc=_('Allow themes to override options in this screenlet ...'))) |
281 | 806 | self.add_option(BoolOption('Screenlet', 'ask_on_option_override', | 816 | self.add_option(BoolOption('Screenlet', 'ask_on_option_override', |
282 | 807 | self.ask_on_option_override, _('Ask on Override'), | 817 | default=self.ask_on_option_override, label=_('Ask on Override'), |
283 | 808 | _('Show a confirmation-dialog when a theme wants to override ')+\ | 818 | desc=_('Show a confirmation-dialog when a theme wants to override ')+\ |
284 | 809 | _('the current options of this Screenlet ...'))) | 819 | _('the current options of this Screenlet ...'))) |
285 | 810 | # disable width/height | 820 | # disable width/height |
286 | 811 | self.disable_option('width') | 821 | self.disable_option('width') |
287 | @@ -959,7 +969,7 @@ | |||
288 | 959 | if self.window.window: | 969 | if self.window.window: |
289 | 960 | self.window.window.set_skip_taskbar_hint(bool(value)) | 970 | self.window.window.set_skip_taskbar_hint(bool(value)) |
290 | 961 | # NOTE: This is the new recommended way of storing options in real-time | 971 | # NOTE: This is the new recommended way of storing options in real-time |
292 | 962 | # (we access the backend through the session here) | 972 | # (we access the backend through the session here) |
293 | 963 | if self.saving_enabled: | 973 | if self.saving_enabled: |
294 | 964 | o = self.get_option_by_name(name) | 974 | o = self.get_option_by_name(name) |
295 | 965 | if o != None: | 975 | if o != None: |
296 | @@ -2084,35 +2094,35 @@ | |||
297 | 2084 | 2094 | ||
298 | 2085 | 2095 | ||
299 | 2086 | def show_notification (self,text): | 2096 | def show_notification (self,text): |
301 | 2087 | """Show notification window at current mouse position.""" | 2097 | """Show notification window at current mouse position.""" |
302 | 2088 | if self.notify == None: | 2098 | if self.notify == None: |
306 | 2089 | self.notify = Notify() | 2099 | self.notify = Notify() |
307 | 2090 | self.notify.text = text | 2100 | self.notify.text = text |
308 | 2091 | self.notify.show() | 2101 | self.notify.show() |
309 | 2092 | 2102 | ||
310 | 2093 | def hide_notification (self): | 2103 | def hide_notification (self): |
312 | 2094 | """hide notification window""" | 2104 | """hide notification window""" |
313 | 2095 | if self.notify != None: | 2105 | if self.notify != None: |
314 | 2096 | self.notify.hide() | 2106 | self.notify.hide() |
315 | 2097 | self.notify = None | 2107 | self.notify = None |
316 | 2098 | 2108 | ||
317 | 2099 | def show_tooltip (self,text,tooltipx,tooltipy): | 2109 | def show_tooltip (self,text,tooltipx,tooltipy): |
319 | 2100 | """Show tooltip window at current mouse position.""" | 2110 | """Show tooltip window at current mouse position.""" |
320 | 2101 | if self.tooltip == None: | 2111 | if self.tooltip == None: |
325 | 2102 | self.tooltip = Tooltip(300, 400) | 2112 | self.tooltip = Tooltip(300, 400) |
326 | 2103 | self.tooltip.text = text | 2113 | self.tooltip.text = text |
327 | 2104 | self.tooltip.x = tooltipx | 2114 | self.tooltip.x = tooltipx |
328 | 2105 | self.tooltip.y = tooltipy | 2115 | self.tooltip.y = tooltipy |
329 | 2106 | self.tooltip.show() | 2116 | self.tooltip.show() |
330 | 2107 | else: | 2117 | else: |
335 | 2108 | #self.tooltip = Tooltip(300, 400) | 2118 | #self.tooltip = Tooltip(300, 400) |
336 | 2109 | self.tooltip.text = text | 2119 | self.tooltip.text = text |
337 | 2110 | self.tooltip.x = tooltipx | 2120 | self.tooltip.x = tooltipx |
338 | 2111 | self.tooltip.y = tooltipy | 2121 | self.tooltip.y = tooltipy |
339 | 2112 | #self.tooltip.show() | 2122 | #self.tooltip.show() |
340 | 2113 | 2123 | ||
341 | 2114 | def hide_tooltip (self): | 2124 | def hide_tooltip (self): |
343 | 2115 | """hide tooltip window""" | 2125 | """hide tooltip window""" |
344 | 2116 | if self.tooltip != None: | 2126 | if self.tooltip != None: |
345 | 2117 | self.tooltip.hide() | 2127 | self.tooltip.hide() |
346 | 2118 | self.tooltip = None | 2128 | self.tooltip = None |
347 | @@ -2204,21 +2214,21 @@ | |||
348 | 2204 | """A window that displays a text and serves as Tooltip (very basic yet).""" | 2214 | """A window that displays a text and serves as Tooltip (very basic yet).""" |
349 | 2205 | 2215 | ||
350 | 2206 | # internals | 2216 | # internals |
353 | 2207 | __timeout = None | 2217 | __timeout = None |
354 | 2208 | 2218 | ||
355 | 2209 | # attribs | 2219 | # attribs |
363 | 2210 | text = '' | 2220 | text = '' |
364 | 2211 | font_name = 'FreeSans 9' | 2221 | font_name = 'FreeSans 9' |
365 | 2212 | width = 100 | 2222 | width = 100 |
366 | 2213 | height = 20 | 2223 | height = 20 |
367 | 2214 | x = 0 | 2224 | x = 0 |
368 | 2215 | y = 0 | 2225 | y = 0 |
369 | 2216 | 2226 | ||
370 | 2217 | def __init__ (self, width, height): | 2227 | def __init__ (self, width, height): |
371 | 2218 | object.__init__(self) | 2228 | object.__init__(self) |
372 | 2219 | # init | 2229 | # init |
375 | 2220 | self.__dict__['width'] = width | 2230 | self.__dict__['width'] = width |
376 | 2221 | self.__dict__['height'] = height | 2231 | self.__dict__['height'] = height |
377 | 2222 | self.window = gtk.Window() | 2232 | self.window = gtk.Window() |
378 | 2223 | self.window.set_app_paintable(True) | 2233 | self.window.set_app_paintable(True) |
379 | 2224 | self.window.set_size_request(width, height) | 2234 | self.window.set_size_request(width, height) |
380 | @@ -2237,7 +2247,7 @@ | |||
381 | 2237 | pango.FontDescription(self.font_name)) | 2247 | pango.FontDescription(self.font_name)) |
382 | 2238 | #self.p_layout.set_width(-1) | 2248 | #self.p_layout.set_width(-1) |
383 | 2239 | self.p_layout.set_width(width * pango.SCALE - 6) | 2249 | self.p_layout.set_width(width * pango.SCALE - 6) |
385 | 2240 | 2250 | ||
386 | 2241 | def __setattr__ (self, name, value): | 2251 | def __setattr__ (self, name, value): |
387 | 2242 | self.__dict__[name] = value | 2252 | self.__dict__[name] = value |
388 | 2243 | if name in ('width', 'height', 'text'): | 2253 | if name in ('width', 'height', 'text'): |
389 | @@ -2253,7 +2263,7 @@ | |||
390 | 2253 | self.window.move(int(value), int(self.y)) | 2263 | self.window.move(int(value), int(self.y)) |
391 | 2254 | elif name == 'y': | 2264 | elif name == 'y': |
392 | 2255 | self.window.move(int(self.x), int(value)) | 2265 | self.window.move(int(self.x), int(value)) |
394 | 2256 | 2266 | ||
395 | 2257 | def show (self): | 2267 | def show (self): |
396 | 2258 | """Show the Tooltip window.""" | 2268 | """Show the Tooltip window.""" |
397 | 2259 | self.cancel_show() | 2269 | self.cancel_show() |
398 | @@ -2264,22 +2274,22 @@ | |||
399 | 2264 | """Show the Tooltip window after a given delay.""" | 2274 | """Show the Tooltip window after a given delay.""" |
400 | 2265 | self.cancel_show() | 2275 | self.cancel_show() |
401 | 2266 | self.__timeout = gobject.timeout_add(delay, self.__show_timeout) | 2276 | self.__timeout = gobject.timeout_add(delay, self.__show_timeout) |
403 | 2267 | 2277 | ||
404 | 2268 | def hide (self): | 2278 | def hide (self): |
405 | 2269 | """Hide the Tooltip window.""" | 2279 | """Hide the Tooltip window.""" |
406 | 2270 | self.cancel_show() | 2280 | self.cancel_show() |
407 | 2271 | self.window.destroy() | 2281 | self.window.destroy() |
409 | 2272 | 2282 | ||
410 | 2273 | def cancel_show (self): | 2283 | def cancel_show (self): |
411 | 2274 | """Cancel showing of the Tooltip.""" | 2284 | """Cancel showing of the Tooltip.""" |
412 | 2275 | if self.__timeout: | 2285 | if self.__timeout: |
413 | 2276 | gobject.source_remove(self.__timeout) | 2286 | gobject.source_remove(self.__timeout) |
414 | 2277 | self.p_context = None | 2287 | self.p_context = None |
415 | 2278 | self.p_layout = None | 2288 | self.p_layout = None |
417 | 2279 | 2289 | ||
418 | 2280 | def __show_timeout (self): | 2290 | def __show_timeout (self): |
419 | 2281 | self.show() | 2291 | self.show() |
421 | 2282 | 2292 | ||
422 | 2283 | def screen_changed (self, window, screen=None): | 2293 | def screen_changed (self, window, screen=None): |
423 | 2284 | if screen == None: | 2294 | if screen == None: |
424 | 2285 | screen = window.get_screen() | 2295 | screen = window.get_screen() |
425 | @@ -2287,10 +2297,10 @@ | |||
426 | 2287 | if not map: | 2297 | if not map: |
427 | 2288 | map = screen.get_rgb_colormap() | 2298 | map = screen.get_rgb_colormap() |
428 | 2289 | window.set_colormap(map) | 2299 | window.set_colormap(map) |
430 | 2290 | 2300 | ||
431 | 2291 | def expose (self, widget, event): | 2301 | def expose (self, widget, event): |
432 | 2292 | ctx = self.window.window.cairo_create() | 2302 | ctx = self.window.window.cairo_create() |
434 | 2293 | ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ? | 2303 | ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ? |
435 | 2294 | # set a clip region for the expose event | 2304 | # set a clip region for the expose event |
436 | 2295 | ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height) | 2305 | ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height) |
437 | 2296 | ctx.clip() | 2306 | ctx.clip() |
438 | @@ -2317,17 +2327,17 @@ | |||
439 | 2317 | """A window that displays a text and serves as Notification (very basic yet).""" | 2327 | """A window that displays a text and serves as Notification (very basic yet).""" |
440 | 2318 | 2328 | ||
441 | 2319 | # internals | 2329 | # internals |
444 | 2320 | __timeout = None | 2330 | __timeout = None |
445 | 2321 | 2331 | ||
446 | 2322 | # attribs | 2332 | # attribs |
453 | 2323 | text = '' | 2333 | text = '' |
454 | 2324 | font_name = 'FreeSans 9' | 2334 | font_name = 'FreeSans 9' |
455 | 2325 | width = 200 | 2335 | width = 200 |
456 | 2326 | height = 100 | 2336 | height = 100 |
457 | 2327 | x = 0 | 2337 | x = 0 |
458 | 2328 | y = 0 | 2338 | y = 0 |
459 | 2329 | gradient = cairo.LinearGradient(0, 100,0, 0) | 2339 | gradient = cairo.LinearGradient(0, 100,0, 0) |
461 | 2330 | 2340 | ||
462 | 2331 | def __init__ (self): | 2341 | def __init__ (self): |
463 | 2332 | object.__init__(self) | 2342 | object.__init__(self) |
464 | 2333 | # init | 2343 | # init |
465 | @@ -2349,7 +2359,7 @@ | |||
466 | 2349 | pango.FontDescription(self.font_name)) | 2359 | pango.FontDescription(self.font_name)) |
467 | 2350 | #self.p_layout.set_width(-1) | 2360 | #self.p_layout.set_width(-1) |
468 | 2351 | self.p_layout.set_width(self.width * pango.SCALE - 6) | 2361 | self.p_layout.set_width(self.width * pango.SCALE - 6) |
470 | 2352 | 2362 | ||
471 | 2353 | def __setattr__ (self, name, value): | 2363 | def __setattr__ (self, name, value): |
472 | 2354 | self.__dict__[name] = value | 2364 | self.__dict__[name] = value |
473 | 2355 | if name in ('text'): | 2365 | if name in ('text'): |
474 | @@ -2369,22 +2379,22 @@ | |||
475 | 2369 | """Show the Notify window after a given delay.""" | 2379 | """Show the Notify window after a given delay.""" |
476 | 2370 | self.cancel_show() | 2380 | self.cancel_show() |
477 | 2371 | self.__timeout = gobject.timeout_add(delay, self.__show_timeout) | 2381 | self.__timeout = gobject.timeout_add(delay, self.__show_timeout) |
479 | 2372 | 2382 | ||
480 | 2373 | def hide (self): | 2383 | def hide (self): |
481 | 2374 | """Hide the Notify window.""" | 2384 | """Hide the Notify window.""" |
482 | 2375 | self.cancel_show() | 2385 | self.cancel_show() |
483 | 2376 | self.window.destroy() | 2386 | self.window.destroy() |
485 | 2377 | 2387 | ||
486 | 2378 | def cancel_show (self): | 2388 | def cancel_show (self): |
487 | 2379 | """Cancel showing of the Notify.""" | 2389 | """Cancel showing of the Notify.""" |
488 | 2380 | if self.__timeout: | 2390 | if self.__timeout: |
489 | 2381 | gobject.source_remove(self.__timeout) | 2391 | gobject.source_remove(self.__timeout) |
490 | 2382 | self.p_context = None | 2392 | self.p_context = None |
491 | 2383 | self.p_layout = None | 2393 | self.p_layout = None |
493 | 2384 | 2394 | ||
494 | 2385 | def __show_timeout (self): | 2395 | def __show_timeout (self): |
495 | 2386 | self.show() | 2396 | self.show() |
497 | 2387 | 2397 | ||
498 | 2388 | def screen_changed (self, window, screen=None): | 2398 | def screen_changed (self, window, screen=None): |
499 | 2389 | if screen == None: | 2399 | if screen == None: |
500 | 2390 | screen = window.get_screen() | 2400 | screen = window.get_screen() |
501 | @@ -2392,10 +2402,10 @@ | |||
502 | 2392 | if not map: | 2402 | if not map: |
503 | 2393 | map = screen.get_rgb_colormap() | 2403 | map = screen.get_rgb_colormap() |
504 | 2394 | window.set_colormap(map) | 2404 | window.set_colormap(map) |
506 | 2395 | 2405 | ||
507 | 2396 | def expose (self, widget, event): | 2406 | def expose (self, widget, event): |
508 | 2397 | ctx = self.window.window.cairo_create() | 2407 | ctx = self.window.window.cairo_create() |
510 | 2398 | ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ? | 2408 | ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ? |
511 | 2399 | # set a clip region for the expose event | 2409 | # set a clip region for the expose event |
512 | 2400 | ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height) | 2410 | ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height) |
513 | 2401 | ctx.clip() | 2411 | ctx.clip() |
514 | 2402 | 2412 | ||
515 | === added directory 'src/lib/options' | |||
516 | === removed file 'src/lib/options.py' | |||
517 | --- src/lib/options.py 2011-02-20 05:06:16 +0000 | |||
518 | +++ src/lib/options.py 1970-01-01 00:00:00 +0000 | |||
519 | @@ -1,1432 +0,0 @@ | |||
520 | 1 | # This application is released under the GNU General Public License | ||
521 | 2 | # v3 (or, at your option, any later version). You can find the full | ||
522 | 3 | # text of the license under http://www.gnu.org/licenses/gpl.txt. | ||
523 | 4 | # By using, editing and/or distributing this software you agree to | ||
524 | 5 | # the terms and conditions of this license. | ||
525 | 6 | # Thank you for using free software! | ||
526 | 7 | |||
527 | 8 | # Options-system (c) RYX (aka Rico Pfaus) 2007 <ryx@ryxperience.com> | ||
528 | 9 | # | ||
529 | 10 | # INFO: | ||
530 | 11 | # - a dynamic Options-system that allows very easy creation of | ||
531 | 12 | # objects with embedded configuration-system. | ||
532 | 13 | # NOTE: The Dialog is not very nice yet - it is not good OOP-practice | ||
533 | 14 | # because too big functions and bad class-layout ... but it works | ||
534 | 15 | # for now ... :) | ||
535 | 16 | # | ||
536 | 17 | # TODO: | ||
537 | 18 | # - option-widgets for all option-types (e.g. ListOptionWidget, ColorOptionWidget) | ||
538 | 19 | # - OptionGroup-class instead of (or behind) add_options_group | ||
539 | 20 | # - TimeOption, DateOption | ||
540 | 21 | # - FileOption needs filter/limit-attribute | ||
541 | 22 | # - allow options to disable/enable other options | ||
542 | 23 | # - support for EditableOptions-subclasses as options | ||
543 | 24 | # - separate OptionEditorWidget from Editor-Dialog | ||
544 | 25 | # - place ui-code into screenlets.options.ui-module | ||
545 | 26 | # - create own widgets for each Option-subclass | ||
546 | 27 | # | ||
547 | 28 | |||
548 | 29 | import screenlets | ||
549 | 30 | import utils | ||
550 | 31 | |||
551 | 32 | import os | ||
552 | 33 | import gtk, gobject | ||
553 | 34 | import xml.dom.minidom | ||
554 | 35 | from xml.dom.minidom import Node | ||
555 | 36 | |||
556 | 37 | # translation stuff | ||
557 | 38 | import gettext | ||
558 | 39 | gettext.textdomain('screenlets') | ||
559 | 40 | gettext.bindtextdomain('screenlets', screenlets.INSTALL_PREFIX + '/share/locale') | ||
560 | 41 | |||
561 | 42 | def _(s): | ||
562 | 43 | return gettext.gettext(s) | ||
563 | 44 | |||
564 | 45 | # ----------------------------------------------------------------------- | ||
565 | 46 | # Option-classes and subclasses | ||
566 | 47 | # ----------------------------------------------------------------------- | ||
567 | 48 | |||
568 | 49 | class Option(gobject.GObject): | ||
569 | 50 | """An Option stores information about a certain object-attribute. It doesn't | ||
570 | 51 | carry information about the value or the object it belongs to - it is only a | ||
571 | 52 | one-way data-storage for describing how to handle attributes.""" | ||
572 | 53 | |||
573 | 54 | __gsignals__ = dict(option_changed=(gobject.SIGNAL_RUN_FIRST, | ||
574 | 55 | gobject.TYPE_NONE, (gobject.TYPE_OBJECT,))) | ||
575 | 56 | |||
576 | 57 | def __init__ (self, group, name, default, label, desc, | ||
577 | 58 | disabled=False, hidden=False, callback=None, protected=False): | ||
578 | 59 | """Creates a new Option with the given information.""" | ||
579 | 60 | super(Option, self).__init__() | ||
580 | 61 | self.name = name | ||
581 | 62 | self.label = label | ||
582 | 63 | self.desc = desc | ||
583 | 64 | self.default = default | ||
584 | 65 | self.disabled = disabled | ||
585 | 66 | self.hidden = hidden | ||
586 | 67 | # for groups (TODO: OptionGroup) | ||
587 | 68 | self.group= group | ||
588 | 69 | # callback to be notified when this option changes | ||
589 | 70 | self.callback = callback | ||
590 | 71 | # real-time update? | ||
591 | 72 | self.realtime = True | ||
592 | 73 | # protected from get/set through service | ||
593 | 74 | self.protected = protected | ||
594 | 75 | |||
595 | 76 | def on_import (self, strvalue): | ||
596 | 77 | """Callback - called when an option gets imported from a string. | ||
597 | 78 | This function MUST return the string-value converted to the required | ||
598 | 79 | type!""" | ||
599 | 80 | return strvalue.replace("\\n", "\n") | ||
600 | 81 | |||
601 | 82 | def on_export (self, value): | ||
602 | 83 | """Callback - called when an option gets exported to a string. The | ||
603 | 84 | value-argument needs to be converted to a string that can be imported | ||
604 | 85 | by the on_import-handler. This handler MUST return the value | ||
605 | 86 | converted to a string!""" | ||
606 | 87 | return str(value).replace("\n", "\\n") | ||
607 | 88 | |||
608 | 89 | |||
609 | 90 | class FileOption (Option): | ||
610 | 91 | """An Option-subclass for string-values that contain filenames. Adds | ||
611 | 92 | a patterns-attribute that can contain a list of patterns to be shown | ||
612 | 93 | in the assigned file selection dialog. The show_pixmaps-attribute | ||
613 | 94 | can be set to True to make the filedialog show all image-types | ||
614 | 95 | supported by gtk.Pixmap. If the directory-attributue is true, the | ||
615 | 96 | dialog will ony allow directories.""" | ||
616 | 97 | |||
617 | 98 | def __init__ (self, group, name, default, label, desc, | ||
618 | 99 | patterns=['*'], image=False, directory=False, **keyword_args): | ||
619 | 100 | Option.__init__(self, group, name, default,label, desc, **keyword_args) | ||
620 | 101 | self.patterns = patterns | ||
621 | 102 | self.image = image | ||
622 | 103 | self.directory = False | ||
623 | 104 | |||
624 | 105 | |||
625 | 106 | class ImageOption (Option): | ||
626 | 107 | """An Option-subclass for string-values that contain filenames of | ||
627 | 108 | image-files.""" | ||
628 | 109 | |||
629 | 110 | |||
630 | 111 | class DirectoryOption (Option): | ||
631 | 112 | """An Option-subclass for filename-strings that contain directories.""" | ||
632 | 113 | |||
633 | 114 | |||
634 | 115 | class BoolOption (Option): | ||
635 | 116 | """An Option for boolean values.""" | ||
636 | 117 | |||
637 | 118 | def on_import (self, strvalue): | ||
638 | 119 | if strvalue == "True": | ||
639 | 120 | return True | ||
640 | 121 | return False | ||
641 | 122 | |||
642 | 123 | |||
643 | 124 | class StringOption (Option): | ||
644 | 125 | """An Option for values of type string.""" | ||
645 | 126 | |||
646 | 127 | def __init__ (self, group, name, default, label, desc, | ||
647 | 128 | choices=None, password=False, **keyword_args): | ||
648 | 129 | Option.__init__(self, group, name, default,label, desc, **keyword_args) | ||
649 | 130 | self.choices = choices | ||
650 | 131 | self.password = password | ||
651 | 132 | |||
652 | 133 | |||
653 | 134 | class IntOption (Option): | ||
654 | 135 | """An Option for values of type number (can be int or float).""" | ||
655 | 136 | |||
656 | 137 | def __init__ (self, group, name, default, label, desc, min=-100000, max=100000, | ||
657 | 138 | increment=1, **keyword_args): | ||
658 | 139 | Option.__init__(self, group, name, default, label, desc, **keyword_args) | ||
659 | 140 | self.min = min | ||
660 | 141 | self.max = max | ||
661 | 142 | self.increment = increment | ||
662 | 143 | |||
663 | 144 | def on_import (self, strvalue): | ||
664 | 145 | """Called when IntOption gets imported. Converts str to int.""" | ||
665 | 146 | try: | ||
666 | 147 | if strvalue[0]=='-': | ||
667 | 148 | return int(strvalue[1:]) * -1 | ||
668 | 149 | return int(strvalue) | ||
669 | 150 | except: | ||
670 | 151 | print "Error during on_import - option: %s." % self.name | ||
671 | 152 | return 0 | ||
672 | 153 | |||
673 | 154 | |||
674 | 155 | class FloatOption (IntOption): | ||
675 | 156 | """An Option for values of type float.""" | ||
676 | 157 | |||
677 | 158 | def __init__ (self, group, name, default, label, desc, digits=1, | ||
678 | 159 | **keyword_args): | ||
679 | 160 | IntOption.__init__(self, group, name, default, label, desc, | ||
680 | 161 | **keyword_args) | ||
681 | 162 | self.digits = digits | ||
682 | 163 | |||
683 | 164 | def on_import (self, strvalue): | ||
684 | 165 | """Called when FloatOption gets imported. Converts str to float.""" | ||
685 | 166 | if strvalue[0]=='-': | ||
686 | 167 | return float(strvalue[1:]) * -1.0 | ||
687 | 168 | return float(strvalue) | ||
688 | 169 | |||
689 | 170 | |||
690 | 171 | class FontOption (Option): | ||
691 | 172 | """An Option for fonts (a simple StringOption).""" | ||
692 | 173 | |||
693 | 174 | |||
694 | 175 | class ColorOption (Option): | ||
695 | 176 | """An Option for colors. Stored as a list with 4 values (r, g, b, a).""" | ||
696 | 177 | |||
697 | 178 | def on_import (self, strvalue): | ||
698 | 179 | """Import (r, g, b, a) from comma-separated string.""" | ||
699 | 180 | # strip braces and spaces | ||
700 | 181 | strvalue = strvalue.lstrip('(') | ||
701 | 182 | strvalue = strvalue.rstrip(')') | ||
702 | 183 | strvalue = strvalue.strip() | ||
703 | 184 | # split value on commas | ||
704 | 185 | tmpval = strvalue.split(',') | ||
705 | 186 | outval = [] | ||
706 | 187 | for f in tmpval: | ||
707 | 188 | # create list and again remove spaces | ||
708 | 189 | outval.append(float(f.strip())) | ||
709 | 190 | return outval | ||
710 | 191 | |||
711 | 192 | def on_export (self, value): | ||
712 | 193 | """Export r, g, b, a to comma-separated string.""" | ||
713 | 194 | l = len(value) | ||
714 | 195 | outval = '' | ||
715 | 196 | for i in xrange(l): | ||
716 | 197 | outval += str(value[i]) | ||
717 | 198 | if i < l-1: | ||
718 | 199 | outval += ',' | ||
719 | 200 | return outval | ||
720 | 201 | |||
721 | 202 | |||
722 | 203 | class ListOption (Option): | ||
723 | 204 | """An Option-type for list of strings.""" | ||
724 | 205 | |||
725 | 206 | def on_import (self, strvalue): | ||
726 | 207 | """Import python-style list from a string (like [1, 2, 'test'])""" | ||
727 | 208 | lst = eval(strvalue) | ||
728 | 209 | return lst | ||
729 | 210 | |||
730 | 211 | def on_export (self, value): | ||
731 | 212 | """Export list as string.""" | ||
732 | 213 | return str(value) | ||
733 | 214 | |||
734 | 215 | |||
735 | 216 | import gnomekeyring | ||
736 | 217 | class AccountOption (Option): | ||
737 | 218 | """An Option-type for username/password combos. Stores the password in | ||
738 | 219 | the gnome-keyring (if available) and only saves username and auth_token | ||
739 | 220 | through the screenlets-backend. | ||
740 | 221 | TODO: | ||
741 | 222 | - not create new token for any change (use "set" instead of "create" if | ||
742 | 223 | the given item already exists) | ||
743 | 224 | - use usual storage if no keyring is available but output warning | ||
744 | 225 | - on_delete-function for removing the data from keyring when the | ||
745 | 226 | Screenlet holding the option gets deleted""" | ||
746 | 227 | |||
747 | 228 | def __init__ (self, group, name, default, label, desc, **keyword_args): | ||
748 | 229 | Option.__init__ (self, group, name, default, label, desc, | ||
749 | 230 | protected=True, **keyword_args) | ||
750 | 231 | # check for availability of keyring | ||
751 | 232 | if not gnomekeyring.is_available(): | ||
752 | 233 | raise Exception('GnomeKeyring is not available!!') # TEMP!!! | ||
753 | 234 | # THIS IS A WORKAROUND FOR A BUG IN KEYRING (usually we would use | ||
754 | 235 | # gnomekeyring.get_default_keyring_sync() here): | ||
755 | 236 | # find first available keyring | ||
756 | 237 | self.keyring_list = gnomekeyring.list_keyring_names_sync() | ||
757 | 238 | if len(self.keyring_list) == 0: | ||
758 | 239 | raise Exception('No keyrings found. Please create one first!') | ||
759 | 240 | else: | ||
760 | 241 | # we prefer the default keyring | ||
761 | 242 | try: | ||
762 | 243 | self.keyring = gnomekeyring.get_default_keyring_sync() | ||
763 | 244 | except: | ||
764 | 245 | if "session" in self.keyring_list: | ||
765 | 246 | print "Warning: No default keyring found, using session keyring. Storage is not permanent!" | ||
766 | 247 | self.keyring = "session" | ||
767 | 248 | else: | ||
768 | 249 | print "Warning: Neither default nor session keyring found, assuming keyring %s!" % self.keyring_list[0] | ||
769 | 250 | self.keyring = self.keyring_list[0] | ||
770 | 251 | |||
771 | 252 | |||
772 | 253 | def on_import (self, strvalue): | ||
773 | 254 | """Import account info from a string (like 'username:auth_token'), | ||
774 | 255 | retrieve the password from the storage and return a tuple containing | ||
775 | 256 | username and password.""" | ||
776 | 257 | # split string into username/auth_token | ||
777 | 258 | #data = strvalue.split(':', 1) | ||
778 | 259 | (name, auth_token) = strvalue.split(':', 1) | ||
779 | 260 | if name and auth_token: | ||
780 | 261 | # read pass from storage | ||
781 | 262 | try: | ||
782 | 263 | pw = gnomekeyring.item_get_info_sync(self.keyring, | ||
783 | 264 | int(auth_token)).get_secret() | ||
784 | 265 | except Exception, ex: | ||
785 | 266 | print "ERROR: Unable to read password from keyring: %s" % ex | ||
786 | 267 | pw = '' | ||
787 | 268 | # return | ||
788 | 269 | return (name, pw) | ||
789 | 270 | else: | ||
790 | 271 | raise Exception('Illegal value in AccountOption.on_import.') | ||
791 | 272 | |||
792 | 273 | def on_export (self, value): | ||
793 | 274 | """Export the given tuple/list containing a username and a password. The | ||
794 | 275 | function stores the password in the gnomekeyring and returns a | ||
795 | 276 | string in form 'username:auth_token'.""" | ||
796 | 277 | # store password in storage | ||
797 | 278 | attribs = dict(name=value[0]) | ||
798 | 279 | auth_token = gnomekeyring.item_create_sync(self.keyring, | ||
799 | 280 | gnomekeyring.ITEM_GENERIC_SECRET, value[0], attribs, value[1], True) | ||
800 | 281 | # build value from username and auth_token | ||
801 | 282 | return value[0] + ':' + str(auth_token) | ||
802 | 283 | |||
803 | 284 | """#TEST: | ||
804 | 285 | o = AccountOption('None', 'pop3_account', ('',''), 'Username/Password', 'Enter username/password here ...') | ||
805 | 286 | # save option to keyring | ||
806 | 287 | exported_account = o.on_export(('RYX', 'mysecretpassword')) | ||
807 | 288 | print exported_account | ||
808 | 289 | # and read option back from keyring | ||
809 | 290 | print o.on_import(exported_account) | ||
810 | 291 | |||
811 | 292 | |||
812 | 293 | import sys | ||
813 | 294 | sys.exit(0)""" | ||
814 | 295 | |||
815 | 296 | class TimeOption (ColorOption): | ||
816 | 297 | """An Option-subclass for string-values that contain dates.""" | ||
817 | 298 | |||
818 | 299 | |||
819 | 300 | # ----------------------------------------------------------------------- | ||
820 | 301 | # EditableOptions-class and needed functions | ||
821 | 302 | # ----------------------------------------------------------------------- | ||
822 | 303 | |||
823 | 304 | def create_option_from_node (node, groupname): | ||
824 | 305 | """Create an Option from an XML-node with option-metadata.""" | ||
825 | 306 | #print "TODO OPTION: " + str(cn) | ||
826 | 307 | otype = node.getAttribute("type") | ||
827 | 308 | oname = node.getAttribute("name") | ||
828 | 309 | ohidden = node.getAttribute("hidden") | ||
829 | 310 | odefault = None | ||
830 | 311 | oinfo = '' | ||
831 | 312 | olabel = '' | ||
832 | 313 | omin = None | ||
833 | 314 | omax = None | ||
834 | 315 | oincrement = 1 | ||
835 | 316 | ochoices = '' | ||
836 | 317 | odigits = None | ||
837 | 318 | if otype and oname: | ||
838 | 319 | # parse children of option-node and save all useful attributes | ||
839 | 320 | for attr in node.childNodes: | ||
840 | 321 | if attr.nodeType == Node.ELEMENT_NODE: | ||
841 | 322 | if attr.nodeName == 'label': | ||
842 | 323 | olabel = attr.firstChild.nodeValue | ||
843 | 324 | elif attr.nodeName == 'info': | ||
844 | 325 | oinfo = attr.firstChild.nodeValue | ||
845 | 326 | elif attr.nodeName == 'default': | ||
846 | 327 | odefault = attr.firstChild.nodeValue | ||
847 | 328 | elif attr.nodeName == 'min': | ||
848 | 329 | omin = attr.firstChild.nodeValue | ||
849 | 330 | elif attr.nodeName == 'max': | ||
850 | 331 | omax = attr.firstChild.nodeValue | ||
851 | 332 | elif attr.nodeName == 'increment': | ||
852 | 333 | oincrement = attr.firstChild.nodeValue | ||
853 | 334 | elif attr.nodeName == 'choices': | ||
854 | 335 | ochoices = attr.firstChild.nodeValue | ||
855 | 336 | elif attr.nodeName == 'digits': | ||
856 | 337 | odigits = attr.firstChild.nodeValue | ||
857 | 338 | # if we have all needed values, create the Option | ||
858 | 339 | if odefault: | ||
859 | 340 | # create correct classname here | ||
860 | 341 | cls = otype[0].upper() + otype.lower()[1:] + 'Option' | ||
861 | 342 | #print 'Create: ' +cls +' / ' + oname + ' ('+otype+')' | ||
862 | 343 | # and build new instance (we use on_import for setting default val) | ||
863 | 344 | clsobj = getattr(__import__(__name__), cls) | ||
864 | 345 | opt = clsobj(groupname, oname, None, olabel, oinfo) | ||
865 | 346 | opt.default = opt.on_import(odefault) | ||
866 | 347 | # set values to the correct types | ||
867 | 348 | if cls == 'IntOption': | ||
868 | 349 | if omin: | ||
869 | 350 | opt.min = int(omin) | ||
870 | 351 | if omax: | ||
871 | 352 | opt.max = int(omax) | ||
872 | 353 | if oincrement: | ||
873 | 354 | opt.increment = int(oincrement) | ||
874 | 355 | elif cls == 'FloatOption': | ||
875 | 356 | if odigits: | ||
876 | 357 | opt.digits = int(odigits) | ||
877 | 358 | if omin: | ||
878 | 359 | opt.min = float(omin) | ||
879 | 360 | if omax: | ||
880 | 361 | opt.max = float(omax) | ||
881 | 362 | if oincrement: | ||
882 | 363 | opt.increment = float(oincrement) | ||
883 | 364 | elif cls == 'StringOption': | ||
884 | 365 | if ochoices: | ||
885 | 366 | opt.choices = ochoices | ||
886 | 367 | return opt | ||
887 | 368 | return None | ||
888 | 369 | |||
889 | 370 | |||
890 | 371 | class EditableOptions(object): | ||
891 | 372 | """The EditableOptions can be inherited from to allow objects to export | ||
892 | 373 | editable options for editing them with the OptionsEditor-class. | ||
893 | 374 | NOTE: This could use some improvement and is very poorly coded :) ...""" | ||
894 | 375 | |||
895 | 376 | def __init__ (self): | ||
896 | 377 | self.__options__ = [] | ||
897 | 378 | self.__options_groups__ = {} | ||
898 | 379 | # This is a workaround to remember the order of groups | ||
899 | 380 | self.__options_groups_ordered__ = [] | ||
900 | 381 | |||
901 | 382 | def add_option (self, option, callback=None, realtime=True): | ||
902 | 383 | """Add an editable option to this object. Editable Options can be edited | ||
903 | 384 | and configured using the OptionsDialog. The optional callback-arg can be | ||
904 | 385 | used to set a callback that gets notified when the option changes its | ||
905 | 386 | value.""" | ||
906 | 387 | #print "Add option: "+option.name | ||
907 | 388 | # if option already editable (i.e. initialized), return | ||
908 | 389 | for o in self.__options__: | ||
909 | 390 | if o.name == option.name: | ||
910 | 391 | return False | ||
911 | 392 | self.__dict__[option.name] = option.default | ||
912 | 393 | # set auto-update (TEMPORARY?) | ||
913 | 394 | option.realtime = realtime | ||
914 | 395 | # add option to group (output error if group is undefined) | ||
915 | 396 | try: | ||
916 | 397 | self.__options_groups__[option.group]['options'].append(option) | ||
917 | 398 | except: | ||
918 | 399 | print "Options: Error - group %s not defined." % option.group | ||
919 | 400 | return False | ||
920 | 401 | # now add the option | ||
921 | 402 | self.__options__.append(option) | ||
922 | 403 | # if callback is set, add callback | ||
923 | 404 | if callback: | ||
924 | 405 | option.connect("option_changed", callback) | ||
925 | 406 | return True | ||
926 | 407 | |||
927 | 408 | |||
928 | 409 | def add_options_group (self, name, group_info): | ||
929 | 410 | """Add a new options-group to this Options-object""" | ||
930 | 411 | self.__options_groups__[name] = {'label':name, | ||
931 | 412 | 'info':group_info, 'options':[]} | ||
932 | 413 | self.__options_groups_ordered__.append(name) | ||
933 | 414 | #print self.options_groups | ||
934 | 415 | |||
935 | 416 | def disable_option (self, name): | ||
936 | 417 | """Disable the inputs for a certain Option.""" | ||
937 | 418 | for o in self.__options__: | ||
938 | 419 | if o.name == name: | ||
939 | 420 | o.disabled = True | ||
940 | 421 | return True | ||
941 | 422 | return False | ||
942 | 423 | |||
943 | 424 | def enable_option(self, name): | ||
944 | 425 | """Enable the inputs for a certain Option.""" | ||
945 | 426 | for o in self.__options__: | ||
946 | 427 | if o.name == name: | ||
947 | 428 | o.disabled = False | ||
948 | 429 | return True | ||
949 | 430 | return False | ||
950 | 431 | |||
951 | 432 | def export_options_as_list (self): | ||
952 | 433 | """Returns all editable options within a list (without groups) | ||
953 | 434 | as key/value tuples.""" | ||
954 | 435 | lst = [] | ||
955 | 436 | for o in self.__options__: | ||
956 | 437 | lst.append((o.name, getattr(self, o.name))) | ||
957 | 438 | return lst | ||
958 | 439 | |||
959 | 440 | def get_option_by_name (self, name): | ||
960 | 441 | """Returns an option in this Options by it's name (or None). | ||
961 | 442 | TODO: this gives wrong results in childclasses ... maybe access | ||
962 | 443 | as class-attribute??""" | ||
963 | 444 | for o in self.__options__: | ||
964 | 445 | if o.name == name: | ||
965 | 446 | return o | ||
966 | 447 | return None | ||
967 | 448 | |||
968 | 449 | def remove_option (self, name): | ||
969 | 450 | """Remove an option from this Options.""" | ||
970 | 451 | for o in self.__options__: | ||
971 | 452 | if o.name == name: | ||
972 | 453 | del o | ||
973 | 454 | return True | ||
974 | 455 | return True | ||
975 | 456 | |||
976 | 457 | def add_options_from_file (self, filename): | ||
977 | 458 | """This function creates options from an XML-file with option-metadata. | ||
978 | 459 | TODO: make this more reusable and place it into module (once the groups | ||
979 | 460 | are own objects)""" | ||
980 | 461 | # create xml document | ||
981 | 462 | try: | ||
982 | 463 | doc = xml.dom.minidom.parse(filename) | ||
983 | 464 | except: | ||
984 | 465 | raise Exception('Invalid XML in metadata-file (or file missing): "%s".' % filename) | ||
985 | 466 | # get rootnode | ||
986 | 467 | root = doc.firstChild | ||
987 | 468 | if not root or root.nodeName != 'screenlet': | ||
988 | 469 | raise Exception('Missing or invalid rootnode in metadata-file: "%s".' % filename) | ||
989 | 470 | # ok, let's check the nodes: this one should contain option-groups | ||
990 | 471 | groups = [] | ||
991 | 472 | for node in root.childNodes: | ||
992 | 473 | # we only want element-nodes | ||
993 | 474 | if node.nodeType == Node.ELEMENT_NODE: | ||
994 | 475 | #print node | ||
995 | 476 | if node.nodeName != 'group' or not node.hasChildNodes(): | ||
996 | 477 | # we only allow groups in the first level (groups need children) | ||
997 | 478 | raise Exception('Error in metadata-file "%s" - only <group>-tags allowed in first level. Groups must contain at least one <info>-element.' % filename) | ||
998 | 479 | else: | ||
999 | 480 | # ok, create a new group and parse its elements | ||
1000 | 481 | group = {} | ||
1001 | 482 | group['name'] = node.getAttribute("name") | ||
1002 | 483 | if not group['name']: | ||
1003 | 484 | raise Exception('No name for group defined in "%s".' % filename) | ||
1004 | 485 | group['info'] = '' | ||
1005 | 486 | group['options'] = [] | ||
1006 | 487 | # check all children in group | ||
1007 | 488 | for on in node.childNodes: | ||
1008 | 489 | if on.nodeType == Node.ELEMENT_NODE: | ||
1009 | 490 | if on.nodeName == 'info': | ||
1010 | 491 | # info-node? set group-info | ||
1011 | 492 | group['info'] = on.firstChild.nodeValue | ||
1012 | 493 | elif on.nodeName == 'option': | ||
1013 | 494 | # option node? parse option node | ||
1014 | 495 | opt = create_option_from_node (on, group['name']) | ||
1015 | 496 | # ok? add it to list | ||
1016 | 497 | if opt: | ||
1017 | 498 | group['options'].append(opt) | ||
1018 | 499 | else: | ||
1019 | 500 | raise Exception('Invalid option-node found in "%s".' % filename) | ||
1020 | 501 | |||
1021 | 502 | # create new group | ||
1022 | 503 | if len(group['options']): | ||
1023 | 504 | self.add_options_group(group['name'], group['info']) | ||
1024 | 505 | for o in group['options']: | ||
1025 | 506 | self.add_option(o) | ||
1026 | 507 | # add group to list | ||
1027 | 508 | #groups.append(group) | ||
1028 | 509 | |||
1029 | 510 | # ----------------------------------------------------------------------- | ||
1030 | 511 | # OptionsDialog and UI-classes | ||
1031 | 512 | # ----------------------------------------------------------------------- | ||
1032 | 513 | |||
1033 | 514 | class ListOptionDialog (gtk.Dialog): | ||
1034 | 515 | """An editing dialog used for editing options of the ListOption-type.""" | ||
1035 | 516 | |||
1036 | 517 | model = None | ||
1037 | 518 | tree = None | ||
1038 | 519 | buttonbox = None | ||
1039 | 520 | |||
1040 | 521 | # call gtk.Dialog.__init__ | ||
1041 | 522 | def __init__ (self): | ||
1042 | 523 | super(ListOptionDialog, self).__init__("Edit List", | ||
1043 | 524 | flags=gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR, | ||
1044 | 525 | buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, | ||
1045 | 526 | gtk.STOCK_OK, gtk.RESPONSE_OK)) | ||
1046 | 527 | # set size | ||
1047 | 528 | self.resize(300, 370) | ||
1048 | 529 | self.set_keep_above(True) # to avoid confusion | ||
1049 | 530 | # init vars | ||
1050 | 531 | self.model = gtk.ListStore(str) | ||
1051 | 532 | # create UI | ||
1052 | 533 | self.create_ui() | ||
1053 | 534 | |||
1054 | 535 | def create_ui (self): | ||
1055 | 536 | """Create the user-interface for this dialog.""" | ||
1056 | 537 | # create outer hbox (tree|buttons) | ||
1057 | 538 | hbox = gtk.HBox() | ||
1058 | 539 | hbox.set_border_width(10) | ||
1059 | 540 | hbox.set_spacing(10) | ||
1060 | 541 | # create tree | ||
1061 | 542 | self.tree = gtk.TreeView(model=self.model) | ||
1062 | 543 | self.tree.set_headers_visible(False) | ||
1063 | 544 | self.tree.set_reorderable(True) | ||
1064 | 545 | #self.tree.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_HORIZONTAL) | ||
1065 | 546 | col = gtk.TreeViewColumn('') | ||
1066 | 547 | cell = gtk.CellRendererText() | ||
1067 | 548 | #cell.set_property('cell-background', 'cyan') | ||
1068 | 549 | cell.set_property('foreground', 'black') | ||
1069 | 550 | col.pack_start(cell, False) | ||
1070 | 551 | col.set_attributes(cell, text=0) | ||
1071 | 552 | self.tree.append_column(col) | ||
1072 | 553 | self.tree.show() | ||
1073 | 554 | hbox.pack_start(self.tree, True, True) | ||
1074 | 555 | #sep = gtk.VSeparator() | ||
1075 | 556 | #sep.show() | ||
1076 | 557 | #hbox.add(sep) | ||
1077 | 558 | # create buttons | ||
1078 | 559 | self.buttonbox = bb = gtk.VButtonBox() | ||
1079 | 560 | self.buttonbox.set_layout(gtk.BUTTONBOX_START) | ||
1080 | 561 | b1 = gtk.Button(stock=gtk.STOCK_ADD) | ||
1081 | 562 | b2 = gtk.Button(stock=gtk.STOCK_EDIT) | ||
1082 | 563 | b3 = gtk.Button(stock=gtk.STOCK_REMOVE) | ||
1083 | 564 | b1.connect('clicked', self.button_callback, 'add') | ||
1084 | 565 | b2.connect('clicked', self.button_callback, 'edit') | ||
1085 | 566 | b3.connect('clicked', self.button_callback, 'remove') | ||
1086 | 567 | bb.add(b1) | ||
1087 | 568 | bb.add(b2) | ||
1088 | 569 | bb.add(b3) | ||
1089 | 570 | self.buttonbox.show_all() | ||
1090 | 571 | #hbox.add(self.buttonbox) | ||
1091 | 572 | hbox.pack_end(self.buttonbox, False) | ||
1092 | 573 | # add everything to outer hbox and show it | ||
1093 | 574 | hbox.show() | ||
1094 | 575 | self.vbox.add(hbox) | ||
1095 | 576 | |||
1096 | 577 | def set_list (self, lst): | ||
1097 | 578 | """Set the list to be edited in this editor.""" | ||
1098 | 579 | for el in lst: | ||
1099 | 580 | self.model.append([el]) | ||
1100 | 581 | |||
1101 | 582 | def get_list (self): | ||
1102 | 583 | """Return the list that is currently being edited in this editor.""" | ||
1103 | 584 | lst = [] | ||
1104 | 585 | for i in self.model: | ||
1105 | 586 | lst.append(i[0]) | ||
1106 | 587 | return lst | ||
1107 | 588 | |||
1108 | 589 | def remove_selected_item (self): | ||
1109 | 590 | """Remove the currently selected item.""" | ||
1110 | 591 | sel = self.tree.get_selection() | ||
1111 | 592 | if sel: | ||
1112 | 593 | it = sel.get_selected()[1] | ||
1113 | 594 | if it: | ||
1114 | 595 | print self.model.get_value(it, 0) | ||
1115 | 596 | self.model.remove(it) | ||
1116 | 597 | |||
1117 | 598 | def entry_dialog (self, default = ''): | ||
1118 | 599 | """Show entry-dialog and return string.""" | ||
1119 | 600 | entry = gtk.Entry() | ||
1120 | 601 | entry.set_text(default) | ||
1121 | 602 | entry.show() | ||
1122 | 603 | dlg = gtk.Dialog("Add/Edit Item", flags=gtk.DIALOG_DESTROY_WITH_PARENT, | ||
1123 | 604 | buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, | ||
1124 | 605 | gtk.RESPONSE_OK)) | ||
1125 | 606 | dlg.set_keep_above(True) | ||
1126 | 607 | dlg.vbox.add(entry) | ||
1127 | 608 | resp = dlg.run() | ||
1128 | 609 | ret = None | ||
1129 | 610 | if resp == gtk.RESPONSE_OK: | ||
1130 | 611 | ret = entry.get_text() | ||
1131 | 612 | dlg.destroy() | ||
1132 | 613 | return ret | ||
1133 | 614 | |||
1134 | 615 | def button_callback (self, widget, id): | ||
1135 | 616 | print "PRESS: %s" % id | ||
1136 | 617 | if id == 'remove': | ||
1137 | 618 | self.remove_selected_item() | ||
1138 | 619 | if id == 'add': | ||
1139 | 620 | new = self.entry_dialog() | ||
1140 | 621 | if new != None: | ||
1141 | 622 | self.model.append([new]) | ||
1142 | 623 | if id == 'edit': | ||
1143 | 624 | sel = self.tree.get_selection() | ||
1144 | 625 | if sel: | ||
1145 | 626 | it = sel.get_selected()[1] | ||
1146 | 627 | if it: | ||
1147 | 628 | new = self.entry_dialog(self.model.get_value(it, 0)) | ||
1148 | 629 | if new != None: | ||
1149 | 630 | #self.model.append([new]) | ||
1150 | 631 | self.model.set_value(it, 0, new) | ||
1151 | 632 | |||
1152 | 633 | |||
1153 | 634 | # TEST | ||
1154 | 635 | """dlg = ListOptionDialog() | ||
1155 | 636 | dlg.set_list(['test1', 'afarew34s', 'fhjh23faj', 'yxcdfs58df', 'hsdf7jsdfh']) | ||
1156 | 637 | dlg.run() | ||
1157 | 638 | print "RESULT: " + str(dlg.get_list()) | ||
1158 | 639 | dlg.destroy() | ||
1159 | 640 | import sys | ||
1160 | 641 | sys.exit(1)""" | ||
1161 | 642 | # /TEST | ||
1162 | 643 | |||
1163 | 644 | class OptionsDialog (gtk.Dialog): | ||
1164 | 645 | """A dynamic options-editor for editing Screenlets which are implementing | ||
1165 | 646 | the EditableOptions-class.""" | ||
1166 | 647 | |||
1167 | 648 | __shown_object = None | ||
1168 | 649 | |||
1169 | 650 | def __init__ (self, width, height): | ||
1170 | 651 | # call gtk.Dialog.__init__ | ||
1171 | 652 | super(OptionsDialog, self).__init__( | ||
1172 | 653 | _("Edit Options"), flags=gtk.DIALOG_DESTROY_WITH_PARENT | | ||
1173 | 654 | gtk.DIALOG_NO_SEPARATOR, | ||
1174 | 655 | buttons = (#gtk.STOCK_REVERT_TO_SAVED, gtk.RESPONSE_APPLY, | ||
1175 | 656 | gtk.STOCK_CLOSE, gtk.RESPONSE_OK)) | ||
1176 | 657 | # set size | ||
1177 | 658 | self.resize(width, height) | ||
1178 | 659 | self.set_keep_above(True) # to avoid confusion | ||
1179 | 660 | self.set_border_width(10) | ||
1180 | 661 | # create attribs | ||
1181 | 662 | self.page_about = None | ||
1182 | 663 | self.page_options = None | ||
1183 | 664 | self.page_themes = None | ||
1184 | 665 | self.vbox_editor = None | ||
1185 | 666 | self.hbox_about = None | ||
1186 | 667 | self.infotext = None | ||
1187 | 668 | self.infoicon = None | ||
1188 | 669 | # create theme-list | ||
1189 | 670 | self.liststore = gtk.ListStore(object) | ||
1190 | 671 | self.tree = gtk.TreeView(model=self.liststore) | ||
1191 | 672 | # create/add outer notebook | ||
1192 | 673 | self.main_notebook = gtk.Notebook() | ||
1193 | 674 | self.main_notebook.show() | ||
1194 | 675 | self.vbox.add(self.main_notebook) | ||
1195 | 676 | # create/init notebook pages | ||
1196 | 677 | self.create_about_page() | ||
1197 | 678 | self.create_themes_page() | ||
1198 | 679 | self.create_options_page() | ||
1199 | 680 | |||
1200 | 681 | # "public" functions | ||
1201 | 682 | |||
1202 | 683 | def reset_to_defaults (self): | ||
1203 | 684 | """Reset all entries for the currently shown object to their default | ||
1204 | 685 | values (the values the object has when it is first created). | ||
1205 | 686 | NOTE: This function resets ALL options, so BE CARFEUL!""" | ||
1206 | 687 | if self.__shown_object: | ||
1207 | 688 | for o in self.__shown_object.__options__: | ||
1208 | 689 | # set default value | ||
1209 | 690 | setattr(self.__shown_object, o.name, o.default) | ||
1210 | 691 | |||
1211 | 692 | def set_info (self, name, info, copyright='', version='', icon=None): | ||
1212 | 693 | """Update the "About"-page with the given information.""" | ||
1213 | 694 | # convert infotext (remove EOLs and TABs) | ||
1214 | 695 | info = info.replace("\n", "") | ||
1215 | 696 | info = info.replace("\t", " ") | ||
1216 | 697 | # create markup | ||
1217 | 698 | markup = '\n<b><span size="xx-large">' + name + '</span></b>' | ||
1218 | 699 | if version: | ||
1219 | 700 | markup += ' <span size="large"><b>' + version + '</b></span>' | ||
1220 | 701 | markup += '\n\n'+info+'\n<span size="small">\n'+copyright+'</span>' | ||
1221 | 702 | self.infotext.set_markup(markup) | ||
1222 | 703 | # icon? | ||
1223 | 704 | if icon: | ||
1224 | 705 | # remove old icon | ||
1225 | 706 | if self.infoicon: | ||
1226 | 707 | self.infoicon.destroy() | ||
1227 | 708 | # set new icon | ||
1228 | 709 | self.infoicon = icon | ||
1229 | 710 | self.infoicon.set_alignment(0.0, 0.10) | ||
1230 | 711 | self.infoicon.show() | ||
1231 | 712 | self.hbox_about.pack_start(self.infoicon, 0, 1, 10) | ||
1232 | 713 | else: | ||
1233 | 714 | self.infoicon.hide() | ||
1234 | 715 | |||
1235 | 716 | def show_options_for_object (self, obj): | ||
1236 | 717 | """Update the OptionsEditor to show the options for the given Object. | ||
1237 | 718 | The Object needs to be an EditableOptions-subclass. | ||
1238 | 719 | NOTE: This needs heavy improvement and should use OptionGroups once | ||
1239 | 720 | they exist""" | ||
1240 | 721 | self.__shown_object = obj | ||
1241 | 722 | # create notebook for groups | ||
1242 | 723 | notebook = gtk.Notebook() | ||
1243 | 724 | self.vbox_editor.add(notebook) | ||
1244 | 725 | for group in obj.__options_groups_ordered__: | ||
1245 | 726 | group_data = obj.__options_groups__[group] | ||
1246 | 727 | # create box for tab-page | ||
1247 | 728 | page = gtk.VBox() | ||
1248 | 729 | page.set_border_width(10) | ||
1249 | 730 | if group_data['info'] != '': | ||
1250 | 731 | info = gtk.Label(group_data['info']) | ||
1251 | 732 | info.show() | ||
1252 | 733 | info.set_alignment(0, 0) | ||
1253 | 734 | page.pack_start(info, 0, 0, 7) | ||
1254 | 735 | sep = gtk.HSeparator() | ||
1255 | 736 | sep.show() | ||
1256 | 737 | #page.pack_start(sep, 0, 0, 5) | ||
1257 | 738 | # create VBox for inputs | ||
1258 | 739 | box = gtk.VBox() | ||
1259 | 740 | box.show() | ||
1260 | 741 | box.set_border_width(5) | ||
1261 | 742 | # add box to page | ||
1262 | 743 | page.add(box) | ||
1263 | 744 | page.show() | ||
1264 | 745 | # add new notebook-page | ||
1265 | 746 | label = gtk.Label(group_data['label']) | ||
1266 | 747 | label.show() | ||
1267 | 748 | notebook.append_page(page, label) | ||
1268 | 749 | # and create inputs | ||
1269 | 750 | for option in group_data['options']: | ||
1270 | 751 | if option.hidden == False: | ||
1271 | 752 | val = getattr(obj, option.name)#obj.__dict__[option.name] | ||
1272 | 753 | w = self.get_widget_for_option(option, val) | ||
1273 | 754 | if w: | ||
1274 | 755 | box.pack_start(w, 0, 0) | ||
1275 | 756 | w.show() | ||
1276 | 757 | notebook.show() | ||
1277 | 758 | # show/hide themes tab, depending on whether the screenlet uses themes | ||
1278 | 759 | if obj.uses_theme and obj.theme_name != '': | ||
1279 | 760 | self.show_themes_for_screenlet(obj) | ||
1280 | 761 | else: | ||
1281 | 762 | self.page_themes.hide() | ||
1282 | 763 | |||
1283 | 764 | def show_themes_for_screenlet (self, obj): | ||
1284 | 765 | """Update the Themes-page to display the available themes for the | ||
1285 | 766 | given Screenlet-object.""" | ||
1286 | 767 | |||
1287 | 768 | |||
1288 | 769 | dircontent = [] | ||
1289 | 770 | screenlets.utils.refresh_available_screenlet_paths() | ||
1290 | 771 | |||
1291 | 772 | for path in screenlets.SCREENLETS_PATH: | ||
1292 | 773 | p = path + '/' + obj.get_short_name() + '/themes' | ||
1293 | 774 | print p | ||
1294 | 775 | #p = '/usr/local/share/screenlets/Clock/themes' # TEMP!!! | ||
1295 | 776 | try: | ||
1296 | 777 | dc = os.listdir(p) | ||
1297 | 778 | for d in dc: | ||
1298 | 779 | dircontent.append({'name':d, 'path':p+'/'}) | ||
1299 | 780 | except: | ||
1300 | 781 | print "Path %s not found." % p | ||
1301 | 782 | |||
1302 | 783 | # list with found themes | ||
1303 | 784 | found_themes = [] | ||
1304 | 785 | |||
1305 | 786 | # check all themes in path | ||
1306 | 787 | for elem in dircontent: | ||
1307 | 788 | # load themes with the same name only once | ||
1308 | 789 | if found_themes.count(elem['name']): | ||
1309 | 790 | continue | ||
1310 | 791 | found_themes.append(elem['name']) | ||
1311 | 792 | # build full path of theme.conf | ||
1312 | 793 | theme_conf = elem['path'] + elem['name'] + '/theme.conf' | ||
1313 | 794 | # if dir contains a theme.conf | ||
1314 | 795 | if os.access(theme_conf, os.F_OK): | ||
1315 | 796 | # load it and create new list entry | ||
1316 | 797 | ini = screenlets.utils.IniReader() | ||
1317 | 798 | if ini.load(theme_conf): | ||
1318 | 799 | # check for section | ||
1319 | 800 | if ini.has_section('Theme'): | ||
1320 | 801 | # get metainfo from theme | ||
1321 | 802 | th_fullname = ini.get_option('name', | ||
1322 | 803 | section='Theme') | ||
1323 | 804 | th_info = ini.get_option('info', | ||
1324 | 805 | section='Theme') | ||
1325 | 806 | th_version = ini.get_option('version', | ||
1326 | 807 | section='Theme') | ||
1327 | 808 | th_author = ini.get_option('author', | ||
1328 | 809 | section='Theme') | ||
1329 | 810 | # create array from metainfo and add it to liststore | ||
1330 | 811 | info = [elem['name'], th_fullname, th_info, th_author, | ||
1331 | 812 | th_version] | ||
1332 | 813 | self.liststore.append([info]) | ||
1333 | 814 | else: | ||
1334 | 815 | # no theme section in theme.conf just add theme-name | ||
1335 | 816 | self.liststore.append([[elem['name'], '-', '-', '-', '-']]) | ||
1336 | 817 | else: | ||
1337 | 818 | # no theme.conf in dir? just add theme-name | ||
1338 | 819 | self.liststore.append([[elem['name'], '-', '-', '-', '-']]) | ||
1339 | 820 | # is it the active theme? | ||
1340 | 821 | if elem['name'] == obj.theme_name: | ||
1341 | 822 | # select it in tree | ||
1342 | 823 | print "active theme is: %s" % elem['name'] | ||
1343 | 824 | sel = self.tree.get_selection() | ||
1344 | 825 | if sel: | ||
1345 | 826 | it = self.liststore.get_iter_from_string(\ | ||
1346 | 827 | str(len(self.liststore)-1)) | ||
1347 | 828 | if it: | ||
1348 | 829 | sel.select_iter(it) | ||
1349 | 830 | |||
1350 | 831 | # UI-creation | ||
1351 | 832 | |||
1352 | 833 | def create_about_page (self): | ||
1353 | 834 | """Create the "About"-tab.""" | ||
1354 | 835 | self.page_about = gtk.HBox() | ||
1355 | 836 | # create about box | ||
1356 | 837 | self.hbox_about = gtk.HBox() | ||
1357 | 838 | self.hbox_about.show() | ||
1358 | 839 | self.page_about.add(self.hbox_about) | ||
1359 | 840 | # create icon | ||
1360 | 841 | self.infoicon = gtk.Image() | ||
1361 | 842 | self.infoicon.show() | ||
1362 | 843 | self.page_about.pack_start(self.infoicon, 0, 1, 10) | ||
1363 | 844 | # create infotext | ||
1364 | 845 | self.infotext = gtk.Label() | ||
1365 | 846 | self.infotext.use_markup = True | ||
1366 | 847 | self.infotext.set_line_wrap(True) | ||
1367 | 848 | self.infotext.set_alignment(0.0, 0.0) | ||
1368 | 849 | self.infotext.show() | ||
1369 | 850 | self.page_about.pack_start(self.infotext, 1, 1, 5) | ||
1370 | 851 | # add page | ||
1371 | 852 | self.page_about.show() | ||
1372 | 853 | self.main_notebook.append_page(self.page_about, gtk.Label(_('About '))) | ||
1373 | 854 | |||
1374 | 855 | def create_options_page (self): | ||
1375 | 856 | """Create the "Options"-tab.""" | ||
1376 | 857 | self.page_options = gtk.HBox() | ||
1377 | 858 | # create vbox for options-editor | ||
1378 | 859 | self.vbox_editor = gtk.VBox(spacing=3) | ||
1379 | 860 | self.vbox_editor.set_border_width(5) | ||
1380 | 861 | self.vbox_editor.show() | ||
1381 | 862 | self.page_options.add(self.vbox_editor) | ||
1382 | 863 | # show/add page | ||
1383 | 864 | self.page_options.show() | ||
1384 | 865 | self.main_notebook.append_page(self.page_options, gtk.Label(_('Options '))) | ||
1385 | 866 | |||
1386 | 867 | def create_themes_page (self): | ||
1387 | 868 | """Create the "Themes"-tab.""" | ||
1388 | 869 | self.page_themes = gtk.VBox(spacing=5) | ||
1389 | 870 | self.page_themes.set_border_width(10) | ||
1390 | 871 | # create info-text list | ||
1391 | 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.')) | ||
1392 | 873 | txt.set_size_request(450, -1) | ||
1393 | 874 | txt.set_line_wrap(True) | ||
1394 | 875 | txt.set_alignment(0.0, 0.0) | ||
1395 | 876 | txt.show() | ||
1396 | 877 | self.page_themes.pack_start(txt, False, True) | ||
1397 | 878 | # create theme-selector list | ||
1398 | 879 | self.tree.set_headers_visible(False) | ||
1399 | 880 | self.tree.connect('cursor-changed', self.__tree_cursor_changed) | ||
1400 | 881 | self.tree.show() | ||
1401 | 882 | col = gtk.TreeViewColumn('') | ||
1402 | 883 | cell = gtk.CellRendererText() | ||
1403 | 884 | col.pack_start(cell, True) | ||
1404 | 885 | #cell.set_property('foreground', 'black') | ||
1405 | 886 | col.set_cell_data_func(cell, self.__render_cell) | ||
1406 | 887 | self.tree.append_column(col) | ||
1407 | 888 | # wrap tree in scrollwin | ||
1408 | 889 | sw = gtk.ScrolledWindow() | ||
1409 | 890 | sw.set_shadow_type(gtk.SHADOW_IN) | ||
1410 | 891 | sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) | ||
1411 | 892 | sw.add(self.tree) | ||
1412 | 893 | sw.show() | ||
1413 | 894 | # add vbox and add tree/buttons | ||
1414 | 895 | vbox = gtk.VBox() | ||
1415 | 896 | vbox.pack_start(sw, True, True) | ||
1416 | 897 | vbox.show() | ||
1417 | 898 | # show/add page | ||
1418 | 899 | self.page_themes.add(vbox) | ||
1419 | 900 | self.page_themes.show() | ||
1420 | 901 | self.main_notebook.append_page(self.page_themes, gtk.Label(_('Themes '))) | ||
1421 | 902 | |||
1422 | 903 | def __render_cell(self, tvcolumn, cell, model, iter): | ||
1423 | 904 | """Callback for rendering the cells in the theme-treeview.""" | ||
1424 | 905 | # get attributes-list from Treemodel | ||
1425 | 906 | attrib = model.get_value(iter, 0) | ||
1426 | 907 | |||
1427 | 908 | # set colors depending on state | ||
1428 | 909 | col = '555555' | ||
1429 | 910 | name_uc = attrib[0][0].upper() + attrib[0][1:] | ||
1430 | 911 | # create markup depending on info | ||
1431 | 912 | if attrib[1] == '-' and attrib[2] == '-': | ||
1432 | 913 | mu = '<b><span weight="ultrabold" size="large">' + name_uc + \ | ||
1433 | 914 | '</span></b> (' + _('no info available') + ')' | ||
1434 | 915 | else: | ||
1435 | 916 | if attrib[1] == None : attrib[1] = '-' | ||
1436 | 917 | if attrib[2] == None : attrib[2] = '-' | ||
1437 | 918 | if attrib[3] == None : attrib[3] = '-' | ||
1438 | 919 | if attrib[4] == None : attrib[4] = '-' | ||
1439 | 920 | mu = '<b><span weight="ultrabold" size="large">' + name_uc + \ | ||
1440 | 921 | '</span></b> v' + attrib[4] + '\n<small><span color="#555555' +\ | ||
1441 | 922 | '">' + attrib[2].replace('\\n', '\n') + \ | ||
1442 | 923 | '</span></small>\n<i><small>by '+str(attrib[3])+'</small></i>' | ||
1443 | 924 | # set markup | ||
1444 | 925 | cell.set_property('markup', mu) | ||
1445 | 926 | |||
1446 | 927 | # UI-callbacks | ||
1447 | 928 | |||
1448 | 929 | def __tree_cursor_changed (self, treeview): | ||
1449 | 930 | """Callback for handling selection changes in the Themes-treeview.""" | ||
1450 | 931 | sel = self.tree.get_selection() | ||
1451 | 932 | if sel: | ||
1452 | 933 | s = sel.get_selected() | ||
1453 | 934 | if s: | ||
1454 | 935 | it = s[1] | ||
1455 | 936 | if it: | ||
1456 | 937 | attribs = self.liststore.get_value(it, 0) | ||
1457 | 938 | if attribs and self.__shown_object: | ||
1458 | 939 | #print attribs | ||
1459 | 940 | # set theme in Screenlet (if not already active) | ||
1460 | 941 | if self.__shown_object.theme_name != attribs[0]: | ||
1461 | 942 | self.__shown_object.theme_name = attribs[0] | ||
1462 | 943 | |||
1463 | 944 | # option-widget creation (should be split in several classes) | ||
1464 | 945 | |||
1465 | 946 | def get_widget_for_option (self, option, value=None): | ||
1466 | 947 | """Return a gtk.*Widget with Label within a HBox for a given option. | ||
1467 | 948 | NOTE: This is incredibly ugly, ideally all Option-subclasses should | ||
1468 | 949 | have their own widgets - like StringOptionWidget, ColorOptionWidget, | ||
1469 | 950 | ... and then be simply created dynamically""" | ||
1470 | 951 | t = option.__class__ | ||
1471 | 952 | widget = None | ||
1472 | 953 | if t == BoolOption: | ||
1473 | 954 | widget = gtk.CheckButton() | ||
1474 | 955 | widget.set_active(value) | ||
1475 | 956 | widget.connect("toggled", self.options_callback, option) | ||
1476 | 957 | elif t == StringOption: | ||
1477 | 958 | if option.choices: | ||
1478 | 959 | # if a list of values is defined, show combobox | ||
1479 | 960 | widget = gtk.combo_box_new_text() | ||
1480 | 961 | p = -1 | ||
1481 | 962 | i = 0 | ||
1482 | 963 | for s in option.choices: | ||
1483 | 964 | widget.append_text(s) | ||
1484 | 965 | if s==value: | ||
1485 | 966 | p = i | ||
1486 | 967 | i+=1 | ||
1487 | 968 | widget.set_active(p) | ||
1488 | 969 | #widget.connect("changed", self.options_callback, option) | ||
1489 | 970 | else: | ||
1490 | 971 | widget = gtk.Entry() | ||
1491 | 972 | widget.set_text(value) | ||
1492 | 973 | # if it is a password, set text to be invisible | ||
1493 | 974 | if option.password: | ||
1494 | 975 | widget.set_visibility(False) | ||
1495 | 976 | #widget.connect("key-press-event", self.options_callback, option) | ||
1496 | 977 | widget.connect("changed", self.options_callback, option) | ||
1497 | 978 | #widget.set_size_request(180, 28) | ||
1498 | 979 | elif t == IntOption or t == FloatOption: | ||
1499 | 980 | widget = gtk.SpinButton() | ||
1500 | 981 | #widget.set_size_request(50, 22) | ||
1501 | 982 | #widget.set_text(str(value)) | ||
1502 | 983 | if t == FloatOption: | ||
1503 | 984 | widget.set_digits(option.digits) | ||
1504 | 985 | widget.set_increments(option.increment, int(option.max/option.increment)) | ||
1505 | 986 | else: | ||
1506 | 987 | widget.set_increments(option.increment, int(option.max/option.increment)) | ||
1507 | 988 | if option.min!=None and option.max!=None: | ||
1508 | 989 | #print "Setting range for input to: %f, %f" % (option.min, option.max) | ||
1509 | 990 | widget.set_range(option.min, option.max) | ||
1510 | 991 | widget.set_value(value) | ||
1511 | 992 | widget.connect("value-changed", self.options_callback, option) | ||
1512 | 993 | elif t == ColorOption: | ||
1513 | 994 | widget = gtk.ColorButton(gtk.gdk.Color(int(value[0]*65535), int(value[1]*65535), int(value[2]*65535))) | ||
1514 | 995 | widget.set_use_alpha(True) | ||
1515 | 996 | # print value | ||
1516 | 997 | # print value[3] | ||
1517 | 998 | widget.set_alpha(int(value[3]*65535)) | ||
1518 | 999 | widget.connect("color-set", self.options_callback, option) | ||
1519 | 1000 | elif t == FontOption: | ||
1520 | 1001 | widget = gtk.FontButton() | ||
1521 | 1002 | widget.set_font_name(value) | ||
1522 | 1003 | widget.connect("font-set", self.options_callback, option) | ||
1523 | 1004 | elif t == FileOption: | ||
1524 | 1005 | widget = gtk.FileChooserButton(_("Choose File")) | ||
1525 | 1006 | widget.set_filename(value) | ||
1526 | 1007 | widget.set_size_request(180, 28) | ||
1527 | 1008 | widget.connect("selection-changed", self.options_callback, option) | ||
1528 | 1009 | elif t == DirectoryOption: | ||
1529 | 1010 | dlg = gtk.FileChooserDialog(buttons=(gtk.STOCK_CANCEL, | ||
1530 | 1011 | gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK), | ||
1531 | 1012 | action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) | ||
1532 | 1013 | widget = gtk.FileChooserButton(dlg) | ||
1533 | 1014 | widget.set_title(_("Choose Directory")) | ||
1534 | 1015 | widget.set_filename(value) | ||
1535 | 1016 | widget.set_size_request(180, 28) | ||
1536 | 1017 | widget.connect("selection-changed", self.options_callback, option) | ||
1537 | 1018 | elif t == ImageOption: | ||
1538 | 1019 | # create entry and button (entry is hidden) | ||
1539 | 1020 | entry = gtk.Entry() | ||
1540 | 1021 | entry.set_text(value) | ||
1541 | 1022 | entry.set_editable(False) | ||
1542 | 1023 | but = gtk.Button() | ||
1543 | 1024 | # util to reload preview image | ||
1544 | 1025 | def create_preview (filename): | ||
1545 | 1026 | if filename and os.path.isfile(filename): | ||
1546 | 1027 | pb = gtk.gdk.pixbuf_new_from_file_at_size(filename, 64, -1) | ||
1547 | 1028 | if pb: | ||
1548 | 1029 | img = gtk.Image() | ||
1549 | 1030 | img.set_from_pixbuf(pb) | ||
1550 | 1031 | return img | ||
1551 | 1032 | img = gtk.image_new_from_stock(gtk.STOCK_MISSING_IMAGE, | ||
1552 | 1033 | gtk.ICON_SIZE_LARGE_TOOLBAR) | ||
1553 | 1034 | img.set_size_request(64, 64) | ||
1554 | 1035 | return img | ||
1555 | 1036 | # create button | ||
1556 | 1037 | def but_callback (widget): | ||
1557 | 1038 | dlg = gtk.FileChooserDialog(buttons=(gtk.STOCK_CANCEL, | ||
1558 | 1039 | gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) | ||
1559 | 1040 | dlg.set_title(_("Choose Image")) | ||
1560 | 1041 | dlg.set_keep_above(True) | ||
1561 | 1042 | dlg.set_filename(entry.get_text()) | ||
1562 | 1043 | flt = gtk.FileFilter() | ||
1563 | 1044 | flt.add_pixbuf_formats() | ||
1564 | 1045 | dlg.set_filter(flt) | ||
1565 | 1046 | prev = gtk.Image() | ||
1566 | 1047 | box = gtk.VBox() | ||
1567 | 1048 | box.set_size_request(150, -1) | ||
1568 | 1049 | box.add(prev) | ||
1569 | 1050 | prev.show() | ||
1570 | 1051 | # add preview widget to filechooser | ||
1571 | 1052 | def preview_callback(widget): | ||
1572 | 1053 | fname = dlg.get_preview_filename() | ||
1573 | 1054 | if fname and os.path.isfile(fname): | ||
1574 | 1055 | pb = gtk.gdk.pixbuf_new_from_file_at_size(fname, 150, -1) | ||
1575 | 1056 | if pb: | ||
1576 | 1057 | prev.set_from_pixbuf(pb) | ||
1577 | 1058 | dlg.set_preview_widget_active(True) | ||
1578 | 1059 | else: | ||
1579 | 1060 | dlg.set_preview_widget_active(False) | ||
1580 | 1061 | dlg.set_preview_widget_active(True) | ||
1581 | 1062 | dlg.connect('selection-changed', preview_callback) | ||
1582 | 1063 | dlg.set_preview_widget(box) | ||
1583 | 1064 | # run | ||
1584 | 1065 | response = dlg.run() | ||
1585 | 1066 | if response == gtk.RESPONSE_OK: | ||
1586 | 1067 | entry.set_text(dlg.get_filename()) | ||
1587 | 1068 | but.set_image(create_preview(dlg.get_filename())) | ||
1588 | 1069 | self.options_callback(dlg, option) | ||
1589 | 1070 | dlg.destroy() | ||
1590 | 1071 | # load preview image | ||
1591 | 1072 | but.set_image(create_preview(value)) | ||
1592 | 1073 | but.connect('clicked', but_callback) | ||
1593 | 1074 | # create widget | ||
1594 | 1075 | widget = gtk.HBox() | ||
1595 | 1076 | widget.add(entry) | ||
1596 | 1077 | widget.add(but) | ||
1597 | 1078 | but.show() | ||
1598 | 1079 | widget.show() | ||
1599 | 1080 | # add tooltips | ||
1600 | 1081 | #but.set_tooltip_text(_('Select Image ...')) | ||
1601 | 1082 | but.set_tooltip_text(option.desc) | ||
1602 | 1083 | |||
1603 | 1084 | elif t == ListOption: | ||
1604 | 1085 | entry= gtk.Entry() | ||
1605 | 1086 | entry.set_editable(False) | ||
1606 | 1087 | entry.set_text(str(value)) | ||
1607 | 1088 | entry.show() | ||
1608 | 1089 | img = gtk.Image() | ||
1609 | 1090 | img.set_from_stock(gtk.STOCK_EDIT, 1) | ||
1610 | 1091 | but = gtk.Button() | ||
1611 | 1092 | but.set_image(img) | ||
1612 | 1093 | def open_listeditor(event): | ||
1613 | 1094 | # open dialog | ||
1614 | 1095 | dlg = ListOptionDialog() | ||
1615 | 1096 | # read string from entry and import it through option-class | ||
1616 | 1097 | # (this is needed to always have an up-to-date value) | ||
1617 | 1098 | dlg.set_list(option.on_import(entry.get_text())) | ||
1618 | 1099 | resp = dlg.run() | ||
1619 | 1100 | if resp == gtk.RESPONSE_OK: | ||
1620 | 1101 | # set text in entry | ||
1621 | 1102 | entry.set_text(str(dlg.get_list())) | ||
1622 | 1103 | # manually call the options-callback | ||
1623 | 1104 | self.options_callback(dlg, option) | ||
1624 | 1105 | dlg.destroy() | ||
1625 | 1106 | but.show() | ||
1626 | 1107 | but.connect("clicked", open_listeditor) | ||
1627 | 1108 | but.set_tooltip_text(_('Open List-Editor ...')) | ||
1628 | 1109 | entry.set_tooltip_text(option.desc) | ||
1629 | 1110 | widget = gtk.HBox() | ||
1630 | 1111 | widget.add(entry) | ||
1631 | 1112 | widget.add(but) | ||
1632 | 1113 | elif t == AccountOption: | ||
1633 | 1114 | widget = gtk.HBox() | ||
1634 | 1115 | vb = gtk.VBox() | ||
1635 | 1116 | input_name = gtk.Entry() | ||
1636 | 1117 | input_name.set_text(value[0]) | ||
1637 | 1118 | input_name.show() | ||
1638 | 1119 | input_pass = gtk.Entry() | ||
1639 | 1120 | input_pass.set_visibility(False) # password | ||
1640 | 1121 | input_pass.set_text(value[1]) | ||
1641 | 1122 | input_pass.show() | ||
1642 | 1123 | but = gtk.Button(_('Apply'), gtk.STOCK_APPLY) | ||
1643 | 1124 | but.show() | ||
1644 | 1125 | but.connect("clicked", self.apply_options_callback, option, widget) | ||
1645 | 1126 | vb.add(input_name) | ||
1646 | 1127 | vb.add(input_pass) | ||
1647 | 1128 | vb.show() | ||
1648 | 1129 | but.set_tooltip_text(_('Apply username/password ...')) | ||
1649 | 1130 | input_name.set_tooltip_text(_('Enter username here ...')) | ||
1650 | 1131 | input_pass.set_tooltip_text(_('Enter password here ...')) | ||
1651 | 1132 | widget.add(vb) | ||
1652 | 1133 | widget.add(but) | ||
1653 | 1134 | elif t == TimeOption: | ||
1654 | 1135 | widget = gtk.HBox() | ||
1655 | 1136 | input_hour = gtk.SpinButton()#climb_rate=1.0) | ||
1656 | 1137 | input_minute = gtk.SpinButton() | ||
1657 | 1138 | input_second = gtk.SpinButton() | ||
1658 | 1139 | input_hour.set_range(0, 23) | ||
1659 | 1140 | input_hour.set_max_length(2) | ||
1660 | 1141 | input_hour.set_increments(1, 1) | ||
1661 | 1142 | input_hour.set_numeric(True) | ||
1662 | 1143 | input_hour.set_value(value[0]) | ||
1663 | 1144 | input_minute.set_range(0, 59) | ||
1664 | 1145 | input_minute.set_max_length(2) | ||
1665 | 1146 | input_minute.set_increments(1, 1) | ||
1666 | 1147 | input_minute.set_numeric(True) | ||
1667 | 1148 | input_minute.set_value(value[1]) | ||
1668 | 1149 | input_second.set_range(0, 59) | ||
1669 | 1150 | input_second.set_max_length(2) | ||
1670 | 1151 | input_second.set_increments(1, 1) | ||
1671 | 1152 | input_second.set_numeric(True) | ||
1672 | 1153 | input_second.set_value(value[2]) | ||
1673 | 1154 | input_hour.connect('value-changed', self.options_callback, option) | ||
1674 | 1155 | input_minute.connect('value-changed', self.options_callback, option) | ||
1675 | 1156 | input_second.connect('value-changed', self.options_callback, option) | ||
1676 | 1157 | input_hour.set_tooltip_text(option.desc) | ||
1677 | 1158 | input_minute.set_tooltip_text(option.desc) | ||
1678 | 1159 | input_second.set_tooltip_text(option.desc) | ||
1679 | 1160 | widget.add(input_hour) | ||
1680 | 1161 | widget.add(gtk.Label(':')) | ||
1681 | 1162 | widget.add(input_minute) | ||
1682 | 1163 | widget.add(gtk.Label(':')) | ||
1683 | 1164 | widget.add(input_second) | ||
1684 | 1165 | widget.add(gtk.Label('h')) | ||
1685 | 1166 | widget.show_all() | ||
1686 | 1167 | else: | ||
1687 | 1168 | widget = gtk.Entry() | ||
1688 | 1169 | print "unsupported type ''" % str(t) | ||
1689 | 1170 | hbox = gtk.HBox() | ||
1690 | 1171 | label = gtk.Label() | ||
1691 | 1172 | label.set_alignment(0.0, 0.0) | ||
1692 | 1173 | label.set_label(option.label) | ||
1693 | 1174 | label.set_size_request(180, 28) | ||
1694 | 1175 | label.show() | ||
1695 | 1176 | hbox.pack_start(label, 0, 1) | ||
1696 | 1177 | if widget: | ||
1697 | 1178 | if option.disabled: # option disabled? | ||
1698 | 1179 | widget.set_sensitive(False) | ||
1699 | 1180 | label.set_sensitive(False) | ||
1700 | 1181 | #label.set_mnemonic_widget(widget) | ||
1701 | 1182 | widget.set_tooltip_text(option.desc) | ||
1702 | 1183 | widget.show() | ||
1703 | 1184 | # check if needs Apply-button | ||
1704 | 1185 | if option.realtime == False: | ||
1705 | 1186 | but = gtk.Button(_('Apply'), gtk.STOCK_APPLY) | ||
1706 | 1187 | but.show() | ||
1707 | 1188 | but.connect("clicked", self.apply_options_callback, | ||
1708 | 1189 | option, widget) | ||
1709 | 1190 | b = gtk.HBox() | ||
1710 | 1191 | b.show() | ||
1711 | 1192 | b.pack_start(widget, 0, 0) | ||
1712 | 1193 | b.pack_start(but, 0, 0) | ||
1713 | 1194 | hbox.pack_start(b, 0, 0) | ||
1714 | 1195 | else: | ||
1715 | 1196 | #hbox.pack_start(widget, -1, 1) | ||
1716 | 1197 | hbox.pack_start(widget, 0, 0) | ||
1717 | 1198 | return hbox | ||
1718 | 1199 | |||
1719 | 1200 | def read_option_from_widget (self, widget, option): | ||
1720 | 1201 | """Read an option's value from the widget and return it.""" | ||
1721 | 1202 | if not widget.window: | ||
1722 | 1203 | return False | ||
1723 | 1204 | # get type of option and read the widget's value | ||
1724 | 1205 | val = None | ||
1725 | 1206 | t = option.__class__ | ||
1726 | 1207 | if t == IntOption: | ||
1727 | 1208 | val = int(widget.get_value()) | ||
1728 | 1209 | elif t == FloatOption: | ||
1729 | 1210 | val = widget.get_value() | ||
1730 | 1211 | elif t == StringOption: | ||
1731 | 1212 | if option.choices: | ||
1732 | 1213 | # if default is a list, handle combobox | ||
1733 | 1214 | val = widget.get_active_text() | ||
1734 | 1215 | else: | ||
1735 | 1216 | val = widget.get_text() | ||
1736 | 1217 | elif t == BoolOption: | ||
1737 | 1218 | val = widget.get_active() | ||
1738 | 1219 | elif t == ColorOption: | ||
1739 | 1220 | col = widget.get_color() | ||
1740 | 1221 | al = widget.get_alpha() | ||
1741 | 1222 | val = (col.red/65535.0, col.green/65535.0, | ||
1742 | 1223 | col.blue/65535.0, al/65535.0) | ||
1743 | 1224 | elif t == FontOption: | ||
1744 | 1225 | val = widget.get_font_name() | ||
1745 | 1226 | elif t == FileOption or t == DirectoryOption or t == ImageOption: | ||
1746 | 1227 | val = widget.get_filename() | ||
1747 | 1228 | #print widget | ||
1748 | 1229 | #elif t == ImageOption: | ||
1749 | 1230 | # val = widget.get_text() | ||
1750 | 1231 | elif t == ListOption: | ||
1751 | 1232 | # the widget is a ListOptionDialog here | ||
1752 | 1233 | val = widget.get_list() | ||
1753 | 1234 | elif t == AccountOption: | ||
1754 | 1235 | # the widget is a HBox containing a VBox containing two Entries | ||
1755 | 1236 | # (ideally we should have a custom widget for the AccountOption) | ||
1756 | 1237 | for c in widget.get_children(): | ||
1757 | 1238 | if c.__class__ == gtk.VBox: | ||
1758 | 1239 | c2 = c.get_children() | ||
1759 | 1240 | val = (c2[0].get_text(), c2[1].get_text()) | ||
1760 | 1241 | elif t == TimeOption: | ||
1761 | 1242 | box = widget.get_parent() | ||
1762 | 1243 | inputs = box.get_children() | ||
1763 | 1244 | val = (int(inputs[0].get_value()), int(inputs[2].get_value()), | ||
1764 | 1245 | int(inputs[4].get_value())) | ||
1765 | 1246 | else: | ||
1766 | 1247 | print "OptionsDialog: Unknown option type: %s" % str(t) | ||
1767 | 1248 | return None | ||
1768 | 1249 | # return the value | ||
1769 | 1250 | return val | ||
1770 | 1251 | |||
1771 | 1252 | # option-widget event-handling | ||
1772 | 1253 | |||
1773 | 1254 | # TODO: custom callback/signal for each option? | ||
1774 | 1255 | def options_callback (self, widget, optionobj): | ||
1775 | 1256 | """Callback for handling changed-events on entries.""" | ||
1776 | 1257 | print "Changed: %s" % optionobj.name | ||
1777 | 1258 | if self.__shown_object: | ||
1778 | 1259 | # if the option is not real-time updated, | ||
1779 | 1260 | if optionobj.realtime == False: | ||
1780 | 1261 | return False | ||
1781 | 1262 | # read option | ||
1782 | 1263 | val = self.read_option_from_widget(widget, optionobj) | ||
1783 | 1264 | if val != None: | ||
1784 | 1265 | #print "SetOption: "+optionobj.name+"="+str(val) | ||
1785 | 1266 | # set option | ||
1786 | 1267 | setattr(self.__shown_object, optionobj.name, val) | ||
1787 | 1268 | # notify option-object's on_changed-handler | ||
1788 | 1269 | optionobj.emit("option_changed", optionobj) | ||
1789 | 1270 | return False | ||
1790 | 1271 | |||
1791 | 1272 | def apply_options_callback (self, widget, optionobj, entry): | ||
1792 | 1273 | """Callback for handling Apply-button presses.""" | ||
1793 | 1274 | if self.__shown_object: | ||
1794 | 1275 | # read option | ||
1795 | 1276 | val = self.read_option_from_widget(entry, optionobj) | ||
1796 | 1277 | if val != None: | ||
1797 | 1278 | #print "SetOption: "+optionobj.name+"="+str(val) | ||
1798 | 1279 | # set option | ||
1799 | 1280 | setattr(self.__shown_object, optionobj.name, val) | ||
1800 | 1281 | # notify option-object's on_changed-handler | ||
1801 | 1282 | optionobj.emit("option_changed", optionobj) | ||
1802 | 1283 | return False | ||
1803 | 1284 | |||
1804 | 1285 | |||
1805 | 1286 | |||
1806 | 1287 | # ------ ONLY FOR TESTING ------------------: | ||
1807 | 1288 | if __name__ == "__main__": | ||
1808 | 1289 | |||
1809 | 1290 | import os | ||
1810 | 1291 | |||
1811 | 1292 | # this is only for testing - should be a Screenlet | ||
1812 | 1293 | class TestObject (EditableOptions): | ||
1813 | 1294 | |||
1814 | 1295 | testlist = ['test1', 'test2', 3, 5, 'Noch ein Test'] | ||
1815 | 1296 | pop3_account = ('Username', '') | ||
1816 | 1297 | |||
1817 | 1298 | # TEST | ||
1818 | 1299 | pin_x = 100 | ||
1819 | 1300 | pin_y = 6 | ||
1820 | 1301 | text_x = 19 | ||
1821 | 1302 | text_y = 35 | ||
1822 | 1303 | font_name = 'Sans 12' | ||
1823 | 1304 | rgba_color = (0.0, 0.0, 1.0, 1.0) | ||
1824 | 1305 | text_prefix = '<b>' | ||
1825 | 1306 | text_suffix = '</b>' | ||
1826 | 1307 | note_text = "" # hidden option because val has its own editing-dialog | ||
1827 | 1308 | random_pin_pos = True | ||
1828 | 1309 | opt1 = 'testval 1' | ||
1829 | 1310 | opt2 = 'testval 2' | ||
1830 | 1311 | filename2 = '' | ||
1831 | 1312 | filename = '' | ||
1832 | 1313 | dirname = '' | ||
1833 | 1314 | font = 'Sans 12' | ||
1834 | 1315 | color = (0.1, 0.5, 0.9, 0.9) | ||
1835 | 1316 | name = 'a name' | ||
1836 | 1317 | name2 = 'another name' | ||
1837 | 1318 | combo_test = 'el2' | ||
1838 | 1319 | flt = 0.5 | ||
1839 | 1320 | x = 10 | ||
1840 | 1321 | y = 25 | ||
1841 | 1322 | width = 30 | ||
1842 | 1323 | height = 50 | ||
1843 | 1324 | is_sticky = False | ||
1844 | 1325 | is_widget = False | ||
1845 | 1326 | time = (12, 32, 49) # a time-value (tuple with ints) | ||
1846 | 1327 | |||
1847 | 1328 | def __init__ (self): | ||
1848 | 1329 | EditableOptions.__init__(self) | ||
1849 | 1330 | # Add group | ||
1850 | 1331 | self.add_options_group('General', | ||
1851 | 1332 | 'The general options for this Object ...') | ||
1852 | 1333 | self.add_options_group('Window', | ||
1853 | 1334 | 'The Window-related options for this Object ...') | ||
1854 | 1335 | self.add_options_group('Test', 'A Test-group ...') | ||
1855 | 1336 | # Add editable options | ||
1856 | 1337 | self.add_option(ListOption('Test', 'testlist', self.testlist, | ||
1857 | 1338 | 'ListOption-Test', 'Testing a ListOption-type ...')) | ||
1858 | 1339 | self.add_option(StringOption('Window', 'name', 'TESTNAME', | ||
1859 | 1340 | 'Testname', 'The name/id of this Screenlet-instance ...'), | ||
1860 | 1341 | realtime=False) | ||
1861 | 1342 | self.add_option(AccountOption('Test', 'pop3_account', | ||
1862 | 1343 | self.pop3_account, 'Username/Password', | ||
1863 | 1344 | 'Enter username/password here ...')) | ||
1864 | 1345 | self.add_option(StringOption('Window', 'name2', 'TESTNAME2', | ||
1865 | 1346 | 'String2', 'Another string-test ...')) | ||
1866 | 1347 | self.add_option(StringOption('Test', 'combo_test', "el1", 'Combo', | ||
1867 | 1348 | 'A StringOption displaying a drop-down-list with choices...', | ||
1868 | 1349 | choices=['el1', 'el2', 'element 3'])) | ||
1869 | 1350 | self.add_option(FloatOption('General', 'flt', 30, | ||
1870 | 1351 | 'A Float', 'Testing a FLOAT-type ...', | ||
1871 | 1352 | min=0, max=gtk.gdk.screen_width(), increment=0.01, digits=4)) | ||
1872 | 1353 | self.add_option(IntOption('General', 'x', 30, | ||
1873 | 1354 | 'X-Position', 'The X-position of this Screenlet ...', | ||
1874 | 1355 | min=0, max=gtk.gdk.screen_width())) | ||
1875 | 1356 | self.add_option(IntOption('General', 'y', 30, | ||
1876 | 1357 | 'Y-Position', 'The Y-position of this Screenlet ...', | ||
1877 | 1358 | min=0, max=gtk.gdk.screen_height())) | ||
1878 | 1359 | self.add_option(IntOption('Test', 'width', 300, | ||
1879 | 1360 | 'Width', 'The width of this Screenlet ...', min=100, max=1000)) | ||
1880 | 1361 | self.add_option(IntOption('Test', 'height', 150, | ||
1881 | 1362 | 'Height', 'The height of this Screenlet ...', | ||
1882 | 1363 | min=100, max=1000)) | ||
1883 | 1364 | self.add_option(BoolOption('General', 'is_sticky', True, | ||
1884 | 1365 | 'Stick to Desktop', 'Show this Screenlet always ...')) | ||
1885 | 1366 | self.add_option(BoolOption('General', 'is_widget', False, | ||
1886 | 1367 | 'Treat as Widget', 'Treat this Screenlet as a "Widget" ...')) | ||
1887 | 1368 | self.add_option(FontOption('Test', 'font', 'Sans 14', | ||
1888 | 1369 | 'Font', 'The font for whatever ...')) | ||
1889 | 1370 | self.add_option(ColorOption('Test', 'color', (1, 0.35, 0.35, 0.7), | ||
1890 | 1371 | 'Color', 'The color for whatever ...')) | ||
1891 | 1372 | self.add_option(FileOption('Test', 'filename', os.environ['HOME'], | ||
1892 | 1373 | 'Filename-Test', 'Testing a FileOption-type ...', | ||
1893 | 1374 | patterns=['*.py', '*.pyc'])) | ||
1894 | 1375 | self.add_option(ImageOption('Test', 'filename2', os.environ['HOME'], | ||
1895 | 1376 | 'Image-Test', 'Testing the ImageOption-type ...')) | ||
1896 | 1377 | self.add_option(DirectoryOption('Test', 'dirname', os.environ['HOME'], | ||
1897 | 1378 | 'Directory-Test', 'Testing a FileOption-type ...')) | ||
1898 | 1379 | self.add_option(TimeOption('Test','time', self.time, | ||
1899 | 1380 | 'TimeOption-Test', 'Testing a TimeOption-type ...')) | ||
1900 | 1381 | # TEST | ||
1901 | 1382 | self.disable_option('width') | ||
1902 | 1383 | self.disable_option('height') | ||
1903 | 1384 | # TEST: load options from file | ||
1904 | 1385 | #self.add_options_from_file('/home/ryx/Desktop/python/screenlets/screenlets-0.0.9/src/share/screenlets/Notes/options.xml') | ||
1905 | 1386 | |||
1906 | 1387 | def __setattr__(self, name, value): | ||
1907 | 1388 | self.__dict__[name] = value | ||
1908 | 1389 | print name + "=" + str(value) | ||
1909 | 1390 | |||
1910 | 1391 | def get_short_name(self): | ||
1911 | 1392 | return self.__class__.__name__[:-6] | ||
1912 | 1393 | |||
1913 | 1394 | |||
1914 | 1395 | |||
1915 | 1396 | # this is only for testing - should be a Screenlet | ||
1916 | 1397 | class TestChildObject (TestObject): | ||
1917 | 1398 | |||
1918 | 1399 | uses_theme = True | ||
1919 | 1400 | theme_name = 'test' | ||
1920 | 1401 | |||
1921 | 1402 | def __init__ (self): | ||
1922 | 1403 | TestObject.__init__(self) | ||
1923 | 1404 | self.add_option(StringOption('Test', 'anothertest', 'ksjhsjgd', | ||
1924 | 1405 | 'Another Test', 'An attribute in the subclass ...')) | ||
1925 | 1406 | self.add_option(StringOption('Test', 'theme_name', self.theme_name, | ||
1926 | 1407 | 'Theme', 'The theme for this Screenelt ...', | ||
1927 | 1408 | choices=['test1', 'test2', 'mytheme', 'blue', 'test'])) | ||
1928 | 1409 | |||
1929 | 1410 | |||
1930 | 1411 | # TEST: load/save | ||
1931 | 1412 | # TEST: option-editing | ||
1932 | 1413 | to = TestChildObject() | ||
1933 | 1414 | #print to.export_options_as_list() | ||
1934 | 1415 | se = OptionsDialog(500, 380)#, treeview=True) | ||
1935 | 1416 | #img = gtk.image_new_from_stock(gtk.STOCK_ABOUT, 5) | ||
1936 | 1417 | img = gtk.Image() | ||
1937 | 1418 | img.set_from_file('../share/screenlets/Notes/icon.svg') | ||
1938 | 1419 | se.set_info('TestOptions', | ||
1939 | 1420 | 'A test for an extended options-dialog with embedded about-info.' + | ||
1940 | 1421 | ' Can be used for the Screenlets to have all in one ...\nNOTE:' + | ||
1941 | 1422 | '<span color="red"> ONLY A TEST!</span>', | ||
1942 | 1423 | '(c) RYX 2007', version='v0.0.1', icon=img) | ||
1943 | 1424 | se.show_options_for_object(to) | ||
1944 | 1425 | resp = se.run() | ||
1945 | 1426 | if resp == gtk.RESPONSE_OK: | ||
1946 | 1427 | print "OK" | ||
1947 | 1428 | else: | ||
1948 | 1429 | print "Cancelled." | ||
1949 | 1430 | se.destroy() | ||
1950 | 1431 | print to.export_options_as_list() | ||
1951 | 1432 | |||
1952 | 1433 | 0 | ||
1953 | === added file 'src/lib/options/__init__.py' | |||
1954 | --- src/lib/options/__init__.py 1970-01-01 00:00:00 +0000 | |||
1955 | +++ src/lib/options/__init__.py 2011-03-28 16:19:24 +0000 | |||
1956 | @@ -0,0 +1,202 @@ | |||
1957 | 1 | # This application is released under the GNU General Public License | ||
1958 | 2 | # v3 (or, at your option, any later version). You can find the full | ||
1959 | 3 | # text of the license under http://www.gnu.org/licenses/gpl.txt. | ||
1960 | 4 | # By using, editing and/or distributing this software you agree to | ||
1961 | 5 | # the terms and conditions of this license. | ||
1962 | 6 | # Thank you for using free software! | ||
1963 | 7 | |||
1964 | 8 | # Options-system (c) RYX (aka Rico Pfaus) 2007 <ryx@ryxperience.com> | ||
1965 | 9 | # Heavily Refactored by Martin Owens (c) 2009 | ||
1966 | 10 | # | ||
1967 | 11 | # INFO: | ||
1968 | 12 | # - a dynamic Options-system that allows very easy creation of | ||
1969 | 13 | # objects with embedded configuration-system. | ||
1970 | 14 | # NOTE: The Dialog is not very nice yet - it is not good OOP-practice | ||
1971 | 15 | # because too big functions and bad class-layout ... but it works | ||
1972 | 16 | # for now ... :) | ||
1973 | 17 | # | ||
1974 | 18 | # TODO: | ||
1975 | 19 | # - option-widgets for all option-types (e.g. ListOptionWidget, ColorOptionWidget) | ||
1976 | 20 | # - OptionGroup-class instead of (or behind) add_options_group | ||
1977 | 21 | # - TimeOption, DateOption | ||
1978 | 22 | # - FileOption needs filter/limit-attribute | ||
1979 | 23 | # - allow options to disable/enable other options | ||
1980 | 24 | # - support for EditableOptions-subclasses as options | ||
1981 | 25 | # - separate OptionEditorWidget from Editor-Dialog | ||
1982 | 26 | # - place ui-code into screenlets.options.ui-module | ||
1983 | 27 | # - create own widgets for each Option-subclass | ||
1984 | 28 | # | ||
1985 | 29 | |||
1986 | 30 | import screenlets | ||
1987 | 31 | |||
1988 | 32 | import os | ||
1989 | 33 | import gtk, gobject | ||
1990 | 34 | |||
1991 | 35 | # translation stuff | ||
1992 | 36 | import gettext | ||
1993 | 37 | gettext.textdomain('screenlets') | ||
1994 | 38 | gettext.bindtextdomain('screenlets', screenlets.INSTALL_PREFIX + '/share/locale') | ||
1995 | 39 | |||
1996 | 40 | def _(s): | ||
1997 | 41 | return gettext.gettext(s) | ||
1998 | 42 | |||
1999 | 43 | from boolean_option import BoolOption | ||
2000 | 44 | from string_option import StringOption | ||
2001 | 45 | from number_option import IntOption, FloatOption | ||
2002 | 46 | from list_option import ListOption | ||
2003 | 47 | from account_option import AccountOption | ||
2004 | 48 | from font_option import FontOption | ||
2005 | 49 | from file_option import FileOption, DirectoryOption, ImageOption | ||
2006 | 50 | from colour_option import ColorOption, ColorsOption | ||
2007 | 51 | from time_option import TimeOption | ||
2008 | 52 | from base import EditableOptions, OptionsDialog | ||
2009 | 53 | |||
2010 | 54 | # ------ ONLY FOR TESTING ------------------: | ||
2011 | 55 | if __name__ == "__main__": | ||
2012 | 56 | |||
2013 | 57 | import os | ||
2014 | 58 | |||
2015 | 59 | # this is only for testing - should be a Screenlet | ||
2016 | 60 | class TestObject (EditableOptions): | ||
2017 | 61 | |||
2018 | 62 | testlist = ['test1', 'test2', 3, 5, 'Noch ein Test'] | ||
2019 | 63 | pop3_account = ('Username', '') | ||
2020 | 64 | |||
2021 | 65 | # TEST | ||
2022 | 66 | pin_x = 100 | ||
2023 | 67 | pin_y = 6 | ||
2024 | 68 | text_x = 19 | ||
2025 | 69 | text_y = 35 | ||
2026 | 70 | font_name = 'Sans 12' | ||
2027 | 71 | rgba_color = (0.0, 0.0, 1.0, 1.0) | ||
2028 | 72 | text_prefix = '<b>' | ||
2029 | 73 | text_suffix = '</b>' | ||
2030 | 74 | note_text = "" # hidden option because val has its own editing-dialog | ||
2031 | 75 | random_pin_pos = True | ||
2032 | 76 | opt1 = 'testval 1' | ||
2033 | 77 | opt2 = 'testval 2' | ||
2034 | 78 | filename2 = '' | ||
2035 | 79 | filename = '' | ||
2036 | 80 | dirname = '' | ||
2037 | 81 | font = 'Sans 12' | ||
2038 | 82 | color = (0.1, 0.5, 0.9, 0.9) | ||
2039 | 83 | name = 'a name' | ||
2040 | 84 | name2 = 'another name' | ||
2041 | 85 | combo_test = 'el2' | ||
2042 | 86 | flt = 0.5 | ||
2043 | 87 | x = 10 | ||
2044 | 88 | y = 25 | ||
2045 | 89 | width = 30 | ||
2046 | 90 | height = 50 | ||
2047 | 91 | is_sticky = False | ||
2048 | 92 | is_widget = False | ||
2049 | 93 | time = (12, 32, 49) # a time-value (tuple with ints) | ||
2050 | 94 | |||
2051 | 95 | def __init__ (self): | ||
2052 | 96 | EditableOptions.__init__(self) | ||
2053 | 97 | # Add group | ||
2054 | 98 | self.add_options_group('General', | ||
2055 | 99 | 'The general options for this Object ...') | ||
2056 | 100 | self.add_options_group('Window', | ||
2057 | 101 | 'The Window-related options for this Object ...') | ||
2058 | 102 | self.add_options_group('Test', 'A Test-group ...') | ||
2059 | 103 | # Add editable options | ||
2060 | 104 | self.add_option(ListOption('Test', 'testlist', default=self.testlist, | ||
2061 | 105 | label='ListOption-Test', desc='Testing a ListOption-type ...')) | ||
2062 | 106 | self.add_option(StringOption('Window', 'name', default='TESTNAME', | ||
2063 | 107 | label='Testname', desc='The name/id of this Screenlet-instance ...'), | ||
2064 | 108 | realtime=False) | ||
2065 | 109 | self.add_option(AccountOption('Test', 'pop3_account', | ||
2066 | 110 | default=self.pop3_account, label='Username/Password', | ||
2067 | 111 | desc='Enter username/password here ...')) | ||
2068 | 112 | self.add_option(StringOption('Window', 'name2', default='TESTNAME2', | ||
2069 | 113 | label='String2', desc='Another string-test ...')) | ||
2070 | 114 | self.add_option(StringOption('Test', 'combo_test', default="el1", | ||
2071 | 115 | label='Combo', desc='A StringOption for a drop-down-list.', | ||
2072 | 116 | choices=['el1', 'el2', 'element 3'])) | ||
2073 | 117 | self.add_option(FloatOption('General', 'flt', default=30.4, | ||
2074 | 118 | label='A Float', desc='Testing a FLOAT-type ...', | ||
2075 | 119 | min=0, max=gtk.gdk.screen_width(), increment=0.01, digits=4)) | ||
2076 | 120 | self.add_option(IntOption('General', 'x', default=30, | ||
2077 | 121 | label='X-Position', desc='The X-position of this Screenlet ...', | ||
2078 | 122 | min=0, max=gtk.gdk.screen_width())) | ||
2079 | 123 | self.add_option(IntOption('General', 'y', default=30, | ||
2080 | 124 | label='Y-Position', desc='The Y-position of this Screenlet ...', | ||
2081 | 125 | min=0, max=gtk.gdk.screen_height())) | ||
2082 | 126 | self.add_option(IntOption('Test', 'width', default=300, | ||
2083 | 127 | label='Width', desc='The width of this Screenlet ...', | ||
2084 | 128 | min=100, max=1000, increment=12)) | ||
2085 | 129 | self.add_option(IntOption('Test', 'height', default=150, | ||
2086 | 130 | label='Height', desc='The height of this Screenlet ...', | ||
2087 | 131 | min=100, max=1000)) | ||
2088 | 132 | self.add_option(BoolOption('General', 'is_sticky', default=True, | ||
2089 | 133 | label='Stick to Desktop', desc='Show this Screenlet always ...')) | ||
2090 | 134 | self.add_option(BoolOption('General', 'is_widget', default=False, | ||
2091 | 135 | label='Treat as Widget', desc='Treat this Screenlet as a "Widget" ...')) | ||
2092 | 136 | self.add_option(FontOption('Test', 'font', default='Sans 14', | ||
2093 | 137 | label='Font', desc='The font for whatever ...')) | ||
2094 | 138 | self.add_option(ColorOption('Test', 'color', default=(1, 0.35, 0.35, 0.7), | ||
2095 | 139 | label='Color', desc='The color for whatever ...')) | ||
2096 | 140 | 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)], | ||
2097 | 141 | label='Multi-Colours', desc='The colors for whatever ...')) | ||
2098 | 142 | self.add_option(ColorsOption('Test', 'rainbow2', default=(1, 0.35, 0.35, 0.7), | ||
2099 | 143 | label='Colours-Up', desc='The colors for whatever ...')) | ||
2100 | 144 | self.add_option(FileOption('Test', 'filename', default=os.environ['HOME'], | ||
2101 | 145 | label='Filename-Test', desc='Testing a FileOption-type ...', | ||
2102 | 146 | patterns=[ ( 'Python Files', ['*.py', '*.pyc'] ) ])) | ||
2103 | 147 | self.add_option(ImageOption('Test', 'filename2', default=os.environ['HOME'], | ||
2104 | 148 | label='Image-Test', desc='Testing the ImageOption-type ...')) | ||
2105 | 149 | self.add_option(DirectoryOption('Test', 'dirname', default=os.environ['HOME'], | ||
2106 | 150 | label='Directory-Test', desc='Testing a FileOption-type ...')) | ||
2107 | 151 | self.add_option(TimeOption('Test','time', default=self.time, | ||
2108 | 152 | label='TimeOption-Test', desc='Testing a TimeOption-type ...')) | ||
2109 | 153 | # TEST | ||
2110 | 154 | self.disable_option('width') | ||
2111 | 155 | self.disable_option('height') | ||
2112 | 156 | # TEST: load options from file | ||
2113 | 157 | #self.add_options_from_file('/home/ryx/Desktop/python/screenlets/screenlets-0.0.9/src/share/screenlets/Notes/options.xml') | ||
2114 | 158 | |||
2115 | 159 | def __setattr__(self, name, value): | ||
2116 | 160 | self.__dict__[name] = value | ||
2117 | 161 | print name + "=" + str(value) | ||
2118 | 162 | |||
2119 | 163 | def get_short_name(self): | ||
2120 | 164 | return self.__class__.__name__[:-6] | ||
2121 | 165 | |||
2122 | 166 | |||
2123 | 167 | # this is only for testing - should be a Screenlet | ||
2124 | 168 | class TestChildObject (TestObject): | ||
2125 | 169 | |||
2126 | 170 | uses_theme = True | ||
2127 | 171 | theme_name = 'test' | ||
2128 | 172 | |||
2129 | 173 | def __init__ (self): | ||
2130 | 174 | TestObject.__init__(self) | ||
2131 | 175 | self.add_option(StringOption('Test', 'anothertest', default='ksjhsjgd', | ||
2132 | 176 | label='Another Test', desc='An attribute in the subclass ...')) | ||
2133 | 177 | self.add_option(StringOption('Test', 'theme_name', default=self.theme_name, | ||
2134 | 178 | label='Theme', desc='The theme for this Screenelt ...', | ||
2135 | 179 | choices=['test1', 'test2', 'mytheme', 'blue', 'test'])) | ||
2136 | 180 | |||
2137 | 181 | # TEST: load/save | ||
2138 | 182 | # TEST: option-editing | ||
2139 | 183 | to = TestChildObject() | ||
2140 | 184 | #print to.export_options_as_list() | ||
2141 | 185 | se = OptionsDialog(500, 380)#, treeview=True) | ||
2142 | 186 | #img = gtk.image_new_from_stock(gtk.STOCK_ABOUT, 5) | ||
2143 | 187 | img = gtk.Image() | ||
2144 | 188 | img.set_from_file('../share/screenlets/Notes/icon.svg') | ||
2145 | 189 | se.set_info('TestOptions', | ||
2146 | 190 | 'A test for an extended options-dialog with embedded about-info.' + | ||
2147 | 191 | ' Can be used for the Screenlets to have all in one ...\nNOTE:' + | ||
2148 | 192 | '<span color="red"> ONLY A TEST!</span>', | ||
2149 | 193 | '(c) RYX 2007', version='v0.0.1', icon=img) | ||
2150 | 194 | se.show_options_for_object(to) | ||
2151 | 195 | resp = se.run() | ||
2152 | 196 | if resp == gtk.RESPONSE_OK: | ||
2153 | 197 | print "OK" | ||
2154 | 198 | else: | ||
2155 | 199 | print "Cancelled." | ||
2156 | 200 | se.destroy() | ||
2157 | 201 | print to.export_options_as_list() | ||
2158 | 202 | |||
2159 | 0 | 203 | ||
2160 | === added file 'src/lib/options/account_option.py' | |||
2161 | --- src/lib/options/account_option.py 1970-01-01 00:00:00 +0000 | |||
2162 | +++ src/lib/options/account_option.py 2011-03-28 16:19:24 +0000 | |||
2163 | @@ -0,0 +1,143 @@ | |||
2164 | 1 | # | ||
2165 | 2 | # Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@gmail.com> | ||
2166 | 3 | # | ||
2167 | 4 | # This program is free software; you can redistribute it and/or modify | ||
2168 | 5 | # it under the terms of the GNU General Public License as published by | ||
2169 | 6 | # the Free Software Foundation; either version 3 of the License, or | ||
2170 | 7 | # (at your option) any later version. | ||
2171 | 8 | # | ||
2172 | 9 | # This program is distributed in the hope that it will be useful, | ||
2173 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2174 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2175 | 12 | # GNU General Public License for more details. | ||
2176 | 13 | # | ||
2177 | 14 | # You should have received a copy of the GNU General Public License | ||
2178 | 15 | # along with this program; if not, write to the Free Software | ||
2179 | 16 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
2180 | 17 | # | ||
2181 | 18 | """ | ||
2182 | 19 | Account options, these classes will display a text box. | ||
2183 | 20 | """ | ||
2184 | 21 | |||
2185 | 22 | import gtk | ||
2186 | 23 | import gnomekeyring | ||
2187 | 24 | |||
2188 | 25 | from screenlets.options import _ | ||
2189 | 26 | from base import Option | ||
2190 | 27 | |||
2191 | 28 | class AccountOption(Option): | ||
2192 | 29 | """ | ||
2193 | 30 | An Option-type for username/password combos. Stores the password in | ||
2194 | 31 | the gnome-keyring (if available) and only saves username and auth_token | ||
2195 | 32 | through the screenlets-backend. | ||
2196 | 33 | TODO: | ||
2197 | 34 | - not create new token for any change (use "set" instead of "create" if | ||
2198 | 35 | the given item already exists) | ||
2199 | 36 | - use usual storage if no keyring is available but output warning | ||
2200 | 37 | - on_delete-function for removing the data from keyring when the | ||
2201 | 38 | Screenlet holding the option gets deleted | ||
2202 | 39 | """ | ||
2203 | 40 | protected = True | ||
2204 | 41 | |||
2205 | 42 | def __init__(self, group, name, *attr, **args): | ||
2206 | 43 | super(AccountOption, self).__init__ (group, name, *attr, **args) | ||
2207 | 44 | # check for availability of keyring | ||
2208 | 45 | if not gnomekeyring.is_available(): | ||
2209 | 46 | raise Exception('GnomeKeyring is not available!!') | ||
2210 | 47 | # THIS IS A WORKAROUND FOR A BUG IN KEYRING (usually we would use | ||
2211 | 48 | # gnomekeyring.get_default_keyring_sync() here): | ||
2212 | 49 | # find first available keyring | ||
2213 | 50 | self.keyring_list = gnomekeyring.list_keyring_names_sync() | ||
2214 | 51 | if len(self.keyring_list) == 0: | ||
2215 | 52 | raise Exception('No keyrings found. Please create one first!') | ||
2216 | 53 | else: | ||
2217 | 54 | # we prefer the default keyring | ||
2218 | 55 | try: | ||
2219 | 56 | self.keyring = gnomekeyring.get_default_keyring_sync() | ||
2220 | 57 | except: | ||
2221 | 58 | if "session" in self.keyring_list: | ||
2222 | 59 | print "Warning: No default keyring found, using session keyring. Storage is not permanent!" | ||
2223 | 60 | self.keyring = "session" | ||
2224 | 61 | else: | ||
2225 | 62 | print "Warning: Neither default nor session keyring found, assuming keyring %s!" % self.keyring_list[0] | ||
2226 | 63 | self.keyring = self.keyring_list[0] | ||
2227 | 64 | |||
2228 | 65 | |||
2229 | 66 | def on_import(self, strvalue): | ||
2230 | 67 | """Import account info from a string (like 'username:auth_token'),. | ||
2231 | 68 | retrieve the password from the storage and return a tuple containing | ||
2232 | 69 | username and password.""" | ||
2233 | 70 | # split string into username/auth_token | ||
2234 | 71 | #data = strvalue.split(':', 1) | ||
2235 | 72 | (name, auth_token) = strvalue.split(':', 1) | ||
2236 | 73 | if name and auth_token: | ||
2237 | 74 | # read pass from storage | ||
2238 | 75 | try: | ||
2239 | 76 | pw = gnomekeyring.item_get_info_sync(self.keyring, | ||
2240 | 77 | int(auth_token)).get_secret() | ||
2241 | 78 | except Exception, ex: | ||
2242 | 79 | print "ERROR: Unable to read password from keyring: %s" % ex | ||
2243 | 80 | pw = '' | ||
2244 | 81 | # return | ||
2245 | 82 | return (name, pw) | ||
2246 | 83 | else: | ||
2247 | 84 | raise Exception('Illegal value in AccountOption.on_import.') | ||
2248 | 85 | |||
2249 | 86 | def on_export(self, value): | ||
2250 | 87 | """Export the given tuple/list containing a username and a password. The | ||
2251 | 88 | function stores the password in the gnomekeyring and returns a | ||
2252 | 89 | string in form 'username:auth_token'.""" | ||
2253 | 90 | # store password in storage | ||
2254 | 91 | attribs = dict(name=value[0]) | ||
2255 | 92 | auth_token = gnomekeyring.item_create_sync(self.keyring, | ||
2256 | 93 | gnomekeyring.ITEM_GENERIC_SECRET, value[0], attribs, value[1], True) | ||
2257 | 94 | # build value from username and auth_token | ||
2258 | 95 | return value[0] + ':' + str(auth_token) | ||
2259 | 96 | |||
2260 | 97 | def generate_widget(self, value): | ||
2261 | 98 | """Generate a textbox for a account options""" | ||
2262 | 99 | self.widget = gtk.HBox() | ||
2263 | 100 | vb = gtk.VBox() | ||
2264 | 101 | input_name = gtk.Entry() | ||
2265 | 102 | input_name.set_text(value[0]) | ||
2266 | 103 | input_name.show() | ||
2267 | 104 | input_pass = gtk.Entry() | ||
2268 | 105 | input_pass.set_visibility(False) # password | ||
2269 | 106 | input_pass.set_text(value[1]) | ||
2270 | 107 | input_pass.show() | ||
2271 | 108 | but = gtk.Button(_('Apply'), gtk.STOCK_APPLY) | ||
2272 | 109 | but.show() | ||
2273 | 110 | but.connect("clicked", self.has_changed) | ||
2274 | 111 | vb.add(input_name) | ||
2275 | 112 | vb.add(input_pass) | ||
2276 | 113 | vb.show() | ||
2277 | 114 | but.set_tooltip_text(_('Apply username/password ...')) | ||
2278 | 115 | input_name.set_tooltip_text(_('Enter username here ...')) | ||
2279 | 116 | input_pass.set_tooltip_text(_('Enter password here ...')) | ||
2280 | 117 | self.widget.add(vb) | ||
2281 | 118 | self.widget.add(but) | ||
2282 | 119 | return self.widget | ||
2283 | 120 | |||
2284 | 121 | def set_value(self, value): | ||
2285 | 122 | """Set the account value as required.""" | ||
2286 | 123 | self.value = value | ||
2287 | 124 | |||
2288 | 125 | def has_changed(self, widget): | ||
2289 | 126 | """Executed when the widget event kicks off.""" | ||
2290 | 127 | # the widget is a HBox containing a VBox containing two Entries | ||
2291 | 128 | # (ideally we should have a custom widget for the AccountOption) | ||
2292 | 129 | for c in self.widget.get_children(): | ||
2293 | 130 | if c.__class__ == gtk.VBox: | ||
2294 | 131 | c2 = c.get_children() | ||
2295 | 132 | self.value = (c2[0].get_text(), c2[1].get_text()) | ||
2296 | 133 | super(AccountOption, self).has_changed() | ||
2297 | 134 | |||
2298 | 135 | """#TEST: | ||
2299 | 136 | o = AccountOption('None', 'pop3_account', ('',''), 'Username/Password', 'Enter username/password here ...') | ||
2300 | 137 | # save option to keyring | ||
2301 | 138 | exported_account = o.on_export(('RYX', 'mysecretpassword')) | ||
2302 | 139 | print exported_account | ||
2303 | 140 | # and read option back from keyring | ||
2304 | 141 | print o.on_import(exported_account) | ||
2305 | 142 | """ | ||
2306 | 143 | |||
2307 | 0 | 144 | ||
2308 | === added file 'src/lib/options/base.py' | |||
2309 | --- src/lib/options/base.py 1970-01-01 00:00:00 +0000 | |||
2310 | +++ src/lib/options/base.py 2011-03-28 16:19:24 +0000 | |||
2311 | @@ -0,0 +1,624 @@ | |||
2312 | 1 | # | ||
2313 | 2 | # Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@gmail.com> | ||
2314 | 3 | # | ||
2315 | 4 | # This program is free software; you can redistribute it and/or modify | ||
2316 | 5 | # it under the terms of the GNU General Public License as published by | ||
2317 | 6 | # the Free Software Foundation; either version 3 of the License, or | ||
2318 | 7 | # (at your option) any later version. | ||
2319 | 8 | # | ||
2320 | 9 | # This program is distributed in the hope that it will be useful, | ||
2321 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2322 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2323 | 12 | # GNU General Public License for more details. | ||
2324 | 13 | # | ||
2325 | 14 | # You should have received a copy of the GNU General Public License | ||
2326 | 15 | # along with this program; if not, write to the Free Software | ||
2327 | 16 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
2328 | 17 | # | ||
2329 | 18 | """ | ||
2330 | 19 | Base classes and basic mechanics for all screenlet options. | ||
2331 | 20 | """ | ||
2332 | 21 | |||
2333 | 22 | import screenlets | ||
2334 | 23 | from screenlets.options import _ | ||
2335 | 24 | |||
2336 | 25 | import os | ||
2337 | 26 | import gtk, gobject | ||
2338 | 27 | import xml.dom.minidom | ||
2339 | 28 | from xml.dom.minidom import Node | ||
2340 | 29 | |||
2341 | 30 | # ----------------------------------------------------------------------- | ||
2342 | 31 | # Option-classes and subclasses | ||
2343 | 32 | # ----------------------------------------------------------------------- | ||
2344 | 33 | |||
2345 | 34 | class Option(gobject.GObject): | ||
2346 | 35 | """An Option stores information about a certain object-attribute. It doesn't | ||
2347 | 36 | carry information about the value or the object it belongs to - it is only a | ||
2348 | 37 | one-way data-storage for describing how to handle attributes.""" | ||
2349 | 38 | __gsignals__ = dict(option_changed=(gobject.SIGNAL_RUN_FIRST, | ||
2350 | 39 | gobject.TYPE_NONE, (gobject.TYPE_OBJECT,))) | ||
2351 | 40 | default = None | ||
2352 | 41 | label = None | ||
2353 | 42 | desc = None | ||
2354 | 43 | hidden = False | ||
2355 | 44 | disabled = False | ||
2356 | 45 | realtime = True | ||
2357 | 46 | protected = False | ||
2358 | 47 | widget = None | ||
2359 | 48 | |||
2360 | 49 | def __init__ (self, group, name, *attr, **args): | ||
2361 | 50 | """Creates a new Option with the given information.""" | ||
2362 | 51 | super(Option, self).__init__() | ||
2363 | 52 | self.name = name | ||
2364 | 53 | self.group = group | ||
2365 | 54 | # To maintain compatability, we parse out the 3 attributes and | ||
2366 | 55 | # Move into known arguments. | ||
2367 | 56 | if len(attr) == 3: | ||
2368 | 57 | args.setdefault('default', attr[0]) | ||
2369 | 58 | args.setdefault('label', attr[1]) | ||
2370 | 59 | args.setdefault('desc', attr[2]) | ||
2371 | 60 | # This should allow any of the class options to be set on init. | ||
2372 | 61 | for name in args.keys(): | ||
2373 | 62 | if hasattr(self, name): | ||
2374 | 63 | # Replaces class variables (defaults) with object vars | ||
2375 | 64 | setattr(self, name, args[name]) | ||
2376 | 65 | |||
2377 | 66 | # XXX for groups (TODO: OptionGroup) | ||
2378 | 67 | # XXX callback to be notified when this option changes | ||
2379 | 68 | # XXX real-time update? | ||
2380 | 69 | # XXX protected from get/set through service | ||
2381 | 70 | |||
2382 | 71 | def on_import(self, strvalue): | ||
2383 | 72 | """Callback - called when an option gets imported from a string. | ||
2384 | 73 | This function MUST return the string-value converted to the required | ||
2385 | 74 | type!""" | ||
2386 | 75 | return strvalue.replace("\\n", "\n") | ||
2387 | 76 | |||
2388 | 77 | def on_export(self, value): | ||
2389 | 78 | """Callback - called when an option gets exported to a string. The | ||
2390 | 79 | value-argument needs to be converted to a string that can be imported | ||
2391 | 80 | by the on_import-handler. This handler MUST return the value | ||
2392 | 81 | converted to a string!""" | ||
2393 | 82 | return str(value).replace("\n", "\\n") | ||
2394 | 83 | |||
2395 | 84 | def generate_widget(self): | ||
2396 | 85 | """This should generate all the required widgets for display.""" | ||
2397 | 86 | raise NotImplementedError, "Generating Widget should be done in child" | ||
2398 | 87 | |||
2399 | 88 | def set_value(self, value): | ||
2400 | 89 | """Set the true/false value to the checkbox widget""" | ||
2401 | 90 | raise NotImplementedError, "Can't update the widget and local value" | ||
2402 | 91 | |||
2403 | 92 | def has_changed(self): | ||
2404 | 93 | """Executed when the widget event kicks off.""" | ||
2405 | 94 | return self.emit("option_changed", self) | ||
2406 | 95 | |||
2407 | 96 | |||
2408 | 97 | def create_option_from_node (node, groupname): | ||
2409 | 98 | """Create an Option from an XML-node with option-metadata.""" | ||
2410 | 99 | #print "TODO OPTION: " + str(cn) | ||
2411 | 100 | otype = node.getAttribute("type") | ||
2412 | 101 | oname = node.getAttribute("name") | ||
2413 | 102 | ohidden = node.getAttribute("hidden") | ||
2414 | 103 | options = { | ||
2415 | 104 | 'default' : None, | ||
2416 | 105 | 'info' : '', | ||
2417 | 106 | 'label' : '', | ||
2418 | 107 | 'min' : None, | ||
2419 | 108 | 'max' : None, | ||
2420 | 109 | 'increment' : 1, | ||
2421 | 110 | 'choices' : '', | ||
2422 | 111 | 'digits' : None, | ||
2423 | 112 | } | ||
2424 | 113 | if otype and oname: | ||
2425 | 114 | # parse children of option-node and save all useful attributes | ||
2426 | 115 | for attr in node.childNodes: | ||
2427 | 116 | if attr.nodeType == Node.ELEMENT_NODE and attr.nodeName in options.keys(): | ||
2428 | 117 | options[attr.nodeName] = attr.firstChild.nodeValue | ||
2429 | 118 | # if we have all needed values, create the Option | ||
2430 | 119 | if options['default']: | ||
2431 | 120 | # create correct classname here | ||
2432 | 121 | cls = otype[0].upper() + otype.lower()[1:] + 'Option' | ||
2433 | 122 | #print 'Create: ' +cls +' / ' + oname + ' ('+otype+')' | ||
2434 | 123 | # and build new instance (we use on_import for setting default val) | ||
2435 | 124 | clsobj = getattr(__import__(__name__), cls) | ||
2436 | 125 | opt = clsobj(groupname, oname, default=None, | ||
2437 | 126 | label=options['label'], desc=options['info']) | ||
2438 | 127 | opt.default = opt.on_import(options['default']) | ||
2439 | 128 | # set values to the correct types | ||
2440 | 129 | if cls == 'IntOption': | ||
2441 | 130 | if options['min']: | ||
2442 | 131 | opt.min = int(options['min']) | ||
2443 | 132 | if options['max']: | ||
2444 | 133 | opt.max = int(options['max']) | ||
2445 | 134 | if options['increment']: | ||
2446 | 135 | opt.increment = int(options['increment']) | ||
2447 | 136 | elif cls == 'FloatOption': | ||
2448 | 137 | if options['digits']: | ||
2449 | 138 | opt.digits = int(options['digits']) | ||
2450 | 139 | if options['min']: | ||
2451 | 140 | opt.min = float(options['min']) | ||
2452 | 141 | if options['min']: | ||
2453 | 142 | opt.max = float(options['max']) | ||
2454 | 143 | if options['increment']: | ||
2455 | 144 | opt.increment = float(options['increment']) | ||
2456 | 145 | elif cls == 'StringOption': | ||
2457 | 146 | if options['choices']: | ||
2458 | 147 | opt.choices = options['choices'] | ||
2459 | 148 | return opt | ||
2460 | 149 | return None | ||
2461 | 150 | |||
2462 | 151 | |||
2463 | 152 | class EditableOptions(object): | ||
2464 | 153 | """The EditableOptions can be inherited from to allow objects to export | ||
2465 | 154 | editable options for editing them with the OptionsEditor-class. | ||
2466 | 155 | NOTE: This could use some improvement and is very poorly coded :) ...""" | ||
2467 | 156 | |||
2468 | 157 | def __init__ (self): | ||
2469 | 158 | self.__options__ = [] | ||
2470 | 159 | self.__options_groups__ = {} | ||
2471 | 160 | # This is a workaround to remember the order of groups | ||
2472 | 161 | self.__options_groups_ordered__ = [] | ||
2473 | 162 | |||
2474 | 163 | def add_option (self, option, callback=None, realtime=True): | ||
2475 | 164 | """Add an editable option to this object. Editable Options can be edited | ||
2476 | 165 | and configured using the OptionsDialog. The optional callback-arg can be | ||
2477 | 166 | used to set a callback that gets notified when the option changes its | ||
2478 | 167 | value.""" | ||
2479 | 168 | #print "Add option: "+option.name | ||
2480 | 169 | # if option already editable (i.e. initialized), return | ||
2481 | 170 | for o in self.__options__: | ||
2482 | 171 | if o.name == option.name: | ||
2483 | 172 | return False | ||
2484 | 173 | self.__dict__[option.name] = option.default | ||
2485 | 174 | # set auto-update (TEMPORARY?) | ||
2486 | 175 | option.realtime = realtime | ||
2487 | 176 | # add option to group (output error if group is undefined) | ||
2488 | 177 | try: | ||
2489 | 178 | self.__options_groups__[option.group]['options'].append(option) | ||
2490 | 179 | except: | ||
2491 | 180 | print "Options: Error - group %s not defined." % option.group | ||
2492 | 181 | return False | ||
2493 | 182 | # now add the option | ||
2494 | 183 | self.__options__.append(option) | ||
2495 | 184 | # if callback is set, add callback | ||
2496 | 185 | if callback: | ||
2497 | 186 | option.connect("option_changed", callback) | ||
2498 | 187 | option.connect("option_changed", self.callback_value_changed) | ||
2499 | 188 | return True | ||
2500 | 189 | |||
2501 | 190 | |||
2502 | 191 | def add_options_group (self, name, group_info): | ||
2503 | 192 | """Add a new options-group to this Options-object""" | ||
2504 | 193 | self.__options_groups__[name] = {'label':name, | ||
2505 | 194 | 'info':group_info, 'options':[]} | ||
2506 | 195 | self.__options_groups_ordered__.append(name) | ||
2507 | 196 | #print self.options_groups | ||
2508 | 197 | |||
2509 | 198 | def disable_option(self, name): | ||
2510 | 199 | """Disable the inputs for a certain Option.""" | ||
2511 | 200 | for o in self.__options__: | ||
2512 | 201 | if o.name == name: | ||
2513 | 202 | o.disabled = True | ||
2514 | 203 | return True | ||
2515 | 204 | return False | ||
2516 | 205 | |||
2517 | 206 | def enable_option(self, name): | ||
2518 | 207 | """Enable the inputs for a certain Option.""" | ||
2519 | 208 | for o in self.__options__: | ||
2520 | 209 | if o.name == name: | ||
2521 | 210 | o.disabled = False | ||
2522 | 211 | return True | ||
2523 | 212 | return False | ||
2524 | 213 | |||
2525 | 214 | def export_options_as_list(self): | ||
2526 | 215 | """Returns all editable options within a list (without groups) | ||
2527 | 216 | as key/value tuples.""" | ||
2528 | 217 | lst = [] | ||
2529 | 218 | for o in self.__options__: | ||
2530 | 219 | lst.append((o.name, o.on_export(getattr(self, o.name)))) | ||
2531 | 220 | return lst | ||
2532 | 221 | |||
2533 | 222 | def callback_value_changed(self, sender, option): | ||
2534 | 223 | """Called when a widget has updated and this needs calling.""" | ||
2535 | 224 | if hasattr(self, option.name): | ||
2536 | 225 | return setattr(self, option.name, option.value) | ||
2537 | 226 | raise KeyError, "Callback tried to set an option that wasn't defined." | ||
2538 | 227 | |||
2539 | 228 | def get_option_by_name (self, name): | ||
2540 | 229 | """Returns an option in this Options by it's name (or None). | ||
2541 | 230 | TODO: this gives wrong results in childclasses ... maybe access | ||
2542 | 231 | as class-attribute??""" | ||
2543 | 232 | for o in self.__options__: | ||
2544 | 233 | if o.name == name: | ||
2545 | 234 | return o | ||
2546 | 235 | return None | ||
2547 | 236 | |||
2548 | 237 | def remove_option (self, name): | ||
2549 | 238 | """Remove an option from this Options.""" | ||
2550 | 239 | for o in self.__options__: | ||
2551 | 240 | if o.name == name: | ||
2552 | 241 | del o | ||
2553 | 242 | return True | ||
2554 | 243 | return True | ||
2555 | 244 | |||
2556 | 245 | def add_options_from_file (self, filename): | ||
2557 | 246 | """This function creates options from an XML-file with option-metadata. | ||
2558 | 247 | TODO: make this more reusable and place it into module (once the groups | ||
2559 | 248 | are own objects)""" | ||
2560 | 249 | # create xml document | ||
2561 | 250 | try: | ||
2562 | 251 | doc = xml.dom.minidom.parse(filename) | ||
2563 | 252 | except: | ||
2564 | 253 | raise Exception('Invalid XML in metadata-file (or file missing): "%s".' % filename) | ||
2565 | 254 | # get rootnode | ||
2566 | 255 | root = doc.firstChild | ||
2567 | 256 | if not root or root.nodeName != 'screenlet': | ||
2568 | 257 | raise Exception('Missing or invalid rootnode in metadata-file: "%s".' % filename) | ||
2569 | 258 | # ok, let's check the nodes: this one should contain option-groups | ||
2570 | 259 | groups = [] | ||
2571 | 260 | for node in root.childNodes: | ||
2572 | 261 | # we only want element-nodes | ||
2573 | 262 | if node.nodeType == Node.ELEMENT_NODE: | ||
2574 | 263 | #print node | ||
2575 | 264 | if node.nodeName != 'group' or not node.hasChildNodes(): | ||
2576 | 265 | # we only allow groups in the first level (groups need children) | ||
2577 | 266 | raise Exception('Error in metadata-file "%s" - only <group>-tags allowed in first level. Groups must contain at least one <info>-element.' % filename) | ||
2578 | 267 | else: | ||
2579 | 268 | # ok, create a new group and parse its elements | ||
2580 | 269 | group = {} | ||
2581 | 270 | group['name'] = node.getAttribute("name") | ||
2582 | 271 | if not group['name']: | ||
2583 | 272 | raise Exception('No name for group defined in "%s".' % filename) | ||
2584 | 273 | group['info'] = '' | ||
2585 | 274 | group['options'] = [] | ||
2586 | 275 | # check all children in group | ||
2587 | 276 | for on in node.childNodes: | ||
2588 | 277 | if on.nodeType == Node.ELEMENT_NODE: | ||
2589 | 278 | if on.nodeName == 'info': | ||
2590 | 279 | # info-node? set group-info | ||
2591 | 280 | group['info'] = on.firstChild.nodeValue | ||
2592 | 281 | elif on.nodeName == 'option': | ||
2593 | 282 | # option node? parse option node | ||
2594 | 283 | opt = create_option_from_node (on, group['name']) | ||
2595 | 284 | # ok? add it to list | ||
2596 | 285 | if opt: | ||
2597 | 286 | group['options'].append(opt) | ||
2598 | 287 | else: | ||
2599 | 288 | raise Exception('Invalid option-node found in "%s".' % filename) | ||
2600 | 289 | |||
2601 | 290 | # create new group | ||
2602 | 291 | if len(group['options']): | ||
2603 | 292 | self.add_options_group(group['name'], group['info']) | ||
2604 | 293 | for o in group['options']: | ||
2605 | 294 | self.add_option(o) | ||
2606 | 295 | # add group to list | ||
2607 | 296 | #groups.append(group) | ||
2608 | 297 | |||
2609 | 298 | |||
2610 | 299 | class OptionsDialog(gtk.Dialog): | ||
2611 | 300 | """A dynamic options-editor for editing Screenlets which are implementing | ||
2612 | 301 | the EditableOptions-class.""" | ||
2613 | 302 | |||
2614 | 303 | __shown_object = None | ||
2615 | 304 | |||
2616 | 305 | def __init__ (self, width, height): | ||
2617 | 306 | # call gtk.Dialog.__init__ | ||
2618 | 307 | super(OptionsDialog, self).__init__( | ||
2619 | 308 | _("Edit Options"), flags=gtk.DIALOG_DESTROY_WITH_PARENT | | ||
2620 | 309 | gtk.DIALOG_NO_SEPARATOR, | ||
2621 | 310 | buttons = (#gtk.STOCK_REVERT_TO_SAVED, gtk.RESPONSE_APPLY, | ||
2622 | 311 | gtk.STOCK_CLOSE, gtk.RESPONSE_OK)) | ||
2623 | 312 | # set size | ||
2624 | 313 | self.resize(width, height) | ||
2625 | 314 | self.set_keep_above(True) # to avoid confusion | ||
2626 | 315 | self.set_border_width(10) | ||
2627 | 316 | # create attribs | ||
2628 | 317 | self.page_about = None | ||
2629 | 318 | self.page_options = None | ||
2630 | 319 | self.page_themes = None | ||
2631 | 320 | self.vbox_editor = None | ||
2632 | 321 | self.hbox_about = None | ||
2633 | 322 | self.infotext = None | ||
2634 | 323 | self.infoicon = None | ||
2635 | 324 | # create theme-list | ||
2636 | 325 | self.liststore = gtk.ListStore(object) | ||
2637 | 326 | self.tree = gtk.TreeView(model=self.liststore) | ||
2638 | 327 | # create/add outer notebook | ||
2639 | 328 | self.main_notebook = gtk.Notebook() | ||
2640 | 329 | self.main_notebook.show() | ||
2641 | 330 | self.vbox.add(self.main_notebook) | ||
2642 | 331 | # create/init notebook pages | ||
2643 | 332 | self.create_about_page() | ||
2644 | 333 | self.create_themes_page() | ||
2645 | 334 | self.create_options_page() | ||
2646 | 335 | |||
2647 | 336 | # "public" functions | ||
2648 | 337 | |||
2649 | 338 | def reset_to_defaults(self): | ||
2650 | 339 | """Reset all entries for the currently shown object to their default | ||
2651 | 340 | values (the values the object has when it is first created). | ||
2652 | 341 | NOTE: This function resets ALL options, so BE CARFEUL!""" | ||
2653 | 342 | if self.__shown_object: | ||
2654 | 343 | for o in self.__shown_object.__options__: | ||
2655 | 344 | # set default value | ||
2656 | 345 | setattr(self.__shown_object, o.name, o.default) | ||
2657 | 346 | |||
2658 | 347 | def set_info(self, name, info, copyright='', version='', icon=None): | ||
2659 | 348 | """Update the "About"-page with the given information.""" | ||
2660 | 349 | # convert infotext (remove EOLs and TABs) | ||
2661 | 350 | info = info.replace("\n", "") | ||
2662 | 351 | info = info.replace("\t", " ") | ||
2663 | 352 | # create markup | ||
2664 | 353 | markup = '\n<b><span size="xx-large">' + name + '</span></b>' | ||
2665 | 354 | if version: | ||
2666 | 355 | markup += ' <span size="large"><b>' + version + '</b></span>' | ||
2667 | 356 | markup += '\n\n'+info+'\n<span size="small">\n'+copyright+'</span>' | ||
2668 | 357 | self.infotext.set_markup(markup) | ||
2669 | 358 | # icon? | ||
2670 | 359 | if icon: | ||
2671 | 360 | # remove old icon | ||
2672 | 361 | if self.infoicon: | ||
2673 | 362 | self.infoicon.destroy() | ||
2674 | 363 | # set new icon | ||
2675 | 364 | self.infoicon = icon | ||
2676 | 365 | self.infoicon.set_alignment(0.0, 0.10) | ||
2677 | 366 | self.infoicon.show() | ||
2678 | 367 | self.hbox_about.pack_start(self.infoicon, 0, 1, 10) | ||
2679 | 368 | else: | ||
2680 | 369 | self.infoicon.hide() | ||
2681 | 370 | |||
2682 | 371 | def show_options_for_object (self, obj): | ||
2683 | 372 | """Update the OptionsEditor to show the options for the given Object. | ||
2684 | 373 | The Object needs to be an EditableOptions-subclass. | ||
2685 | 374 | NOTE: This needs heavy improvement and should use OptionGroups once | ||
2686 | 375 | they exist""" | ||
2687 | 376 | self.__shown_object = obj | ||
2688 | 377 | # create notebook for groups | ||
2689 | 378 | notebook = gtk.Notebook() | ||
2690 | 379 | self.vbox_editor.add(notebook) | ||
2691 | 380 | for group in obj.__options_groups_ordered__: | ||
2692 | 381 | group_data = obj.__options_groups__[group] | ||
2693 | 382 | # create box for tab-page | ||
2694 | 383 | page = gtk.VBox() | ||
2695 | 384 | page.set_border_width(10) | ||
2696 | 385 | if group_data['info'] != '': | ||
2697 | 386 | info = gtk.Label(group_data['info']) | ||
2698 | 387 | info.show() | ||
2699 | 388 | info.set_alignment(0, 0) | ||
2700 | 389 | page.pack_start(info, 0, 0, 7) | ||
2701 | 390 | sep = gtk.HSeparator() | ||
2702 | 391 | sep.show() | ||
2703 | 392 | #page.pack_start(sep, 0, 0, 5) | ||
2704 | 393 | # create VBox for inputs | ||
2705 | 394 | box = gtk.VBox() | ||
2706 | 395 | box.show() | ||
2707 | 396 | box.set_border_width(5) | ||
2708 | 397 | # add box to page | ||
2709 | 398 | page.add(box) | ||
2710 | 399 | page.show() | ||
2711 | 400 | # add new notebook-page | ||
2712 | 401 | label = gtk.Label(group_data['label']) | ||
2713 | 402 | label.show() | ||
2714 | 403 | notebook.append_page(page, label) | ||
2715 | 404 | # and create inputs | ||
2716 | 405 | for option in group_data['options']: | ||
2717 | 406 | if option.hidden == False: | ||
2718 | 407 | val = getattr(obj, option.name) | ||
2719 | 408 | w = self.generate_widget( option, val ) | ||
2720 | 409 | if w: | ||
2721 | 410 | box.pack_start(w, 0, 0) | ||
2722 | 411 | w.show() | ||
2723 | 412 | notebook.show() | ||
2724 | 413 | # show/hide themes tab, depending on whether the screenlet uses themes | ||
2725 | 414 | if obj.uses_theme and obj.theme_name != '': | ||
2726 | 415 | self.show_themes_for_screenlet(obj) | ||
2727 | 416 | else: | ||
2728 | 417 | self.page_themes.hide() | ||
2729 | 418 | |||
2730 | 419 | def generate_widget(self, option, value): | ||
2731 | 420 | """Generate the required widgets and add the label.""" | ||
2732 | 421 | widget = option.generate_widget(value) | ||
2733 | 422 | hbox = gtk.HBox() | ||
2734 | 423 | label = gtk.Label() | ||
2735 | 424 | label.set_alignment(0.0, 0.0) | ||
2736 | 425 | label.set_label(option.label) | ||
2737 | 426 | label.set_size_request(180, 28) | ||
2738 | 427 | label.show() | ||
2739 | 428 | hbox.pack_start(label, 0, 1) | ||
2740 | 429 | if widget: | ||
2741 | 430 | if option.disabled: | ||
2742 | 431 | widget.set_sensitive(False) | ||
2743 | 432 | label.set_sensitive(False) | ||
2744 | 433 | widget.set_tooltip_text(option.desc) | ||
2745 | 434 | widget.show() | ||
2746 | 435 | # check if needs Apply-button | ||
2747 | 436 | if option.realtime == False: | ||
2748 | 437 | but = gtk.Button(_('Apply'), gtk.STOCK_APPLY) | ||
2749 | 438 | but.show() | ||
2750 | 439 | but.connect("clicked", option.has_changed) | ||
2751 | 440 | b = gtk.HBox() | ||
2752 | 441 | b.show() | ||
2753 | 442 | b.pack_start(widget, 0, 0) | ||
2754 | 443 | b.pack_start(but, 0, 0) | ||
2755 | 444 | hbox.pack_start(b, 0, 0) | ||
2756 | 445 | else: | ||
2757 | 446 | hbox.pack_start(widget, 0, 0) | ||
2758 | 447 | return hbox | ||
2759 | 448 | |||
2760 | 449 | |||
2761 | 450 | def show_themes_for_screenlet (self, obj): | ||
2762 | 451 | """Update the Themes-page to display the available themes for the | ||
2763 | 452 | given Screenlet-object.""" | ||
2764 | 453 | dircontent = [] | ||
2765 | 454 | screenlets.utils.refresh_available_screenlet_paths() | ||
2766 | 455 | # now check all paths for themes | ||
2767 | 456 | for path in screenlets.SCREENLETS_PATH: | ||
2768 | 457 | p = path + '/' + obj.get_short_name() + '/themes' | ||
2769 | 458 | print p | ||
2770 | 459 | #p = '/usr/local/share/screenlets/Clock/themes' # TEMP!!! | ||
2771 | 460 | try: | ||
2772 | 461 | dc = os.listdir(p) | ||
2773 | 462 | for d in dc: | ||
2774 | 463 | dircontent.append({'name':d, 'path':p+'/'}) | ||
2775 | 464 | except: | ||
2776 | 465 | print "Path %s not found." % p | ||
2777 | 466 | # list with found themes | ||
2778 | 467 | found_themes = [] | ||
2779 | 468 | # check all themes in path | ||
2780 | 469 | for elem in dircontent: | ||
2781 | 470 | # load themes with the same name only once | ||
2782 | 471 | if found_themes.count(elem['name']): | ||
2783 | 472 | continue | ||
2784 | 473 | found_themes.append(elem['name']) | ||
2785 | 474 | # build full path of theme.conf | ||
2786 | 475 | theme_conf = elem['path'] + elem['name'] + '/theme.conf' | ||
2787 | 476 | # if dir contains a theme.conf | ||
2788 | 477 | if os.access(theme_conf, os.F_OK): | ||
2789 | 478 | # load it and create new list entry | ||
2790 | 479 | ini = screenlets.utils.IniReader() | ||
2791 | 480 | if ini.load(theme_conf): | ||
2792 | 481 | # check for section | ||
2793 | 482 | if ini.has_section('Theme'): | ||
2794 | 483 | # get metainfo from theme | ||
2795 | 484 | th_fullname = ini.get_option('name', | ||
2796 | 485 | section='Theme') | ||
2797 | 486 | th_info = ini.get_option('info', | ||
2798 | 487 | section='Theme') | ||
2799 | 488 | th_version = ini.get_option('version', | ||
2800 | 489 | section='Theme') | ||
2801 | 490 | th_author = ini.get_option('author', | ||
2802 | 491 | section='Theme') | ||
2803 | 492 | # create array from metainfo and add it to liststore | ||
2804 | 493 | info = [elem['name'], th_fullname, th_info, th_author, | ||
2805 | 494 | th_version] | ||
2806 | 495 | self.liststore.append([info]) | ||
2807 | 496 | else: | ||
2808 | 497 | # no theme section in theme.conf just add theme-name | ||
2809 | 498 | self.liststore.append([[elem['name'], '-', '-', '-', '-']]) | ||
2810 | 499 | else: | ||
2811 | 500 | # no theme.conf in dir? just add theme-name | ||
2812 | 501 | self.liststore.append([[elem['name'], '-', '-', '-', '-']]) | ||
2813 | 502 | # is it the active theme? | ||
2814 | 503 | if elem['name'] == obj.theme_name: | ||
2815 | 504 | # select it in tree | ||
2816 | 505 | print "active theme is: %s" % elem['name'] | ||
2817 | 506 | sel = self.tree.get_selection() | ||
2818 | 507 | if sel: | ||
2819 | 508 | it = self.liststore.get_iter_from_string(\ | ||
2820 | 509 | str(len(self.liststore)-1)) | ||
2821 | 510 | if it: | ||
2822 | 511 | sel.select_iter(it) | ||
2823 | 512 | # UI-creation | ||
2824 | 513 | |||
2825 | 514 | def create_about_page (self): | ||
2826 | 515 | """Create the "About"-tab.""" | ||
2827 | 516 | self.page_about = gtk.HBox() | ||
2828 | 517 | # create about box | ||
2829 | 518 | self.hbox_about = gtk.HBox() | ||
2830 | 519 | self.hbox_about.show() | ||
2831 | 520 | self.page_about.add(self.hbox_about) | ||
2832 | 521 | # create icon | ||
2833 | 522 | self.infoicon = gtk.Image() | ||
2834 | 523 | self.infoicon.show() | ||
2835 | 524 | self.page_about.pack_start(self.infoicon, 0, 1, 10) | ||
2836 | 525 | # create infotext | ||
2837 | 526 | self.infotext = gtk.Label() | ||
2838 | 527 | self.infotext.use_markup = True | ||
2839 | 528 | self.infotext.set_line_wrap(True) | ||
2840 | 529 | self.infotext.set_alignment(0.0, 0.0) | ||
2841 | 530 | self.infotext.show() | ||
2842 | 531 | self.page_about.pack_start(self.infotext, 1, 1, 5) | ||
2843 | 532 | # add page | ||
2844 | 533 | self.page_about.show() | ||
2845 | 534 | self.main_notebook.append_page(self.page_about, gtk.Label(_('About '))) | ||
2846 | 535 | |||
2847 | 536 | def create_options_page (self): | ||
2848 | 537 | """Create the "Options"-tab.""" | ||
2849 | 538 | self.page_options = gtk.HBox() | ||
2850 | 539 | # create vbox for options-editor | ||
2851 | 540 | self.vbox_editor = gtk.VBox(spacing=3) | ||
2852 | 541 | self.vbox_editor.set_border_width(5) | ||
2853 | 542 | self.vbox_editor.show() | ||
2854 | 543 | self.page_options.add(self.vbox_editor) | ||
2855 | 544 | # show/add page | ||
2856 | 545 | self.page_options.show() | ||
2857 | 546 | self.main_notebook.append_page(self.page_options, gtk.Label(_('Options '))) | ||
2858 | 547 | |||
2859 | 548 | def create_themes_page (self): | ||
2860 | 549 | """Create the "Themes"-tab.""" | ||
2861 | 550 | self.page_themes = gtk.VBox(spacing=5) | ||
2862 | 551 | self.page_themes.set_border_width(10) | ||
2863 | 552 | # create info-text list | ||
2864 | 553 | 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.')) | ||
2865 | 554 | txt.set_size_request(450, -1) | ||
2866 | 555 | txt.set_line_wrap(True) | ||
2867 | 556 | txt.set_alignment(0.0, 0.0) | ||
2868 | 557 | txt.show() | ||
2869 | 558 | self.page_themes.pack_start(txt, False, True) | ||
2870 | 559 | # create theme-selector list | ||
2871 | 560 | self.tree.set_headers_visible(False) | ||
2872 | 561 | self.tree.connect('cursor-changed', self.__tree_cursor_changed) | ||
2873 | 562 | self.tree.show() | ||
2874 | 563 | col = gtk.TreeViewColumn('') | ||
2875 | 564 | cell = gtk.CellRendererText() | ||
2876 | 565 | col.pack_start(cell, True) | ||
2877 | 566 | #cell.set_property('foreground', 'black') | ||
2878 | 567 | col.set_cell_data_func(cell, self.__render_cell) | ||
2879 | 568 | self.tree.append_column(col) | ||
2880 | 569 | # wrap tree in scrollwin | ||
2881 | 570 | sw = gtk.ScrolledWindow() | ||
2882 | 571 | sw.set_shadow_type(gtk.SHADOW_IN) | ||
2883 | 572 | sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) | ||
2884 | 573 | sw.add(self.tree) | ||
2885 | 574 | sw.show() | ||
2886 | 575 | # add vbox and add tree/buttons | ||
2887 | 576 | vbox = gtk.VBox() | ||
2888 | 577 | vbox.pack_start(sw, True, True) | ||
2889 | 578 | vbox.show() | ||
2890 | 579 | # show/add page | ||
2891 | 580 | self.page_themes.add(vbox) | ||
2892 | 581 | self.page_themes.show() | ||
2893 | 582 | self.main_notebook.append_page(self.page_themes, gtk.Label(_('Themes '))) | ||
2894 | 583 | |||
2895 | 584 | def __render_cell(self, tvcolumn, cell, model, iter): | ||
2896 | 585 | """Callback for rendering the cells in the theme-treeview.""" | ||
2897 | 586 | # get attributes-list from Treemodel | ||
2898 | 587 | attrib = model.get_value(iter, 0) | ||
2899 | 588 | |||
2900 | 589 | # set colors depending on state | ||
2901 | 590 | col = '555555' | ||
2902 | 591 | name_uc = attrib[0][0].upper() + attrib[0][1:] | ||
2903 | 592 | # create markup depending on info | ||
2904 | 593 | if attrib[1] == '-' and attrib[2] == '-': | ||
2905 | 594 | mu = '<b><span weight="ultrabold" size="large">' + name_uc + \ | ||
2906 | 595 | '</span></b> (' + _('no info available') + ')' | ||
2907 | 596 | else: | ||
2908 | 597 | if attrib[1] == None : attrib[1] = '-' | ||
2909 | 598 | if attrib[2] == None : attrib[2] = '-' | ||
2910 | 599 | if attrib[3] == None : attrib[3] = '-' | ||
2911 | 600 | if attrib[4] == None : attrib[4] = '-' | ||
2912 | 601 | mu = '<b><span weight="ultrabold" size="large">' + name_uc + \ | ||
2913 | 602 | '</span></b> v' + attrib[4] + '\n<small><span color="#555555' +\ | ||
2914 | 603 | '">' + attrib[2].replace('\\n', '\n') + \ | ||
2915 | 604 | '</span></small>\n<i><small>by '+str(attrib[3])+'</small></i>' | ||
2916 | 605 | # set markup | ||
2917 | 606 | cell.set_property('markup', mu) | ||
2918 | 607 | |||
2919 | 608 | # UI-callbacks | ||
2920 | 609 | |||
2921 | 610 | def __tree_cursor_changed (self, treeview): | ||
2922 | 611 | """Callback for handling selection changes in the Themes-treeview.""" | ||
2923 | 612 | sel = self.tree.get_selection() | ||
2924 | 613 | if sel: | ||
2925 | 614 | s = sel.get_selected() | ||
2926 | 615 | if s: | ||
2927 | 616 | it = s[1] | ||
2928 | 617 | if it: | ||
2929 | 618 | attribs = self.liststore.get_value(it, 0) | ||
2930 | 619 | if attribs and self.__shown_object: | ||
2931 | 620 | #print attribs | ||
2932 | 621 | # set theme in Screenlet (if not already active) | ||
2933 | 622 | if self.__shown_object.theme_name != attribs[0]: | ||
2934 | 623 | self.__shown_object.theme_name = attribs[0] | ||
2935 | 624 | |||
2936 | 0 | 625 | ||
2937 | === added file 'src/lib/options/boolean_option.py' | |||
2938 | --- src/lib/options/boolean_option.py 1970-01-01 00:00:00 +0000 | |||
2939 | +++ src/lib/options/boolean_option.py 2011-03-28 16:19:24 +0000 | |||
2940 | @@ -0,0 +1,54 @@ | |||
2941 | 1 | # | ||
2942 | 2 | # Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@gmail.com> | ||
2943 | 3 | # | ||
2944 | 4 | # This program is free software; you can redistribute it and/or modify | ||
2945 | 5 | # it under the terms of the GNU General Public License as published by | ||
2946 | 6 | # the Free Software Foundation; either version 3 of the License, or | ||
2947 | 7 | # (at your option) any later version. | ||
2948 | 8 | # | ||
2949 | 9 | # This program is distributed in the hope that it will be useful, | ||
2950 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2951 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2952 | 12 | # GNU General Public License for more details. | ||
2953 | 13 | # | ||
2954 | 14 | # You should have received a copy of the GNU General Public License | ||
2955 | 15 | # along with this program; if not, write to the Free Software | ||
2956 | 16 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
2957 | 17 | # | ||
2958 | 18 | """ | ||
2959 | 19 | Boolean options, these classes will display a boolean | ||
2960 | 20 | Checkbox as required and control the formating of data. | ||
2961 | 21 | """ | ||
2962 | 22 | |||
2963 | 23 | import gtk | ||
2964 | 24 | |||
2965 | 25 | from screenlets.options import _ | ||
2966 | 26 | from base import Option | ||
2967 | 27 | |||
2968 | 28 | class BoolOption(Option): | ||
2969 | 29 | """An Option for boolean values.""" | ||
2970 | 30 | def on_import(self, strvalue): | ||
2971 | 31 | """When a boolean is imported from the config.""" | ||
2972 | 32 | return strvalue.lower() == "true" | ||
2973 | 33 | |||
2974 | 34 | def on_export(self, value): | ||
2975 | 35 | """When a boolean is exported to the config.""" | ||
2976 | 36 | return str(value) | ||
2977 | 37 | |||
2978 | 38 | def generate_widget(self, value): | ||
2979 | 39 | """Generate a checkbox for a boolean option.""" | ||
2980 | 40 | if not self.widget: | ||
2981 | 41 | self.widget = gtk.CheckButton() | ||
2982 | 42 | self.set_value(value) | ||
2983 | 43 | self.widget.connect("toggled", self.has_changed) | ||
2984 | 44 | return self.widget | ||
2985 | 45 | |||
2986 | 46 | def set_value(self, value): | ||
2987 | 47 | """Set the true/false value to the checkbox widget""" | ||
2988 | 48 | self.value = value | ||
2989 | 49 | self.widget.set_active(self.value) | ||
2990 | 50 | |||
2991 | 51 | def has_changed(self, widget): | ||
2992 | 52 | """Executed when the widget event kicks off.""" | ||
2993 | 53 | self.set_value( self.widget.get_active() ) | ||
2994 | 54 | super(BoolOption, self).has_changed() | ||
2995 | 0 | 55 | ||
2996 | === added file 'src/lib/options/colour_option.py' | |||
2997 | --- src/lib/options/colour_option.py 1970-01-01 00:00:00 +0000 | |||
2998 | +++ src/lib/options/colour_option.py 2011-03-28 16:19:24 +0000 | |||
2999 | @@ -0,0 +1,149 @@ | |||
3000 | 1 | # | ||
3001 | 2 | # Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@gmail.com> | ||
3002 | 3 | # | ||
3003 | 4 | # This program is free software; you can redistribute it and/or modify | ||
3004 | 5 | # it under the terms of the GNU General Public License as published by | ||
3005 | 6 | # the Free Software Foundation; either version 3 of the License, or | ||
3006 | 7 | # (at your option) any later version. | ||
3007 | 8 | # | ||
3008 | 9 | # This program is distributed in the hope that it will be useful, | ||
3009 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3010 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3011 | 12 | # GNU General Public License for more details. | ||
3012 | 13 | # | ||
3013 | 14 | # You should have received a copy of the GNU General Public License | ||
3014 | 15 | # along with this program; if not, write to the Free Software | ||
3015 | 16 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
3016 | 17 | # | ||
3017 | 18 | """ | ||
3018 | 19 | Color options, these classes will display a text box. | ||
3019 | 20 | """ | ||
3020 | 21 | |||
3021 | 22 | import gtk | ||
3022 | 23 | |||
3023 | 24 | from screenlets.options import _ | ||
3024 | 25 | from base import Option | ||
3025 | 26 | |||
3026 | 27 | class ColorOption(Option): | ||
3027 | 28 | """An Option for color options.""" | ||
3028 | 29 | def on_import(self, strvalue): | ||
3029 | 30 | """Import (r, g, b, a) from comma-separated string.""" | ||
3030 | 31 | # strip braces and spaces | ||
3031 | 32 | strvalue = strvalue.lstrip('(') | ||
3032 | 33 | strvalue = strvalue.rstrip(')') | ||
3033 | 34 | strvalue = strvalue.strip() | ||
3034 | 35 | # split value on commas | ||
3035 | 36 | tmpval = strvalue.split(',') | ||
3036 | 37 | outval = [] | ||
3037 | 38 | for f in tmpval: | ||
3038 | 39 | # create list and again remove spaces | ||
3039 | 40 | outval.append(float(f.strip())) | ||
3040 | 41 | return outval | ||
3041 | 42 | |||
3042 | 43 | def on_export(self, value): | ||
3043 | 44 | """Export r, g, b, a to comma-separated string.""" | ||
3044 | 45 | l = len(value) | ||
3045 | 46 | outval = '' | ||
3046 | 47 | for i in xrange(l): | ||
3047 | 48 | if type(value[i]) == float: | ||
3048 | 49 | outval += "%0.5f" % value[i] | ||
3049 | 50 | else: | ||
3050 | 51 | outval += str(value[i]) | ||
3051 | 52 | if i < l-1: | ||
3052 | 53 | outval += ',' | ||
3053 | 54 | return outval | ||
3054 | 55 | |||
3055 | 56 | def generate_widget(self, value): | ||
3056 | 57 | """Generate a textbox for a color options""" | ||
3057 | 58 | self.widget = self.get_box_from_colour( value ) | ||
3058 | 59 | self.set_value(value) | ||
3059 | 60 | return self.widget | ||
3060 | 61 | |||
3061 | 62 | def set_value(self, value): | ||
3062 | 63 | """Set the color value as required.""" | ||
3063 | 64 | self.value = value | ||
3064 | 65 | |||
3065 | 66 | def has_changed(self, widget): | ||
3066 | 67 | """Executed when the widget event kicks off.""" | ||
3067 | 68 | self.value = self.get_colour_from_box(self.widget) | ||
3068 | 69 | super(ColorOption, self).has_changed() | ||
3069 | 70 | |||
3070 | 71 | def get_box_from_colour(self, colour): | ||
3071 | 72 | """Turn a colour array into a colour widget""" | ||
3072 | 73 | result = gtk.ColorButton(gtk.gdk.Color( | ||
3073 | 74 | int(colour[0]*65535), int(colour[1]*65535), int(colour[2]*65535))) | ||
3074 | 75 | result.set_use_alpha(True) | ||
3075 | 76 | result.set_alpha(int(colour[3]*65535)) | ||
3076 | 77 | result.connect("color-set", self.has_changed) | ||
3077 | 78 | return result | ||
3078 | 79 | |||
3079 | 80 | def get_colour_from_box(self, widget): | ||
3080 | 81 | """Turn a colour widget into a colour array""" | ||
3081 | 82 | colour = widget.get_color() | ||
3082 | 83 | return ( | ||
3083 | 84 | colour.red/65535.0, | ||
3084 | 85 | colour.green/65535.0, | ||
3085 | 86 | colour.blue/65535.0, | ||
3086 | 87 | widget.get_alpha()/65535.0 | ||
3087 | 88 | ) | ||
3088 | 89 | |||
3089 | 90 | |||
3090 | 91 | class ColorsOption(ColorOption): | ||
3091 | 92 | """Allow a list of colours to be created""" | ||
3092 | 93 | def on_import(self, value): | ||
3093 | 94 | """Importing multiple colours""" | ||
3094 | 95 | result = [] | ||
3095 | 96 | for col in value.split(';'): | ||
3096 | 97 | if col: | ||
3097 | 98 | result.append(super(ColorsOption, self).on_import(col)) | ||
3098 | 99 | return result | ||
3099 | 100 | |||
3100 | 101 | def on_export(self, value): | ||
3101 | 102 | """Exporting multiple colours""" | ||
3102 | 103 | result = "" | ||
3103 | 104 | for col in value: | ||
3104 | 105 | result += super(ColorsOption, self).on_export(col)+';' | ||
3105 | 106 | return result | ||
3106 | 107 | |||
3107 | 108 | def generate_widget(self, value): | ||
3108 | 109 | """Generate a textbox for a color options""" | ||
3109 | 110 | self.widget = gtk.HBox() | ||
3110 | 111 | if type(value[0]) in [int, float]: | ||
3111 | 112 | value = [value] | ||
3112 | 113 | for col in value: | ||
3113 | 114 | self.add_colour_box(self.widget, col, False) | ||
3114 | 115 | |||
3115 | 116 | but = gtk.Button('Add', gtk.STOCK_ADD) | ||
3116 | 117 | but.show() | ||
3117 | 118 | but.connect("clicked", self.add_colour_box) | ||
3118 | 119 | self.widget.pack_end(but) | ||
3119 | 120 | |||
3120 | 121 | self.set_value(value) | ||
3121 | 122 | return self.widget | ||
3122 | 123 | |||
3123 | 124 | def del_colour_box(self, widget, event): | ||
3124 | 125 | """Remove a colour box from the array when right clicked""" | ||
3125 | 126 | if event.button == 3: | ||
3126 | 127 | if len(self.widget.get_children()) > 2: | ||
3127 | 128 | self.widget.remove(widget) | ||
3128 | 129 | self.has_changed(widget) | ||
3129 | 130 | |||
3130 | 131 | def add_colour_box(self, widget, col=None, update=True): | ||
3131 | 132 | """Add a new box for colours""" | ||
3132 | 133 | if not col: | ||
3133 | 134 | col = self.value[-1] | ||
3134 | 135 | new_box = self.get_box_from_colour( col ) | ||
3135 | 136 | new_box.connect("button_press_event", self.del_colour_box) | ||
3136 | 137 | self.widget.pack_start(new_box, padding=1) | ||
3137 | 138 | new_box.show() | ||
3138 | 139 | if update: | ||
3139 | 140 | self.has_changed(widget) | ||
3140 | 141 | |||
3141 | 142 | def has_changed(self, widget): | ||
3142 | 143 | """The colour widgets have changed!""" | ||
3143 | 144 | self.value = [] | ||
3144 | 145 | for c in self.widget.get_children(): | ||
3145 | 146 | if type(c) == gtk.ColorButton: | ||
3146 | 147 | self.value.append(self.get_colour_from_box( c )) | ||
3147 | 148 | super(ColorOption, self).has_changed() | ||
3148 | 149 | |||
3149 | 0 | 150 | ||
3150 | === added file 'src/lib/options/file_option.py' | |||
3151 | --- src/lib/options/file_option.py 1970-01-01 00:00:00 +0000 | |||
3152 | +++ src/lib/options/file_option.py 2011-03-28 16:19:24 +0000 | |||
3153 | @@ -0,0 +1,179 @@ | |||
3154 | 1 | # | ||
3155 | 2 | # Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@gmail.com> | ||
3156 | 3 | # | ||
3157 | 4 | # This program is free software; you can redistribute it and/or modify | ||
3158 | 5 | # it under the terms of the GNU General Public License as published by | ||
3159 | 6 | # the Free Software Foundation; either version 3 of the License, or | ||
3160 | 7 | # (at your option) any later version. | ||
3161 | 8 | # | ||
3162 | 9 | # This program is distributed in the hope that it will be useful, | ||
3163 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3164 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3165 | 12 | # GNU General Public License for more details. | ||
3166 | 13 | # | ||
3167 | 14 | # You should have received a copy of the GNU General Public License | ||
3168 | 15 | # along with this program; if not, write to the Free Software | ||
3169 | 16 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
3170 | 17 | # | ||
3171 | 18 | """ | ||
3172 | 19 | File options, these classes will display a file select widget. | ||
3173 | 20 | """ | ||
3174 | 21 | |||
3175 | 22 | import gtk | ||
3176 | 23 | import os | ||
3177 | 24 | |||
3178 | 25 | from screenlets.options import _ | ||
3179 | 26 | from base import Option | ||
3180 | 27 | |||
3181 | 28 | class FileOption(Option): | ||
3182 | 29 | """ | ||
3183 | 30 | An Option-subclass for string-values that contain filenames. Adds | ||
3184 | 31 | a patterns-attribute that can contain a list of patterns to be shown | ||
3185 | 32 | in the assigned file selection dialog. The show_pixmaps-attribute | ||
3186 | 33 | can be set to True to make the filedialog show all image-types. | ||
3187 | 34 | supported by gtk.Pixmap. If the directory-attributue is true, the | ||
3188 | 35 | dialog will ony allow directories. | ||
3189 | 36 | |||
3190 | 37 | XXX - Some of this doen't yet work, unknown reason. | ||
3191 | 38 | """ | ||
3192 | 39 | patterns = [ ( 'All Files', ['*'] ) ] | ||
3193 | 40 | image = False | ||
3194 | 41 | _gtk_file_mode = gtk.FILE_CHOOSER_ACTION_OPEN | ||
3195 | 42 | _opener_title = _("Choose File") | ||
3196 | 43 | |||
3197 | 44 | def on_import(self, strvalue): | ||
3198 | 45 | """When a file is imported from the config.""" | ||
3199 | 46 | return strvalue | ||
3200 | 47 | |||
3201 | 48 | def on_export(self, value): | ||
3202 | 49 | """When a file is exported to the config.""" | ||
3203 | 50 | return str(value) | ||
3204 | 51 | |||
3205 | 52 | def generate_widget(self, value): | ||
3206 | 53 | """Generate a special widget for file options""" | ||
3207 | 54 | dlg = self.generate_file_opener() | ||
3208 | 55 | self.widget = gtk.FileChooserButton(dlg) | ||
3209 | 56 | self.widget.set_title(self._opener_title) | ||
3210 | 57 | self.widget.set_size_request(180, 28) | ||
3211 | 58 | self.set_value(value) | ||
3212 | 59 | self.widget.connect("selection-changed", self.has_changed) | ||
3213 | 60 | return self.widget | ||
3214 | 61 | |||
3215 | 62 | def generate_file_opener(self): | ||
3216 | 63 | """Generate a file opener widget""" | ||
3217 | 64 | dlg = gtk.FileChooserDialog(action=self._gtk_file_mode, | ||
3218 | 65 | buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, | ||
3219 | 66 | gtk.STOCK_OPEN, gtk.RESPONSE_OK), | ||
3220 | 67 | ) | ||
3221 | 68 | dlg.set_keep_above(True) | ||
3222 | 69 | self.set_filters(dlg) | ||
3223 | 70 | return dlg | ||
3224 | 71 | |||
3225 | 72 | def set_filters(self, dlg): | ||
3226 | 73 | """Add file filters to the dialog widget""" | ||
3227 | 74 | if self.patterns: | ||
3228 | 75 | for filter in self.patterns: | ||
3229 | 76 | fil = gtk.FileFilter() | ||
3230 | 77 | fil.set_name("%s (%s)" % (filter[0], ','.join(filter[1]))) | ||
3231 | 78 | for pattern in filter[1]: | ||
3232 | 79 | fil.add_pattern(pattern) | ||
3233 | 80 | dlg.add_filter(fil) | ||
3234 | 81 | |||
3235 | 82 | def set_value(self, value): | ||
3236 | 83 | """Set the file value as required.""" | ||
3237 | 84 | self.widget.set_filename(value) | ||
3238 | 85 | self.value = value | ||
3239 | 86 | |||
3240 | 87 | def has_changed(self, widget): | ||
3241 | 88 | """Executed when the widget event kicks off.""" | ||
3242 | 89 | self.value = self.widget.get_filename() | ||
3243 | 90 | super(FileOption, self).has_changed() | ||
3244 | 91 | |||
3245 | 92 | |||
3246 | 93 | class DirectoryOption(FileOption): | ||
3247 | 94 | """Directory is based on file widgets""" | ||
3248 | 95 | _gtk_file_mode = gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER | ||
3249 | 96 | _opener_title = _("Choose Directory") | ||
3250 | 97 | |||
3251 | 98 | |||
3252 | 99 | class ImageOption(FileOption): | ||
3253 | 100 | """Image is based on file widgets""" | ||
3254 | 101 | _opener_title = _("Choose Image") | ||
3255 | 102 | |||
3256 | 103 | def set_filters(self, dlg): | ||
3257 | 104 | """Add the standard pixbug formats""" | ||
3258 | 105 | flt = gtk.FileFilter() | ||
3259 | 106 | flt.add_pixbuf_formats() | ||
3260 | 107 | dlg.set_filter(flt) | ||
3261 | 108 | |||
3262 | 109 | def generate_widget(self, value): | ||
3263 | 110 | """Crazy image opener widget generation.""" | ||
3264 | 111 | # create entry and button (entry is hidden) | ||
3265 | 112 | self._entry = gtk.Entry() | ||
3266 | 113 | self._entry.set_text(value) | ||
3267 | 114 | self._entry.set_editable(False) | ||
3268 | 115 | but = gtk.Button() | ||
3269 | 116 | # load preview image | ||
3270 | 117 | but.set_image(self.create_preview(value)) | ||
3271 | 118 | but.connect('clicked', self.but_callback) | ||
3272 | 119 | # create widget | ||
3273 | 120 | self.widget = gtk.HBox() | ||
3274 | 121 | self.widget.add(self._entry) | ||
3275 | 122 | self.widget.add(but) | ||
3276 | 123 | but.show() | ||
3277 | 124 | self.widget.show() | ||
3278 | 125 | # add tooltips | ||
3279 | 126 | but.set_tooltip_text(_('Select Image ...')) | ||
3280 | 127 | but.set_tooltip_text(self.desc) | ||
3281 | 128 | return self.widget | ||
3282 | 129 | |||
3283 | 130 | def create_preview(self, filename): | ||
3284 | 131 | """Utililty method to reload preview image""" | ||
3285 | 132 | if filename and os.path.isfile(filename): | ||
3286 | 133 | pb = gtk.gdk.pixbuf_new_from_file_at_size(filename, 64, -1) | ||
3287 | 134 | if pb: | ||
3288 | 135 | img = gtk.Image() | ||
3289 | 136 | img.set_from_pixbuf(pb) | ||
3290 | 137 | return img | ||
3291 | 138 | img = gtk.image_new_from_stock(gtk.STOCK_MISSING_IMAGE, | ||
3292 | 139 | gtk.ICON_SIZE_LARGE_TOOLBAR) | ||
3293 | 140 | img.set_size_request(64, 64) | ||
3294 | 141 | return img | ||
3295 | 142 | |||
3296 | 143 | def but_callback(self, widget): | ||
3297 | 144 | """Create button""" | ||
3298 | 145 | dlg = gtk.FileChooserDialog(buttons=(gtk.STOCK_CANCEL, | ||
3299 | 146 | gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK)) | ||
3300 | 147 | dlg.set_keep_above(True) | ||
3301 | 148 | dlg.set_filename(self._entry.get_text()) | ||
3302 | 149 | prev = gtk.Image() | ||
3303 | 150 | box = gtk.VBox() | ||
3304 | 151 | box.set_size_request(150, -1) | ||
3305 | 152 | box.add(prev) | ||
3306 | 153 | prev.show() | ||
3307 | 154 | dlg.set_preview_widget_active(True) | ||
3308 | 155 | dlg.connect('selection-changed', self.preview_callback, dlg, prev) | ||
3309 | 156 | dlg.set_preview_widget(box) | ||
3310 | 157 | response = dlg.run() | ||
3311 | 158 | if response == gtk.RESPONSE_OK: | ||
3312 | 159 | self._entry.set_text(dlg.get_filename()) | ||
3313 | 160 | widget.set_image(self.create_preview(dlg.get_filename())) | ||
3314 | 161 | self.has_changed(self.widget) | ||
3315 | 162 | dlg.destroy() | ||
3316 | 163 | |||
3317 | 164 | def preview_callback(self, widget, dlg, prev): | ||
3318 | 165 | """add preview widget to filechooser""" | ||
3319 | 166 | fname = dlg.get_preview_filename() | ||
3320 | 167 | if fname and os.path.isfile(fname): | ||
3321 | 168 | pb = gtk.gdk.pixbuf_new_from_file_at_size(fname, 150, -1) | ||
3322 | 169 | if pb: | ||
3323 | 170 | prev.set_from_pixbuf(pb) | ||
3324 | 171 | dlg.set_preview_widget_active(True) | ||
3325 | 172 | else: | ||
3326 | 173 | dlg.set_preview_widget_active(False) | ||
3327 | 174 | |||
3328 | 175 | def has_changed(self, widget): | ||
3329 | 176 | """Executed when the widget event kicks off.""" | ||
3330 | 177 | self.value = self._entry.get_text() | ||
3331 | 178 | super(FileOption, self).has_changed() | ||
3332 | 179 | |||
3333 | 0 | 180 | ||
3334 | === added file 'src/lib/options/font_option.py' | |||
3335 | --- src/lib/options/font_option.py 1970-01-01 00:00:00 +0000 | |||
3336 | +++ src/lib/options/font_option.py 2011-03-28 16:19:24 +0000 | |||
3337 | @@ -0,0 +1,52 @@ | |||
3338 | 1 | # | ||
3339 | 2 | # Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@gmail.com> | ||
3340 | 3 | # | ||
3341 | 4 | # This program is free software; you can redistribute it and/or modify | ||
3342 | 5 | # it under the terms of the GNU General Public License as published by | ||
3343 | 6 | # the Free Software Foundation; either version 3 of the License, or | ||
3344 | 7 | # (at your option) any later version. | ||
3345 | 8 | # | ||
3346 | 9 | # This program is distributed in the hope that it will be useful, | ||
3347 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3348 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3349 | 12 | # GNU General Public License for more details. | ||
3350 | 13 | # | ||
3351 | 14 | # You should have received a copy of the GNU General Public License | ||
3352 | 15 | # along with this program; if not, write to the Free Software | ||
3353 | 16 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
3354 | 17 | # | ||
3355 | 18 | """ | ||
3356 | 19 | Font options, these classes will display a text box. | ||
3357 | 20 | """ | ||
3358 | 21 | |||
3359 | 22 | import gtk | ||
3360 | 23 | |||
3361 | 24 | from screenlets.options import _ | ||
3362 | 25 | from base import Option | ||
3363 | 26 | |||
3364 | 27 | class FontOption(Option): | ||
3365 | 28 | """An class for font options.""" | ||
3366 | 29 | def on_import(self, strvalue): | ||
3367 | 30 | """When a font is imported from the config.""" | ||
3368 | 31 | return strvalue | ||
3369 | 32 | |||
3370 | 33 | def on_export(self, value): | ||
3371 | 34 | """When a font is exported to the config.""" | ||
3372 | 35 | return str(value) | ||
3373 | 36 | |||
3374 | 37 | def generate_widget(self, value): | ||
3375 | 38 | """Generate a special widget for font options""" | ||
3376 | 39 | self.widget = gtk.FontButton() | ||
3377 | 40 | self.set_value(value) | ||
3378 | 41 | self.widget.connect("font-set", self.has_changed) | ||
3379 | 42 | return self.widget | ||
3380 | 43 | |||
3381 | 44 | def set_value(self, value): | ||
3382 | 45 | """Set the font value as required.""" | ||
3383 | 46 | self.widget.set_font_name(value) | ||
3384 | 47 | self.value = value | ||
3385 | 48 | |||
3386 | 49 | def has_changed(self, widget): | ||
3387 | 50 | """Executed when the widget event kicks off.""" | ||
3388 | 51 | self.value = widget.get_font_name() | ||
3389 | 52 | super(FontOption, self).has_changed() | ||
3390 | 0 | 53 | ||
3391 | === added file 'src/lib/options/list_option.py' | |||
3392 | --- src/lib/options/list_option.py 1970-01-01 00:00:00 +0000 | |||
3393 | +++ src/lib/options/list_option.py 2011-03-28 16:19:24 +0000 | |||
3394 | @@ -0,0 +1,210 @@ | |||
3395 | 1 | # | ||
3396 | 2 | # Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@gmail.com> | ||
3397 | 3 | # | ||
3398 | 4 | # This program is free software; you can redistribute it and/or modify | ||
3399 | 5 | # it under the terms of the GNU General Public License as published by | ||
3400 | 6 | # the Free Software Foundation; either version 3 of the License, or | ||
3401 | 7 | # (at your option) any later version. | ||
3402 | 8 | # | ||
3403 | 9 | # This program is distributed in the hope that it will be useful, | ||
3404 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3405 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3406 | 12 | # GNU General Public License for more details. | ||
3407 | 13 | # | ||
3408 | 14 | # You should have received a copy of the GNU General Public License | ||
3409 | 15 | # along with this program; if not, write to the Free Software | ||
3410 | 16 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
3411 | 17 | # | ||
3412 | 18 | """ | ||
3413 | 19 | List options, these classes will display all sorts of crazy shit. | ||
3414 | 20 | """ | ||
3415 | 21 | |||
3416 | 22 | import gtk | ||
3417 | 23 | |||
3418 | 24 | from screenlets.options import _ | ||
3419 | 25 | from base import Option | ||
3420 | 26 | |||
3421 | 27 | class ListOption(Option): | ||
3422 | 28 | """An Option for string options.""" | ||
3423 | 29 | def on_import(self, strvalue): | ||
3424 | 30 | """When a list is imported from the config.""" | ||
3425 | 31 | return eval(strvalue) | ||
3426 | 32 | |||
3427 | 33 | def on_export(self, value): | ||
3428 | 34 | """When a list is exported to the config.""" | ||
3429 | 35 | return str(value) | ||
3430 | 36 | |||
3431 | 37 | def generate_widget(self, value): | ||
3432 | 38 | """Generate some widgets for a list.""" | ||
3433 | 39 | self._entry = gtk.Entry() | ||
3434 | 40 | self._entry.set_editable(False) | ||
3435 | 41 | self.set_value(value) | ||
3436 | 42 | self._entry.show() | ||
3437 | 43 | img = gtk.Image() | ||
3438 | 44 | img.set_from_stock(gtk.STOCK_EDIT, 1) | ||
3439 | 45 | but = gtk.Button() | ||
3440 | 46 | but.set_image(img) | ||
3441 | 47 | but.show() | ||
3442 | 48 | but.connect("clicked", self.open_listeditor) | ||
3443 | 49 | but.set_tooltip_text(_('Open List-Editor ...')) | ||
3444 | 50 | self._entry.set_tooltip_text(self.desc) | ||
3445 | 51 | self.widget = gtk.HBox() | ||
3446 | 52 | self.widget.add(self._entry) | ||
3447 | 53 | self.widget.add(but) | ||
3448 | 54 | return self.widget | ||
3449 | 55 | |||
3450 | 56 | def open_listeditor(self, event): | ||
3451 | 57 | # open dialog | ||
3452 | 58 | dlg = ListOptionDialog() | ||
3453 | 59 | # read string from entry and import it through option-class | ||
3454 | 60 | # (this is needed to always have an up-to-date value) | ||
3455 | 61 | dlg.set_list(self.on_import(self._entry.get_text())) | ||
3456 | 62 | resp = dlg.run() | ||
3457 | 63 | if resp == gtk.RESPONSE_OK: | ||
3458 | 64 | # set text in entry | ||
3459 | 65 | self._entry.set_text(str(dlg.get_list())) | ||
3460 | 66 | # manually call the options-callback | ||
3461 | 67 | self.has_changed(dlg) | ||
3462 | 68 | dlg.destroy() | ||
3463 | 69 | |||
3464 | 70 | def set_value(self, value): | ||
3465 | 71 | """Set the list string value as required.""" | ||
3466 | 72 | self._entry.set_text(str(value)) | ||
3467 | 73 | self.value = value | ||
3468 | 74 | |||
3469 | 75 | def has_changed(self, widget): | ||
3470 | 76 | """Executed when the widget event kicks off.""" | ||
3471 | 77 | self.value = widget.get_list() | ||
3472 | 78 | super(ListOption, self).has_changed() | ||
3473 | 79 | |||
3474 | 80 | |||
3475 | 81 | class ListOptionDialog(gtk.Dialog): | ||
3476 | 82 | """An editing dialog used for editing options of the ListOption-type.""" | ||
3477 | 83 | model = None | ||
3478 | 84 | tree = None | ||
3479 | 85 | buttonbox = None | ||
3480 | 86 | |||
3481 | 87 | # call gtk.Dialog.__init__ | ||
3482 | 88 | def __init__ (self): | ||
3483 | 89 | super(ListOptionDialog, self).__init__( | ||
3484 | 90 | "Edit List", | ||
3485 | 91 | flags=gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_NO_SEPARATOR, | ||
3486 | 92 | buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, | ||
3487 | 93 | gtk.STOCK_OK, gtk.RESPONSE_OK) | ||
3488 | 94 | ) | ||
3489 | 95 | # set size | ||
3490 | 96 | self.resize(300, 370) | ||
3491 | 97 | self.set_keep_above(True) | ||
3492 | 98 | # init vars | ||
3493 | 99 | self.model = gtk.ListStore(str) | ||
3494 | 100 | # create UI | ||
3495 | 101 | self.create_ui() | ||
3496 | 102 | |||
3497 | 103 | def create_ui (self): | ||
3498 | 104 | """Create the user-interface for this dialog.""" | ||
3499 | 105 | # create outer hbox (tree|buttons) | ||
3500 | 106 | hbox = gtk.HBox() | ||
3501 | 107 | hbox.set_border_width(10) | ||
3502 | 108 | hbox.set_spacing(10) | ||
3503 | 109 | # create tree | ||
3504 | 110 | self.tree = gtk.TreeView(model=self.model) | ||
3505 | 111 | self.tree.set_headers_visible(False) | ||
3506 | 112 | self.tree.set_reorderable(True) | ||
3507 | 113 | #self.tree.set_grid_lines(gtk.TREE_VIEW_GRID_LINES_HORIZONTAL) | ||
3508 | 114 | col = gtk.TreeViewColumn('') | ||
3509 | 115 | cell = gtk.CellRendererText() | ||
3510 | 116 | #cell.set_property('cell-background', 'cyan') | ||
3511 | 117 | cell.set_property('foreground', 'black') | ||
3512 | 118 | col.pack_start(cell, False) | ||
3513 | 119 | col.set_attributes(cell, text=0) | ||
3514 | 120 | self.tree.append_column(col) | ||
3515 | 121 | self.tree.show() | ||
3516 | 122 | hbox.pack_start(self.tree, True, True) | ||
3517 | 123 | #sep = gtk.VSeparator() | ||
3518 | 124 | #sep.show() | ||
3519 | 125 | #hbox.add(sep) | ||
3520 | 126 | # create buttons | ||
3521 | 127 | self.buttonbox = bb = gtk.VButtonBox() | ||
3522 | 128 | self.buttonbox.set_layout(gtk.BUTTONBOX_START) | ||
3523 | 129 | b1 = gtk.Button(stock=gtk.STOCK_ADD) | ||
3524 | 130 | b2 = gtk.Button(stock=gtk.STOCK_EDIT) | ||
3525 | 131 | b3 = gtk.Button(stock=gtk.STOCK_REMOVE) | ||
3526 | 132 | b1.connect('clicked', self.button_callback, 'add') | ||
3527 | 133 | b2.connect('clicked', self.button_callback, 'edit') | ||
3528 | 134 | b3.connect('clicked', self.button_callback, 'remove') | ||
3529 | 135 | bb.add(b1) | ||
3530 | 136 | bb.add(b2) | ||
3531 | 137 | bb.add(b3) | ||
3532 | 138 | self.buttonbox.show_all() | ||
3533 | 139 | #hbox.add(self.buttonbox) | ||
3534 | 140 | hbox.pack_end(self.buttonbox, False) | ||
3535 | 141 | # add everything to outer hbox and show it | ||
3536 | 142 | hbox.show() | ||
3537 | 143 | self.vbox.add(hbox) | ||
3538 | 144 | |||
3539 | 145 | def set_list (self, lst): | ||
3540 | 146 | """Set the list to be edited in this editor.""" | ||
3541 | 147 | for el in lst: | ||
3542 | 148 | self.model.append([el]) | ||
3543 | 149 | |||
3544 | 150 | def get_list (self): | ||
3545 | 151 | """Return the list that is currently being edited in this editor.""" | ||
3546 | 152 | lst = [] | ||
3547 | 153 | for i in self.model: | ||
3548 | 154 | lst.append(i[0]) | ||
3549 | 155 | return lst | ||
3550 | 156 | |||
3551 | 157 | def remove_selected_item (self): | ||
3552 | 158 | """Remove the currently selected item.""" | ||
3553 | 159 | sel = self.tree.get_selection() | ||
3554 | 160 | if sel: | ||
3555 | 161 | it = sel.get_selected()[1] | ||
3556 | 162 | if it: | ||
3557 | 163 | print self.model.get_value(it, 0) | ||
3558 | 164 | self.model.remove(it) | ||
3559 | 165 | |||
3560 | 166 | def entry_dialog (self, default = ''): | ||
3561 | 167 | """Show entry-dialog and return string.""" | ||
3562 | 168 | entry = gtk.Entry() | ||
3563 | 169 | entry.set_text(default) | ||
3564 | 170 | entry.show() | ||
3565 | 171 | dlg = gtk.Dialog("Add/Edit Item", flags=gtk.DIALOG_DESTROY_WITH_PARENT, | ||
3566 | 172 | buttons = (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, | ||
3567 | 173 | gtk.RESPONSE_OK)) | ||
3568 | 174 | dlg.set_keep_above(True) | ||
3569 | 175 | dlg.vbox.add(entry) | ||
3570 | 176 | resp = dlg.run() | ||
3571 | 177 | ret = None | ||
3572 | 178 | if resp == gtk.RESPONSE_OK: | ||
3573 | 179 | ret = entry.get_text() | ||
3574 | 180 | dlg.destroy() | ||
3575 | 181 | return ret | ||
3576 | 182 | |||
3577 | 183 | def button_callback (self, widget, id): | ||
3578 | 184 | print "PRESS: %s" % id | ||
3579 | 185 | if id == 'remove': | ||
3580 | 186 | self.remove_selected_item() | ||
3581 | 187 | if id == 'add': | ||
3582 | 188 | new = self.entry_dialog() | ||
3583 | 189 | if new != None: | ||
3584 | 190 | self.model.append([new]) | ||
3585 | 191 | if id == 'edit': | ||
3586 | 192 | sel = self.tree.get_selection() | ||
3587 | 193 | if sel: | ||
3588 | 194 | it = sel.get_selected()[1] | ||
3589 | 195 | if it: | ||
3590 | 196 | new = self.entry_dialog(self.model.get_value(it, 0)) | ||
3591 | 197 | if new != None: | ||
3592 | 198 | #self.model.append([new]) | ||
3593 | 199 | self.model.set_value(it, 0, new) | ||
3594 | 200 | |||
3595 | 201 | # TEST>- | ||
3596 | 202 | """dlg = ListOptionDialog() | ||
3597 | 203 | dlg.set_list(['test1', 'afarew34s', 'fhjh23faj', 'yxcdfs58df', 'hsdf7jsdfh']) | ||
3598 | 204 | dlg.run() | ||
3599 | 205 | print "RESULT: " + str(dlg.get_list()) | ||
3600 | 206 | dlg.destroy() | ||
3601 | 207 | import sys | ||
3602 | 208 | sys.exit(1)""" | ||
3603 | 209 | # /TEST | ||
3604 | 210 | |||
3605 | 0 | 211 | ||
3606 | === added file 'src/lib/options/number_option.py' | |||
3607 | --- src/lib/options/number_option.py 1970-01-01 00:00:00 +0000 | |||
3608 | +++ src/lib/options/number_option.py 2011-03-28 16:19:24 +0000 | |||
3609 | @@ -0,0 +1,90 @@ | |||
3610 | 1 | # | ||
3611 | 2 | # Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@gmail.com> | ||
3612 | 3 | # | ||
3613 | 4 | # This program is free software; you can redistribute it and/or modify | ||
3614 | 5 | # it under the terms of the GNU General Public License as published by | ||
3615 | 6 | # the Free Software Foundation; either version 3 of the License, or | ||
3616 | 7 | # (at your option) any later version. | ||
3617 | 8 | # | ||
3618 | 9 | # This program is distributed in the hope that it will be useful, | ||
3619 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3620 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3621 | 12 | # GNU General Public License for more details. | ||
3622 | 13 | # | ||
3623 | 14 | # You should have received a copy of the GNU General Public License | ||
3624 | 15 | # along with this program; if not, write to the Free Software | ||
3625 | 16 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
3626 | 17 | # | ||
3627 | 18 | """ | ||
3628 | 19 | Integer and Float options, these classes will display a spin box. | ||
3629 | 20 | """ | ||
3630 | 21 | |||
3631 | 22 | import gtk | ||
3632 | 23 | |||
3633 | 24 | from screenlets.options import _ | ||
3634 | 25 | from base import Option | ||
3635 | 26 | |||
3636 | 27 | class IntOption(Option): | ||
3637 | 28 | """An Option for integer options.""" | ||
3638 | 29 | min = -100000 | ||
3639 | 30 | max = 100000 | ||
3640 | 31 | increment = 1 | ||
3641 | 32 | |||
3642 | 33 | def on_import(self, strvalue): | ||
3643 | 34 | """When a integer is imported from the config.""" | ||
3644 | 35 | try: | ||
3645 | 36 | if strvalue[0]=='-': | ||
3646 | 37 | return int(strvalue[1:]) * -1 | ||
3647 | 38 | return int(strvalue) | ||
3648 | 39 | except: | ||
3649 | 40 | sys.stderr.write(_("Error during on_import - option: %s.\n") % self.name) | ||
3650 | 41 | return 0 | ||
3651 | 42 | |||
3652 | 43 | return int(strvalue) | ||
3653 | 44 | |||
3654 | 45 | def on_export(self, value): | ||
3655 | 46 | """When a string is exported to the config.""" | ||
3656 | 47 | return str(value) | ||
3657 | 48 | |||
3658 | 49 | def generate_widget(self, value): | ||
3659 | 50 | """Generate a spin button for integer options""" | ||
3660 | 51 | self.widget = gtk.SpinButton() | ||
3661 | 52 | self.widget.set_increments(self.increment, int(self.max / self.increment)) | ||
3662 | 53 | if self.min != None and self.max != None: | ||
3663 | 54 | self.widget.set_range(self.min, self.max) | ||
3664 | 55 | self.set_value(value) | ||
3665 | 56 | self.widget.connect("value-changed", self.has_changed) | ||
3666 | 57 | return self.widget | ||
3667 | 58 | |||
3668 | 59 | def set_value(self, value): | ||
3669 | 60 | """Set the int value, including the value of the widget.""" | ||
3670 | 61 | self.value = value | ||
3671 | 62 | self.widget.set_value(value) | ||
3672 | 63 | |||
3673 | 64 | def has_changed(self, widget): | ||
3674 | 65 | """Executed when the widget event kicks off.""" | ||
3675 | 66 | self.value = int(widget.get_value()) | ||
3676 | 67 | super(IntOption, self).has_changed() | ||
3677 | 68 | |||
3678 | 69 | |||
3679 | 70 | class FloatOption(IntOption): | ||
3680 | 71 | """An option for float numbers.""" | ||
3681 | 72 | digits = 0 | ||
3682 | 73 | |||
3683 | 74 | def on_import (self, strvalue): | ||
3684 | 75 | """Called when FloatOption gets imported. Converts str to float.""" | ||
3685 | 76 | if strvalue[0]=='-': | ||
3686 | 77 | return float(strvalue[1:]) * -1.0 | ||
3687 | 78 | return float(strvalue) | ||
3688 | 79 | |||
3689 | 80 | def generate_widget(self, value): | ||
3690 | 81 | """Do the same as int but add the number of ditgits""" | ||
3691 | 82 | super(FloatOption, self).generate_widget(value) | ||
3692 | 83 | self.widget.set_digits(self.digits) | ||
3693 | 84 | return self.widget | ||
3694 | 85 | |||
3695 | 86 | def has_changed(self, widget): | ||
3696 | 87 | """Executed when the widget event kicks off.""" | ||
3697 | 88 | self.value = float(widget.get_value()) | ||
3698 | 89 | super(IntOption, self).has_changed() | ||
3699 | 90 | |||
3700 | 0 | 91 | ||
3701 | === added file 'src/lib/options/string_option.py' | |||
3702 | --- src/lib/options/string_option.py 1970-01-01 00:00:00 +0000 | |||
3703 | +++ src/lib/options/string_option.py 2011-03-28 16:19:24 +0000 | |||
3704 | @@ -0,0 +1,79 @@ | |||
3705 | 1 | # | ||
3706 | 2 | # Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@gmail.com> | ||
3707 | 3 | # | ||
3708 | 4 | # This program is free software; you can redistribute it and/or modify | ||
3709 | 5 | # it under the terms of the GNU General Public License as published by | ||
3710 | 6 | # the Free Software Foundation; either version 3 of the License, or | ||
3711 | 7 | # (at your option) any later version. | ||
3712 | 8 | # | ||
3713 | 9 | # This program is distributed in the hope that it will be useful, | ||
3714 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3715 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3716 | 12 | # GNU General Public License for more details. | ||
3717 | 13 | # | ||
3718 | 14 | # You should have received a copy of the GNU General Public License | ||
3719 | 15 | # along with this program; if not, write to the Free Software | ||
3720 | 16 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
3721 | 17 | # | ||
3722 | 18 | """ | ||
3723 | 19 | String options, these classes will display a text box. | ||
3724 | 20 | """ | ||
3725 | 21 | |||
3726 | 22 | import gtk | ||
3727 | 23 | |||
3728 | 24 | from screenlets.options import _ | ||
3729 | 25 | from base import Option | ||
3730 | 26 | |||
3731 | 27 | class StringOption(Option): | ||
3732 | 28 | """An Option for string options.""" | ||
3733 | 29 | choices = None | ||
3734 | 30 | password = False | ||
3735 | 31 | |||
3736 | 32 | def on_import(self, strvalue): | ||
3737 | 33 | """When a string is imported from the config.""" | ||
3738 | 34 | return strvalue | ||
3739 | 35 | |||
3740 | 36 | def on_export(self, value): | ||
3741 | 37 | """When a string is exported to the config.""" | ||
3742 | 38 | return str(value) | ||
3743 | 39 | |||
3744 | 40 | def generate_widget(self, value): | ||
3745 | 41 | """Generate a textbox for a string options""" | ||
3746 | 42 | if self.choices: | ||
3747 | 43 | # if a list of values is defined, show combobox | ||
3748 | 44 | self.widget = gtk.combo_box_new_text() | ||
3749 | 45 | p = -1 | ||
3750 | 46 | i = 0 | ||
3751 | 47 | for s in self.choices: | ||
3752 | 48 | self.widget.append_text(s) | ||
3753 | 49 | if s==value: | ||
3754 | 50 | p = i | ||
3755 | 51 | i+=1 | ||
3756 | 52 | self.widget.set_active(p) | ||
3757 | 53 | else: | ||
3758 | 54 | self.widget = gtk.Entry() | ||
3759 | 55 | # if it is a password, set text to be invisible | ||
3760 | 56 | if self.password: | ||
3761 | 57 | self.widget.set_visibility(False) | ||
3762 | 58 | |||
3763 | 59 | self.set_value(value) | ||
3764 | 60 | self.widget.connect("changed", self.has_changed) | ||
3765 | 61 | #self.widget.set_size_request(180, 28) | ||
3766 | 62 | return self.widget | ||
3767 | 63 | |||
3768 | 64 | def set_value(self, value): | ||
3769 | 65 | """Set the string value as required.""" | ||
3770 | 66 | self.value = value | ||
3771 | 67 | if self.choices: | ||
3772 | 68 | # TODO self.widget.set_active(p) | ||
3773 | 69 | pass | ||
3774 | 70 | else: | ||
3775 | 71 | self.widget.set_text(value) | ||
3776 | 72 | |||
3777 | 73 | def has_changed(self, widget): | ||
3778 | 74 | """Executed when the widget event kicks off.""" | ||
3779 | 75 | if self.choices: | ||
3780 | 76 | self.set_value( widget.get_active_text() ) | ||
3781 | 77 | else: | ||
3782 | 78 | self.set_value( widget.get_text() ) | ||
3783 | 79 | super(StringOption, self).has_changed() | ||
3784 | 0 | 80 | ||
3785 | === added file 'src/lib/options/time_option.py' | |||
3786 | --- src/lib/options/time_option.py 1970-01-01 00:00:00 +0000 | |||
3787 | +++ src/lib/options/time_option.py 2011-03-28 16:19:24 +0000 | |||
3788 | @@ -0,0 +1,80 @@ | |||
3789 | 1 | # | ||
3790 | 2 | # Copyright (C) 2009 Martin Owens (DoctorMO) <doctormo@gmail.com> | ||
3791 | 3 | # | ||
3792 | 4 | # This program is free software; you can redistribute it and/or modify | ||
3793 | 5 | # it under the terms of the GNU General Public License as published by | ||
3794 | 6 | # the Free Software Foundation; either version 3 of the License, or | ||
3795 | 7 | # (at your option) any later version. | ||
3796 | 8 | # | ||
3797 | 9 | # This program is distributed in the hope that it will be useful, | ||
3798 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
3799 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
3800 | 12 | # GNU General Public License for more details. | ||
3801 | 13 | # | ||
3802 | 14 | # You should have received a copy of the GNU General Public License | ||
3803 | 15 | # along with this program; if not, write to the Free Software | ||
3804 | 16 | # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
3805 | 17 | # | ||
3806 | 18 | """ | ||
3807 | 19 | Time options, these classes will display a text box. | ||
3808 | 20 | """ | ||
3809 | 21 | |||
3810 | 22 | import gtk | ||
3811 | 23 | |||
3812 | 24 | from screenlets.options import _ | ||
3813 | 25 | from colour_option import ColorOption | ||
3814 | 26 | |||
3815 | 27 | class TimeOption(ColorOption): | ||
3816 | 28 | """An class for time options.""" | ||
3817 | 29 | |||
3818 | 30 | def generate_widget(self, value): | ||
3819 | 31 | """Generate a textbox for a time options""" | ||
3820 | 32 | self.widget = gtk.HBox() | ||
3821 | 33 | input_hour = gtk.SpinButton() | ||
3822 | 34 | input_minute = gtk.SpinButton() | ||
3823 | 35 | input_second = gtk.SpinButton() | ||
3824 | 36 | input_hour.set_range(0, 23) | ||
3825 | 37 | input_hour.set_max_length(2) | ||
3826 | 38 | input_hour.set_increments(1, 1) | ||
3827 | 39 | input_hour.set_numeric(True) | ||
3828 | 40 | input_hour.set_value(value[0]) | ||
3829 | 41 | input_minute.set_range(0, 59) | ||
3830 | 42 | input_minute.set_max_length(2) | ||
3831 | 43 | input_minute.set_increments(1, 1) | ||
3832 | 44 | input_minute.set_numeric(True) | ||
3833 | 45 | input_minute.set_value(value[1]) | ||
3834 | 46 | input_second.set_range(0, 59) | ||
3835 | 47 | input_second.set_max_length(2) | ||
3836 | 48 | input_second.set_increments(1, 1) | ||
3837 | 49 | input_second.set_numeric(True) | ||
3838 | 50 | input_second.set_value(value[2]) | ||
3839 | 51 | input_hour.connect('value-changed', self.has_changed) | ||
3840 | 52 | input_minute.connect('value-changed', self.has_changed) | ||
3841 | 53 | input_second.connect('value-changed', self.has_changed) | ||
3842 | 54 | input_hour.set_tooltip_text(self.desc) | ||
3843 | 55 | input_minute.set_tooltip_text(self.desc) | ||
3844 | 56 | input_second.set_tooltip_text(self.desc) | ||
3845 | 57 | self.widget.add(input_hour) | ||
3846 | 58 | self.widget.add(gtk.Label(':')) | ||
3847 | 59 | self.widget.add(input_minute) | ||
3848 | 60 | self.widget.add(gtk.Label(':')) | ||
3849 | 61 | self.widget.add(input_second) | ||
3850 | 62 | self.widget.add(gtk.Label('h')) | ||
3851 | 63 | self.widget.show_all() | ||
3852 | 64 | return self.widget | ||
3853 | 65 | |||
3854 | 66 | def set_value(self, value): | ||
3855 | 67 | """Set the time value as required.""" | ||
3856 | 68 | self.value = value | ||
3857 | 69 | |||
3858 | 70 | def has_changed(self, widget): | ||
3859 | 71 | """Executed when the widget event kicks off.""" | ||
3860 | 72 | box = widget.get_parent() | ||
3861 | 73 | inputs = box.get_children() | ||
3862 | 74 | self.value = ( | ||
3863 | 75 | int(inputs[0].get_value()), | ||
3864 | 76 | int(inputs[2].get_value()), | ||
3865 | 77 | int(inputs[4].get_value()), | ||
3866 | 78 | ) | ||
3867 | 79 | super(ColorOption, self).has_changed() | ||
3868 | 80 |
It seems to function generally, but acts a bit differently from the old one. For example text option does not save multiline text (probably it needs to be escaped). If nobody is against it, I think the code should be merged if Martin is willing to work on the code and fix bugs when they appear...