| Home | Trees | Indices | Help |
|
|---|
|
|
1 # This application is released under the GNU General Public License
2 # v3 (or, at your option, any later version). You can find the full
3 # text of the license under http://www.gnu.org/licenses/gpl.txt.
4 # By using, editing and/or distributing this software you agree to
5 # the terms and conditions of this license.
6 # Thank you for using free software!
7
8 # Screenlets main module (c) RYX (aka Rico Pfaus) 2007 <ryx@ryxperience.com>
9 #
10 ##@mainpage
11 #
12 ##@section intro_sec General Information
13 #
14 # INFO:
15 # - Screenlets are small owner-drawn applications that can be described as
16 # " the virtual representation of things lying/standing around on your desk".
17 # Sticknotes, clocks, rulers, ... the possibilities are endless. The goal of
18 # the Screenlets is to simplify the creation of fully themeable mini-apps that
19 # each solve basic desktop-work-related needs and generally improve the
20 # usability and eye-candy of the modern Linux-desktop.
21 #
22 # TODO: (possible improvements, not essential)
23 # - still more error-handling and maybe custom exceptions!!!
24 # - improve xml-based menu (is implemented, but I'm not happy with it)
25 # - switching themes slowly increases the memory usage (possible leak)
26 # - maybe attributes for dependancies/requirements (e.g. special
27 # python-libs or certain Screenlets)
28 # -
29 #
30
31 import pygtk
32 pygtk.require('2.0')
33 import gtk
34 import cairo, pango
35 import gobject
36 import rsvg
37 import os
38 import glob
39 import gettext
40 import math
41
42 # import screenlet-submodules
43 from options import *
44 import services
45 import utils
46
47 # TEST
48 import XmlMenu
49 # /TEST
50
51
52 #-------------------------------------------------------------------------------
53 # CONSTANTS
54 #-------------------------------------------------------------------------------
55
56 # the application name
57 APP_NAME = "Screenlets"
58
59 # the version of the Screenlets-baseclass in use
60 VERSION = "0.0.12"
61
62 # the application copyright
63 COPYRIGHT = "(c) RYX (Rico Pfaus) <ryx@ryxperience.com>"
64
65 # the application authors
66 AUTHORS = ["RYX (Rico Pfaus) <ryx@ryxperience.com>", "Whise (Helder Fraga)<helder.fraga@hotmail.com>","Hendrik Kaju (sorcerer)"]
67
68 # the application comments
69 COMMENTS = "Screenlets are small owner-drawn applications (written in Python, a very simple object-oriented programming-language) that can be described as 'the virtual representation of things lying/standing around on your desk'. Sticknotes, clocks, rulers, ... the possibilities are endless."
70
71 # the application website
72 WEBSITE = 'http://www.screenlets.org'
73
74 # the third party screenlets download site
75 THIRD_PARTY_DOWNLOAD = "http://screenlets.org/index.php/Category:UserScreenlets"
76
77 # install prefix (/usr or /usr/local) DO NOT CHANGE YET, WILL CHANGE WITH v0.1.0
78 INSTALL_PREFIX = '/usr'
79
80 # the global PATH where the screenlets are installed
81 PATH = INSTALL_PREFIX + '/share/screenlets'
82
83 # A list containing all the paths to search for screenlet-"packages"
84 # (these paths get searched when a new screenlet-instance shall be
85 # loaded through the module-loader function or a screenlet needs data
86 # from its personal dir)
87 SCREENLETS_PATH = [os.environ['HOME'] + '/.screenlets', PATH]
88
89 # translation stuff
90 gettext.textdomain('screenlets')
91 gettext.bindtextdomain('screenlets', '/usr/share/locale')
92
95
96
97 #-------------------------------------------------------------------------------
98 # CLASSES
99 #-------------------------------------------------------------------------------
100
102 """A container with constants for the default menuitems"""
103
104 # default menuitem constants (is it right to increase like this?)
105 NONE = 0
106 DELETE = 1
107 THEMES = 2
108 INFO = 4
109 SIZE = 8
110 WINDOW_MENU = 16
111 PROPERTIES = 32
112 # EXPERIMENTAL!! If you use this, the file menu.xml in the
113 # Screenlet's data-dir is used for generating the menu ...
114 XML = 512
115 # the default items
116 STANDARD = 1|2|8|16|32
117
118
120 """ScreenletThemes are simple storages that allow loading files
121 as svg-handles within a theme-directory. Each Screenlet can have
122 its own theme-directory. It is up to the Screenlet-developer if he
123 wants to let his Screenlet support themes or not. Themes are
124 turned off by default - if your Screenlet uses Themes, just set the
125 attribute 'theme_name' to the name of the theme's dir you want to use.
126 TODO: remove dict-inheritance"""
127
128 # meta-info (set through theme.conf)
129 __name__ = ''
130 __author__ = ''
131 __version__ = ''
132 __info__ = ''
133
134 # attributes
135 path = ""
136 loaded = False
137 width = 0
138 height = 0
139 option_overrides = {}
140 p_fdesc = None
141 p_layout = None
142 tooltip = None
143 notify = None
144
146 # set theme-path and load all files in path
147 self.path = path
148 self.svgs = {}
149 self.pngs = {}
150 self.option_overrides = {}
151 self.loaded = self.__load_all()
152 if self.loaded == False:
153 raise Exception(_("Error while loading ScreenletTheme in: ") + path)
154
156 if name in ("width", "height"):
157 if self.loaded and len(self)>0:
158 size=self[0].get_dimension_data()
159 if name=="width":
160 return size[0]
161 else:
162 return size[1]
163 else:
164 return object.__getattr__(self, name)
165
167 """Apply this theme's overridden options to the given Screenlet."""
168 # disable the canvas-updates in the screenlet
169 screenlet.disable_updates = True
170 # theme_name needs special care (must be applied last)
171 theme_name = ''
172 # loop through overrides and appply them
173 for name in self.option_overrides:
174 print _("Override: ") + name
175 o = screenlet.get_option_by_name(name)
176 if o and not o.protected:
177 if name == 'theme_name':
178 # import/remember theme-name, but not apply yet
179 theme_name = o.on_import(self.option_overrides[name])
180 else:
181 # set option in screenlet
182 setattr(screenlet, name,
183 o.on_import(self.option_overrides[name]))
184 else:
185 print _("WARNING: Option '%s' not found or protected.") % name
186 # now apply theme
187 if theme_name != '':
188 screenlet.theme_name = theme_name
189 # re-enable updates and call redraw/reshape
190 screenlet.disable_updates = False
191 screenlet.redraw_canvas()
192 screenlet.update_shape()
193
195 """Checks if a file with filename is loaded in this theme."""
196 try:
197 if self[filename]:
198 return True
199 except:
200 #raise Exception
201 return False
202
204 """Draws text"""
205 ctx.save()
206 ctx.translate(x, y)
207 if self.p_layout == None :
208
209 self.p_layout = ctx.create_layout()
210 else:
211
212 ctx.update_layout(self.p_layout)
213 self.p_fdesc = pango.FontDescription()
214 self.p_fdesc.set_family_static(font)
215 self.p_fdesc.set_size(size * pango.SCALE)
216 self.p_layout.set_font_description(self.p_fdesc)
217 self.p_layout.set_width(width * pango.SCALE)
218 self.p_layout.set_alignment(allignment)
219 self.p_layout.set_markup(text)
220 ctx.show_layout(self.p_layout)
221 ctx.restore()
222
223
225 """Draws a circule"""
226 ctx.save()
227 ctx.translate(x, y)
228 ctx.arc(width/2,height/2,min(height,width)/2,0,2*math.pi)
229 if fill:ctx.fill()
230 else: ctx.stroke()
231 ctx.restore()
232
233 - def draw_line(self,ctx,start_x,start_y,end_x,end_y,line_width = 1,close=False,preserve=False):
234 """Draws a line"""
235 ctx.save()
236 ctx.move_to(start_x, start_y)
237 ctx.set_line_width(line_width)
238 ctx.rel_line_to(end_x, end_y)
239 if close : ctx.close_path()
240 if preserve: ctx.stroke_preserve()
241 else: ctx.stroke()
242 ctx.restore()
243
245 """Draws a rectangle"""
246 ctx.save()
247 ctx.translate(x, y)
248 ctx.rectangle (0,0,width,height)
249 if fill:ctx.fill()
250 else: ctx.stroke()
251 ctx.restore()
252
254 """Draws a rounded rectangle"""
255 ctx.save()
256 ctx.translate(x, y)
257 padding=0 # Padding from the edges of the window
258 rounded=rounded_angle # How round to make the edges 20 is ok
259 w = width
260 h = height
261
262 # Move to top corner
263 ctx.move_to(0+padding+rounded, 0+padding)
264
265 # Top right corner and round the edge
266 ctx.line_to(w-padding-rounded, 0+padding)
267 ctx.arc(w-padding-rounded, 0+padding+rounded, rounded, math.pi/2, 0)
268
269 # Bottom right corner and round the edge
270 ctx.line_to(w-padding, h-padding-rounded)
271 ctx.arc(w-padding-rounded, h-padding-rounded, rounded, 0, math.pi/2)
272
273 # Bottom left corner and round the edge.
274 ctx.line_to(0+padding+rounded, h-padding)
275 ctx.arc(0+padding+rounded, h-padding-rounded, rounded, math.pi+math.pi/2, math.pi)
276
277 # Top left corner and round the edge
278 ctx.line_to(0+padding, 0+padding+rounded)
279 ctx.arc(0+padding+rounded, 0+padding+rounded, rounded, math.pi/2, 0)
280
281 # Fill in the shape.
282 if fill:ctx.fill()
283 else: ctx.stroke()
284 ctx.restore()
285
287 """Show notification window at current mouse position."""
288 if self.notify == None:
289 self.notify = Notify()
290 self.notify.text = text
291 self.notify.show()
292
294 """hide notification window"""
295 if self.notify != None:
296 self.notify.hide()
297 self.notify = None
298
300 """Show tooltip window at current mouse position."""
301 if self.tooltip == None:
302 self.tooltip = Tooltip(300, 400)
303 self.tooltip.text = text
304 self.tooltip.x = tooltipx
305 self.tooltip.y = tooltipy
306 self.tooltip.show()
307
309 """hide tooltip window"""
310 if self.tooltip != None:
311 self.tooltip.hide()
312 self.tooltip = None
313
315 """Check if this theme contains overrides for options."""
316 return len(self.option_overrides) > 0
317
319 """Load a config-file from this theme's dir and save vars in list."""
320 ini = utils.IniReader()
321 if ini.load(filename):
322 if ini.has_section('Theme'):
323 self.__name__ = ini.get_option('name', section='Theme')
324 self.__author__ = ini.get_option('author', section='Theme')
325 self.__version__ = ini.get_option('version', section='Theme')
326 self.__info__ = ini.get_option('info', section='Theme')
327 if ini.has_section('Options'):
328 opts = ini.list_options(section='Options')
329 if opts:
330 for o in opts:
331 self.option_overrides[o[0]] = o[1]
332 print _("theme.conf loaded: ")
333 print _("Name: ") + str(self.__name__)
334 print _("Author: ") +str(self.__author__)
335 print _("Version: ") +str(self.__version__)
336 print _("Info: ") +str(self.__info__)
337 else:
338 print _("Failed to load theme.conf")
339
340
342 """Load an SVG-file into this theme and reference it as ref_name."""
343 if self.has_key(filename):
344 del self[filename]
345 self[filename] = rsvg.Handle(self.path + "/" + filename)
346 self.svgs[filename[:-4]] = self[filename]
347 if self[filename] != None:
348 # set width/height
349 size=self[filename].get_dimension_data()
350 if size:
351 self.width = size[0]
352 self.height = size[1]
353 return True
354 else:
355 return False
356
358 """Load a PNG-file into this theme and reference it as ref_name."""
359 if self.has_key(filename):
360 del self[filename]
361 self[filename] = cairo.ImageSurface.create_from_png(self.path +
362 "/" + filename)
363 self.pngs[filename[:-4]] = self[filename]
364 if self[filename] != None:
365 return True
366 else:
367 return False
368
370 """Load all files in the theme's path. Currently only loads SVGs and
371 PNGs."""
372 # clear overrides
373 #self.__option_overrides = {}
374 # read dir
375 dirlst = glob.glob(self.path + '/*')
376 if len(dirlst)==0:
377 return False
378 plen = len(self.path) + 1
379 for file in dirlst:
380 fname = file[plen:]
381 if fname.endswith('.svg'):
382 # svg file
383 if self.load_svg(fname) == False:
384 return False
385 elif fname.endswith('.png'):
386 # svg file
387 if self.load_png(fname) == False:
388 return False
389 elif fname == "theme.conf":
390 print _("theme.conf found! Loading option-overrides.")
391 # theme.conf
392 if self.load_conf(file) == False:
393 return False
394 return True
395
400
401 # TODO: fix function, rsvg handles are not freed properly
403 """Deletes the Theme's contents and frees all rsvg-handles.
404 TODO: freeing rsvg-handles does NOT work for some reason"""
405 self.option_overrides.clear()
406 for filename in self:
407 #self[filename].close()
408 del filename
409 self.clear()
410
411 # TEST: render-function
412 # should be used like "theme.render(context, 'notes-bg')" and then use
413 # either an svg or png image
415 """Render an image from within this theme to the given context. This
416 function can EITHER use png OR svg images, so it is possible to
417 create themes using both image-formats when a Screenlet uses this
418 function for drawing its images. The image name has to be defined
419 without the extension and the function will automatically select
420 the available one (SVG is prefered over PNG)."""
421 """if self.has_key(name + '.svg'):
422 self[name + '.svg'].render_cairo(ctx)
423 else:
424 ctx.set_source_surface(self[name + '.png'], 0, 0)
425 ctx.paint()"""
426 try:
427 #self[name + '.svg'].render_cairo(ctx)
428 self.svgs[name].render_cairo(ctx)
429 except:
430 #ctx.set_source_surface(self[name + '.png'], 0, 0)
431 ctx.set_source_surface(self.pngs[name], 0, 0)
432 ctx.paint()
433
434 #else:
435 # ctx.set_source_pixbuf(self[name + '.png'], 0, 0)
436 # ctx.paint()
437
438
440 """A Screenlet is a (i.e. contains a) shaped gtk-window that is
441 fully invisible by default. Subclasses of Screenlet can render
442 their owner-drawn graphics on fully transparent background."""
443
444 # default meta-info for Screenlets
445 __name__ = _('No name set for this Screenlet')
446 __version__ = '0.0'
447 __author__ = _('No author defined for this Screenlet')
448 __desc__ = _('No info set for this Screenlet')
449 __requires__ = [] # still unused
450 #__target_version__ = '0.0.0'
451 #__backend_version__ = '0.0.1'
452
453 # attributes (TODO: remove them here and add them to the constructor,
454 # because they only should exist per instance)
455 id = '' # id-attribute for handling instances
456 window = None # the gtk.Window behind the scenes
457 theme = None # the assigned ScreenletTheme
458 uses_theme = True # flag indicating whether Screenlet uses themes
459 menu = None # the right-click gtk.Menu
460 is_dragged = False # TODO: make this work
461 quit_on_close = True # if True, closing this instance quits gtk
462 saving_enabled = True # if False, saving is disabled
463 dragging_over = False # true if something is dragged over
464 disable_updates = False # to temporarily avoid refresh/reshape
465 p_context = None # PangoContext
466 p_layout = None # PangoLayout
467
468 # default editable options, available for all Screenlets
469 x = 0
470 y = 0
471 mousex = 0
472 mousey = 0
473 width = 100
474 height = 100
475 scale = 1.0
476 theme_name = ""
477 is_sticky = False
478 is_widget = False
479 keep_above = True
480 keep_below = False
481 skip_pager = True
482 skip_taskbar = True
483 lock_position = False
484 allow_option_override = True # if False, overrides are ignored
485 ask_on_option_override = True # if True, overrides need confirmation
486 has_started = False
487
488 # internals (deprecated? we still don't get the end of a begin_move_drag)
489 __lastx = 0
490 __lasty = 0
491
492 # some menuitems (needed for checking/unchecking)
493 # DEPRECATED: remove - don't really work anyway ... (or fix the menu?)
494 __mi_keep_above = None
495 __mi_keep_below = None
496 __mi_widget = None
497 __mi_sticky = None
498 __mi_lock = None
499 # for custom signals (which aren't acutally used ... yet)
500 __gsignals__ = dict(screenlet_removed=(gobject.SIGNAL_RUN_FIRST,
501 gobject.TYPE_NONE, (gobject.TYPE_OBJECT,)))
502
503 - def __init__ (self, id='', width=100, height=100, parent_window=None,
504 show_window=True, is_widget=False, is_sticky=False,
505 uses_theme=True, path=os.getcwd(), drag_drop=False, session=None,
506 enable_saving=True, service_class=services.ScreenletService,
507 uses_pango=False):
508 """Constructor - should only be subclassed"""
509 # call gobject and EditableOptions superclasses
510 super(Screenlet, self).__init__()
511 EditableOptions.__init__(self)
512 # init properties
513 self.id = id
514 self.session = session
515 self.service = None
516 # if we have an id and a service-class, register our service
517 if self.id and service_class:
518 self.register_service(service_class)
519 # notify service about adding this instance
520 self.service.instance_added(self.id)
521 self.width = width
522 self.height = height
523 self.is_dragged = False
524 self.__path__ = path
525 self.saving_enabled = enable_saving # used by session
526 # set some attributes without calling __setattr__
527 self.__dict__['theme_name'] = ""
528 self.__dict__['is_widget'] = is_widget
529 self.__dict__['is_sticky'] = is_sticky
530 self.__dict__['x'] = 0
531 self.__dict__['y'] = 0
532 # TEST: set scale relative to theme size (NOT WORKING)
533 #self.__dict__['scale'] = width/100.0
534 # /TEST
535 # shape bitmap
536 self.__shape_bitmap = None
537 self.__shape_bitmap_width = 0
538 self.__shape_bitmap_height = 0
539 # "editable" options, first create a group
540 self.add_options_group('Screenlet',
541 _('The basic settings for this Screenlet-instance.'))
542 # if this Screenlet uses themes, add theme-specific options
543 # (NOTE: this option became hidden with 0.0.9 and doesn't use
544 # get_available_themes anymore for showing the choices)
545 if uses_theme:
546 self.uses_theme = True
547 self.add_option(StringOption('Screenlet', 'theme_name',
548 'default', '', '', hidden=True))
549 # create/add options
550 self.add_option(IntOption('Screenlet', 'x',
551 0, _('X-Position'), _('The X-position of this Screenlet ...'),
552 min=0, max=gtk.gdk.screen_width()))
553 self.add_option(IntOption('Screenlet', 'y',
554 0, _('Y-Position'), _('The Y-position of this Screenlet ...'),
555 min=0, max=gtk.gdk.screen_height()))
556 self.add_option(IntOption('Screenlet', 'width',
557 width, _('Width'), _('The width of this Screenlet ...'),
558 min=16, max=1000, hidden=True))
559 self.add_option(IntOption('Screenlet', 'height',
560 height, _('Height'), _('The height of this Screenlet ...'),
561 min=16, max=1000, hidden=True))
562 self.add_option(FloatOption('Screenlet', 'scale',
563 self.scale, _('Scale'), _('The scale-factor of this Screenlet ...'),
564 min=0.1, max=10.0, digits=2, increment=0.1))
565 self.add_option(BoolOption('Screenlet', 'is_sticky',
566 is_sticky, _('Stick to Desktop'),
567 _('Show this Screenlet on all workspaces ...')))
568 self.add_option(BoolOption('Screenlet', 'is_widget',
569 is_widget, _('Treat as Widget'),
570 _('Treat this Screenlet as a "Widget" ...')))
571 self.add_option(BoolOption('Screenlet', 'lock_position',
572 self.lock_position, _('Lock position'),
573 _('Stop the screenlet from being moved...')))
574 self.add_option(BoolOption('Screenlet', 'keep_above',
575 self.keep_above, _('Keep above'),
576 _('Keep this Screenlet above other windows ...')))
577 self.add_option(BoolOption('Screenlet', 'keep_below',
578 self.keep_below, _('Keep below'),
579 _('Keep this Screenlet below other windows ...')))
580 self.add_option(BoolOption('Screenlet', 'skip_pager',
581 self.skip_pager, _('Skip Pager'),
582 _('Set this Screenlet to show/hide in pagers ...')))
583 self.add_option(BoolOption('Screenlet', 'skip_taskbar',
584 self.skip_pager, _('Skip Taskbar'),
585 _('Set this Screenlet to show/hide in taskbars ...')))
586 if uses_theme:
587 self.add_option(BoolOption('Screenlet', 'allow_option_override',
588 self.allow_option_override, _('Allow overriding Options'),
589 _('Allow themes to override options in this screenlet ...')))
590 self.add_option(BoolOption('Screenlet', 'ask_on_option_override',
591 self.ask_on_option_override, _('Ask on Override'),
592 _('Show a confirmation-dialog when a theme wants to override ')+\
593 _('the current options of this Screenlet ...')))
594 # disable width/height
595 self.disable_option('width')
596 self.disable_option('height')
597 # create window
598 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
599 if parent_window:
600 self.window.set_parent_window(parent_window)
601 self.window.set_transient_for(parent_window)
602 self.window.set_destroy_with_parent(True)
603 self.window.resize(width, height)
604 self.window.set_decorated(False)
605 self.window.set_app_paintable(True)
606 # create pango layout, if active
607 if uses_pango:
608 self.p_context = self.window.get_pango_context()
609 if self.p_context:
610 self.p_layout = pango.Layout(self.p_context)
611 self.p_layout.set_font_description(\
612 pango.FontDescription("Sans 12"))
613 # set type hint
614 #self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)
615 self.window.set_keep_above(True)
616 self.window.set_skip_taskbar_hint(True)
617 self.window.set_skip_pager_hint(True)
618 if is_sticky:
619 self.window.stick()
620 self.alpha_screen_changed(self.window)
621 self.update_shape()
622 #self.window.set_events(gtk.gdk.BUTTON_PRESS_MASK)
623 self.window.set_events(gtk.gdk.ALL_EVENTS_MASK)
624 self.window.connect("composited-changed", self.composite_changed)
625 self.window.connect("delete_event", self.delete_event)
626 self.window.connect("destroy", self.destroy)
627 self.window.connect("expose_event", self.expose)
628 self.window.connect("button-press-event", self.button_press)
629 self.window.connect("button-release-event", self.button_release)
630 self.window.connect("configure-event", self.configure_event)
631 self.window.connect("screen-changed", self.alpha_screen_changed)
632 self.window.connect("realize", self.realize_event)
633 self.window.connect("enter-notify-event", self.enter_notify_event)
634 self.window.connect("leave-notify-event", self.leave_notify_event)
635 self.window.connect("focus-in-event", self.focus_in_event)
636 self.window.connect("focus-out-event", self.focus_out_event)
637 self.window.connect("scroll-event", self.scroll_event)
638 self.window.connect("motion-notify-event",self.motion_notify_event)
639 self.window.connect("map-event", self.map_event)
640 self.window.connect("unmap-event", self.unmap_event)
641 # add key-handlers (TODO: use keyword-attrib to activate?)
642 self.window.connect("key-press-event", self.key_press)
643 # drag/drop support (NOTE: still experimental and incomplete)
644 if drag_drop:
645 self.window.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
646 gtk.DEST_DEFAULT_DROP, #gtk.DEST_DEFAULT_ALL,
647 [("text/plain", 0, 0),
648 ("image", 0, 1),
649 ("text/uri-list", 0, 2)],
650 gtk.gdk.ACTION_COPY)
651 self.window.connect("drag_data_received", self.drag_data_received)
652 self.window.connect("drag-begin", self.drag_begin)
653 self.window.connect("drag-end", self.drag_end)
654 self.window.connect("drag-motion", self.drag_motion)
655 self.window.connect("drag-leave", self.drag_leave)
656 # create menu
657 self.menu = gtk.Menu()
658 # show window so it can realize , but hiding it so we can show it only when atributes have been set , this fixes some placement errors arround the screen egde
659 if show_window:
660 self.window.show()
661 self.window.hide()
662
664 # set the value in GObject (ESSENTIAL!!!!)
665 self.on_before_set_atribute(name, value)
666 gobject.GObject.__setattr__(self, name, value)
667 # And do other actions
668 if name=="x" or name=="y":
669 self.window.move(self.x, self.y)
670 elif name == 'scale':
671 self.window.resize(int(self.width * self.scale),
672 int(self.height * self.scale))
673 # TODO: call on_resize-handler here !!!!
674 self.on_scale()
675 self.redraw_canvas()
676 self.update_shape()
677 elif name == "theme_name":
678 #self.__dict__ ['theme_name'] = value
679 print _("LOAD NEW THEME: ") + value
680 print _("FOUND: ") + str(self.find_theme(value))
681 #self.load_theme(self.get_theme_dir() + value)
682 # load theme
683 path = self.find_theme(value)
684 if path:
685 self.load_theme(path)
686 #self.load_first_theme(value)
687 self.redraw_canvas()
688 self.update_shape()
689 elif name in ("width", "height"):
690 #self.__dict__ [name] = value
691 if self.window:
692 self.window.resize(int(self.width*self.scale), int(self.height*self.scale))
693 #self.redraw_canvas()
694 self.update_shape()
695 elif name == "is_widget":
696 if self.has_started:
697 self.set_is_widget(value)
698 elif name == "is_sticky":
699 if value == True:
700 self.window.stick()
701 else:
702 self.window.unstick()
703 #if self.__mi_sticky:
704 # self.__mi_sticky.set_active(value)
705 elif name == "keep_above":
706 self.window.set_keep_above(bool(value))
707 #self.__mi_keep_above.set_active(value)
708 elif name == "keep_below":
709 self.window.set_keep_below(bool(value))
710 #self.__mi_keep_below.set_active(value)
711 elif name == "skip_pager":
712 if self.window.window:
713 self.window.window.set_skip_pager_hint(bool(value))
714 elif name == "skip_taskbar":
715 if self.window.window:
716 self.window.window.set_skip_taskbar_hint(bool(value))
717 # NOTE: This is the new recommended way of storing options in real-time
718 # (we access the backend through the session here)
719 if self.saving_enabled:
720 o = self.get_option_by_name(name)
721 if o != None:
722 self.session.backend.save_option(self.id, o.name,
723 o.on_export(value))
724 self.on_after_set_atribute(name, value)
725 # /TEST
726
727 #-----------------------------------------------------------------------
728 # Screenlet's public functions
729 #-----------------------------------------------------------------------
730
731 # NOTE: This function is deprecated and will get removed. The
732 # XML-based menus should be preferred
844
858
860 """Fills the given cairo.Context with fully transparent white."""
861 ctx.save()
862 ctx.set_source_rgba(1, 1, 1, 0)
863 ctx.set_operator (cairo.OPERATOR_SOURCE)
864 ctx.paint()
865 ctx.restore()
866
868 """Close this Screenlet
869 TODO: send close-notify instead of destroying window?"""
870 #self.save_settings()
871 self.window.unmap()
872 self.window.destroy()
873 #self.window.event(gtk.gdk.Event(gtk.gdk.DELETE))
874
876 """Create drag-icon and -mask for drag-operation. Returns a 2-tuple
877 with the icon and the mask. To supply your own icon you can use the
878 on_create_drag_icon-handler and return the icon/mask as 2-tuple."""
879 w = self.width
880 h = self.height
881 icon, mask = self.on_create_drag_icon()
882 if icon == None:
883 # create icon
884 icon = gtk.gdk.Pixmap(self.window.window, w, h)
885 ctx = icon.cairo_create()
886 self.clear_cairo_context(ctx)
887 self.on_draw(ctx)
888 if mask == None:
889 # create mask
890 mask = gtk.gdk.Pixmap(self.window.window, w, h)
891 ctx = mask.cairo_create()
892 self.clear_cairo_context(ctx)
893 self.on_draw_shape(ctx)
894 return (icon, mask)
895
899
901 """Find the first occurence of a theme and return its global path."""
902 sn = self.get_short_name()
903 for p in SCREENLETS_PATH:
904 fpath = p + '/' + sn + '/themes/' + name
905 if os.path.isdir(fpath):
906 return fpath
907 return None
908
910 """Return the short name of this screenlet. This returns the classname
911 of the screenlet without trailing "Screenlet". Please always use
912 this function if you want to retrieve the short name of a Screenlet."""
913 return self.__class__.__name__[:-9]
914
916 """@DEPRECATED: Return the name of this screenlet's personal directory."""
917 p = utils.find_first_screenlet_path(self.get_short_name())
918 if p:
919 return p
920 else:
921 if self.__path__ != '':
922 return self.__path__
923 else:
924 return os.getcwd()
925
927 """@DEPRECATED: Return the name of this screenlet's personal theme-dir.
928 (Only returns the dir under the screenlet's location"""
929 return self.get_screenlet_dir() + "/themes/"
930
932 """Returns a list with the names of all available themes in this
933 Screenlet's theme-directory."""
934 lst = []
935 for p in SCREENLETS_PATH:
936 d = p + '/' + self.get_short_name() + '/themes/'
937 if os.path.isdir(d):
938 #dirname = self.get_theme_dir()
939 dirlst = glob.glob(d + '*')
940 dirlst.sort()
941 tdlen = len(d)
942 for fname in dirlst:
943 dname = fname[tdlen:]
944 # TODO: check if it's a dir
945 lst.append(dname)
946 return lst
947
949 """Called when screenlet finishes loading"""
950 self.has_started = True
951 self.on_init()
952 try: self.window.show()
953 except: print 'unable to show window'
954 # the keep above and keep bellow must be reset after the window is shown this is absolutly necessary
955 self.keep_above= self.keep_above
956 self.keep_below= self.keep_below
957 if self.is_widget:
958 self.set_is_widget(True)
963
964 # EXPERIMENTAL:
965 # NOTE: load_theme does NOT call redraw_canvas and update_shape!!!!!
966 # To do all in one, set attribute self.theme_name instead
968 """Load a theme for this Screenlet from the given path. NOTE:
969 load_theme does NOT call redraw_canvas and update_shape!!!!! To do all
970 in one call, set the attribute self.theme_name instead."""
971 if self.theme:
972 self.theme.free()
973 del self.theme
974 self.theme = ScreenletTheme(path)
975 # check for errors
976 if self.theme.loaded == False:
977 print _("Error while loading theme: ") + path
978 self.theme = None
979 else:
980 # call user-defined handler
981 self.on_load_theme()
982 # if override options is allowed, apply them
983 if self.allow_option_override:
984 if self.theme.has_overrides():
985 if self.ask_on_option_override==True and \
986 show_question(self,
987 _('This theme wants to override your settings for ')+\
988 _('this Screenlet. Do you want to allow that?')) == False:
989 return
990 self.theme.apply_option_overrides(self)
991 # /EXPERIMENTAL
992
996
998 """Register or create the given ScreenletService-(sub)class as the new
999 service for this Screenlet. If self is not the first instance in the
1000 current session, the service from the first instance will be used
1001 instead and no new service is created."""
1002 if self.session:
1003 if len(self.session.instances) == 0:
1004 # if it is the basic service, add name to call
1005 if service_classobj==services.ScreenletService:
1006 self.service = service_classobj(self, self.get_short_name())
1007 else:
1008 # else only pass this screenlet
1009 self.service = service_classobj(self)
1010 else:
1011 self.service = self.session.instances[0].service
1012 # TODO: throw exception??
1013 return True
1014 return False
1015
1017 """Set this window to be treated as a Widget (only supported by
1018 compiz using the widget-plugin yet)"""
1019 if value==True:
1020 # set window type to utility
1021 #self.window.window.set_type_hint(
1022 # gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
1023 # set _compiz_widget-property on window
1024 self.window.window.property_change("_COMPIZ_WIDGET",
1025 gtk.gdk.SELECTION_TYPE_WINDOW,
1026 32, gtk.gdk.PROP_MODE_REPLACE, (True,))
1027 else:
1028 # set window type to normal
1029 #self.window.window.set_type_hint(
1030 # gtk.gdk.WINDOW_TYPE_HINT_NORMAL)
1031 # set _compiz_widget-property
1032 self.window.window.property_delete("_COMPIZ_WIDGET")
1033 # notify handler
1034 self.on_switch_widget_state(value)
1035
1037 """Show this Screenlet's underlying gtk.Window"""
1038 self.window.show()
1039 self.window.move(self.x, self.y)
1040 self.on_show()
1041
1043 """Show the EditableSettingsDialog for this Screenlet."""
1044 se = OptionsDialog(490, 450)
1045 img = gtk.Image()
1046 try:
1047 d = self.get_screenlet_dir()
1048 if os.path.isfile(d + '/icon.svg'):
1049 icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.svg')
1050 elif os.path.isfile(d + '/icon.png'):
1051 icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.png')
1052 img.set_from_pixbuf(icn)
1053 except:
1054 img.set_from_stock(gtk.STOCK_PROPERTIES, 5)
1055 se.set_title(self.__name__)
1056 se.set_info(self.__name__, self.__desc__, '(c) by ' + self.__author__,
1057 version='v' + self.__version__, icon=img)
1058 se.show_options_for_object(self)
1059 resp = se.run()
1060 if resp == gtk.RESPONSE_REJECT: # TODO!!!!!
1061 se.reset_to_defaults()
1062 else:
1063 self.update_shape()
1064 se.destroy()
1065
1067 """Redraw the entire Screenlet's window area.
1068 TODO: store window alloaction in class and change when size changes."""
1069 # if updates are disabled, just exit
1070 if self.disable_updates:
1071 return
1072 if self.window:
1073 x, y, w, h = self.window.get_allocation()
1074 rect = gtk.gdk.Rectangle(x, y, w, h)
1075 if self.window.window:
1076 self.window.window.invalidate_rect(rect, True)
1077 self.window.window.process_updates(True)
1078
1080 """Redraw the given Rectangle (x, y, width, height) within the
1081 current Screenlet's window."""
1082 # if updates are disabled, just exit
1083 if self.disable_updates:
1084 return
1085 if self.window:
1086 rect = gtk.gdk.Rectangle(x, y, width, height)
1087 if self.window.window:
1088 self.window.window.invalidate_rect(rect, True)
1089 self.window.window.process_updates(True)
1090
1092 """Removed shaped window , in case the nom composited shape has been set"""
1093 if self.window.window:
1094 self.window.window.shape_combine_mask(None,0,0)
1095
1097 """Update window shape (only call this when shape has changed
1098 because it is very ressource intense if ran too often)."""
1099 # if updates are disabled, just exit
1100 if self.disable_updates:
1101 return
1102 print _("UPDATING SHAPE")
1103 # TODO:
1104 #if not self.window.is_composited():
1105 # self.update_shape_non_composited()
1106 # calculate new width/height of shape bitmap
1107 w = int(self.width * self.scale)
1108 h = int(self.height * self.scale)
1109 # if 0 set it to 100 to avoid crashes and stay interactive
1110 if w==0: w = 100
1111 if h==0: h = 100
1112 # if size changed, recreate shape bitmap
1113 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
1114 data = ''.zfill(w*h)
1115 self.__shape_bitmap = gtk.gdk.bitmap_create_from_data(None, data,
1116 w, h)
1117 self.__shape_bitmap_width = w
1118 self.__shape_bitmap_height = h
1119 # create context and draw shape
1120 ctx = self.__shape_bitmap.cairo_create()
1121 self.clear_cairo_context(ctx) #TEST
1122
1123 # shape the window acording if the window is composited or not
1124
1125 if self.window.is_composited():
1126
1127 self.on_draw_shape(ctx)
1128 # and cut window with mask
1129 self.window.input_shape_combine_mask(self.__shape_bitmap, 0, 0)
1130 else:
1131 try: self.on_draw(ctx) #Works better then the shape method on non composited windows
1132 except: self.on_draw_shape(ctx) # if error on on_draw use standard shape method
1133 # and cut window with mask
1134 self.window.shape_combine_mask(self.__shape_bitmap,0,0)
1135
1137 """TEST: This function is intended to shape the window whenever no
1138 composited environment can be found. (NOT WORKING YET!!!!)"""
1139 #pixbuf = gtk.gdk.GdkPixbuf.new_from_file)
1140 # calculate new width/height of shape bitmap
1141 w = int(self.width * self.scale)
1142 h = int(self.height * self.scale)
1143 # if 0 set it to 100 to avoid crashes and stay interactive
1144 if w==0: w = 100
1145 if h==0: h = 100
1146 # if size changed, recreate shape bitmap
1147 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
1148 data = ''.zfill(w*h)
1149 self.__shape_bitmap = gtk.gdk.pixbuf_new_from_data(data,
1150 gtk.gdk.COLORSPACE_RGB, True, 1, w, h, w)
1151 self.__shape_bitmap_width = w
1152 self.__shape_bitmap_height = h
1153 # and render window contents to it
1154 # TOOD!!
1155 if self.__shape_bitmap:
1156 # create new mask
1157 (pixmap,mask) = self.__shape_bitmap.render_pixmap_and_mask(255)
1158 # apply new mask to window
1159 self.window.shape_combine_mask(mask)
1160
1161 # ----------------------------------------------------------------------
1162 # Screenlet's event-handler dummies
1163 # ----------------------------------------------------------------------
1164
1166 """Called when the Screenlet gets deleted. Return True to cancel.
1167 TODO: sometimes not properly called"""
1168 return not show_question(self, _("To quit all %s's, use 'Quit' instead. ") % self.__class__.__name__ +\
1169 _('Really delete this %s and its settings?') % self.get_short_name())
1170 """return not show_question(self, 'Deleting this instance of the '+\
1171 self.__name__ + ' will also delete all your personal '+\
1172 'changes you made to it!! If you just want to close the '+\
1173 'application, use "Quit" instead. Are you sure you want to '+\
1174 'delete this instance?')
1175 return False"""
1176
1177 # TODO: on_drag
1178 # TODO: on_drag_end
1179
1183
1187
1188
1190 """Called when the screenlet's drag-icon is created. You can supply
1191 your own icon and mask by returning them as a 2-tuple."""
1192 return (None, None)
1193
1197
1201
1205
1206
1210
1214
1218
1220 """Callback for drawing the Screenlet's window - override
1221 in subclasses to implement your own drawing."""
1222 pass
1223
1225 """Callback for drawing the Screenlet's shape - override
1226 in subclasses to draw the window's input-shape-mask."""
1227 pass
1228
1232
1236
1240
1242 """Called when the Screenlet's options have been applied and the
1243 screenlet finished its initialization. If you want to have your
1244 Screenlet do things on startup you should use this handler."""
1245 pass
1246
1250
1254
1258
1260 """Called when a buttonpress-event occured in Screenlet's window.
1261 Returning True causes the event to be not further propagated."""
1262 return False
1263
1267
1271
1275
1277 """Called when a buttonrelease-event occured in Screenlet's window.
1278 Returning True causes the event to be not further propagated."""
1279 return False
1280
1284
1287
1291
1295
1299
1303
1307
1311
1312 # ----------------------------------------------------------------------
1313 # Screenlet's event-handlers for GTK-events
1314 # ----------------------------------------------------------------------
1315
1317 """set colormap for window"""
1318 if screen==None:
1319 screen = window.get_screen()
1320 map = screen.get_rgba_colormap()
1321 if map:
1322 pass
1323 else:
1324 map = screen.get_rgb_colormap()
1325 window.set_colormap(map)
1326
1358
1365
1367 #this handle is called when composition changed
1368 self.remove_shape() # removing previous set shape , this is absolutly necessary
1369 self.window.hide() # hiding the window and showing it again so the window can convert to the right composited state
1370 self.is_sticky = self.is_sticky #changing from non composited to composited makes the screenlets loose sticky state , this fixes that
1371 self.window.show()
1372 print 'composite change to ' + str(self.window.is_composited())
1373 self.redraw_canvas()
1374 self.update_shape()
1375 self.is_sticky = self.is_sticky #and again ...
1376 self.on_composite_changed()
1377
1378 # NOTE: this should somehow handle the end of a move_drag-operation
1380 #print "onConfigure"
1381 #print event
1382 #if self.is_dragged == True:
1383 # set new position and cause a save of this Screenlet (not use
1384 # setattr to avoid conflicts with the window.move in __setattr__)
1385 if event.x != self.x:
1386 self.__dict__['x'] = event.x
1387 if self.session:
1388 self.session.backend.save_option(self.id, 'x', str(event.x))
1389 if event.y != self.y:
1390 self.__dict__['y'] = event.y
1391 if self.session:
1392 self.session.backend.save_option(self.id, 'y', str(event.y))
1393 return False
1394
1396 # cancel event?
1397 print "delete_event"
1398 if self.on_delete() == True:
1399 print _("Cancel delete_event")
1400 return True
1401 else:
1402 self.close()
1403 return False
1404
1406 # call user-defined on_quit-handler
1407 self.on_quit()
1408 #print "destroy signal occurred"
1409 self.emit("screenlet_removed", self)
1410 # close gtk?
1411 if self.quit_on_close:
1412 if self.session: # if we have a session, flush current data
1413 self.session.backend.flush()
1414 gtk.main_quit()
1415 else:
1416 del self # ??? does this really work???
1417
1422 #return False
1423
1426
1431
1433 #print "Drag motion"
1434 if self.dragging_over == False:
1435 self.dragging_over = True
1436 self.on_drag_enter(drag_context, x, y, timestamp)
1437 return False
1438
1443
1447 #self.redraw_canvas()
1448
1450 ctx = widget.window.cairo_create()
1451 # clear context
1452 self.clear_cairo_context(ctx)
1453 # set a clip region for the expose event
1454 ctx.rectangle(event.area.x, event.area.y,
1455 event.area.width, event.area.height)
1456 ctx.clip()
1457 # scale context
1458 #ctx.scale(self.scale, self.scale)
1459 # call drawing method
1460 self.on_draw(ctx)
1461 # and delete context (needed?)
1462 del ctx
1463 return False
1464
1466 self.on_focus(event)
1467
1469 self.on_unfocus(event)
1470
1472 """Handle keypress events, needed for in-place editing."""
1473 self.on_key_down(event.keyval, event.string, event)
1474
1478
1549
1551 self.on_map()
1552
1554 self.on_unmap()
1555
1557 self.mousex = event.x / self.scale
1558 self.mousey = event.y / self.scale
1559 self.on_mouse_move(event)
1560
1562 """called when window has been realized"""
1563 if self.window.window:
1564 self.window.window.set_back_pixmap(None, False) # needed?
1565
1566 self.on_realize()
1567
1569 if event.direction == gtk.gdk.SCROLL_UP:
1570 self.on_scroll_up()
1571 elif event.direction == gtk.gdk.SCROLL_DOWN:
1572 self.on_scroll_down()
1573 return False
1574
1575
1576
1577 # TEST!!!
1579 """A simple base-class for creating owner-drawn gtk-widgets"""
1580
1581 __widget=None
1582
1583 mouse_inside = False
1584 width = 32
1585 height = 32
1586
1588 # call superclass
1589 super(ShapedWidget, self).__init__()
1590 # create/setup widget
1591 #self.__widget = gtk.Widget()
1592 self.set_app_paintable(True)
1593 self.set_size_request(width, height)
1594 # connect handlers
1595 self.set_events(gtk.gdk.ALL_EVENTS_MASK)
1596 self.connect("expose-event", self.expose_event)
1597 self.connect("button-press-event", self.button_press)
1598 self.connect("button-release-event", self.button_release)
1599 self.connect("enter-notify-event", self.enter_notify)
1600 self.connect("leave-notify-event", self.leave_notify)
1601
1602 # EXPERIMENTAL: TODO: cache bitmap until size changes
1604 """update widget's shape (only call this when shape has changed)"""
1605 data = ""
1606 for i in xrange(self.width*self.height):
1607 data += "0"
1608 bitmap = gtk.gdk.bitmap_create_from_data(None,
1609 data, self.width, self.height)
1610 ctx = bitmap.cairo_create()
1611 ctx.set_source_rgba(1, 1, 1, 0)
1612 ctx.set_operator (cairo.OPERATOR_SOURCE)
1613 ctx.paint()
1614 self.draw_shape(ctx)
1615 self.input_shape_combine_mask(bitmap, 0, 0)
1616 print "Updating shape."
1617
1622
1627
1631 #print "mouse enter"
1632
1636 #print "mouse leave"
1637
1640
1642 self.draw(ctx)
1643
1645 ctx = widget.window.cairo_create()
1646 # set a clip region for the expose event
1647 ctx.rectangle(event.area.x, event.area.y,
1648 event.area.width, event.area.height)
1649 ctx.clip()
1650 # clear context
1651 ctx.set_source_rgba(1, 1, 1, 0)
1652 ctx.set_operator (cairo.OPERATOR_SOURCE)
1653 ctx.paint()
1654 # call drawing method
1655 self.draw(ctx)
1656 # and delete context
1657 del ctx
1658 return False
1659
1661 """A window that displays a text and serves as Tooltip (very basic yet)."""
1662
1663 # internals
1664 __timeout = None
1665
1666 # attribs
1667 text = ''
1668 font_name = 'FreeSans 9'
1669 width = 100
1670 height = 20
1671 x = 0
1672 y = 0
1673
1675 object.__init__(self)
1676 # init
1677 self.__dict__['width'] = width
1678 self.__dict__['height'] = height
1679 self.window = gtk.Window()
1680 self.window.set_app_paintable(True)
1681 self.window.set_size_request(width, height)
1682 self.window.set_decorated(False)
1683 self.window.set_accept_focus(False)
1684 self.window.set_skip_pager_hint(True)
1685 self.window.set_skip_taskbar_hint(True)
1686 self.window.set_keep_above(True)
1687 self.screen_changed(self.window)
1688 self.window.connect("expose_event", self.expose)
1689 self.window.connect("screen-changed", self.screen_changed)
1690 #self.window.show()
1691 self.p_context = self.window.get_pango_context()
1692 self.p_layout = pango.Layout(self.p_context)
1693 self.p_layout.set_font_description(\
1694 pango.FontDescription(self.font_name))
1695 #self.p_layout.set_width(-1)
1696 self.p_layout.set_width(width * pango.SCALE - 6)
1697
1699 self.__dict__[name] = value
1700 if name in ('width', 'height', 'text'):
1701 if name== 'width':
1702 self.p_layout.set_width(width)
1703 elif name == 'text':
1704 self.p_layout.set_markup(value)
1705 ink_rect, logical_rect = self.p_layout.get_pixel_extents()
1706 self.height = min(max(logical_rect[3], 16), 400) + 6
1707 self.window.set_size_request(self.width, self.height)
1708 self.window.queue_draw()
1709 elif name == 'x':
1710 self.window.move(int(value), int(self.y))
1711 elif name == 'y':
1712 self.window.move(int(self.x), int(value))
1713
1715 """Show the Tooltip window."""
1716 self.cancel_show()
1717 self.window.show()
1718 self.window.set_keep_above(True)
1719
1721 """Show the Tooltip window after a given delay."""
1722 self.cancel_show()
1723 self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
1724
1729
1731 """Cancel showing of the Tooltip."""
1732 if self.__timeout:
1733 gobject.source_remove(self.__timeout)
1734 self.p_context = None
1735 self.p_layout = None
1736
1738 self.show()
1739
1741 if screen == None:
1742 screen = window.get_screen()
1743 map = screen.get_rgba_colormap()
1744 if not map:
1745 map = screen.get_rgb_colormap()
1746 window.set_colormap(map)
1747
1749 ctx = self.window.window.cairo_create()
1750 ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ?
1751 # set a clip region for the expose event
1752 ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
1753 ctx.clip()
1754 # clear context
1755 ctx.set_source_rgba(1, 1, 1, 0)
1756 ctx.set_operator (cairo.OPERATOR_SOURCE)
1757 ctx.paint()
1758 # draw rectangle
1759 ctx.set_source_rgba(1, 1, 0.5, 1)
1760 ctx.rectangle(0, 0, self.width, self.height)
1761 ctx.fill()
1762 # draw text
1763 ctx.save()
1764 ctx.translate(3, 3)
1765 ctx.set_source_rgba(0, 0, 0, 1)
1766 ctx.show_layout(self.p_layout)
1767 ctx.fill()
1768 ctx.restore()
1769 ctx.rectangle(0, 0, self.width, self.height)
1770 ctx.set_source_rgba(0, 0, 0, 0.7)
1771 ctx.stroke()
1772
1774 """A window that displays a text and serves as Notification (very basic yet)."""
1775
1776 # internals
1777 __timeout = None
1778
1779 # attribs
1780 text = ''
1781 font_name = 'FreeSans 9'
1782 width = 200
1783 height = 100
1784 x = 0
1785 y = 0
1786 gradient = cairo.LinearGradient(0, 100,0, 0)
1787
1789 object.__init__(self)
1790 # init
1791 self.window = gtk.Window()
1792 self.window.set_app_paintable(True)
1793 self.window.set_size_request(self.width, self.height)
1794 self.window.set_decorated(False)
1795 self.window.set_accept_focus(False)
1796 self.window.set_skip_pager_hint(True)
1797 self.window.set_skip_taskbar_hint(True)
1798 self.window.set_keep_above(True)
1799 self.screen_changed(self.window)
1800 self.window.connect("expose_event", self.expose)
1801 self.window.connect("screen-changed", self.screen_changed)
1802 #self.window.show()
1803 self.p_context = self.window.get_pango_context()
1804 self.p_layout = pango.Layout(self.p_context)
1805 self.p_layout.set_font_description(\
1806 pango.FontDescription(self.font_name))
1807 #self.p_layout.set_width(-1)
1808 self.p_layout.set_width(self.width * pango.SCALE - 6)
1809
1811 self.__dict__[name] = value
1812 if name in ('text'):
1813 if name == 'text':
1814 self.p_layout.set_markup(value)
1815 ink_rect, logical_rect = self.p_layout.get_pixel_extents()
1816 self.window.queue_draw()
1817
1819 """Show the Notify window."""
1820 self.window.move(gtk.gdk.screen_width() - self.width, gtk.gdk.screen_height() - self.height)
1821 self.cancel_show()
1822 self.window.show()
1823 self.window.set_keep_above(True)
1824
1826 """Show the Notify window after a given delay."""
1827 self.cancel_show()
1828 self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
1829
1834
1836 """Cancel showing of the Notify."""
1837 if self.__timeout:
1838 gobject.source_remove(self.__timeout)
1839 self.p_context = None
1840 self.p_layout = None
1841
1843 self.show()
1844
1846 if screen == None:
1847 screen = window.get_screen()
1848 map = screen.get_rgba_colormap()
1849 if not map:
1850 map = screen.get_rgb_colormap()
1851 window.set_colormap(map)
1852
1854 ctx = self.window.window.cairo_create()
1855 ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ?
1856 # set a clip region for the expose event
1857 ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
1858 ctx.clip()
1859 # clear context
1860 ctx.set_source_rgba(1, 1, 1, 0)
1861 ctx.set_operator (cairo.OPERATOR_SOURCE)
1862 ctx.paint()
1863 # draw rectangle
1864 self.gradient.add_color_stop_rgba(1,0.3, 0.3, 0.3, 0.9)
1865 self.gradient.add_color_stop_rgba(0.3, 0, 0, 0, 0.9)
1866 ctx.set_source(self.gradient)
1867 ctx.rectangle(0, 0, self.width, self.height)
1868 ctx.fill()
1869 # draw text
1870 ctx.save()
1871 ctx.translate(3, 3)
1872 ctx.set_source_rgba(1, 1, 1, 1)
1873 ctx.show_layout(self.p_layout)
1874 ctx.fill()
1875 ctx.restore()
1876 ctx.rectangle(0, 0, self.width, self.height)
1877 ctx.set_source_rgba(0, 0, 0, 0.7)
1878 ctx.stroke()
1879
1880 # TEST (as the name implies)
1881 """class TestWidget(ShapedWidget):
1882
1883 def __init__(self, width, height):
1884 #ShapedWidget.__init__(self, width, height)
1885 super(TestWidget, self).__init__(width, height)
1886
1887 def draw(self, ctx):
1888 if self.mouse_inside:
1889 ctx.set_source_rgba(1, 0, 0, 0.8)
1890 else:
1891 ctx.set_source_rgba(1, 1, 0, 0.8)
1892 ctx.rectangle(0, 0, 32, 32)
1893 ctx.fill()
1894 """
1895
1896
1897 # ------------------------------------------------------------------------------
1898 # MODULE-FUNCTIONS
1899 # ------------------------------------------------------------------------------
1900
1901 # the new recommended way of launching a screenlet from the "outside"
1903 """Launch a screenlet, either through its service or by launching a new
1904 process of the given screenlet. Name has to be the name of the Screenlet's
1905 class without trailing 'Screenlet'.
1906 NOTE: we could only launch the file here"""
1907 # check for service
1908 if services.service_is_running(name):
1909 # add screenlet through service, if running
1910 srvc = services.get_service_by_name(name)
1911 if srvc:
1912 try:
1913 srvc.add('') # empty string for auto-creating ID
1914 return True
1915 except Exception, ex:
1916 print "Error while adding instance by service: %s" % ex
1917 # service not running or error? launch screenlet's file
1918 path = utils.find_first_screenlet_path(name)
1919 if path:
1920 # get full path of screenlet's file
1921 slfile = path + '/' + name + 'Screenlet.py'
1922 # launch screenlet as separate process
1923 print "Launching Screenlet from: %s" % slfile
1924 if debug:
1925 print "Logging output goes to: $HOME/.config/Screenlets/%sScreenlet.log" % name
1926 out = '$HOME/.config/Screenlets/%sScreenlet.log' % name
1927 else:
1928 out = '/dev/null'
1929 os.system('python -u %s > %s &' % (slfile, out))
1930 return True
1931 else:
1932 print "Screenlet '%s' could not be launched." % name
1933 return False
1934
1936 """Show a message for the given Screenlet (may contain Pango-Markup).
1937 If screenlet is None, this function can be used by other objects as well."""
1938 if screenlet == None:
1939 md = gtk.MessageDialog(None, type=gtk.MESSAGE_INFO,
1940 buttons=gtk.BUTTONS_OK)
1941 md.set_title(title)
1942 else:
1943 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_INFO,
1944 buttons=gtk.BUTTONS_OK)
1945 md.set_title(screenlet.__name__)
1946 md.set_markup(message)
1947 md.run()
1948 md.destroy()
1949
1951 """Show a question for the given Screenlet (may contain Pango-Markup)."""
1952 if screenlet == None:
1953 md = gtk.MessageDialog(None, type=gtk.MESSAGE_QUESTION,
1954 buttons=gtk.BUTTONS_YES_NO)
1955 md.set_title(title)
1956 else:
1957 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_QUESTION,
1958 buttons=gtk.BUTTONS_YES_NO)
1959 md.set_title(screenlet.__name__)
1960 md.set_markup(message)
1961 response = md.run()
1962 md.destroy()
1963 if response == gtk.RESPONSE_YES:
1964 return True
1965 return False
1966
1968 """Show an error for the given Screenlet (may contain Pango-Markup)."""
1969 if screenlet == None:
1970 md = gtk.MessageDialog(None, type=gtk.MESSAGE_ERROR,
1971 buttons=gtk.BUTTONS_OK)
1972 md.set_title(title)
1973 else:
1974 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_ERROR,
1975 buttons=gtk.BUTTONS_OK)
1976 md.set_title(screenlet.__name__)
1977 md.set_markup(message)
1978 md.run()
1979 md.destroy()
1980
1982 """Raise a fatal error to stdout and stderr and exit with an errorcode."""
1983 import sys
1984 msg = 'FATAL ERROR: %s\n' % message
1985 sys.stdout.write(msg)
1986 sys.stderr.write(msg)
1987 sys.exit(1)
1988
1989 # LEGACY support: functions that are not used any longer (raise fatal error)
1990
1992 fatal_error("This screenlet seems to be written for an older version of the framework. Please download a newer version of the %s." % name)
1993
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0beta1 on Mon Apr 21 12:37:41 2008 | http://epydoc.sourceforge.net |