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.12"
61
62
63 COPYRIGHT = "(c) RYX (Rico Pfaus) <ryx@ryxperience.com>"
64
65
66 AUTHORS = ["RYX (Rico Pfaus) <ryx@ryxperience.com>", "Whise (Helder Fraga)<helder.fraga@hotmail.com>","Hendrik Kaju (sorcerer)"]
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
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
193
194 - def check_entry (self, filename):
195 """Checks if a file with filename is loaded in this theme."""
196 try:
197 if self[filename]:
198 return True
199 except:
200
201 return False
202
203 - def draw_text(self, ctx, text, x, y, font, size, width, allignment):
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
258 rounded=rounded_angle
259 w = width
260 h = height
261
262
263 ctx.move_to(0+padding+rounded, 0+padding)
264
265
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
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
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
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
282 if fill:ctx.fill()
283 else: ctx.stroke()
284 ctx.restore()
285
292
298
307
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
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
373
374
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
383 if self.load_svg(fname) == False:
384 return False
385 elif fname.endswith('.png'):
386
387 if self.load_png(fname) == False:
388 return False
389 elif fname == "theme.conf":
390 print _("theme.conf found! Loading option-overrides.")
391
392 if self.load_conf(file) == False:
393 return False
394 return True
395
397 """Re-Load all files in the theme's path."""
398 self.free()
399 self.__load_all()
400
401
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
408 del filename
409 self.clear()
410
411
412
413
414 - def render (self, ctx, name):
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
428 self.svgs[name].render_cairo(ctx)
429 except:
430
431 ctx.set_source_surface(self.pngs[name], 0, 0)
432 ctx.paint()
433
434
435
436
437
438
439 -class Screenlet (gobject.GObject, EditableOptions):
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
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__ = []
450
451
452
453
454
455 id = ''
456 window = None
457 theme = None
458 uses_theme = True
459 menu = None
460 is_dragged = False
461 quit_on_close = True
462 saving_enabled = True
463 dragging_over = False
464 disable_updates = False
465 p_context = None
466 p_layout = None
467
468
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
485 ask_on_option_override = True
486 has_started = False
487
488
489 __lastx = 0
490 __lasty = 0
491
492
493
494 __mi_keep_above = None
495 __mi_keep_below = None
496 __mi_widget = None
497 __mi_sticky = None
498 __mi_lock = None
499
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
510 super(Screenlet, self).__init__()
511 EditableOptions.__init__(self)
512
513 self.id = id
514 self.session = session
515 self.service = None
516
517 if self.id and service_class:
518 self.register_service(service_class)
519
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
526
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
533
534
535
536 self.__shape_bitmap = None
537 self.__shape_bitmap_width = 0
538 self.__shape_bitmap_height = 0
539
540 self.add_options_group('Screenlet',
541 _('The basic settings for this Screenlet-instance.'))
542
543
544
545 if uses_theme:
546 self.uses_theme = True
547 self.add_option(StringOption('Screenlet', 'theme_name',
548 'default', '', '', hidden=True))
549
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
595 self.disable_option('width')
596 self.disable_option('height')
597
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
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
614
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
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
642 self.window.connect("key-press-event", self.key_press)
643
644 if drag_drop:
645 self.window.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
646 gtk.DEST_DEFAULT_DROP,
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
657 self.menu = gtk.Menu()
658
659 if show_window:
660 self.window.show()
661 self.window.hide()
662
664
665 self.on_before_set_atribute(name, value)
666 gobject.GObject.__setattr__(self, name, value)
667
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
674 self.on_scale()
675 self.redraw_canvas()
676 self.update_shape()
677 elif name == "theme_name":
678
679 print _("LOAD NEW THEME: ") + value
680 print _("FOUND: ") + str(self.find_theme(value))
681
682
683 path = self.find_theme(value)
684 if path:
685 self.load_theme(path)
686
687 self.redraw_canvas()
688 self.update_shape()
689 elif name in ("width", "height"):
690
691 if self.window:
692 self.window.resize(int(self.width*self.scale), int(self.height*self.scale))
693
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
704
705 elif name == "keep_above":
706 self.window.set_keep_above(bool(value))
707
708 elif name == "keep_below":
709 self.window.set_keep_below(bool(value))
710
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
718
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
726
727
728
729
730
731
732
734 """Appends the default menu-items to self.menu. You can add on OR'ed
735 flag with DefaultMenuItems you want to add."""
736 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'
737
738 if len(self.menu.get_children()) > 0:
739 self.add_menuitem("", "-")
740
741
742
743
744 menu = self.menu
745
746 if flags & DefaultMenuItem.XML:
747
748 xfile = self.get_screenlet_dir() + "/menu.xml"
749 xmlmenu = XmlMenu.create_menu_from_file(xfile,
750 self.menuitem_callback)
751 if xmlmenu:
752 self.menu = xmlmenu
753 pass
754
755 if flags & DefaultMenuItem.SIZE:
756 size_item = gtk.MenuItem(_("Size"))
757 size_item.show()
758 size_menu = gtk.Menu()
759 menu.append(size_item)
760 size_item.set_submenu(size_menu)
761
762 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):
763 s = str(int(i * 100))
764 item = gtk.MenuItem(s + " %")
765 item.connect("activate", self.menuitem_callback,
766 "scale:"+str(i))
767 item.show()
768 size_menu.append(item)
769
770 if flags & DefaultMenuItem.THEMES:
771 themes_item = gtk.MenuItem(_("Theme"))
772 themes_item.show()
773 themes_menu = gtk.Menu()
774 menu.append(themes_item)
775 themes_item.set_submenu(themes_menu)
776
777 lst = self.get_available_themes()
778 for tname in lst:
779 item = gtk.MenuItem(tname)
780 item.connect("activate", self.menuitem_callback,
781 "theme:"+tname)
782 item.show()
783 themes_menu.append(item)
784
785 if flags & DefaultMenuItem.WINDOW_MENU:
786 winmenu_item = gtk.MenuItem(_("Window"))
787 winmenu_item.show()
788 winmenu_menu = gtk.Menu()
789 menu.append(winmenu_item)
790 winmenu_item.set_submenu(winmenu_menu)
791
792 self.__mi_lock = item = gtk.CheckMenuItem(_("Lock"))
793 item.set_active(self.lock_position)
794 item.connect("activate", self.menuitem_callback,
795 "option:lock")
796 item.show()
797 winmenu_menu.append(item)
798
799 self.__mi_sticky = item = gtk.CheckMenuItem(_("Sticky"))
800 item.set_active(self.is_sticky)
801 item.connect("activate", self.menuitem_callback,
802 "option:sticky")
803 item.show()
804 winmenu_menu.append(item)
805
806 self.__mi_widget = item = gtk.CheckMenuItem(_("Widget"))
807 item.set_active(self.is_widget)
808 item.connect("activate", self.menuitem_callback,
809 "option:widget")
810 item.show()
811 winmenu_menu.append(item)
812
813 self.__mi_keep_above = item = gtk.CheckMenuItem(_("Keep above"))
814 item.set_active(self.keep_above)
815 item.connect("activate", self.menuitem_callback,
816 "option:keep_above")
817 item.show()
818 winmenu_menu.append(item)
819
820 self.__mi_keep_below = item = gtk.CheckMenuItem(_("Keep below"))
821 item.set_active(self.keep_below)
822 item.connect("activate", self.menuitem_callback,
823 "option:keep_below")
824 item.show()
825 winmenu_menu.append(item)
826
827 if flags & DefaultMenuItem.PROPERTIES:
828 self.add_menuitem("", "-")
829 self.add_menuitem("options", _("Properties..."))
830
831 if flags & DefaultMenuItem.INFO:
832 self.add_menuitem("", "-")
833 self.add_menuitem("info", _("Info..."))
834
835 if flags & DefaultMenuItem.DELETE:
836 self.add_menuitem("", "-")
837 self.add_menuitem("delete", _("Delete Screenlet ..."))
838
839 self.add_menuitem("", "-")
840 self.add_menuitem("quit_instance", _("Quit this %s ...") % self.get_short_name())
841
842 self.add_menuitem("", "-")
843 self.add_menuitem("quit", _("Quit all %ss ...") % self.get_short_name())
844
846 """Simple way to add menuitems to the right-click menu."""
847 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'
848 if callback == None:
849 callback = self.menuitem_callback
850 if label == "-":
851 menu_item = gtk.SeparatorMenuItem()
852 else:
853 menu_item = gtk.MenuItem(label)
854 menu_item.connect("activate", callback, id)
855 self.menu.append(menu_item)
856 menu_item.show()
857 return menu_item
858
859 - def clear_cairo_context (self, ctx):
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
871 self.window.unmap()
872 self.window.destroy()
873
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
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
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
897 """Enable/Disable realtime-saving of options."""
898 self.saving_enabled = enabled
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
939 dirlst = glob.glob(d + '*')
940 dirlst.sort()
941 tdlen = len(d)
942 for fname in dirlst:
943 dname = fname[tdlen:]
944
945 lst.append(dname)
946 return lst
947
960 """Hides this Screenlet's underlying gtk.Window"""
961 self.window.hide()
962 self.on_hide()
963
964
965
966
991
992
994 """If the Screenlet runs as stand-alone app, starts gtk.main()"""
995 gtk.main()
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
1005 if service_classobj==services.ScreenletService:
1006 self.service = service_classobj(self, self.get_short_name())
1007 else:
1008
1009 self.service = service_classobj(self)
1010 else:
1011 self.service = self.session.instances[0].service
1012
1013 return True
1014 return False
1015
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:
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
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
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
1100 if self.disable_updates:
1101 return
1102 print _("UPDATING SHAPE")
1103
1104
1105
1106
1107 w = int(self.width * self.scale)
1108 h = int(self.height * self.scale)
1109
1110 if w==0: w = 100
1111 if h==0: h = 100
1112
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
1120 ctx = self.__shape_bitmap.cairo_create()
1121 self.clear_cairo_context(ctx)
1122
1123
1124
1125 if self.window.is_composited():
1126
1127 self.on_draw_shape(ctx)
1128
1129 self.window.input_shape_combine_mask(self.__shape_bitmap, 0, 0)
1130 else:
1131 try: self.on_draw(ctx)
1132 except: self.on_draw_shape(ctx)
1133
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
1140
1141 w = int(self.width * self.scale)
1142 h = int(self.height * self.scale)
1143
1144 if w==0: w = 100
1145 if h==0: h = 100
1146
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
1154
1155 if self.__shape_bitmap:
1156
1157 (pixmap,mask) = self.__shape_bitmap.render_pixmap_and_mask(255)
1158
1159 self.window.shape_combine_mask(mask)
1160
1161
1162
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
1178
1179
1181 """Called after setting screenlet atributes"""
1182 pass
1183
1185 """Called before setting screenlet atributes"""
1186 pass
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
1195 """Called when screenlet was mapped"""
1196 pass
1197
1199 """Called when screenlet was unmapped"""
1200 pass
1201
1203 """Called when composite state has changed"""
1204 pass
1205
1206
1208 """Called when the Screenlet gets dragged."""
1209 pass
1210
1212 """Called when something gets dragged into the Screenlets area."""
1213 pass
1214
1216 """Called when something gets dragged out of the Screenlets area."""
1217 pass
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
1229 - def on_drop (self, x, y, sel_data, timestamp):
1230 """Called when a selection is dropped on this Screenlet."""
1231 return False
1232
1234 """Called when the Screenlet's window receives focus."""
1235 pass
1236
1238 """Called when the Screenlet gets hidden."""
1239 pass
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
1247 - def on_key_down (self, keycode, keyvalue, event=None):
1248 """Called when a key is pressed within the screenlet's window."""
1249 pass
1250
1252 """Called when the theme is reloaded (after loading, before redraw)."""
1253 pass
1254
1256 """Called when a menuitem is selected."""
1257 pass
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
1265 """Called when the mouse enters the Screenlet's window."""
1266 pass
1267
1269 """Called when the mouse leaves the Screenlet's window."""
1270 pass
1271
1273 """Called when the mouse moves in the Screenlet's window."""
1274 pass
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
1282 """Callback for handling destroy-event. Perform your cleanup here!"""
1283 return True
1284
1286 """"Callback for handling the realize-event."""
1287
1289 """Called when Screenlet.scale is changed."""
1290 pass
1291
1295
1299
1301 """Called when the Screenlet gets shown after being hidden."""
1302 pass
1303
1307
1309 """Called when the Screenlet's window loses focus."""
1310 pass
1311
1312
1313
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
1377
1378
1394
1396
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
1405 - def destroy (self, widget, data=None):
1417
1422
1423
1425 return self.on_drop(x, y, sel_data, timestamp)
1426
1427 - def drag_end (self, widget, drag_context):
1428 print _("End drag")
1429 self.is_dragged = False
1430 return False
1431
1432 - def drag_motion (self, widget, drag_context, x, y, timestamp):
1438
1439 - def drag_leave (self, widget, drag_context, timestamp):
1443
1447
1448
1449 - def expose (self, widget, event):
1450 ctx = widget.window.cairo_create()
1451
1452 self.clear_cairo_context(ctx)
1453
1454 ctx.rectangle(event.area.x, event.area.y,
1455 event.area.width, event.area.height)
1456 ctx.clip()
1457
1458
1459
1460 self.on_draw(ctx)
1461
1462 del ctx
1463 return False
1464
1467
1470
1472 """Handle keypress events, needed for in-place editing."""
1473 self.on_key_down(event.keyval, event.string, event)
1474
1478
1549
1552
1555
1560
1567
1574
1575
1576
1577
1659
1772
1774 """A window that displays a text and serves as Notification (very basic yet)."""
1775
1776
1777 __timeout = None
1778
1779
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
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
1824
1829
1834
1841
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
1853 - def expose (self, widget, event):
1854 ctx = self.window.window.cairo_create()
1855 ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL)
1856
1857 ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
1858 ctx.clip()
1859
1860 ctx.set_source_rgba(1, 1, 1, 0)
1861 ctx.set_operator (cairo.OPERATOR_SOURCE)
1862 ctx.paint()
1863
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
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
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
1899
1900
1901
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
1908 if services.service_is_running(name):
1909
1910 srvc = services.get_service_by_name(name)
1911 if srvc:
1912 try:
1913 srvc.add('')
1914 return True
1915 except Exception, ex:
1916 print "Error while adding instance by service: %s" % ex
1917
1918 path = utils.find_first_screenlet_path(name)
1919 if path:
1920
1921 slfile = path + '/' + name + 'Screenlet.py'
1922
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
1967 -def show_error (screenlet, message, title='Error'):
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
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