1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
43 from options import *
44 import services
45 import utils
46
47
48 import XmlMenu
49
50
51
52
53
54
55
56
57 APP_NAME = "Screenlets"
58
59
60 VERSION = "0.0.13"
61
62
63 COPYRIGHT = "(c) RYX (Rico Pfaus) <ryx@ryxperience.com>\nWhise (Helder Fraga) <helder.fraga@hotmail.com>"
64
65
66 AUTHORS = ["RYX (Rico Pfaus) <ryx@ryxperience.com>", "Whise (Helder Fraga)<helder.fraga@hotmail.com>","Sorcerer (Hendrik Kaju)"]
67
68
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
72 WEBSITE = 'http://www.screenlets.org'
73
74
75 THIRD_PARTY_DOWNLOAD = "http://screenlets.org/index.php/Category:UserScreenlets"
76
77
78 INSTALL_PREFIX = '/usr'
79
80
81 PATH = INSTALL_PREFIX + '/share/screenlets'
82
83
84
85
86
87 SCREENLETS_PATH = [os.environ['HOME'] + '/.screenlets', PATH]
88
89
90 gettext.textdomain('screenlets')
91 gettext.bindtextdomain('screenlets', '/usr/share/locale')
92
94 return gettext.gettext(s)
95
96
97
98
99
100
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
129 __name__ = ''
130 __author__ = ''
131 __version__ = ''
132 __info__ = ''
133
134
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
145
155
157 if name in ("width", "height"):
158 if self.loaded and len(self)>0:
159 size=self[0].get_dimension_data()
160 if name=="width":
161 return size[0]
162 else:
163 return size[1]
164 else:
165 return object.__getattr__(self, name)
166
194
195 - def check_entry (self, filename):
196 """Checks if a file with filename is loaded in this theme."""
197 try:
198 if self[filename]:
199 return True
200 except:
201
202 return False
203
204 - def get_text_width(self, ctx, text, font):
205 """Returns the pixel width of a given text"""
206 ctx.save()
207 ctx.move_to(0,0)
208 p_layout = ctx.create_layout()
209 p_fdesc = pango.FontDescription(font)
210 p_layout.set_font_description(p_fdesc)
211 p_layout.set_text(text)
212 extents, lextents = p_layout.get_pixel_extents()
213 ctx.restore()
214 return extents[2]
215
216 - def get_text_extents(self, ctx, text, font):
217 """Returns the pixel extents of a given text"""
218 ctx.save()
219 ctx.move_to(0,0)
220 p_layout = ctx.create_layout()
221 p_fdesc = pango.FontDescription(font)
222 p_layout.set_font_description(p_fdesc)
223 p_layout.set_text(text)
224 extents, lextents = p_layout.get_pixel_extents()
225 ctx.restore()
226 return extents
227
228 - def draw_text(self, ctx, text, x, y, font, size, width, allignment,ellipsize = pango.ELLIPSIZE_NONE):
229 """Draws text"""
230 ctx.save()
231 ctx.translate(x, y)
232 if self.p_layout == None :
233
234 self.p_layout = ctx.create_layout()
235 else:
236
237 ctx.update_layout(self.p_layout)
238 self.p_fdesc = pango.FontDescription()
239 self.p_fdesc.set_family_static(font)
240 self.p_fdesc.set_size(size * pango.SCALE)
241 self.p_layout.set_font_description(self.p_fdesc)
242 self.p_layout.set_width(width * pango.SCALE)
243 self.p_layout.set_alignment(allignment)
244 self.p_layout.set_ellipsize(ellipsize)
245 self.p_layout.set_markup(text)
246 ctx.show_layout(self.p_layout)
247 ctx.restore()
248
249
251 """Draws a circule"""
252 ctx.save()
253 ctx.translate(x, y)
254 ctx.arc(width/2,height/2,min(height,width)/2,0,2*math.pi)
255 if fill:ctx.fill()
256 else: ctx.stroke()
257 ctx.restore()
258
259 - def draw_line(self,ctx,start_x,start_y,end_x,end_y,line_width = 1,close=False,preserve=False):
260 """Draws a line"""
261 ctx.save()
262 ctx.move_to(start_x, start_y)
263 ctx.set_line_width(line_width)
264 ctx.rel_line_to(end_x, end_y)
265 if close : ctx.close_path()
266 if preserve: ctx.stroke_preserve()
267 else: ctx.stroke()
268 ctx.restore()
269
271 """Draws a rectangle"""
272 ctx.save()
273 ctx.translate(x, y)
274 ctx.rectangle (0,0,width,height)
275 if fill:ctx.fill()
276 else: ctx.stroke()
277 ctx.restore()
278
280 """Draws a rounded rectangle"""
281 ctx.save()
282 ctx.translate(x, y)
283 padding=0
284 rounded=rounded_angle
285 w = width
286 h = height
287
288
289 ctx.move_to(0+padding+rounded, 0+padding)
290
291
292 ctx.line_to(w-padding-rounded, 0+padding)
293 ctx.arc(w-padding-rounded, 0+padding+rounded, rounded, math.pi/2, 0)
294
295
296 ctx.line_to(w-padding, h-padding-rounded)
297 ctx.arc(w-padding-rounded, h-padding-rounded, rounded, 0, math.pi/2)
298
299
300 ctx.line_to(0+padding+rounded, h-padding)
301 ctx.arc(0+padding+rounded, h-padding-rounded, rounded, math.pi+math.pi/2, math.pi)
302
303
304 ctx.line_to(0+padding, 0+padding+rounded)
305 ctx.arc(0+padding+rounded, 0+padding+rounded, rounded, math.pi/2, 0)
306
307
308 if fill:ctx.fill()
309 else: ctx.stroke()
310 ctx.restore()
311
313 """Gets a picture width and height"""
314
315 pixbuf = gtk.gdk.pixbuf_new_from_file(pix)
316 iw = pixbuf.get_width()
317 ih = pixbuf.get_height()
318 puxbuf = None
319 return iw,ih
320
322 """Draws a picture from specified path"""
323
324 ctx.save()
325 ctx.translate(x, y)
326 pixbuf = gtk.gdk.pixbuf_new_from_file(pix)
327 format = cairo.FORMAT_RGB24
328 if pixbuf.get_has_alpha():
329 format = cairo.FORMAT_ARGB32
330
331 iw = pixbuf.get_width()
332 ih = pixbuf.get_height()
333 image = cairo.ImageSurface(format, iw, ih)
334 image = ctx.set_source_pixbuf(pixbuf, 0, 0)
335
336 ctx.paint()
337 puxbuf = None
338 image = None
339 ctx.restore()
340
341
342
344 """Draws a picture from specified path with a certain width and height"""
345
346 ctx.save()
347 ctx.translate(x, y)
348 pixbuf = gtk.gdk.pixbuf_new_from_file(pix).scale_simple(w,h,gtk.gdk.INTERP_HYPER)
349 format = cairo.FORMAT_RGB24
350 if pixbuf.get_has_alpha():
351 format = cairo.FORMAT_ARGB32
352
353 iw = pixbuf.get_width()
354 ih = pixbuf.get_height()
355 image = cairo.ImageSurface(format, iw, ih)
356
357 matrix = cairo.Matrix(xx=iw/w, yy=ih/h)
358 image = ctx.set_source_pixbuf(pixbuf, 0, 0)
359 if image != None :image.set_matrix(matrix)
360 ctx.paint()
361 puxbuf = None
362 image = None
363 ctx.restore()
364
371
377
386
392
394 """Check if this theme contains overrides for options."""
395 return len(self.option_overrides) > 0
396
398 """Load a config-file from this theme's dir and save vars in list."""
399 ini = utils.IniReader()
400 if ini.load(filename):
401 if ini.has_section('Theme'):
402 self.__name__ = ini.get_option('name', section='Theme')
403 self.__author__ = ini.get_option('author', section='Theme')
404 self.__version__ = ini.get_option('version', section='Theme')
405 self.__info__ = ini.get_option('info', section='Theme')
406 if ini.has_section('Options'):
407 opts = ini.list_options(section='Options')
408 if opts:
409 for o in opts:
410 self.option_overrides[o[0]] = o[1]
411 print _("theme.conf loaded: ")
412 print _("Name: ") + str(self.__name__)
413 print _("Author: ") +str(self.__author__)
414 print _("Version: ") +str(self.__version__)
415 print _("Info: ") +str(self.__info__)
416 else:
417 print _("Failed to load theme.conf")
418
419
421 """Load an SVG-file into this theme and reference it as ref_name."""
422 if self.has_key(filename):
423 del self[filename]
424 self[filename] = rsvg.Handle(self.path + "/" + filename)
425 self.svgs[filename[:-4]] = self[filename]
426 if self[filename] != None:
427
428 size=self[filename].get_dimension_data()
429 if size:
430 self.width = size[0]
431 self.height = size[1]
432 return True
433 else:
434 return False
435
437 """Load a PNG-file into this theme and reference it as ref_name."""
438 if self.has_key(filename):
439 del self[filename]
440 self[filename] = cairo.ImageSurface.create_from_png(self.path +
441 "/" + filename)
442 self.pngs[filename[:-4]] = self[filename]
443 if self[filename] != None:
444 return True
445 else:
446 return False
447
449 """Load all files in the theme's path. Currently only loads SVGs and
450 PNGs."""
451
452
453
454 dirlst = glob.glob(self.path + '/*')
455 if len(dirlst)==0:
456 return False
457 plen = len(self.path) + 1
458 for file in dirlst:
459 fname = file[plen:]
460 if fname.endswith('.svg'):
461
462 if self.load_svg(fname) == False:
463 return False
464 elif fname.endswith('.png'):
465
466 if self.load_png(fname) == False:
467 return False
468 elif fname == "theme.conf":
469 print _("theme.conf found! Loading option-overrides.")
470
471 if self.load_conf(file) == False:
472 return False
473 return True
474
476 """Re-Load all files in the theme's path."""
477 self.free()
478 self.__load_all()
479
480
482 """Deletes the Theme's contents and frees all rsvg-handles.
483 TODO: freeing rsvg-handles does NOT work for some reason"""
484 self.option_overrides.clear()
485 for filename in self:
486
487 del filename
488 self.clear()
489
490
491
492
493 - def render (self, ctx, name):
494 """Render an image from within this theme to the given context. This
495 function can EITHER use png OR svg images, so it is possible to
496 create themes using both image-formats when a Screenlet uses this
497 function for drawing its images. The image name has to be defined
498 without the extension and the function will automatically select
499 the available one (SVG is prefered over PNG)."""
500 """if self.has_key(name + '.svg'):
501 self[name + '.svg'].render_cairo(ctx)
502 else:
503 ctx.set_source_surface(self[name + '.png'], 0, 0)
504 ctx.paint()"""
505 try:
506
507 self.svgs[name].render_cairo(ctx)
508 except:
509
510 ctx.set_source_surface(self.pngs[name], 0, 0)
511 ctx.paint()
512
513
514
515
516
517
518 -class Screenlet (gobject.GObject, EditableOptions):
519 """A Screenlet is a (i.e. contains a) shaped gtk-window that is
520 fully invisible by default. Subclasses of Screenlet can render
521 their owner-drawn graphics on fully transparent background."""
522
523
524 __name__ = _('No name set for this Screenlet')
525 __version__ = '0.0'
526 __author__ = _('No author defined for this Screenlet')
527 __desc__ = _('No info set for this Screenlet')
528 __requires__ = []
529
530
531
532
533
534 id = ''
535 window = None
536 theme = None
537 uses_theme = True
538 draw_buttons = True
539 show_buttons = True
540 menu = None
541 is_dragged = False
542 quit_on_close = True
543 saving_enabled = True
544 dragging_over = False
545 disable_updates = False
546 p_context = None
547 p_layout = None
548
549
550 x = 0
551 y = 0
552 mousex = 0
553 mousey = 0
554 width = 100
555 height = 100
556 scale = 1.0
557 opacity = 1.0
558 theme_name = ""
559 is_sticky = False
560 is_widget = False
561 keep_above = True
562 keep_below = False
563 skip_pager = True
564 first_run = False
565 skip_taskbar = True
566 lock_position = False
567 allow_option_override = True
568 ask_on_option_override = True
569 has_started = False
570 has_focus = False
571
572 __lastx = 0
573 __lasty = 0
574
575
576
577 __mi_keep_above = None
578 __mi_keep_below = None
579 __mi_widget = None
580 __mi_sticky = None
581 __mi_lock = None
582
583 __gsignals__ = dict(screenlet_removed=(gobject.SIGNAL_RUN_FIRST,
584 gobject.TYPE_NONE, (gobject.TYPE_OBJECT,)))
585
586 - def __init__ (self, id='', width=100, height=100, parent_window=None,
587 show_window=True, is_widget=False, is_sticky=False,
588 uses_theme=True, draw_buttons=True,path=os.getcwd(), drag_drop=False, session=None,
589 enable_saving=True, service_class=services.ScreenletService,
590 uses_pango=False):
591 """Constructor - should only be subclassed"""
592
593 super(Screenlet, self).__init__()
594 EditableOptions.__init__(self)
595
596 self.id = id
597 self.session = session
598 self.service = None
599
600 if self.id and service_class:
601 self.register_service(service_class)
602
603 self.service.instance_added(self.id)
604 self.width = width
605 self.height = height
606 self.is_dragged = False
607 self.__path__ = path
608 self.saving_enabled = enable_saving
609
610 self.__dict__['theme_name'] = ""
611 self.__dict__['is_widget'] = is_widget
612 self.__dict__['is_sticky'] = is_sticky
613 self.__dict__['x'] = 0
614 self.__dict__['y'] = 0
615
616
617
618
619 self.__shape_bitmap = None
620 self.__shape_bitmap_width = 0
621 self.__shape_bitmap_height = 0
622
623 self.add_options_group('Screenlet',
624 _('The basic settings for this Screenlet-instance.'))
625
626
627
628 if draw_buttons: self.draw_buttons = True
629 else: self.draw_buttons = False
630 if uses_theme:
631 self.uses_theme = True
632 self.add_option(StringOption('Screenlet', 'theme_name',
633 'default', '', '', hidden=True))
634
635 self.add_option(IntOption('Screenlet', 'x',
636 0, _('X-Position'), _('The X-position of this Screenlet .os.path.exists(d)..'),
637 min=0, max=gtk.gdk.screen_width()))
638 self.add_option(IntOption('Screenlet', 'y',
639 0, _('Y-Position'), _('The Y-position of this Screenlet ...'),
640 min=0, max=gtk.gdk.screen_height()))
641 self.add_option(IntOption('Screenlet', 'width',
642 width, _('Width'), _('The width of this Screenlet ...'),
643 min=16, max=1000, hidden=True))
644 self.add_option(IntOption('Screenlet', 'height',
645 height, _('Height'), _('The height of this Screenlet ...'),
646 min=16, max=1000, hidden=True))
647 self.add_option(FloatOption('Screenlet', 'scale',
648 self.scale, _('Scale'), _('The scale-factor of this Screenlet ...'),
649 min=0.1, max=10.0, digits=2, increment=0.1))
650 self.add_option(FloatOption('Screenlet', 'opacity',
651 self.opacity, _('Opacity'), _('The opacity of the Screenlet window ...'),
652 min=0.1, max=1.0, digits=2, increment=0.1))
653 self.add_option(BoolOption('Screenlet', 'is_sticky',
654 is_sticky, _('Stick to Desktop'),
655 _('Show this Screenlet on all workspaces ...')))
656 self.add_option(BoolOption('Screenlet', 'is_widget',
657 is_widget, _('Treat as Widget'),
658 _('Treat this Screenlet as a "Widget" ...')))
659 self.add_option(BoolOption('Screenlet', 'lock_position',
660 self.lock_position, _('Lock position'),
661 _('Stop the screenlet from being moved...')))
662 self.add_option(BoolOption('Screenlet', 'keep_above',
663 self.keep_above, _('Keep above'),
664 _('Keep this Screenlet above other windows ...')))
665 self.add_option(BoolOption('Screenlet', 'keep_below',
666 self.keep_below, _('Keep below'),
667 _('Keep this Screenlet below other windows ...')))
668 self.add_option(BoolOption('Screenlet', 'draw_buttons',
669 self.draw_buttons, _('Draw button controls'),
670 _('Draw buttons in top right corner')))
671 self.add_option(BoolOption('Screenlet', 'skip_pager',
672 self.skip_pager, _('Skip Pager'),
673 _('Set this Screenlet to show/hide in pagers ...')))
674 self.add_option(BoolOption('Screenlet', 'skip_taskbar',
675 self.skip_pager, _('Skip Taskbar'),
676 _('Set this Screenlet to show/hide in taskbars ...')))
677 if uses_theme:
678 self.add_option(BoolOption('Screenlet', 'allow_option_override',
679 self.allow_option_override, _('Allow overriding Options'),
680 _('Allow themes to override options in this screenlet ...')))
681 self.add_option(BoolOption('Screenlet', 'ask_on_option_override',
682 self.ask_on_option_override, _('Ask on Override'),
683 _('Show a confirmation-dialog when a theme wants to override ')+\
684 _('the current options of this Screenlet ...')))
685
686 self.disable_option('width')
687 self.disable_option('height')
688
689 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
690 if parent_window:
691 self.window.set_parent_window(parent_window)
692 self.window.set_transient_for(parent_window)
693 self.window.set_destroy_with_parent(True)
694 self.window.resize(width, height)
695 self.window.set_decorated(False)
696 self.window.set_app_paintable(True)
697
698 if uses_pango:
699 self.p_context = self.window.get_pango_context()
700 if self.p_context:
701 self.p_layout = pango.Layout(self.p_context)
702 self.p_layout.set_font_description(\
703 pango.FontDescription("Sans 12"))
704
705 self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_TOOLBAR)
706 self.window.set_keep_above(True)
707 self.window.set_skip_taskbar_hint(True)
708 self.window.set_skip_pager_hint(True)
709 if is_sticky:
710 self.window.stick()
711 self.alpha_screen_changed(self.window)
712 self.update_shape()
713
714 self.window.set_events(gtk.gdk.ALL_EVENTS_MASK)
715 self.window.connect("composited-changed", self.composite_changed)
716 self.window.connect("delete_event", self.delete_event)
717 self.window.connect("destroy", self.destroy)
718 self.window.connect("expose_event", self.expose)
719 self.window.connect("button-press-event", self.button_press)
720 self.window.connect("button-release-event", self.button_release)
721 self.window.connect("configure-event", self.configure_event)
722 self.window.connect("screen-changed", self.alpha_screen_changed)
723 self.window.connect("realize", self.realize_event)
724 self.window.connect("enter-notify-event", self.enter_notify_event)
725 self.window.connect("leave-notify-event", self.leave_notify_event)
726 self.window.connect("focus-in-event", self.focus_in_event)
727 self.window.connect("focus-out-event", self.focus_out_event)
728 self.window.connect("scroll-event", self.scroll_event)
729 self.window.connect("motion-notify-event",self.motion_notify_event)
730
731 self.window.connect("key-press-event", self.key_press)
732
733 if drag_drop:
734 self.window.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
735 gtk.DEST_DEFAULT_DROP,
736 [("text/plain", 0, 0),
737 ("image", 0, 1),
738 ("text/uri-list", 0, 2)],
739 gtk.gdk.ACTION_COPY)
740 self.window.connect("drag_data_received", self.drag_data_received)
741 self.window.connect("drag-begin", self.drag_begin)
742 self.window.connect("drag-end", self.drag_end)
743 self.window.connect("drag-motion", self.drag_motion)
744 self.window.connect("drag-leave", self.drag_leave)
745
746 self.menu = gtk.Menu()
747
748
749
750 if show_window:
751 self.window.show()
752 print os.environ['HOME'] + '/.config/Screenlets/' + self.__name__[:-9] + '/default/'+ self.id
753 if not os.path.exists(os.environ['HOME'] + '/.config/Screenlets/' + self.__name__[:-9] + '/default/'+ self.id + '.ini'):
754 self.first_run = True
755 self.window.hide()
756
758
759 self.on_before_set_atribute(name, value)
760 gobject.GObject.__setattr__(self, name, value)
761
762 if name=="x" or name=="y":
763 if self.has_started:
764 self.window.move(self.x, self.y)
765 elif name == 'opacity':
766 self.window.set_opacity(value)
767 elif name == 'scale':
768 self.window.resize(int(self.width * self.scale),
769 int(self.height * self.scale))
770
771 self.on_scale()
772 self.redraw_canvas()
773 self.update_shape()
774
775
776 elif name == "theme_name":
777
778 print _("LOAD NEW THEME: ") + value
779 print _("FOUND: ") + str(self.find_theme(value))
780
781
782 path = self.find_theme(value)
783 if path:
784 self.load_theme(path)
785
786 self.redraw_canvas()
787 self.update_shape()
788 elif name in ("width", "height"):
789
790 if self.window:
791 self.window.resize(int(self.width*self.scale), int(self.height*self.scale))
792
793 self.update_shape()
794 elif name == "is_widget":
795 if self.has_started:
796 self.set_is_widget(value)
797 elif name == "is_sticky":
798 if value == True:
799 self.window.stick()
800 else:
801 self.window.unstick()
802
803
804 elif name == "keep_above":
805 if self.has_started == True:
806 self.window.set_keep_above(bool(value))
807
808 elif name == "keep_below":
809 if self.has_started == True:
810 self.window.set_keep_below(bool(value))
811
812 elif name == "skip_pager":
813 if self.window.window:
814 self.window.window.set_skip_pager_hint(bool(value))
815 elif name == "skip_taskbar":
816 if self.window.window:
817 self.window.window.set_skip_taskbar_hint(bool(value))
818
819
820 if self.saving_enabled:
821 o = self.get_option_by_name(name)
822 if o != None:
823 self.session.backend.save_option(self.id, o.name,
824 o.on_export(value))
825 self.on_after_set_atribute(name, value)
826
827
828
829
830
831
832
833
835 """Appends the default menu-items to self.menu. You can add on OR'ed
836 flag with DefaultMenuItems you want to add."""
837 if not self.has_started: print 'WARNING - add_default_menuitems and add_menuitems should be set in on_init ,menu values will be displayed incorrectly'
838
839 if len(self.menu.get_children()) > 0:
840 self.add_menuitem("", "-")
841
842
843
844
845 menu = self.menu
846
847 if flags & DefaultMenuItem.XML:
848
849 xfile = self.get_screenlet_dir() + "/menu.xml"
850 xmlmenu = XmlMenu.create_menu_from_file(xfile,
851 self.menuitem_callback)
852 if xmlmenu:
853 self.menu = xmlmenu
854 pass
855
856 if flags & DefaultMenuItem.SIZE:
857 size_item = gtk.MenuItem(_("Size"))
858 size_item.show()
859 size_menu = gtk.Menu()
860 menu.append(size_item)
861 size_item.set_submenu(size_menu)
862
863 for i in (0.2,0.3,0.4, 0.5,0.6, 0.7,0.8,0.9, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0, 7.5, 10):
864 s = str(int(i * 100))
865 item = gtk.MenuItem(s + " %")
866 item.connect("activate", self.menuitem_callback,
867 "scale:"+str(i))
868 item.show()
869 size_menu.append(item)
870
871 if flags & DefaultMenuItem.THEMES:
872 themes_item = gtk.MenuItem(_("Theme"))
873 themes_item.show()
874 themes_menu = gtk.Menu()
875 menu.append(themes_item)
876 themes_item.set_submenu(themes_menu)
877
878 lst = self.get_available_themes()
879 for tname in lst:
880 item = gtk.MenuItem(tname)
881 item.connect("activate", self.menuitem_callback,
882 "theme:"+tname)
883 item.show()
884 themes_menu.append(item)
885
886 if flags & DefaultMenuItem.WINDOW_MENU:
887 winmenu_item = gtk.MenuItem(_("Window"))
888 winmenu_item.show()
889 winmenu_menu = gtk.Menu()
890 menu.append(winmenu_item)
891 winmenu_item.set_submenu(winmenu_menu)
892
893 self.__mi_lock = item = gtk.CheckMenuItem(_("Lock"))
894 item.set_active(self.lock_position)
895 item.connect("activate", self.menuitem_callback,
896 "option:lock")
897 item.show()
898 winmenu_menu.append(item)
899
900 self.__mi_sticky = item = gtk.CheckMenuItem(_("Sticky"))
901 item.set_active(self.is_sticky)
902 item.connect("activate", self.menuitem_callback,
903 "option:sticky")
904 item.show()
905 winmenu_menu.append(item)
906
907 self.__mi_widget = item = gtk.CheckMenuItem(_("Widget"))
908 item.set_active(self.is_widget)
909 item.connect("activate", self.menuitem_callback,
910 "option:widget")
911 item.show()
912 winmenu_menu.append(item)
913
914 self.__mi_keep_above = item = gtk.CheckMenuItem(_("Keep above"))
915 item.set_active(self.keep_above)
916 item.connect("activate", self.menuitem_callback,
917 "option:keep_above")
918 item.show()
919 winmenu_menu.append(item)
920
921 self.__mi_keep_below = item = gtk.CheckMenuItem(_("Keep below"))
922 item.set_active(self.keep_below)
923 item.connect("activate", self.menuitem_callback,
924 "option:keep_below")
925 item.show()
926 winmenu_menu.append(item)
927
928 if flags & DefaultMenuItem.PROPERTIES:
929 self.add_menuitem("", "-")
930 self.add_menuitem("options", _("Properties..."))
931
932 if flags & DefaultMenuItem.INFO:
933 self.add_menuitem("", "-")
934 self.add_menuitem("info", _("Info..."))
935
936 if flags & DefaultMenuItem.DELETE:
937 self.add_menuitem("", "-")
938 self.add_menuitem("delete", _("Delete Screenlet ..."))
939
940 self.add_menuitem("", "-")
941 self.add_menuitem("quit_instance", _("Quit this %s ...") % self.get_short_name())
942
943 self.add_menuitem("", "-")
944 self.add_menuitem("quit", _("Quit all %ss ...") % self.get_short_name())
945
947 """Simple way to add menuitems to the right-click menu."""
948 if not self.has_started: print 'WARNING - add_default_menuitems and add_menuitems should be set in on_init ,menu values will be displayed incorrectly'
949 if callback == None:
950 callback = self.menuitem_callback
951 if label == "-":
952 menu_item = gtk.SeparatorMenuItem()
953 else:
954 menu_item = gtk.MenuItem(label)
955 menu_item.connect("activate", callback, id)
956 self.menu.append(menu_item)
957 menu_item.show()
958 return menu_item
959
982
983 - def clear_cairo_context (self, ctx):
984 """Fills the given cairo.Context with fully transparent white."""
985 ctx.save()
986 ctx.set_source_rgba(1, 1, 1, 0)
987 ctx.set_operator (cairo.OPERATOR_SOURCE)
988 ctx.paint()
989 ctx.restore()
990
992 """Close this Screenlet
993 TODO: send close-notify instead of destroying window?"""
994
995 self.window.unmap()
996 self.window.destroy()
997
998
1000 """Create drag-icon and -mask for drag-operation. Returns a 2-tuple
1001 with the icon and the mask. To supply your own icon you can use the
1002 on_create_drag_icon-handler and return the icon/mask as 2-tuple."""
1003 w = self.width
1004 h = self.height
1005 icon, mask = self.on_create_drag_icon()
1006 if icon == None:
1007
1008 icon = gtk.gdk.Pixmap(self.window.window, w, h)
1009 ctx = icon.cairo_create()
1010 self.clear_cairo_context(ctx)
1011 self.on_draw(ctx)
1012 if mask == None:
1013
1014 mask = gtk.gdk.Pixmap(self.window.window, w, h)
1015 ctx = mask.cairo_create()
1016 self.clear_cairo_context(ctx)
1017 self.on_draw_shape(ctx)
1018 return (icon, mask)
1019
1021 """Enable/Disable realtime-saving of options."""
1022 self.saving_enabled = enabled
1023
1025 """Find the first occurence of a theme and return its global path."""
1026 sn = self.get_short_name()
1027 for p in SCREENLETS_PATH:
1028 fpath = p + '/' + sn + '/themes/' + name
1029 if os.path.isdir(fpath):
1030 return fpath
1031 return None
1032
1034 """Return the short name of this screenlet. This returns the classname
1035 of the screenlet without trailing "Screenlet". Please always use
1036 this function if you want to retrieve the short name of a Screenlet."""
1037 return self.__class__.__name__[:-9]
1038
1040 """@DEPRECATED: Return the name of this screenlet's personal directory."""
1041 p = utils.find_first_screenlet_path(self.get_short_name())
1042 if p:
1043 return p
1044 else:
1045 if self.__path__ != '':
1046 return self.__path__
1047 else:
1048 return os.getcwd()
1049
1051 """@DEPRECATED: Return the name of this screenlet's personal theme-dir.
1052 (Only returns the dir under the screenlet's location"""
1053 return self.get_screenlet_dir() + "/themes/"
1054
1056 """Returns a list with the names of all available themes in this
1057 Screenlet's theme-directory."""
1058 lst = []
1059 for p in SCREENLETS_PATH:
1060 d = p + '/' + self.get_short_name() + '/themes/'
1061 if os.path.isdir(d):
1062
1063 dirlst = glob.glob(d + '*')
1064 dirlst.sort()
1065 tdlen = len(d)
1066 for fname in dirlst:
1067 dname = fname[tdlen:]
1068
1069 lst.append(dname)
1070 return lst
1071
1109
1111 """Hides this Screenlet's underlying gtk.Window"""
1112 self.window.hide()
1113 self.on_hide()
1114
1115
1116
1117
1141
1142
1144 """If the Screenlet runs as stand-alone app, starts gtk.main()"""
1145 gtk.main()
1146
1148 """Register or create the given ScreenletService-(sub)class as the new
1149 service for this Screenlet. If self is not the first instance in the
1150 current session, the service from the first instance will be used
1151 instead and no new service is created."""
1152 if self.session:
1153 if len(self.session.instances) == 0:
1154
1155 if service_classobj==services.ScreenletService:
1156 self.service = service_classobj(self, self.get_short_name())
1157 else:
1158
1159 self.service = service_classobj(self)
1160 else:
1161 self.service = self.session.instances[0].service
1162
1163 return True
1164 return False
1165
1185
1187 """Show this Screenlet's underlying gtk.Window"""
1188 self.window.show()
1189 self.window.move(self.x, self.y)
1190 self.on_show()
1191
1193 """Show the EditableSettingsDialog for this Screenlet."""
1194 se = OptionsDialog(490, 450)
1195 img = gtk.Image()
1196 try:
1197 d = self.get_screenlet_dir()
1198 if os.path.isfile(d + '/icon.svg'):
1199 icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.svg')
1200 elif os.path.isfile(d + '/icon.png'):
1201 icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.png')
1202 img.set_from_pixbuf(icn)
1203 except:
1204 img.set_from_stock(gtk.STOCK_PROPERTIES, 5)
1205 se.set_title(self.__name__)
1206 se.set_info(self.__name__, self.__desc__, '(c) ' + self.__author__,
1207 version='v' + self.__version__, icon=img)
1208 se.show_options_for_object(self)
1209 resp = se.run()
1210 if resp == gtk.RESPONSE_REJECT:
1211 se.reset_to_defaults()
1212 else:
1213 self.update_shape()
1214 se.destroy()
1215
1230
1231
1243
1245 """Removed shaped window , in case the nom composited shape has been set"""
1246 if self.window.window:
1247 self.window.window.shape_combine_mask(None,0,0)
1248
1250 """Update window shape (only call this when shape has changed
1251 because it is very ressource intense if ran too often)."""
1252
1253 if self.disable_updates:
1254 return
1255 print _("UPDATING SHAPE")
1256
1257
1258
1259
1260 w = int(self.width * self.scale)
1261 h = int(self.height * self.scale)
1262
1263 if w==0: w = 100
1264 if h==0: h = 100
1265
1266 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
1267 data = ''.zfill(w*h)
1268 self.__shape_bitmap = gtk.gdk.bitmap_create_from_data(None, data,
1269 w, h)
1270 self.__shape_bitmap_width = w
1271 self.__shape_bitmap_height = h
1272
1273 ctx = self.__shape_bitmap.cairo_create()
1274 self.clear_cairo_context(ctx)
1275 if self.has_focus and self.draw_buttons and self.show_buttons:
1276 ctx.save()
1277 theme1 = gtk.icon_theme_get_default()
1278
1279
1280 close = theme1.load_icon ("gtk-close", 16, 0)
1281 prop = theme1.load_icon ("gtk-properties", 16, 0)
1282
1283
1284
1285
1286 ctx.translate((self.width*self.scale)-16,0)
1287 ctx.set_source_pixbuf(close, 0, 0)
1288 ctx.paint()
1289 ctx.restore()
1290 ctx.save()
1291 ctx.translate((self.width*self.scale)-32,0)
1292 ctx.set_source_pixbuf(prop, 0, 0)
1293 ctx.paint()
1294 ctx.restore()
1295
1296
1297 if self.window.is_composited():
1298
1299 self.on_draw_shape(ctx)
1300
1301 self.window.input_shape_combine_mask(self.__shape_bitmap, 0, 0)
1302 else:
1303 try: self.on_draw(ctx)
1304 except: self.on_draw_shape(ctx)
1305
1306 self.window.shape_combine_mask(self.__shape_bitmap,0,0)
1307
1309 """TEST: This function is intended to shape the window whenever no
1310 composited environment can be found. (NOT WORKING YET!!!!)"""
1311
1312
1313 w = int(self.width * self.scale)
1314 h = int(self.height * self.scale)
1315
1316 if w==0: w = 100
1317 if h==0: h = 100
1318
1319 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
1320 data = ''.zfill(w*h)
1321 self.__shape_bitmap = gtk.gdk.pixbuf_new_from_data(data,
1322 gtk.gdk.COLORSPACE_RGB, True, 1, w, h, w)
1323 self.__shape_bitmap_width = w
1324 self.__shape_bitmap_height = h
1325
1326
1327 if self.__shape_bitmap:
1328
1329 (pixmap,mask) = self.__shape_bitmap.render_pixmap_and_mask(255)
1330
1331 self.window.shape_combine_mask(mask)
1332
1333
1334
1335
1336
1338 """Called when the Screenlet gets deleted. Return True to cancel.
1339 TODO: sometimes not properly called"""
1340 return not show_question(self, _("To quit all %s's, use 'Quit' instead. ") % self.__class__.__name__ +\
1341 _('Really delete this %s and its settings?') % self.get_short_name())
1342 """return not show_question(self, 'Deleting this instance of the '+\
1343 self.__name__ + ' will also delete all your personal '+\
1344 'changes you made to it!! If you just want to close the '+\
1345 'application, use "Quit" instead. Are you sure you want to '+\
1346 'delete this instance?')
1347 return False"""
1348
1349
1350
1351
1353 """Called after setting screenlet atributes"""
1354 pass
1355
1357 """Called before setting screenlet atributes"""
1358 pass
1359
1360
1362 """Called when the screenlet's drag-icon is created. You can supply
1363 your own icon and mask by returning them as a 2-tuple."""
1364 return (None, None)
1365
1367 """Called when composite state has changed"""
1368 pass
1369
1370
1372 """Called when the Screenlet gets dragged."""
1373 pass
1374
1376 """Called when something gets dragged into the Screenlets area."""
1377 pass
1378
1380 """Called when something gets dragged out of the Screenlets area."""
1381 pass
1382
1384 """Callback for drawing the Screenlet's window - override
1385 in subclasses to implement your own drawing."""
1386 pass
1387
1389 """Callback for drawing the Screenlet's shape - override
1390 in subclasses to draw the window's input-shape-mask."""
1391 pass
1392
1393 - def on_drop (self, x, y, sel_data, timestamp):
1394 """Called when a selection is dropped on this Screenlet."""
1395 return False
1396
1398 """Called when the Screenlet's window receives focus."""
1399 pass
1400
1402 """Called when the Screenlet gets hidden."""
1403 pass
1404
1406 """Called when the Screenlet's options have been applied and the
1407 screenlet finished its initialization. If you want to have your
1408 Screenlet do things on startup you should use this handler."""
1409 pass
1410
1411 - def on_key_down (self, keycode, keyvalue, event=None):
1412 """Called when a key is pressed within the screenlet's window."""
1413 pass
1414
1416 """Called when the theme is reloaded (after loading, before redraw)."""
1417 pass
1418
1420 """Called when a menuitem is selected."""
1421 pass
1422
1424 """Called when a buttonpress-event occured in Screenlet's window.
1425 Returning True causes the event to be not further propagated."""
1426 return False
1427
1429 """Called when the mouse enters the Screenlet's window."""
1430 pass
1431
1433 """Called when the mouse leaves the Screenlet's window."""
1434 pass
1435
1437 """Called when the mouse moves in the Screenlet's window."""
1438 pass
1439
1441 """Called when a buttonrelease-event occured in Screenlet's window.
1442 Returning True causes the event to be not further propagated."""
1443 return False
1444
1446 """Callback for handling destroy-event. Perform your cleanup here!"""
1447 return True
1448
1450 """"Callback for handling the realize-event."""
1451
1453 """Called when Screenlet.scale is changed."""
1454 pass
1455
1459
1463
1465 """Called when the Screenlet gets shown after being hidden."""
1466 pass
1467
1471
1473 """Called when the Screenlet's window loses focus."""
1474 pass
1475
1476
1477
1478
1479
1481 """set colormap for window"""
1482 if screen==None:
1483 screen = window.get_screen()
1484 map = screen.get_rgba_colormap()
1485 if map:
1486 pass
1487 else:
1488 map = screen.get_rgb_colormap()
1489 window.set_colormap(map)
1490
1530
1537
1559
1560
1576
1578
1579 print "delete_event"
1580 if self.on_delete() == True:
1581 print _("Cancel delete_event")
1582 return True
1583 else:
1584 self.close()
1585 return False
1586
1587 - def destroy (self, widget, data=None):
1599
1604
1605
1607 return self.on_drop(x, y, sel_data, timestamp)
1608
1609 - def drag_end (self, widget, drag_context):
1610 print _("End drag")
1611 self.is_dragged = False
1612 return False
1613
1614 - def drag_motion (self, widget, drag_context, x, y, timestamp):
1620
1621 - def drag_leave (self, widget, drag_context, timestamp):
1625
1630
1631
1632
1633 - def expose (self, widget, event):
1634 ctx = widget.window.cairo_create()
1635
1636 self.clear_cairo_context(ctx)
1637
1638 ctx.rectangle(event.area.x, event.area.y,
1639 event.area.width, event.area.height)
1640 ctx.clip()
1641
1642
1643
1644
1645 self.on_draw(ctx)
1646
1647 del ctx
1648 return False
1649
1655
1656
1657
1658
1664
1665
1666
1668 """Handle keypress events, needed for in-place editing."""
1669 self.on_key_down(event.keyval, event.string, event)
1670
1675
1676
1677
1748
1753
1760
1767
1768
1769
1770
1852
1965
1967 """A window that displays a text and serves as Notification (very basic yet)."""
1968
1969
1970 __timeout = None
1971
1972
1973 text = ''
1974 font_name = 'FreeSans 9'
1975 width = 200
1976 height = 100
1977 x = 0
1978 y = 0
1979 gradient = cairo.LinearGradient(0, 100,0, 0)
1980
2002
2004 self.__dict__[name] = value
2005 if name in ('text'):
2006 if name == 'text':
2007 self.p_layout.set_markup(value)
2008 ink_rect, logical_rect = self.p_layout.get_pixel_extents()
2009 self.window.queue_draw()
2010
2017
2022
2027
2034
2037
2039 if screen == None:
2040 screen = window.get_screen()
2041 map = screen.get_rgba_colormap()
2042 if not map:
2043 map = screen.get_rgb_colormap()
2044 window.set_colormap(map)
2045
2046 - def expose (self, widget, event):
2047 ctx = self.window.window.cairo_create()
2048 ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL)
2049
2050 ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
2051 ctx.clip()
2052
2053 ctx.set_source_rgba(1, 1, 1, 0)
2054 ctx.set_operator (cairo.OPERATOR_SOURCE)
2055 ctx.paint()
2056
2057 self.gradient.add_color_stop_rgba(1,0.3, 0.3, 0.3, 0.9)
2058 self.gradient.add_color_stop_rgba(0.3, 0, 0, 0, 0.9)
2059 ctx.set_source(self.gradient)
2060 ctx.rectangle(0, 0, self.width, self.height)
2061 ctx.fill()
2062
2063 ctx.save()
2064 ctx.translate(3, 3)
2065 ctx.set_source_rgba(1, 1, 1, 1)
2066 ctx.show_layout(self.p_layout)
2067 ctx.fill()
2068 ctx.restore()
2069 ctx.rectangle(0, 0, self.width, self.height)
2070 ctx.set_source_rgba(0, 0, 0, 0.7)
2071 ctx.stroke()
2072
2073
2074 """class TestWidget(ShapedWidget):
2075
2076 def __init__(self, width, height):
2077 #ShapedWidget.__init__(self, width, height)
2078 super(TestWidget, self).__init__(width, height)
2079
2080 def draw(self, ctx):
2081 if self.mouse_inside:
2082 ctx.set_source_rgba(1, 0, 0, 0.8)
2083 else:
2084 ctx.set_source_rgba(1, 1, 0, 0.8)
2085 ctx.rectangle(0, 0, 32, 32)
2086 ctx.fill()
2087 """
2088
2089
2090
2091
2092
2093
2094
2096 """Launch a screenlet, either through its service or by launching a new
2097 process of the given screenlet. Name has to be the name of the Screenlet's
2098 class without trailing 'Screenlet'.
2099 NOTE: we could only launch the file here"""
2100
2101 if services.service_is_running(name):
2102
2103 srvc = services.get_service_by_name(name)
2104 if srvc:
2105 try:
2106 srvc.add('')
2107 return True
2108 except Exception, ex:
2109 print "Error while adding instance by service: %s" % ex
2110
2111 path = utils.find_first_screenlet_path(name)
2112 if path:
2113
2114 slfile = path + '/' + name + 'Screenlet.py'
2115
2116 print "Launching Screenlet from: %s" % slfile
2117 if debug:
2118 print "Logging output goes to: $HOME/.config/Screenlets/%sScreenlet.log" % name
2119 out = '$HOME/.config/Screenlets/%sScreenlet.log' % name
2120 else:
2121 out = '/dev/null'
2122 os.system('python -u %s > %s &' % (slfile, out))
2123 return True
2124 else:
2125 print "Screenlet '%s' could not be launched." % name
2126 return False
2127
2129 """Show a message for the given Screenlet (may contain Pango-Markup).
2130 If screenlet is None, this function can be used by other objects as well."""
2131 if screenlet == None:
2132 md = gtk.MessageDialog(None, type=gtk.MESSAGE_INFO,
2133 buttons=gtk.BUTTONS_OK)
2134 md.set_title(title)
2135 else:
2136 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_INFO,
2137 buttons=gtk.BUTTONS_OK)
2138 md.set_title(screenlet.__name__)
2139 md.set_markup(message)
2140 md.run()
2141 md.destroy()
2142
2144 """Show a question for the given Screenlet (may contain Pango-Markup)."""
2145 if screenlet == None:
2146 md = gtk.MessageDialog(None, type=gtk.MESSAGE_QUESTION,
2147 buttons=gtk.BUTTONS_YES_NO)
2148 md.set_title(title)
2149 else:
2150 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_QUESTION,
2151 buttons=gtk.BUTTONS_YES_NO)
2152 md.set_title(screenlet.__name__)
2153 md.set_markup(message)
2154 response = md.run()
2155 md.destroy()
2156 if response == gtk.RESPONSE_YES:
2157 return True
2158 return False
2159
2160 -def show_error (screenlet, message, title='Error'):
2161 """Show an error for the given Screenlet (may contain Pango-Markup)."""
2162 if screenlet == None:
2163 md = gtk.MessageDialog(None, type=gtk.MESSAGE_ERROR,
2164 buttons=gtk.BUTTONS_OK)
2165 md.set_title(title)
2166 else:
2167 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_ERROR,
2168 buttons=gtk.BUTTONS_OK)
2169 md.set_title(screenlet.__name__)
2170 md.set_markup(message)
2171 md.run()
2172 md.destroy()
2173
2175 """Raise a fatal error to stdout and stderr and exit with an errorcode."""
2176 import sys
2177 msg = 'FATAL ERROR: %s\n' % message
2178 sys.stdout.write(msg)
2179 sys.stderr.write(msg)
2180 sys.exit(1)
2181
2182
2183
2185 fatal_error("This screenlet seems to be written for an older version of the framework. Please download a newer version of the %s." % name)
2186