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