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!
8
# Screenlets main module (c) RYX (aka Rico Pfaus) 2007 <ryx@ryxperience.com> ,
9
# Whise aka Helder Fraga <helder.fraga@hotmail.com>
13
##@section intro_sec General Information
16
# - Screenlets are small owner-drawn applications that can be described as
17
# " the virtual representation of things lying/standing around on your desk".
18
# Sticknotes, clocks, rulers, ... the possibilities are endless. The goal of
19
# the Screenlets is to simplify the creation of fully themeable mini-apps that
20
# each solve basic desktop-work-related needs and generally improve the
21
# usability and eye-candy of the modern Linux-desktop.
23
# TODO: (possible improvements, not essential)
24
# - still more error-handling and maybe custom exceptions!!!
25
# - improve xml-based menu (is implemented, but I'm not happy with it)
26
# - switching themes slowly increases the memory usage (possible leak)
27
# - maybe attributes for dependancies/requirements (e.g. special
28
# python-libs or certain Screenlets)
33
INSTALL_PREFIX = open("/etc/screenlets/prefix").read()[:-1]
35
INSTALL_PREFIX = '/usr'
45
except ImportError: print 'No module RSVG , graphics will not be so good'
52
# import screenlet-submodules
59
from menu import DefaultMenuItem, add_menuitem
60
from drawing import Drawing
64
gettext.textdomain('screenlets')
65
gettext.bindtextdomain('screenlets', INSTALL_PREFIX + '/share/locale')
68
return gettext.gettext(s)
70
#-------------------------------------------------------------------------------
72
#-------------------------------------------------------------------------------
74
# the application name
75
APP_NAME = "Screenlets"
77
# the version of the Screenlets-baseclass in use
80
# the application copyright
81
COPYRIGHT = "(c) RYX (Rico Pfaus) <ryx@ryxperience.com>\nWhise (Helder Fraga) <helder.fraga@hotmail.com>"
83
# the application authors
84
AUTHORS = ["RYX (Rico Pfaus) <ryx@ryxperience.com>", "Whise (Helder Fraga)<helder.fraga@hotmail.com>","Sorcerer (Hendrik Kaju)"]
86
# the application comments
87
COMMENTS = "Screenlets is a widget framework that consists of 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. Screenlet also tries to include some compatibility with other widget frameworks,like web widgets and super karamba themes"
89
DOCUMENTERS = ["Documentation generated by epydoc"]
91
ARTISTS = ["ODD radio screenlet theme by ODDie\nPasodoble mail theme by jEsuSdA\nSome themes by RYX\nSome themes by Whise\nMore to come..."]
93
TRANSLATORS = "Special thanks for translators\nFull Translator list on https://translations.launchpad.net/screenlets/"
95
# the application website
96
WEBSITE = 'http://www.screenlets.org'
98
# The Screenlets download page. Notice that if you translate this, you also have to create/translate the page for your language on the Screenlets.org (it's a Wiki!)
99
THIRD_PARTY_DOWNLOAD = _("http://www.screenlets.org/index.php/Get_more_screenlets")
102
#-------------------------------------------------------------------------------
104
#-------------------------------------------------------------------------------
105
DIR_TMP = '/tmp/screenlets/'
109
TMP_FILE = 'screenlets.' + os.environ['USER'] + '.running'
111
DIR_USER_ROOT = screenlets.INSTALL_PREFIX + '/share/screenlets'
113
DIR_USER = os.environ['HOME'] + '/.screenlets'
115
DIR_CONFIG = os.environ['HOME'] + '/.config/Screenlets'
117
# note that this is the order how themes are preferred to each other
118
# don't change the order just like that
119
SCREENLETS_PATH = [DIR_USER, DIR_USER_ROOT]
121
SCREENLETS_PACK_PREFIX = "screenlets-pack-"
123
#-------------------------------------------------------------------------------
125
#-------------------------------------------------------------------------------
127
DAEMON_BUS = 'org.screenlets.ScreenletsDaemon'
129
DAEMON_PATH = '/org/screenlets/ScreenletsDaemon'
131
DAEMON_IFACE = 'org.screenlets.ScreenletsDaemon'
139
subprocess.call(["dpkg"], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT)
145
subprocess.call(["apt-add-repository"], stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT)
149
#-------------------------------------------------------------------------------
151
#-------------------------------------------------------------------------------
153
class DefaultMenuItem(object):
154
"""A container with constants for the default menuitems"""
156
# default menuitem constants (is it right to increase like this?)
167
# EXPERIMENTAL!! If you use this, the file menu.xml in the
168
# Screenlet's data-dir is used for generating the menu ...
172
STANDARD = 1|2|8|16|32|64|128|256|1024
175
class ScreenletTheme (dict):
176
"""ScreenletThemes are simple storages that allow loading files
177
as svg-handles within a theme-directory. Each Screenlet can have
178
its own theme-directory. It is up to the Screenlet-developer if he
179
wants to let his Screenlet support themes or not. Themes are
180
turned off by default - if your Screenlet uses Themes, just set the
181
attribute 'theme_name' to the name of the theme's dir you want to use.
182
TODO: remove dict-inheritance"""
184
# meta-info (set through theme.conf)
195
option_overrides = {}
202
def __init__ (self, path):
203
# set theme-path and load all files in path
207
self.option_overrides = {}
208
self.loaded = self.__load_all()
209
if self.loaded == False:
210
raise Exception("Error while loading ScreenletTheme in: " + path)
212
def __getattr__ (self, name):
213
if name in ("width", "height"):
214
if self.loaded and len(self)>0:
215
size=self[0].get_dimension_data()
221
return object.__getattr__(self, name)
223
def apply_option_overrides (self, screenlet):
224
"""Apply this theme's overridden options to the given Screenlet."""
225
# disable the canvas-updates in the screenlet
226
screenlet.disable_updates = True
227
# theme_name needs special care (must be applied last)
229
# loop through overrides and appply them
230
for name in self.option_overrides:
231
print "Override: " + name
232
o = screenlet.get_option_by_name(name)
233
if o and not o.protected:
234
if name == 'theme_name':
235
# import/remember theme-name, but not apply yet
236
theme_name = o.on_import(self.option_overrides[name])
238
# set option in screenlet
239
setattr(screenlet, name,
240
o.on_import(self.option_overrides[name]))
242
print "WARNING: Option '%s' not found or protected." % name
245
screenlet.theme_name = theme_name
246
# re-enable updates and call redraw/reshape
247
screenlet.disable_updates = False
248
screenlet.redraw_canvas()
249
screenlet.update_shape()
251
def check_entry (self, filename):
252
"""Checks if a file with filename is loaded in this theme."""
260
def get_text_width(self, ctx, text, font):
261
"""@DEPRECATED Moved to Screenlets class: Returns the pixel width of a given text"""
264
if self.p_layout == None :
266
self.p_layout = ctx.create_layout()
269
ctx.update_layout(self.p_layout)
270
self.p_fdesc = pango.FontDescription(font)
271
self.p_layout.set_font_description(self.p_fdesc)
272
self.p_layout.set_text(text)
273
extents, lextents = self.p_layout.get_pixel_extents()
277
def get_text_extents(self, ctx, text, font):
278
"""@DEPRECATED Moved to Screenlets class: Returns the pixel extents of a given text"""
281
if self.p_layout == None :
283
self.p_layout = ctx.create_layout()
286
ctx.update_layout(self.p_layout)
287
self.p_fdesc = pango.FontDescription(font)
288
self.p_layout.set_font_description(self.p_fdesc)
289
self.p_layout.set_text(text)
290
extents, lextents = self.p_layout.get_pixel_extents()
294
def draw_text(self, ctx, text, x, y, font, size, width, allignment, weight = 0, ellipsize = pango.ELLIPSIZE_NONE):
295
"""@DEPRECATED Moved to Screenlets class: Draws text"""
298
if self.p_layout == None :
300
self.p_layout = ctx.create_layout()
303
ctx.update_layout(self.p_layout)
304
self.p_fdesc = pango.FontDescription()
305
self.p_fdesc.set_family_static(font)
306
self.p_fdesc.set_size(size * pango.SCALE)
307
self.p_fdesc.set_weight(weight)
308
self.p_layout.set_font_description(self.p_fdesc)
309
self.p_layout.set_width(width * pango.SCALE)
310
self.p_layout.set_alignment(allignment)
311
self.p_layout.set_ellipsize(ellipsize)
312
self.p_layout.set_markup(text)
313
ctx.show_layout(self.p_layout)
317
def draw_circle(self,ctx,x,y,width,height,fill=True):
318
"""@DEPRECATED Moved to Screenlets class: Draws a circule"""
321
ctx.arc(width/2,height/2,min(height,width)/2,0,2*math.pi)
326
def draw_line(self,ctx,start_x,start_y,end_x,end_y,line_width = 1,close=False,preserve=False):
327
"""@DEPRECATED Moved to Screenlets class: Draws a line"""
329
ctx.move_to(start_x, start_y)
330
ctx.set_line_width(line_width)
331
ctx.rel_line_to(end_x, end_y)
332
if close : ctx.close_path()
333
if preserve: ctx.stroke_preserve()
337
def draw_rectangle(self,ctx,x,y,width,height,fill=True):
338
"""@DEPRECATED Moved to Screenlets class: Draws a rectangle"""
341
ctx.rectangle (0,0,width,height)
346
def draw_rounded_rectangle(self,ctx,x,y,rounded_angle,width,height,fill=True):
347
"""@DEPRECATED Moved to Screenlets class: Draws a rounded rectangle"""
350
padding=0 # Padding from the edges of the window
351
rounded=rounded_angle # How round to make the edges 20 is ok
356
ctx.move_to(0+padding+rounded, 0+padding)
358
# Top right corner and round the edge
359
ctx.line_to(w-padding-rounded, 0+padding)
360
ctx.arc(w-padding-rounded, 0+padding+rounded, rounded, (math.pi/2 )+(math.pi) , 0)
362
# Bottom right corner and round the edge
363
ctx.line_to(w-padding, h-padding-rounded)
364
ctx.arc(w-padding-rounded, h-padding-rounded, rounded, 0, math.pi/2)
366
# Bottom left corner and round the edge.
367
ctx.line_to(0+padding+rounded, h-padding)
368
ctx.arc(0+padding+rounded, h-padding-rounded, rounded,math.pi/2, math.pi)
370
# Top left corner and round the edge
371
ctx.line_to(0+padding, 0+padding+rounded)
372
ctx.arc(0+padding+rounded, 0+padding+rounded, rounded, math.pi, (math.pi/2 )+(math.pi))
379
def get_image_size(self,pix):
380
"""@DEPRECATED Moved to Screenlets class: Gets a picture width and height"""
382
pixbuf = gtk.gdk.pixbuf_new_from_file(pix)
383
iw = pixbuf.get_width()
384
ih = pixbuf.get_height()
388
def draw_image(self,ctx,x,y, pix):
389
"""@DEPRECATED Moved to Screenlets class: Draws a picture from specified path"""
393
pixbuf = gtk.gdk.pixbuf_new_from_file(pix)
394
format = cairo.FORMAT_RGB24
395
if pixbuf.get_has_alpha():
396
format = cairo.FORMAT_ARGB32
398
iw = pixbuf.get_width()
399
ih = pixbuf.get_height()
400
image = cairo.ImageSurface(format, iw, ih)
401
image = ctx.set_source_pixbuf(pixbuf, 0, 0)
410
def draw_scaled_image(self,ctx,x,y, pix, w, h):
411
"""@DEPRECATED Moved to Screenlets class: Draws a picture from specified path with a certain width and height"""
415
pixbuf = gtk.gdk.pixbuf_new_from_file(pix).scale_simple(w,h,gtk.gdk.INTERP_HYPER)
416
format = cairo.FORMAT_RGB24
417
if pixbuf.get_has_alpha():
418
format = cairo.FORMAT_ARGB32
420
iw = pixbuf.get_width()
421
ih = pixbuf.get_height()
422
image = cairo.ImageSurface(format, iw, ih)
424
matrix = cairo.Matrix(xx=iw/w, yy=ih/h)
425
image = ctx.set_source_pixbuf(pixbuf, 0, 0)
426
if image != None :image.set_matrix(matrix)
432
def show_notification (self,text):
433
"""@DEPRECATED Moved to Screenlets class: Show notification window at current mouse position."""
434
if self.notify == None:
435
self.notify = Notify()
436
self.notify.text = text
439
def hide_notification (self):
440
"""@DEPRECATED Moved to Screenlets class: hide notification window"""
441
if self.notify != None:
445
def show_tooltip (self,text,tooltipx,tooltipy):
446
"""@DEPRECATED: Moved to Screenlets class: Show tooltip window at current mouse position."""
447
if self.tooltip == None:
448
self.tooltip = Tooltip(300, 400)
449
self.tooltip.text = text
450
self.tooltip.x = tooltipx
451
self.tooltip.y = tooltipy
454
def hide_tooltip (self):
455
"""@DEPRECATED Moved to Screenlets class: hide tooltip window"""
456
if self.tooltip != None:
460
def has_overrides (self):
461
"""Check if this theme contains overrides for options."""
462
return len(self.option_overrides) > 0
464
def load_conf (self, filename):
465
"""Load a config-file from this theme's dir and save vars in list."""
466
ini = utils.IniReader()
467
if ini.load(filename):
468
if ini.has_section('Theme'):
469
self.__name__ = ini.get_option('name', section='Theme')
470
self.__author__ = ini.get_option('author', section='Theme')
471
self.__version__ = ini.get_option('version', section='Theme')
472
self.__info__ = ini.get_option('info', section='Theme')
473
if ini.has_section('Options'):
474
opts = ini.list_options(section='Options')
477
self.option_overrides[o[0]] = o[1]
478
print "Loaded theme config from:", filename
479
print "\tName: " + str(self.__name__)
480
print "\tAuthor: " +str(self.__author__)
481
print "\tVersion: " +str(self.__version__)
482
print "\tInfo: " +str(self.__info__)
484
print "Failed to theme config from", filename
487
def load_svg (self, filename):
488
"""Load an SVG-file into this theme and reference it as ref_name."""
489
if self.has_key(filename):
492
self[filename] = rsvg.Handle(self.path + "/" + filename)
493
self.svgs[filename[:-4]] = self[filename]
494
if self[filename] != None:
496
size=self[filename].get_dimension_data()
499
self.height = size[1]
501
except NameError, ex:
502
self[filename] = gtk.gdk.pixbuf_new_from_file(self.path + '/' + filename)
503
self.svgs[filename[:-4]] = self[filename]
504
if self[filename] != None:
506
self.width = self[filename].get_width()
507
self.height = self[filename].get_height()
513
#self[filename] = None
515
def load_png (self, filename):
516
"""Load a PNG-file into this theme and reference it as ref_name."""
517
if self.has_key(filename):
519
self[filename] = cairo.ImageSurface.create_from_png(self.path +
521
self.pngs[filename[:-4]] = self[filename]
522
if self[filename] != None:
526
#self[filename] = None
528
def __load_all (self):
529
"""Load all files in the theme's path. Currently only loads SVGs and
532
#self.__option_overrides = {}
534
dirlst = glob.glob(self.path + '/*')
537
plen = len(self.path) + 1
540
if fname.endswith('.svg'):
542
if self.load_svg(fname) == False:
544
elif fname.endswith('.png'):
546
if self.load_png(fname) == False:
548
elif fname == "theme.conf":
549
print "theme.conf found! Loading option-overrides."
551
if self.load_conf(file) == False:
553
# print "Theme %s loaded from %s" % (self.__name__, self.path)
557
"""Re-Load all files in the theme's path."""
561
# TODO: fix function, rsvg handles are not freed properly
563
"""Deletes the Theme's contents and frees all rsvg-handles.
564
TODO: freeing rsvg-handles does NOT work for some reason"""
565
self.option_overrides.clear()
566
for filename in self:
568
self[filename].free()
569
except AttributeError:pass
570
#self[filename].close()
574
# TEST: render-function
575
# should be used like "theme.render(context, 'notes-bg')" and then use
576
# either an svg or png image
577
def render (self, ctx, name):
578
"""Render an image from within this theme to the given context. This
579
function can EITHER use png OR svg images, so it is possible to
580
create themes using both image-formats when a Screenlet uses this
581
function for drawing its images. The image name has to be defined
582
without the extension and the function will automatically select
583
the available one (SVG is prefered over PNG)."""
585
### Render Graphics even if rsvg is not available###
586
if os.path.isfile (self.path + '/' + name + '.svg'):
589
self.svgs[name].render_cairo(ctx)
592
ctx.set_source_pixbuf(self.svgs[name], 0, 0)
597
ctx.set_source_surface(self.pngs[name], 0, 0)
600
elif os.path.isfile (self.path + '/' + name + '.png'):
601
ctx.set_source_surface(self.pngs[name], 0, 0)
606
def render_png_colorized(self, ctx, name,color):
608
ctx.set_source_rgba(color[0], color[1], color[2], color[3])
609
ctx.set_source_surface(self.pngs[name], 0, 0)
610
ctx.mask_surface(image, 0, 0)
615
class Screenlet (gobject.GObject, EditableOptions, Drawing):
616
"""A Screenlet is a (i.e. contains a) shaped gtk-window that is
617
fully invisible by default. Subclasses of Screenlet can render
618
their owner-drawn graphics on fully transparent background."""
620
# default meta-info for Screenlets
621
__name__ = _('No name set for this Screenlet')
623
__author__ = _('No author defined for this Screenlet')
624
__desc__ = _('No info set for this Screenlet')
626
#__target_version__ = '0.0.0'
627
#__backend_version__ = '0.0.1'
629
# attributes (TODO: remove them here and add them to the constructor,
630
# because they only should exist per instance)
631
id = '' # id-attribute for handling instances
632
window = None # the gtk.Window behind the scenes
633
theme = None # the assigned ScreenletTheme
634
uses_theme = True # flag indicating whether Screenlet uses themes
637
menu = None # the right-click gtk.Menu
638
is_dragged = False # TODO: make this work
639
quit_on_close = True # if True, closing this instance quits gtk
640
saving_enabled = True # if False, saving is disabled
641
dragging_over = False # true if something is dragged over
642
disable_updates = False # to temporarily avoid refresh/reshape
643
p_context = None # PangoContext
644
p_layout = None # PangoLayout
646
# default editable options, available for all Screenlets
651
mouse_is_over = False
665
lock_position = False
666
allow_option_override = True # if False, overrides are ignored
667
ask_on_option_override = True # if True, overrides need confirmation
668
ignore_requirements = False # if True, DEB requirements are ignored
669
resize_on_scroll = True
672
# internals (deprecated? we still don't get the end of a begin_move_drag)
673
gtk_icon_theme = None
680
# some menuitems (needed for checking/unchecking)
681
# DEPRECATED: remove - don't really work anyway ... (or fix the menu?)
682
__mi_keep_above = None
683
__mi_keep_below = None
687
# for custom signals (which aren't acutally used ... yet)
688
__gsignals__ = dict(screenlet_removed=(gobject.SIGNAL_RUN_FIRST,
689
gobject.TYPE_NONE, (gobject.TYPE_OBJECT,)))
691
def __init__ (self, id='', width=100, height=100, parent_window=None,
692
show_window=True, is_widget=False, is_sticky=False,
693
uses_theme=True, draw_buttons=True,path=os.getcwd(), drag_drop=False, session=None,
694
enable_saving=True, service_class=services.ScreenletService,
695
uses_pango=False, is_sizable=True,resize_on_scroll=True, ask_on_option_override=False):
696
"""Constructor - should only be subclassed"""
698
# call gobject and EditableOptions superclasses
699
super(Screenlet, self).__init__()
700
EditableOptions.__init__(self)
703
self.session = session
705
self.__desc__ = self.__doc__
707
# if we have an id and a service-class, register our service
708
if self.id and service_class:
709
self.register_service(service_class)
710
# notify service about adding this instance
711
self.service.instance_added(self.id)
714
self.is_dragged = False
716
self.saving_enabled = enable_saving # used by session
717
# set some attributes without calling __setattr__
718
self.__dict__['theme_name'] = ""
719
self.__dict__['is_widget'] = is_widget
720
self.__dict__['is_sticky'] = is_sticky
721
self.__dict__['draw_buttons'] = draw_buttons
722
self.resize_on_scroll = resize_on_scroll
723
self.__dict__['x'] = 0
724
self.__dict__['y'] = 0
725
# TEST: set scale relative to theme size (NOT WORKING)
726
#self.__dict__['scale'] = width/100.0
729
self.__shape_bitmap = None
730
self.__shape_bitmap_width = 0
731
self.__shape_bitmap_height = 0
732
# "editable" options, first create a group
733
self.add_options_group('Screenlet',
734
_('The basic settings for this Screenlet-instance.'))
735
# if this Screenlet uses themes, add theme-specific options
736
# (NOTE: this option became hidden with 0.0.9 and doesn't use
737
# get_available_themes anymore for showing the choices)
738
self.gtk_icon_theme = gtk.icon_theme_get_default()
739
self.load_buttons(None)
740
self.gtk_icon_theme.connect("changed", self.load_buttons)
741
if draw_buttons: self.draw_buttons = True
742
else: self.draw_buttons = False
744
self.uses_theme = True
745
self.add_option(StringOption('Screenlet', 'theme_name',
746
'default', '', '', hidden=True))
748
self.add_option(IntOption('Screenlet', 'x',
749
0, _('X-Position'), _('The X-position of this Screenlet ...'),
750
min=0, max=gtk.gdk.screen_width()))
751
self.add_option(IntOption('Screenlet', 'y',
752
0, _('Y-Position'), _('The Y-position of this Screenlet ...'),
753
min=0, max=gtk.gdk.screen_height()))
754
self.add_option(IntOption('Screenlet', 'width',
755
width, _('Width'), _('The width of this Screenlet ...'),
756
min=16, max=1000, hidden=True))
757
self.add_option(IntOption('Screenlet', 'height',
758
height, _('Height'), _('The height of this Screenlet ...'),
759
min=16, max=1000, hidden=True))
760
self.add_option(FloatOption('Screenlet', 'scale',
761
self.scale, _('Scale'), _('The scale-factor of this Screenlet ...'),
762
min=0.1, max=10.0, digits=2, increment=0.1))
763
self.add_option(FloatOption('Screenlet', 'opacity',
764
self.opacity, _('Opacity'), _('The opacity of the Screenlet window ...'),
765
min=0.1, max=1.0, digits=2, increment=0.1))
766
self.add_option(BoolOption('Screenlet', 'is_sticky',
767
is_sticky, _('Stick to Desktop'),
768
_('Show this Screenlet on all workspaces ...')))
769
self.add_option(BoolOption('Screenlet', 'is_widget',
770
is_widget, _('Treat as Widget'),
771
_('Treat this Screenlet as a "Widget" ...')))
772
self.add_option(BoolOption('Screenlet', 'is_dragged',
773
self.is_dragged, "Is the screenlet dragged","Is the screenlet dragged", hidden=True))
774
self.add_option(BoolOption('Screenlet', 'is_sizable',
775
is_sizable, "Can the screenlet be resized","is_sizable", hidden=True))
776
self.add_option(BoolOption('Screenlet', 'is_visible',
777
self.is_visible, "Usefull to use screenlets as gnome panel applets","is_visible", hidden=True))
778
self.add_option(BoolOption('Screenlet', 'lock_position',
779
self.lock_position, _('Lock position'),
780
_('Stop the screenlet from being moved...')))
781
self.add_option(BoolOption('Screenlet', 'keep_above',
782
self.keep_above, _('Keep above'),
783
_('Keep this Screenlet above other windows ...')))
784
self.add_option(BoolOption('Screenlet', 'keep_below',
785
self.keep_below, _('Keep below'),
786
_('Keep this Screenlet below other windows ...')))
787
self.add_option(BoolOption('Screenlet', 'draw_buttons',
788
self.draw_buttons, _('Draw button controls'),
789
_('Draw buttons in top right corner')))
790
self.add_option(BoolOption('Screenlet', 'skip_pager',
791
self.skip_pager, _('Skip Pager'),
792
_('Set this Screenlet to show/hide in pagers ...')))
793
self.add_option(BoolOption('Screenlet', 'skip_taskbar',
794
self.skip_pager, _('Skip Taskbar'),
795
_('Set this Screenlet to show/hide in taskbars ...')))
796
self.add_option(BoolOption('Screenlet', 'resize_on_scroll',
797
self.resize_on_scroll, _("Resize on mouse scroll"),"resize_on_scroll"))
798
self.add_option(BoolOption('Screenlet', 'ignore_requirements',
799
self.ignore_requirements, _('Ignore requirements'),
800
_('Set this Screenlet to ignore/demand DEB requirements ...')))
802
self.ask_on_option_override = ask_on_option_override
803
self.add_option(BoolOption('Screenlet', 'allow_option_override',
804
self.allow_option_override, _('Allow overriding Options'),
805
_('Allow themes to override options in this screenlet ...')))
806
self.add_option(BoolOption('Screenlet', 'ask_on_option_override',
807
self.ask_on_option_override, _('Ask on Override'),
808
_('Show a confirmation-dialog when a theme wants to override ')+\
809
_('the current options of this Screenlet ...')))
810
# disable width/height
811
self.disable_option('width')
812
self.disable_option('height')
814
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
816
self.window.set_parent_window(parent_window)
817
self.window.set_transient_for(parent_window)
818
self.window.set_destroy_with_parent(True)
819
self.window.resize(width, height)
820
self.window.set_decorated(False)
821
self.window.set_app_paintable(True)
822
# create pango layout, if active
824
self.p_context = self.window.get_pango_context()
826
self.p_layout = pango.Layout(self.p_context)
827
self.p_layout.set_font_description(\
828
pango.FontDescription("Sans 12"))
831
if str(sensors.sys_get_window_manager()).lower() == 'kwin':
832
print "WARNING - You are using kwin window manager , screenlets doesnt have full compatibility with this window manager"
833
#self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK)
834
elif str(sensors.sys_get_window_manager()).lower() == 'sawfish':
835
print "WARNING - You are using kwin window manager , screenlets doesnt have full compatibility with this window manager"
837
self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_TOOLBAR)
838
self.window.set_keep_above(True)
839
self.window.set_skip_taskbar_hint(True)
840
self.window.set_skip_pager_hint(True)
843
self.alpha_screen_changed(self.window)
845
#self.window.set_events(gtk.gdk.BUTTON_PRESS_MASK)
846
self.window.set_events(gtk.gdk.ALL_EVENTS_MASK)
847
self.window.connect("composited-changed", self.composite_changed)
848
self.window.connect("delete_event", self.delete_event)
849
self.window.connect("destroy", self.destroy)
850
self.window.connect("expose_event", self.expose)
851
self.window.connect("button-press-event", self.button_press)
852
self.window.connect("button-release-event", self.button_release)
853
self.window.connect("configure-event", self.configure_event)
854
self.window.connect("screen-changed", self.alpha_screen_changed)
855
self.window.connect("realize", self.realize_event)
856
self.window.connect("enter-notify-event", self.enter_notify_event)
857
self.window.connect("leave-notify-event", self.leave_notify_event)
858
self.window.connect("focus-in-event", self.focus_in_event)
859
self.window.connect("focus-out-event", self.focus_out_event)
860
self.window.connect("scroll-event", self.scroll_event)
861
self.window.connect("motion-notify-event",self.motion_notify_event)
862
self.window.connect("map-event", self.map_event)
863
self.window.connect("unmap-event", self.unmap_event)
864
# add key-handlers (TODO: use keyword-attrib to activate?)
865
self.window.connect("key-press-event", self.key_press)
866
# drag/drop support (NOTE: still experimental and incomplete)
868
self.window.drag_dest_set(gtk.DEST_DEFAULT_MOTION |
869
gtk.DEST_DEFAULT_DROP, #gtk.DEST_DEFAULT_ALL,
870
[("text/plain", 0, 0),
872
("text/uri-list", 0, 2)],
874
self.window.connect("drag_data_received", self.drag_data_received)
875
self.window.connect("drag-begin", self.drag_begin)
876
self.window.connect("drag-end", self.drag_end)
877
self.window.connect("drag-motion", self.drag_motion)
878
self.window.connect("drag-leave", self.drag_leave)
880
self.menu = gtk.Menu()
881
# 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
886
# print os.environ['HOME'] + '/.config/Screenlets/' + self.__name__[:-9] + '/default/'+ self.id
887
if not os.path.exists(os.environ['HOME'] + '/.config/Screenlets/' + self.__name__[:-9] + '/default/'+ self.id + '.ini'):
888
self.first_run = True
891
#Make opacity available only when composite is enabled
892
if not self.window.is_composited () :
893
self.disable_option('opacity')
895
def __setattr__ (self, name, value):
896
# set the value in GObject (ESSENTIAL!!!!)
897
self.on_before_set_atribute(name, value)
898
gobject.GObject.__setattr__(self, name, value)
899
# And do other actions
900
if name=="x" or name=="y":
902
self.window.move(self.x, self.y)
903
elif name == 'opacity':
904
self.window.set_opacity(value)
905
elif name == 'scale':
906
self.window.resize(int(self.width * self.scale),
907
int(self.height * self.scale))
908
# TODO: call on_resize-handler here !!!!
914
elif name == "theme_name":
915
#self.__dict__ ['theme_name'] = value
916
#self.load_theme(self.get_theme_dir() + value)
918
print "Theme set to: '%s'" % value
919
path = self.find_theme(value)
921
self.load_theme(path)
922
#self.load_first_theme(value)
925
elif name in ("width", "height"):
926
#self.__dict__ [name] = value
928
self.window.resize(int(self.width*self.scale), int(self.height*self.scale))
929
#self.redraw_canvas()
931
elif name == "is_widget":
933
self.set_is_widget(value)
934
elif name == "is_visible":
940
elif name == "is_sticky":
944
self.window.unstick()
945
#if self.__mi_sticky:
946
# self.__mi_sticky.set_active(value)
947
elif name == "keep_above":
948
if self.has_started == True:
949
self.window.set_keep_above(bool(value))
950
#self.__mi_keep_above.set_active(value)
951
elif name == "keep_below":
952
if self.has_started == True:
953
self.window.set_keep_below(bool(value))
954
#self.__mi_keep_below.set_active(value)
955
elif name == "skip_pager":
956
if self.window.window:
957
self.window.window.set_skip_pager_hint(bool(value))
958
elif name == "skip_taskbar":
959
if self.window.window:
960
self.window.window.set_skip_taskbar_hint(bool(value))
961
# NOTE: This is the new recommended way of storing options in real-time
962
# (we access the backend through the session here)
963
if self.saving_enabled:
964
o = self.get_option_by_name(name)
966
self.session.backend.save_option(self.id, o.name,
968
self.on_after_set_atribute(name, value)
971
#-----------------------------------------------------------------------
972
# Screenlet's public functions
973
#-----------------------------------------------------------------------
975
def check_requirements (self):
976
'''Checks if required DEB packages are installed'''
981
# operators=['>', '=', '<']
983
commandstr = 'apt-cache policy %s 2>/dev/null | sed -n "2 p" | grep -v ":[ \t]*([a-z \t]*)" | sed -r -e "s/(\s*[^\s]+:\s*)(.*)/\\2/"'
984
for req in self.__requires__:
986
# req = req.replace(' ', '')
987
if req.find('(') != -1:
988
# package version is specified with an operator (no logical operators supported yet!)
990
package = req[:pos].strip()
991
version_str = req[pos+1:]
992
version_str = version_str[:version_str.find(')')]
993
while version_str.find(' ') != -1:
994
version_str = req.replace(' ', ' ')
995
res = version_str.split(' ')
999
# when only package name is specified
1001
# version of the deb package if unspecified
1004
installed_version = os.popen(commandstr % package).readline().replace('\n', '')
1006
if len(installed_version) < 1:
1007
req_feedback += _("\n%(package)s %(version)s required, NOT INSTALLED!") % {"package":package, "version":version}
1010
req_feedback += _("\n%(package)s %(version)s installed, req %(required)s.") % {"package":package, "version":installed_version, "required":version}
1011
# will fail only if dpkg says that version is too old
1012
# otherwise it's responsibility of developer to provide
1013
# correct version id and operator (won't detect problems with these)
1014
if operator is not None:
1015
comp_command = "dpkg --compare-versions \"" + installed_version + "\" \"" + operator + "\" \"" + version + "\""
1016
# print comp_command
1017
if subprocess.call(comp_command, shell=True) != 0:
1020
screenlets.show_message (self,_("Requirements for the Screenlet are not satisfied! Use the package manager of your system to install required packages.\n\nREQUIREMENTS:\n%s") % req_feedback, "Requirements not satisfied")
1022
def add_default_menuitems (self, flags=DefaultMenuItem.STANDARD):
1023
"""Appends the default menu-items to self.menu. You can add on OR'ed
1024
flag with DefaultMenuItems you want to add."""
1025
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'
1029
# children already exist? add separator
1030
if len(menu.get_children()) > 0:
1031
self.add_menuitem("", "-")
1033
if flags & DefaultMenuItem.XML:
1034
# create XML-menu from screenletpath/menu.xml
1035
xfile = self.get_screenlet_dir() + "/menu.xml"
1036
xmlmenu = screenlets.menu.create_menu_from_file(xfile,
1037
self.menuitem_callback)
1040
# add size-selection
1041
if flags & DefaultMenuItem.SIZE:
1042
size_item = gtk.MenuItem(_("Size"))
1044
size_menu = gtk.Menu()
1045
menu.append(size_item)
1046
size_item.set_submenu(size_menu)
1047
#for i in xrange(10):
1048
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):
1049
s = str(int(i * 100))
1050
item = gtk.MenuItem(s + " %")
1051
item.connect("activate", self.menuitem_callback,
1054
size_menu.append(item)
1055
# create theme-selection menu
1056
if flags & DefaultMenuItem.THEMES:
1057
themes_item = gtk.MenuItem(_("Theme"))
1059
themes_menu = gtk.Menu()
1060
menu.append(themes_item)
1061
themes_item.set_submenu(themes_menu)
1062
# create theme-list from theme-directory
1063
lst = self.get_available_themes()
1065
item = gtk.MenuItem(tname)
1066
item.connect("activate", self.menuitem_callback, "theme:"+tname)
1068
themes_menu.append(item)
1070
# add window-options menu
1071
if flags & DefaultMenuItem.WINDOW_MENU:
1072
winmenu_item = gtk.MenuItem(_("Window"))
1074
winmenu_menu = gtk.Menu()
1075
menu.append(winmenu_item)
1076
winmenu_item.set_submenu(winmenu_menu)
1077
# add "lock"-menuitem
1078
self.__mi_lock = item = gtk.CheckMenuItem(_("Lock"))
1079
item.set_active(self.lock_position)
1080
item.connect("activate", self.menuitem_callback,
1083
winmenu_menu.append(item)
1084
# add "Sticky"-menuitem
1085
self.__mi_sticky = item = gtk.CheckMenuItem(_("Sticky"))
1086
item.set_active(self.is_sticky)
1087
item.connect("activate", self.menuitem_callback,
1090
winmenu_menu.append(item)
1091
# add "Widget"-menuitem
1092
self.__mi_widget = item = gtk.CheckMenuItem(_("Widget"))
1093
item.set_active(self.is_widget)
1094
item.connect("activate", self.menuitem_callback,
1097
winmenu_menu.append(item)
1098
# add "Keep above"-menuitem
1099
self.__mi_keep_above = item = gtk.CheckMenuItem(_("Keep above"))
1100
item.set_active(self.keep_above)
1101
item.connect("activate", self.menuitem_callback,
1102
"option:keep_above")
1104
winmenu_menu.append(item)
1105
# add "Keep Below"-menuitem
1106
self.__mi_keep_below = item = gtk.CheckMenuItem(_("Keep below"))
1107
item.set_active(self.keep_below)
1108
item.connect("activate", self.menuitem_callback,
1109
"option:keep_below")
1111
winmenu_menu.append(item)
1114
if flags & DefaultMenuItem.PROPERTIES:
1115
add_menuitem(menu, "-", self.menuitem_callback, "")
1116
add_menuitem(menu, _("Properties..."), self.menuitem_callback, "options")
1118
if flags & DefaultMenuItem.INFO:
1119
add_menuitem(menu, _("Info..."), self.menuitem_callback, "info")
1121
if flags & DefaultMenuItem.ADD:
1122
add_menuitem(menu, "-", self.menuitem_callback, "")
1123
add_menuitem(menu, _("Add one more %s") % self.get_short_name(), self.menuitem_callback, "add")
1125
if flags & DefaultMenuItem.DELETE:
1126
add_menuitem(menu, _("Delete this %s") % self.get_short_name(), self.menuitem_callback, "delete")
1128
if flags & DefaultMenuItem.QUIT:
1129
add_menuitem(menu, "-", self.menuitem_callback, "")
1130
add_menuitem(menu, _("Quit this %s") % self.get_short_name(), self.menuitem_callback, "quit_instance")
1132
if flags & DefaultMenuItem.QUIT_ALL:
1133
add_menuitem(menu, _("Quit all %ss") % self.get_short_name(), self.menuitem_callback, "quit")
1135
def add_menuitem (self, id, label, callback=None):
1136
"""Simple way to add menuitems to a right-click menu.
1137
This function wraps screenlets.menu.add_menuitem.
1138
For backwards compatibility, the order of the parameters
1139
to this function is switched."""
1140
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'
1141
if callback is None:
1142
callback = self.menuitem_callback
1143
# call menu.add_menuitem
1144
return add_menuitem(self.menu, label, callback, id)
1146
def add_submenuitem (self, id, label, lst, callback=None):
1147
"""Simple way to add submenuitems to the right-click menu through a list."""
1148
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'
1150
submenu = gtk.MenuItem(label)
1152
sub_menu = gtk.Menu()
1153
self.menu.append(submenu)
1154
submenu.set_submenu(sub_menu)
1155
# create theme-list from theme-directory
1158
item = gtk.MenuItem(tname)
1159
item.connect("activate", self.menuitem_callback,
1162
sub_menu.append(item)
1168
def load_buttons(self, event):
1169
self.closeb = self.gtk_icon_theme.load_icon ("gtk-close", 16, 0)
1170
self.prop = self.gtk_icon_theme.load_icon ("gtk-properties", 16, 0)
1172
def create_buttons(self):
1174
ctx = self.window.window.cairo_create()
1176
#ctx.set_source_rgba(0.5,0.5,0.5,0.6)
1177
#self.theme.draw_rounded_rectangle(ctx,(self.width*self.scale)-36,0,5,36,16)
1178
#close = theme1.load_icon ("gtk-close", 16, 0)
1179
#prop = theme1.load_icon ("gtk-properties", 16, 0)
1180
#zoom1 = theme1.load_icon ("gtk-zoom-in", 16, 0)
1181
#zoom2 = theme1.load_icon ("gtk-zoom-out", 16, 0)
1182
#close = gtk.image_new_from_stock(gtk.STOCK_CLOSE, 16)
1183
ctx.translate((self.width*self.scale)-16,0)
1184
ctx.set_source_pixbuf(self.closeb, 0, 0)
1188
ctx.translate((self.width*self.scale)-32,0)
1189
ctx.set_source_pixbuf(self.prop, 0, 0)
1193
def clear_cairo_context (self, ctx):
1194
"""Fills the given cairo.Context with fully transparent white."""
1196
ctx.set_source_rgba(1, 1, 1, 0)
1197
ctx.set_operator (cairo.OPERATOR_SOURCE)
1202
"""Close this Screenlet
1203
TODO: send close-notify instead of destroying window?"""
1204
#self.save_settings()
1206
self.window.destroy()
1207
#self.window.event(gtk.gdk.Event(gtk.gdk.DELETE))
1209
def create_drag_icon (self):
1210
"""Create drag-icon and -mask for drag-operation. Returns a 2-tuple
1211
with the icon and the mask. To supply your own icon you can use the
1212
on_create_drag_icon-handler and return the icon/mask as 2-tuple."""
1215
icon, mask = self.on_create_drag_icon()
1218
icon = gtk.gdk.Pixmap(self.window.window, w, h)
1219
ctx = icon.cairo_create()
1220
self.clear_cairo_context(ctx)
1224
mask = gtk.gdk.Pixmap(self.window.window, w, h)
1225
ctx = mask.cairo_create()
1226
self.clear_cairo_context(ctx)
1227
self.on_draw_shape(ctx)
1230
def enable_saving (self, enabled=True):
1231
"""Enable/Disable realtime-saving of options."""
1232
self.saving_enabled = enabled
1234
def find_theme (self, name):
1235
"""Find the best occurence of a theme and return its global path."""
1236
sn = self.get_short_name()
1237
utils.refresh_available_screenlet_paths()
1238
for p in SCREENLETS_PATH:
1239
fpath = p + '/' + sn + '/themes/' + name
1240
if os.path.isdir(fpath):
1244
def get_short_name (self):
1245
"""Return the short name of this screenlet. This returns the classname
1246
of the screenlet without trailing "Screenlet". Please always use
1247
this function if you want to retrieve the short name of a Screenlet."""
1248
return self.__class__.__name__[:-9]
1250
def get_screenlet_dir (self):
1251
"""Return the name of this screenlet's personal directory."""
1252
p = utils.find_first_screenlet_path(self.get_short_name())
1256
if self.__path__ != '':
1257
return self.__path__
1261
def get_theme_dir (self):
1262
"""Return the name of this screenlet's personal theme-dir.
1263
(Only returns the dir under the screenlet's location"""
1264
return self.get_screenlet_dir() + "/themes/"
1266
def get_available_themes (self):
1267
"""Returns a list with the names of all available themes in this
1268
Screenlet's theme-directories."""
1270
utils.refresh_available_screenlet_paths()
1271
for p in SCREENLETS_PATH:
1272
d = p + '/' + self.get_short_name() + '/themes/'
1273
if os.path.isdir(d):
1274
#dirname = self.get_theme_dir()
1275
dirlst = glob.glob(d + '*')
1278
for fname in dirlst:
1279
if os.path.isdir(fname):
1280
dname = fname[tdlen:]
1281
if not dname in lst:
1286
self.window.present()
1287
self.has_started = True
1288
self.is_dragged = False
1289
self.keep_above= self.keep_above
1290
self.keep_below= self.keep_below
1291
self.skip_taskbar = self.skip_taskbar
1292
self.window.set_skip_taskbar_hint(self.skip_taskbar)
1293
self.window.set_keep_above(self.keep_above)
1294
self.window.set_keep_below(self.keep_below)
1296
self.set_is_widget(True)
1297
self.has_focus = False
1299
def finish_loading(self):
1300
"""Called when screenlet finishes loading"""
1303
self.window.present()
1306
# the keep above and keep bellow must be reset after the window is shown this is absolutly necessary
1308
self.window.move(self.x, self.y)
1310
if DEBIAN and not self.ignore_requirements:
1311
self.check_requirements()
1314
self.has_started = True
1315
self.is_dragged = False
1316
self.keep_above= self.keep_above
1317
self.keep_below= self.keep_below
1318
self.is_sticky = self.is_sticky
1319
self.skip_taskbar = self.skip_taskbar
1320
self.window.set_skip_taskbar_hint(self.skip_taskbar)
1321
self.window.set_keep_above(self.keep_above)
1322
self.window.set_keep_below(self.keep_below)
1326
self.set_is_widget(True)
1327
self.has_focus = False
1328
ini = utils.IniReader()
1329
if ini.load (os.environ['HOME'] + '/.screenlets' + '/config.ini') and self.first_run:
1331
if ini.get_option('Lock', section='Options') == 'True':
1332
self.lock_position = True
1333
elif ini.get_option('Lock', section='Options') == 'False':
1334
self.lock_position = False
1335
if ini.get_option('Sticky', section='Options') == 'True':
1336
self.is_sticky = True
1337
elif ini.get_option('Sticky', section='Options') == 'False':
1338
self.is_sticky = False
1339
if ini.get_option('Widget', section='Options') == 'True':
1340
self.is_widget = True
1341
elif ini.get_option('Widget', section='Options') == 'False':
1342
self.is_widget = False
1343
if ini.get_option('Keep_above', section='Options') == 'True':
1344
self.keep_above = True
1345
elif ini.get_option('Keep_above', section='Options') == 'False':
1346
self.keep_above = False
1347
if ini.get_option('Keep_below', section='Options') == 'True':
1348
self.keep_below = True
1349
elif ini.get_option('Keep_below', section='Options') == 'False':
1350
self.keep_below = False
1351
if ini.get_option('draw_buttons', section='Options') == 'True':
1352
self.draw_buttons = True
1353
elif ini.get_option('draw_buttons', section='Options') == 'False':
1354
self.draw_buttons = False
1357
"""Hides this Screenlet's underlying gtk.Window"""
1362
# NOTE: load_theme does NOT call redraw_canvas and update_shape!!!!!
1363
# To do all in one, set attribute self.theme_name instead
1364
def load_theme (self, path):
1365
"""Load a theme for this Screenlet from the given path. NOTE:
1366
load_theme does NOT call redraw_canvas and update_shape!!!!! To do all
1367
in one call, set the attribute self.theme_name instead."""
1371
self.theme = ScreenletTheme(path)
1373
if self.theme.loaded == False:
1374
print "Error while loading theme: " + path
1377
# call user-defined handler
1378
self.on_load_theme()
1379
# if override options is allowed, apply them
1380
if self.allow_option_override:
1381
if self.theme.has_overrides():
1382
if self.ask_on_option_override==True and \
1384
_('This theme wants to override your settings for this Screenlet. Do you want to allow that?')) == False:
1386
self.theme.apply_option_overrides(self)
1390
"""If the Screenlet runs as stand-alone app, starts gtk.main()"""
1393
def register_service (self, service_classobj):
1394
"""Register or create the given ScreenletService-(sub)class as the new
1395
service for this Screenlet. If self is not the first instance in the
1396
current session, the service from the first instance will be used
1397
instead and no new service is created."""
1399
if len(self.session.instances) == 0:
1400
# if it is the basic service, add name to call
1401
if service_classobj==services.ScreenletService:#BUG
1402
self.service = service_classobj(self, self.get_short_name())
1404
# else only pass this screenlet
1405
self.service = service_classobj(self)
1407
self.service = self.session.instances[0].service
1408
# TODO: throw exception??
1412
def set_is_widget (self, value):
1413
"""Set this window to be treated as a Widget (only supported by
1414
compiz using the widget-plugin yet)"""
1416
# set window type to utility
1417
#self.window.window.set_type_hint(
1418
# gtk.gdk.WINDOW_TYPE_HINT_UTILITY)
1419
# set _compiz_widget-property on window
1420
self.window.window.property_change("_COMPIZ_WIDGET",
1421
gtk.gdk.SELECTION_TYPE_WINDOW,
1422
32, gtk.gdk.PROP_MODE_REPLACE, (True,))
1424
# set window type to normal
1425
#self.window.window.set_type_hint(
1426
# gtk.gdk.WINDOW_TYPE_HINT_NORMAL)
1427
# set _compiz_widget-property
1428
self.window.window.property_delete("_COMPIZ_WIDGET")
1430
self.on_switch_widget_state(value)
1433
"""Show this Screenlet's underlying gtk.Window"""
1435
self.window.move(self.x, self.y)
1438
def show_settings_dialog (self):
1439
"""Show the EditableSettingsDialog for this Screenlet."""
1440
se = OptionsDialog(490, 450)
1443
d = self.get_screenlet_dir()
1444
if os.path.isfile(d + '/icon.svg'):
1445
icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.svg')
1446
elif os.path.isfile(d + '/icon.png'):
1447
icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.png')
1448
img.set_from_pixbuf(icn)
1450
img.set_from_stock(gtk.STOCK_PROPERTIES, 5)
1451
se.set_title(self.__name__)
1452
se.set_info(self.__name__, glib.markup_escape_text(self.__desc__), '(c) ' + glib.markup_escape_text(self.__author__),
1453
version='v' + self.__version__, icon=img)
1454
se.show_options_for_object(self)
1456
if resp == gtk.RESPONSE_REJECT: # TODO!!!!!
1457
se.reset_to_defaults()
1462
def redraw_canvas (self):
1463
"""Redraw the entire Screenlet's window area.
1464
TODO: store window alloaction in class and change when size changes."""
1465
# if updates are disabled, just exit
1466
if self.disable_updates:
1469
x, y, w, h = self.window.get_allocation()
1470
rect = gtk.gdk.Rectangle(x, y, w, h)
1471
if self.window.window:
1472
self.window.window.invalidate_rect(rect, True)
1473
self.window.window.process_updates(True)
1474
# if self.has_focus and self.draw_buttons and self.show_buttons:
1475
# self.create_buttons()
1478
def redraw_canvas_area (self, x, y, width, height):
1479
"""Redraw the given Rectangle (x, y, width, height) within the
1480
current Screenlet's window."""
1481
# if updates are disabled, just exit
1482
if self.disable_updates:
1485
rect = gtk.gdk.Rectangle(x, y, width, height)
1486
if self.window.window:
1487
self.window.window.invalidate_rect(rect, True)
1488
self.window.window.process_updates(True)
1490
def remove_shape(self):
1491
"""Removed shaped window , in case the nom composited shape has been set"""
1492
if self.window.window:
1493
self.window.window.shape_combine_mask(None,0,0)
1495
w = self.window.allocation.width
1496
h = self.window.allocation.height
1498
# if 0 return to avoid crashing
1499
if w==0 or h==0: return False
1500
# if size changed, recreate shape bitmap
1501
if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
1502
self.__shape_bitmap = screenlets.create_empty_bitmap(w, h)
1503
self.__shape_bitmap_width = w
1504
self.__shape_bitmap_height = h
1507
ctx = self.__shape_bitmap.cairo_create()
1508
self.clear_cairo_context(ctx)
1510
# shape the window acording if the window is composited or not
1511
if self.window.is_composited():
1512
# log.debug(_("Updating input shape"))
1513
self.on_draw_shape(ctx)
1514
self.main_view.set_shape(self.__shape_bitmap, True)
1517
self.on_draw_shape(ctx)
1520
# log.debug(_("Updating window shape"))
1521
self.main_view.set_shape(self.__shape_bitmap, False)
1523
def update_shape (self):
1524
"""Update window shape (only call this when shape has changed
1525
because it is very ressource intense if ran too often)."""
1526
# if updates are disabled, just exit
1527
if self.disable_updates:
1529
#print "UPDATING SHAPE"
1531
#if not self.window.is_composited():
1532
# self.update_shape_non_composited()
1533
# calculate new width/height of shape bitmap
1534
w = int(self.width * self.scale)
1535
h = int(self.height * self.scale)
1536
# if 0 set it to 100 to avoid crashes and stay interactive
1539
# if size changed, recreate shape bitmap
1540
if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
1541
data = ''.zfill(w*h)
1542
self.__shape_bitmap = gtk.gdk.bitmap_create_from_data(None, data,
1544
self.__shape_bitmap_width = w
1545
self.__shape_bitmap_height = h
1546
# create context and draw shape
1547
ctx = self.__shape_bitmap.cairo_create()
1548
self.clear_cairo_context(ctx) #TEST
1549
if self.has_focus and self.draw_buttons and self.show_buttons:
1551
#theme1 = gtk.icon_theme_get_default()
1552
#ctx.set_source_rgba(0.5,0.5,0.5,0.6)
1553
#self.theme.draw_rounded_rectangle(ctx,(self.width*self.scale)-36,0,5,36,16)
1554
#close = theme1.load_icon ("gtk-close", 16, 0)
1555
#prop = theme1.load_icon ("gtk-properties", 16, 0)
1556
#zoom1 = theme1.load_icon ("gtk-zoom-in", 16, 0)
1557
#zoom2 = theme1.load_icon ("gtk-zoom-out", 16, 0)
1558
#close = gtk.image_new_from_stock(gtk.STOCK_CLOSE, 16)
1559
ctx.translate((self.width*self.scale)-16,0)
1560
ctx.set_source_pixbuf(self.closeb, 0, 0)
1564
ctx.translate((self.width*self.scale)-32,0)
1565
ctx.set_source_pixbuf(self.prop, 0, 0)
1568
# shape the window acording if the window is composited or not
1570
if self.window.is_composited():
1572
self.on_draw_shape(ctx)
1573
# and cut window with mask
1574
self.window.input_shape_combine_mask(self.__shape_bitmap, 0, 0)
1576
try: self.on_draw(ctx) #Works better then the shape method on non composited windows
1577
except: self.on_draw_shape(ctx) # if error on on_draw use standard shape method
1578
# and cut window with mask
1579
self.window.shape_combine_mask(self.__shape_bitmap,0,0)
1580
self.on_update_shape()
1582
def update_shape_non_composited (self):
1583
"""TEST: This function is intended to shape the window whenever no
1584
composited environment can be found. (NOT WORKING YET!!!!)"""
1585
#pixbuf = gtk.gdk.GdkPixbuf.new_from_file)
1586
# calculate new width/height of shape bitmap
1587
w = int(self.width * self.scale)
1588
h = int(self.height * self.scale)
1589
# if 0 set it to 100 to avoid crashes and stay interactive
1592
# if size changed, recreate shape bitmap
1593
if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height:
1594
data = ''.zfill(w*h)
1595
self.__shape_bitmap = gtk.gdk.pixbuf_new_from_data(data,
1596
gtk.gdk.COLORSPACE_RGB, True, 1, w, h, w)
1597
self.__shape_bitmap_width = w
1598
self.__shape_bitmap_height = h
1599
# and render window contents to it
1601
if self.__shape_bitmap:
1603
(pixmap,mask) = self.__shape_bitmap.render_pixmap_and_mask(255)
1604
# apply new mask to window
1605
self.window.shape_combine_mask(mask)
1607
def redraw_canvas_and_update_shape (self):
1608
self.redraw_canvas()
1611
# ----------------------------------------------------------------------
1612
# Screenlet's event-handler dummies
1613
# ----------------------------------------------------------------------
1615
def on_delete (self):
1616
"""Called when the Screenlet gets deleted. Return True to cancel.
1617
TODO: sometimes not properly called"""
1618
return not show_question(self, _("To quit all %s's, use 'Quit' instead. ") % self.__class__.__name__ +\
1619
_('Really delete this %s and its settings?') % self.get_short_name())
1620
"""return not show_question(self, 'Deleting this instance of the '+\
1621
self.__name__ + ' will also delete all your personal '+\
1622
'changes you made to it!! If you just want to close the '+\
1623
'application, use "Quit" instead. Are you sure you want to '+\
1624
'delete this instance?')
1630
def on_after_set_atribute(self,name, value):
1631
"""Called after setting screenlet atributes"""
1634
def on_before_set_atribute(self,name, value):
1635
"""Called before setting screenlet atributes"""
1639
def on_create_drag_icon (self):
1640
"""Called when the screenlet's drag-icon is created. You can supply
1641
your own icon and mask by returning them as a 2-tuple."""
1645
"""Called when screenlet was mapped"""
1649
"""Called when screenlet was unmapped"""
1652
def on_composite_changed(self):
1653
"""Called when composite state has changed"""
1657
def on_drag_begin (self, drag_context):
1658
"""Called when the Screenlet gets dragged."""
1661
def on_drag_enter (self, drag_context, x, y, timestamp):
1662
"""Called when something gets dragged into the Screenlets area."""
1665
def on_drag_leave (self, drag_context, timestamp):
1666
"""Called when something gets dragged out of the Screenlets area."""
1669
def on_draw (self, ctx):
1670
"""Callback for drawing the Screenlet's window - override
1671
in subclasses to implement your own drawing."""
1674
def on_draw_shape (self, ctx):
1675
"""Callback for drawing the Screenlet's shape - override
1676
in subclasses to draw the window's input-shape-mask."""
1679
def on_drop (self, x, y, sel_data, timestamp):
1680
"""Called when a selection is dropped on this Screenlet."""
1683
def on_focus (self, event):
1684
"""Called when the Screenlet's window receives focus."""
1688
"""Called when the Screenlet gets hidden."""
1692
"""Called when the Screenlet's options have been applied and the
1693
screenlet finished its initialization. If you want to have your
1694
Screenlet do things on startup you should use this handler."""
1697
def on_key_down (self, keycode, keyvalue, event=None):
1698
"""Called when a key is pressed within the screenlet's window."""
1701
def on_load_theme (self):
1702
"""Called when the theme is reloaded (after loading, before redraw)."""
1705
def on_menuitem_select (self, id):
1706
"""Called when a menuitem is selected."""
1709
def on_mouse_down (self, event):
1710
"""Called when a buttonpress-event occured in Screenlet's window.
1711
Returning True causes the event to be not further propagated."""
1714
def on_mouse_enter (self, event):
1715
"""Called when the mouse enters the Screenlet's window."""
1718
def on_mouse_leave (self, event):
1719
"""Called when the mouse leaves the Screenlet's window."""
1722
def on_mouse_move(self, event):
1723
"""Called when the mouse moves in the Screenlet's window."""
1726
def on_mouse_up (self, event):
1727
"""Called when a buttonrelease-event occured in Screenlet's window.
1728
Returning True causes the event to be not further propagated."""
1732
"""Callback for handling destroy-event. Perform your cleanup here!"""
1735
def on_realize (self):
1736
""""Callback for handling the realize-event."""
1738
def on_scale (self):
1739
"""Called when Screenlet.scale is changed."""
1742
def on_scroll_up (self):
1743
"""Called when mousewheel is scrolled up (button4)."""
1746
def on_scroll_down (self):
1747
"""Called when mousewheel is scrolled down (button5)."""
1751
"""Called when the Screenlet gets shown after being hidden."""
1754
def on_switch_widget_state (self, state):
1755
"""Called when the Screenlet enters/leaves "Widget"-state."""
1758
def on_unfocus (self, event):
1759
"""Called when the Screenlet's window loses focus."""
1762
def on_update_shape(self):
1763
"""Called when the Screenlet's window is updating shape"""
1765
# ----------------------------------------------------------------------
1766
# Screenlet's event-handlers for GTK-events
1767
# ----------------------------------------------------------------------
1769
def alpha_screen_changed (self, window, screen=None):
1770
"""set colormap for window"""
1772
screen = window.get_screen()
1773
map = screen.get_rgba_colormap()
1777
map = screen.get_rgb_colormap()
1778
window.set_colormap(map)
1780
def button_press (self, widget, event):
1782
#print "Button press"
1783
# set flags for user-handler
1786
# call user-handler for onmousedownbegin_move_drag
1787
if self.on_mouse_down(event) == True:
1789
# unhandled? continue
1791
if self.mousex >= self.width - (32/self.scale) and self.mousey <= (16/self.scale) and self.draw_buttons and self.show_buttons and self.has_focus:
1792
if self.mousex >= self.width - (16/self.scale):
1793
self.menuitem_callback(widget,'quit_instance')
1794
elif self.mousex <= self.width -(16/self.scale):
1795
self.menuitem_callback(widget,'info')
1796
elif self.lock_position == False:
1797
if event.button == 1:
1798
self.is_dragged = True
1799
widget.begin_move_drag(event.button, int(event.x_root),
1800
int(event.y_root), event.time)
1802
if event.button == 3:
1804
self.__mi_lock.set_active(self.lock_position)
1805
self.__mi_sticky.set_active(self.is_sticky)
1806
self.__mi_widget.set_active(self.is_widget)
1807
self.__mi_keep_above.set_active(self.keep_above)
1808
self.__mi_keep_below.set_active(self.keep_below)
1810
self.menu.popup(None, None, None, event.button, event.time)
1811
#elif event.button == 4:
1812
# print "MOUSEWHEEL"
1814
#elif event.button == 5:
1815
# print "MOUSEWHEEL"
1819
def button_release (self, widget, event):
1820
print "Button release"
1822
self.focus_in_event(self, None)
1823
self.is_dragged = False # doesn't work!!! we don't get an event when move_drag ends :( ...
1824
if self.on_mouse_up(event):
1828
def composite_changed(self,widget):
1829
#this handle is called when composition changed
1830
self.remove_shape() # removing previous set shape , this is absolutly necessary
1831
self.window.hide() # hiding the window and showing it again so the window can convert to the right composited state
1832
self.is_sticky = self.is_sticky #changing from non composited to composited makes the screenlets loose sticky state , this fixes that
1833
self.keep_above= self.keep_above
1834
self.keep_below= self.keep_below
1836
#print 'Compositing method changed to %s' % str(self.window.is_composited())
1838
self.redraw_canvas()
1840
if not self.window.is_composited () :
1841
self.show_buttons = False
1842
self.disable_option("opacity")
1843
# print 'Warning - Buttons will not be shown until screenlet is restarted'
1845
if self.window.is_composited () :
1846
self.enable_option("opacity")
1848
self.is_sticky = self.is_sticky #and again ...
1849
self.keep_above= self.keep_above
1850
self.keep_below= self.keep_below
1851
self.window.set_keep_above(self.keep_above)
1852
self.window.set_keep_below(self.keep_below)
1853
self.on_composite_changed()
1855
# NOTE: this should somehow handle the end of a move_drag-operation
1856
def configure_event (self, widget, event):
1857
#print "onConfigure"
1859
#if self.is_dragged == True:
1860
# set new position and cause a save of this Screenlet (not use
1861
# setattr to avoid conflicts with the window.move in __setattr__)
1862
if event.x != self.x:
1863
self.__dict__['x'] = event.x
1865
self.session.backend.save_option(self.id, 'x', str(event.x))
1866
# self.is_dragged = False
1867
if event.y != self.y:
1868
self.__dict__['y'] = event.y
1870
self.session.backend.save_option(self.id, 'y', str(event.y))
1871
# self.is_dragged = False
1874
def delete_event (self, widget, event, data=None):
1876
print "delete_event"
1877
if self.on_delete() == True:
1878
print "Cancel delete_event"
1884
def destroy (self, widget, data=None):
1885
# call user-defined on_quit-handler
1887
#print "destroy signal occurred"
1888
self.emit("screenlet_removed", self)
1890
if self.quit_on_close:
1891
if self.session: # if we have a session, flush current data
1892
self.session.backend.flush()
1895
del self # ??? does this really work???
1897
def drag_begin (self, widget, drag_context):
1899
self.is_dragged = True
1900
self.on_drag_begin(drag_context)
1903
def drag_data_received (self, widget, dc, x, y, sel_data, info, timestamp):
1904
return self.on_drop(x, y, sel_data, timestamp)
1906
def drag_end (self, widget, drag_context):
1908
self.is_dragged = False
1911
def drag_motion (self, widget, drag_context, x, y, timestamp):
1912
#print "Drag motion"
1913
if self.dragging_over == False:
1914
self.dragging_over = True
1915
self.on_drag_enter(drag_context, x, y, timestamp)
1918
def drag_leave (self, widget, drag_context, timestamp):
1919
self.dragging_over = False
1920
self.on_drag_leave(drag_context, timestamp)
1923
def enter_notify_event (self, widget, event):
1924
#self.__mouse_inside = True
1925
self.__dict__['mouse_is_over'] = True
1926
self.on_mouse_enter(event)
1928
#self.redraw_canvas()
1930
def expose (self, widget, event):
1931
ctx = widget.window.cairo_create()
1933
self.clear_cairo_context(ctx)
1934
# set a clip region for the expose event
1935
ctx.rectangle(event.area.x, event.area.y,
1936
event.area.width, event.area.height)
1940
#ctx.scale(self.scale, self.scale)
1941
# call drawing method
1943
if self.show_buttons and self.draw_buttons and self.has_focus:
1944
self.create_buttons()
1945
# and delete context (needed?)
1949
def focus_in_event (self, widget, event):
1950
if self.skip_taskbar==False or self.skip_pager==False or self.is_dragged==True or event is None:
1951
#Screenlet always gets focus after being dragged so this is a good method
1952
#to control the end of a move_drag operation!!!!!
1953
#This code happens on the end of a move_drag
1954
self.is_dragged=False
1955
self.has_focus = True
1956
self.on_focus(event)
1958
self.redraw_canvas()
1963
def focus_out_event (self, widget, event):
1964
if self.is_dragged==False:
1965
self.has_focus = False
1966
self.on_unfocus(event)
1968
self.redraw_canvas()
1972
def key_press (self, widget, event):
1973
"""Handle keypress events, needed for in-place editing."""
1974
self.on_key_down(event.keyval, event.string, event)
1976
def leave_notify_event (self, widget, event):
1977
#self.__mouse_inside = False
1978
#self.is_dragged = False
1979
self.__dict__['mouse_is_over'] = False
1980
self.on_mouse_leave(event)
1982
#self.redraw_canvas()
1984
def menuitem_callback (self, widget, id):
1986
if not self.on_delete():
1988
self.session.delete_instance (self.id)
1989
# notify about being rmeoved (does this get send???)
1990
self.service.instance_removed(self.id)
1991
elif id == "quit_instance":
1992
print 'Quitting current screenlet instance'
1993
self.session.quit_instance (self.id)
1994
self.service.instance_removed(self.id)
1998
self.service.add("")
1999
elif id in ("info", "about", "settings", "options", "properties"):
2000
# show settings dialog
2001
self.show_settings_dialog()
2002
elif id.startswith('scale:'):
2003
self.scale = float(id[6:])
2004
elif id[:5] == "size:": # DEPRECATED??
2005
# set size and update shape (redraw is done by setting height)
2006
#self.__dict__['width'] = int(id[5:])
2007
self.width = int(id[5:])
2008
self.height = int(id[5:])
2010
elif id[:6]=="theme:":
2011
print "Screenlet: Set theme %s" % id[6:]
2013
self.theme_name = id[6:]
2014
elif id[:8] == "setting:":
2015
# set a boolean option to the opposite state
2017
if type(self.__dict__[id[8:]]) == bool:
2018
self.__dict__[id[8:]] = not self.__dict__[id[8:]] # UNSAFE!!
2020
print "Error: Cannot set missing or non-boolean value '"\
2022
elif id[:7] == "option:":
2023
# NOTE: this part should be removed and XML-menus
2024
# should be used by default ... maybe
2027
if self.__mi_lock.get_active () != self.lock_position:
2028
self.lock_position = not self.lock_position
2029
elif id[7:]=="sticky":
2030
if self.__mi_sticky.get_active () != self.is_sticky:
2031
self.is_sticky = not self.is_sticky
2033
elif id[7:]=="widget":
2034
if self.__mi_widget.get_active () != self.is_widget:
2035
self.is_widget = not self.is_widget
2036
elif id[7:]=="keep_above":
2037
if self.__mi_keep_above.get_active () != self.keep_above:
2038
self.keep_above = not self.keep_above
2039
self.__mi_keep_above.set_active(self.keep_above)
2040
if self.keep_below and self.keep_above :
2041
self.keep_below = False
2042
self.__mi_keep_below.set_active(False)
2043
elif id[7:]=="keep_below":
2044
if self.__mi_keep_below.get_active () != self.keep_below:
2045
self.keep_below = not self.keep_below
2046
self.__mi_keep_below.set_active(self.keep_below)
2047
if self.keep_below and self.keep_above :
2048
self.keep_above = False
2049
self.__mi_keep_above.set_active(False)
2051
#print "Item: " + string
2054
self.on_menuitem_select(id)
2057
def map_event(self, widget, event):
2060
def unmap_event(self, widget, event):
2063
def motion_notify_event(self, widget, event):
2064
self.__dict__['mousex'] = event.x / self.scale
2065
self.__dict__['mousey'] = event.y / self.scale
2067
self.on_mouse_move(event)
2069
def realize_event (self, widget):
2070
"""called when window has been realized"""
2071
if self.window.window:
2072
self.window.window.set_back_pixmap(None, False) # needed?
2076
def scroll_event (self, widget, event):
2077
if event.direction == gtk.gdk.SCROLL_UP:
2078
if self.has_focus and self.is_sizable and self.resize_on_scroll: self.scale = self.scale +0.1
2080
elif event.direction == gtk.gdk.SCROLL_DOWN:
2081
if self.has_focus and self.is_sizable and self.resize_on_scroll: self.scale = self.scale -0.1
2082
self.on_scroll_down()
2086
def show_notification (self,text):
2087
"""Show notification window at current mouse position."""
2088
if self.notify == None:
2089
self.notify = Notify()
2090
self.notify.text = text
2093
def hide_notification (self):
2094
"""hide notification window"""
2095
if self.notify != None:
2099
def show_tooltip (self,text,tooltipx,tooltipy):
2100
"""Show tooltip window at current mouse position."""
2101
if self.tooltip == None:
2102
self.tooltip = Tooltip(300, 400)
2103
self.tooltip.text = text
2104
self.tooltip.x = tooltipx
2105
self.tooltip.y = tooltipy
2108
#self.tooltip = Tooltip(300, 400)
2109
self.tooltip.text = text
2110
self.tooltip.x = tooltipx
2111
self.tooltip.y = tooltipy
2112
#self.tooltip.show()
2114
def hide_tooltip (self):
2115
"""hide tooltip window"""
2116
if self.tooltip != None:
2121
class ShapedWidget (gtk.DrawingArea):
2122
"""A simple base-class for creating owner-drawn gtk-widgets"""
2126
mouse_inside = False
2130
def __init__ (self, width, height):
2132
super(ShapedWidget, self).__init__()
2133
# create/setup widget
2134
#self.__widget = gtk.Widget()
2135
self.set_app_paintable(True)
2136
self.set_size_request(width, height)
2138
self.set_events(gtk.gdk.ALL_EVENTS_MASK)
2139
self.connect("expose-event", self.expose_event)
2140
self.connect("button-press-event", self.button_press)
2141
self.connect("button-release-event", self.button_release)
2142
self.connect("enter-notify-event", self.enter_notify)
2143
self.connect("leave-notify-event", self.leave_notify)
2145
# EXPERIMENTAL: TODO: cache bitmap until size changes
2146
def update_shape (self):
2147
"""update widget's shape (only call this when shape has changed)"""
2149
for i in xrange(self.width*self.height):
2151
bitmap = gtk.gdk.bitmap_create_from_data(None,
2152
data, self.width, self.height)
2153
ctx = bitmap.cairo_create()
2154
ctx.set_source_rgba(1, 1, 1, 0)
2155
ctx.set_operator (cairo.OPERATOR_SOURCE)
2157
self.draw_shape(ctx)
2158
self.input_shape_combine_mask(bitmap, 0, 0)
2159
print "Updating shape."
2161
def button_press (self, widget, event):
2163
print "left button pressed!"
2166
def button_release (self, widget, event):
2167
#if event.button==1:
2168
#print "left button release!"
2171
def enter_notify (self, widget, event):
2172
self.mouse_inside = True
2174
#print "mouse enter"
2176
def leave_notify (self, widget, event):
2177
self.mouse_inside = False
2179
#print "mouse leave"
2181
def draw (self, ctx):
2184
def draw_shape (self, ctx):
2187
def expose_event (self, widget, event):
2188
ctx = widget.window.cairo_create()
2189
# set a clip region for the expose event
2190
ctx.rectangle(event.area.x, event.area.y,
2191
event.area.width, event.area.height)
2194
ctx.set_source_rgba(1, 1, 1, 0)
2195
ctx.set_operator (cairo.OPERATOR_SOURCE)
2197
# call drawing method
2199
# and delete context
2203
class Tooltip(object):
2204
"""A window that displays a text and serves as Tooltip (very basic yet)."""
2211
font_name = 'FreeSans 9'
2217
def __init__ (self, width, height):
2218
object.__init__(self)
2220
self.__dict__['width'] = width
2221
self.__dict__['height'] = height
2222
self.window = gtk.Window()
2223
self.window.set_app_paintable(True)
2224
self.window.set_size_request(width, height)
2225
self.window.set_decorated(False)
2226
self.window.set_accept_focus(False)
2227
self.window.set_skip_pager_hint(True)
2228
self.window.set_skip_taskbar_hint(True)
2229
self.window.set_keep_above(True)
2230
self.screen_changed(self.window)
2231
self.window.connect("expose_event", self.expose)
2232
self.window.connect("screen-changed", self.screen_changed)
2234
self.p_context = self.window.get_pango_context()
2235
self.p_layout = pango.Layout(self.p_context)
2236
self.p_layout.set_font_description(\
2237
pango.FontDescription(self.font_name))
2238
#self.p_layout.set_width(-1)
2239
self.p_layout.set_width(width * pango.SCALE - 6)
2241
def __setattr__ (self, name, value):
2242
self.__dict__[name] = value
2243
if name in ('width', 'height', 'text'):
2245
self.p_layout.set_width(width)
2246
elif name == 'text':
2247
self.p_layout.set_markup(value)
2248
ink_rect, logical_rect = self.p_layout.get_pixel_extents()
2249
self.height = min(max(logical_rect[3], 16), 400) + 6
2250
self.window.set_size_request(self.width, self.height)
2251
self.window.queue_draw()
2253
self.window.move(int(value), int(self.y))
2255
self.window.move(int(self.x), int(value))
2258
"""Show the Tooltip window."""
2261
self.window.set_keep_above(True)
2263
def show_delayed (self, delay):
2264
"""Show the Tooltip window after a given delay."""
2266
self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
2269
"""Hide the Tooltip window."""
2271
self.window.destroy()
2273
def cancel_show (self):
2274
"""Cancel showing of the Tooltip."""
2276
gobject.source_remove(self.__timeout)
2277
self.p_context = None
2278
self.p_layout = None
2280
def __show_timeout (self):
2283
def screen_changed (self, window, screen=None):
2285
screen = window.get_screen()
2286
map = screen.get_rgba_colormap()
2288
map = screen.get_rgb_colormap()
2289
window.set_colormap(map)
2291
def expose (self, widget, event):
2292
ctx = self.window.window.cairo_create()
2293
ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ?
2294
# set a clip region for the expose event
2295
ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
2298
ctx.set_source_rgba(1, 1, 1, 0)
2299
ctx.set_operator (cairo.OPERATOR_SOURCE)
2302
ctx.set_source_rgba(1, 1, 0.5, 1)
2303
ctx.rectangle(0, 0, self.width, self.height)
2308
ctx.set_source_rgba(0, 0, 0, 1)
2309
ctx.show_layout(self.p_layout)
2312
ctx.rectangle(0, 0, self.width, self.height)
2313
ctx.set_source_rgba(0, 0, 0, 0.7)
2316
class Notify(object):
2317
"""A window that displays a text and serves as Notification (very basic yet)."""
2324
font_name = 'FreeSans 9'
2329
gradient = cairo.LinearGradient(0, 100,0, 0)
2331
def __init__ (self):
2332
object.__init__(self)
2334
self.window = gtk.Window()
2335
self.window.set_app_paintable(True)
2336
self.window.set_size_request(self.width, self.height)
2337
self.window.set_decorated(False)
2338
self.window.set_accept_focus(False)
2339
self.window.set_skip_pager_hint(True)
2340
self.window.set_skip_taskbar_hint(True)
2341
self.window.set_keep_above(True)
2342
self.screen_changed(self.window)
2343
self.window.connect("expose_event", self.expose)
2344
self.window.connect("screen-changed", self.screen_changed)
2346
self.p_context = self.window.get_pango_context()
2347
self.p_layout = pango.Layout(self.p_context)
2348
self.p_layout.set_font_description(\
2349
pango.FontDescription(self.font_name))
2350
#self.p_layout.set_width(-1)
2351
self.p_layout.set_width(self.width * pango.SCALE - 6)
2353
def __setattr__ (self, name, value):
2354
self.__dict__[name] = value
2355
if name in ('text'):
2357
self.p_layout.set_markup(value)
2358
ink_rect, logical_rect = self.p_layout.get_pixel_extents()
2359
self.window.queue_draw()
2362
"""Show the Notify window."""
2363
self.window.move(gtk.gdk.screen_width() - self.width, gtk.gdk.screen_height() - self.height)
2366
self.window.set_keep_above(True)
2368
def show_delayed (self, delay):
2369
"""Show the Notify window after a given delay."""
2371
self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
2374
"""Hide the Notify window."""
2376
self.window.destroy()
2378
def cancel_show (self):
2379
"""Cancel showing of the Notify."""
2381
gobject.source_remove(self.__timeout)
2382
self.p_context = None
2383
self.p_layout = None
2385
def __show_timeout (self):
2388
def screen_changed (self, window, screen=None):
2390
screen = window.get_screen()
2391
map = screen.get_rgba_colormap()
2393
map = screen.get_rgb_colormap()
2394
window.set_colormap(map)
2396
def expose (self, widget, event):
2397
ctx = self.window.window.cairo_create()
2398
ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ?
2399
# set a clip region for the expose event
2400
ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height)
2403
ctx.set_source_rgba(1, 1, 1, 0)
2404
ctx.set_operator (cairo.OPERATOR_SOURCE)
2407
self.gradient.add_color_stop_rgba(1,0.3, 0.3, 0.3, 0.9)
2408
self.gradient.add_color_stop_rgba(0.3, 0, 0, 0, 0.9)
2409
ctx.set_source(self.gradient)
2410
ctx.rectangle(0, 0, self.width, self.height)
2415
ctx.set_source_rgba(1, 1, 1, 1)
2416
ctx.show_layout(self.p_layout)
2419
ctx.rectangle(0, 0, self.width, self.height)
2420
ctx.set_source_rgba(0, 0, 0, 0.7)
2423
# TEST (as the name implies)
2424
"""class TestWidget(ShapedWidget):
2426
def __init__(self, width, height):
2427
#ShapedWidget.__init__(self, width, height)
2428
super(TestWidget, self).__init__(width, height)
2430
def draw(self, ctx):
2431
if self.mouse_inside:
2432
ctx.set_source_rgba(1, 0, 0, 0.8)
2434
ctx.set_source_rgba(1, 1, 0, 0.8)
2435
ctx.rectangle(0, 0, 32, 32)
2440
# ------------------------------------------------------------------------------
2442
# ------------------------------------------------------------------------------
2444
# the new recommended way of launching a screenlet from the "outside"
2445
def launch_screenlet (name, debug=False):
2446
"""Launch a screenlet, either through its service or by launching a new
2447
process of the given screenlet. Name has to be the name of the Screenlet's
2448
class without trailing 'Screenlet'.
2449
NOTE: we could only launch the file here"""
2451
if services.service_is_running(name):
2452
# add screenlet through service, if running
2453
srvc = services.get_service_by_name(name)
2456
srvc.add('') # empty string for auto-creating ID
2458
except Exception, ex:
2459
print "Error while adding instance by service: %s" % ex
2460
# service not running or error? launch screenlet's file
2461
path = utils.find_first_screenlet_path(name)
2463
# get full path of screenlet's file
2464
slfile = path + '/' + name + 'Screenlet.py'
2465
# launch screenlet as separate process
2466
print "Launching Screenlet from: %s" % slfile
2468
print "Logging output goes to: $HOME/.config/Screenlets/%sScreenlet.log" % name
2469
out = '$HOME/.config/Screenlets/%sScreenlet.log' % name
2472
os.system('python -u %s > %s &' % (slfile, out))
2475
print "Screenlet '%s' could not be launched." % name
2478
def show_message (screenlet, message, title=''):
2479
"""Show a message for the given Screenlet (may contain Pango-Markup).
2480
If screenlet is None, this function can be used by other objects as well."""
2481
if screenlet == None:
2482
md = gtk.MessageDialog(None, type=gtk.MESSAGE_INFO,
2483
buttons=gtk.BUTTONS_OK)
2486
md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_INFO,
2487
buttons=gtk.BUTTONS_OK)
2488
md.set_title(screenlet.__name__)
2489
md.set_markup(message)
2493
def show_question (screenlet, message, title=''):
2494
"""Show a question for the given Screenlet (may contain Pango-Markup)."""
2495
if screenlet == None:
2496
md = gtk.MessageDialog(None, type=gtk.MESSAGE_QUESTION,
2497
buttons=gtk.BUTTONS_YES_NO)
2500
md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_QUESTION,
2501
buttons=gtk.BUTTONS_YES_NO)
2502
md.set_title(screenlet.__name__)
2503
md.set_markup(message)
2506
if response == gtk.RESPONSE_YES:
2510
def show_error (screenlet, message, title='Error'):
2511
"""Show an error for the given Screenlet (may contain Pango-Markup)."""
2512
if screenlet == None:
2513
md = gtk.MessageDialog(None, type=gtk.MESSAGE_ERROR,
2514
buttons=gtk.BUTTONS_OK)
2517
md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_ERROR,
2518
buttons=gtk.BUTTONS_OK)
2519
md.set_title(screenlet.__name__)
2520
md.set_markup(message)
2524
def fatal_error (message):
2525
"""Raise a fatal error to stdout and stderr and exit with an errorcode."""
2527
msg = 'FATAL ERROR: %s\n' % message
2528
sys.stdout.write(msg)
2529
sys.stderr.write(msg)
2532
# LEGACY support: functions that are not used any longer (raise fatal error)
2534
def create_new_instance (name):
2535
fatal_error("This screenlet seems to be written for an older version of the framework. Please download a newer version of the %s." % name)