Package screenlets
[hide private]
[frames] | no frames]

Source Code for Package screenlets

   1  # This application is released under the GNU General Public License  
   2  # v3 (or, at your option, any later version). You can find the full  
   3  # text of the license under http://www.gnu.org/licenses/gpl.txt.  
   4  # By using, editing and/or distributing this software you agree to  
   5  # the terms and conditions of this license.  
   6  # Thank you for using free software! 
   7   
   8  # Screenlets main module (c) RYX (aka Rico Pfaus) 2007 <ryx@ryxperience.com> ,  
   9  # Whise aka Helder Fraga <helder.fraga@hotmail.com> 
  10  # 
  11  ##@mainpage 
  12  # 
  13  ##@section intro_sec General Information 
  14  # 
  15  # INFO: 
  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. 
  22  # 
  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) 
  29  # - 
  30  # 
  31   
  32  try: 
  33          INSTALL_PREFIX = open("/etc/screenlets/prefix").read()[:-1]  
  34  except: 
  35          INSTALL_PREFIX = '/usr' 
  36   
  37  import pygtk 
  38  pygtk.require('2.0') 
  39  import gtk 
  40  import cairo, pango 
  41  import gobject 
  42  import glib 
  43  try: 
  44          import rsvg 
  45  except ImportError: print 'No module RSVG , graphics will not be so good' 
  46  import os 
  47  import subprocess 
  48  import glob 
  49  import gettext 
  50  import math 
  51   
  52  # import screenlet-submodules 
  53  from options import * 
  54  import services 
  55  import utils 
  56  import sensors 
  57  # TEST 
  58  import menu 
  59  from menu import DefaultMenuItem, add_menuitem 
  60  from drawing import Drawing 
  61  # /TEST 
  62   
  63  # translation stuff 
  64  gettext.textdomain('screenlets') 
  65  gettext.bindtextdomain('screenlets', INSTALL_PREFIX +  '/share/locale') 
  66   
67 -def _(s):
68 return gettext.gettext(s)
69 70 #------------------------------------------------------------------------------- 71 # CONSTANTS 72 #------------------------------------------------------------------------------- 73 74 # the application name 75 APP_NAME = "Screenlets" 76 77 # the version of the Screenlets-baseclass in use 78 VERSION = "0.1.3" 79 80 # the application copyright 81 COPYRIGHT = "(c) RYX (Rico Pfaus) <ryx@ryxperience.com>\nWhise (Helder Fraga) <helder.fraga@hotmail.com>" 82 83 # the application authors 84 AUTHORS = ["RYX (Rico Pfaus) <ryx@ryxperience.com>", "Whise (Helder Fraga)<helder.fraga@hotmail.com>","Sorcerer (Hendrik Kaju)"] 85 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" 88 89 DOCUMENTERS = ["Documentation generated by epydoc"] 90 91 ARTISTS = ["ODD radio screenlet theme by ODDie\nPasodoble mail theme by jEsuSdA\nSome themes by RYX\nSome themes by Whise\nMore to come..."] 92 93 TRANSLATORS = "Special thanks for translators\nFull Translator list on https://translations.launchpad.net/screenlets/" 94 95 # the application website 96 WEBSITE = 'http://www.screenlets.org' 97 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") 100 101 102 #------------------------------------------------------------------------------- 103 # PATHS 104 #------------------------------------------------------------------------------- 105 DIR_TMP = '/tmp/screenlets/' 106 107 TMP_DIR = DIR_TMP 108 109 TMP_FILE = 'screenlets.' + os.environ['USER'] + '.running' 110 111 DIR_USER_ROOT = screenlets.INSTALL_PREFIX + '/share/screenlets' 112 113 DIR_USER = os.environ['HOME'] + '/.screenlets' 114 115 DIR_CONFIG = os.environ['HOME'] + '/.config/Screenlets' 116 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] 120 121 SCREENLETS_PACK_PREFIX = "screenlets-pack-" 122 123 #------------------------------------------------------------------------------- 124 # DBUS 125 #------------------------------------------------------------------------------- 126 127 DAEMON_BUS = 'org.screenlets.ScreenletsDaemon' 128 129 DAEMON_PATH = '/org/screenlets/ScreenletsDaemon' 130 131 DAEMON_IFACE = 'org.screenlets.ScreenletsDaemon' 132 133 #Other stuff 134 135 DEBUG_MODE = True 136 137 #------------------------------------------------------------------------------- 138 # CLASSES 139 #------------------------------------------------------------------------------- 140
141 -class DefaultMenuItem(object):
142 """A container with constants for the default menuitems""" 143 144 # default menuitem constants (is it right to increase like this?) 145 NONE = 0 146 DELETE = 1 147 THEMES = 2 148 INFO = 4 149 SIZE = 8 150 WINDOW_MENU = 16 151 PROPERTIES = 32 152 DELETE = 64 153 QUIT = 128 154 QUIT_ALL = 256 155 # EXPERIMENTAL!! If you use this, the file menu.xml in the 156 # Screenlet's data-dir is used for generating the menu ... 157 XML = 512 158 ADD = 1024 159 # the default items 160 STANDARD = 1|2|8|16|32|64|128|256|1024
161 162
163 -class ScreenletTheme (dict):
164 """ScreenletThemes are simple storages that allow loading files 165 as svg-handles within a theme-directory. Each Screenlet can have 166 its own theme-directory. It is up to the Screenlet-developer if he 167 wants to let his Screenlet support themes or not. Themes are 168 turned off by default - if your Screenlet uses Themes, just set the 169 attribute 'theme_name' to the name of the theme's dir you want to use. 170 TODO: remove dict-inheritance""" 171 172 # meta-info (set through theme.conf) 173 __name__ = '' 174 __author__ = '' 175 __version__ = '' 176 __info__ = '' 177 178 # attributes 179 path = "" 180 loaded = False 181 width = 0 182 height = 0 183 option_overrides = {} 184 p_fdesc = None 185 p_layout = None 186 tooltip = None 187 notify = None 188 189
190 - def __init__ (self, path):
191 # set theme-path and load all files in path 192 self.path = path 193 self.svgs = {} 194 self.pngs = {} 195 self.option_overrides = {} 196 self.loaded = self.__load_all() 197 if self.loaded == False: 198 raise Exception("Error while loading ScreenletTheme in: " + path)
199
200 - def __getattr__ (self, name):
201 if name in ("width", "height"): 202 if self.loaded and len(self)>0: 203 size=self[0].get_dimension_data() 204 if name=="width": 205 return size[0] 206 else: 207 return size[1] 208 else: 209 return object.__getattr__(self, name)
210
211 - def apply_option_overrides (self, screenlet):
212 """Apply this theme's overridden options to the given Screenlet.""" 213 # disable the canvas-updates in the screenlet 214 screenlet.disable_updates = True 215 # theme_name needs special care (must be applied last) 216 theme_name = '' 217 # loop through overrides and appply them 218 for name in self.option_overrides: 219 print "Override: " + name 220 o = screenlet.get_option_by_name(name) 221 if o and not o.protected: 222 if name == 'theme_name': 223 # import/remember theme-name, but not apply yet 224 theme_name = o.on_import(self.option_overrides[name]) 225 else: 226 # set option in screenlet 227 setattr(screenlet, name, 228 o.on_import(self.option_overrides[name])) 229 else: 230 print "WARNING: Option '%s' not found or protected." % name 231 # now apply theme 232 if theme_name != '': 233 screenlet.theme_name = theme_name 234 # re-enable updates and call redraw/reshape 235 screenlet.disable_updates = False 236 screenlet.redraw_canvas() 237 screenlet.update_shape()
238
239 - def check_entry (self, filename):
240 """Checks if a file with filename is loaded in this theme.""" 241 try: 242 if self[filename]: 243 return True 244 except: 245 #raise Exception 246 return False
247
248 - def get_text_width(self, ctx, text, font):
249 """@DEPRECATED Moved to Screenlets class: Returns the pixel width of a given text""" 250 ctx.save() 251 252 if self.p_layout == None : 253 254 self.p_layout = ctx.create_layout() 255 else: 256 257 ctx.update_layout(self.p_layout) 258 self.p_fdesc = pango.FontDescription(font) 259 self.p_layout.set_font_description(self.p_fdesc) 260 self.p_layout.set_text(text) 261 extents, lextents = self.p_layout.get_pixel_extents() 262 ctx.restore() 263 return extents[2]
264
265 - def get_text_extents(self, ctx, text, font):
266 """@DEPRECATED Moved to Screenlets class: Returns the pixel extents of a given text""" 267 ctx.save() 268 269 if self.p_layout == None : 270 271 self.p_layout = ctx.create_layout() 272 else: 273 274 ctx.update_layout(self.p_layout) 275 self.p_fdesc = pango.FontDescription(font) 276 self.p_layout.set_font_description(self.p_fdesc) 277 self.p_layout.set_text(text) 278 extents, lextents = self.p_layout.get_pixel_extents() 279 ctx.restore() 280 return extents
281
282 - def draw_text(self, ctx, text, x, y, font, size, width, allignment, weight = 0, ellipsize = pango.ELLIPSIZE_NONE):
283 """@DEPRECATED Moved to Screenlets class: Draws text""" 284 ctx.save() 285 ctx.translate(x, y) 286 if self.p_layout == None : 287 288 self.p_layout = ctx.create_layout() 289 else: 290 291 ctx.update_layout(self.p_layout) 292 self.p_fdesc = pango.FontDescription() 293 self.p_fdesc.set_family_static(font) 294 self.p_fdesc.set_size(size * pango.SCALE) 295 self.p_fdesc.set_weight(weight) 296 self.p_layout.set_font_description(self.p_fdesc) 297 self.p_layout.set_width(width * pango.SCALE) 298 self.p_layout.set_alignment(allignment) 299 self.p_layout.set_ellipsize(ellipsize) 300 self.p_layout.set_markup(text) 301 ctx.show_layout(self.p_layout) 302 ctx.restore()
303 304
305 - def draw_circle(self,ctx,x,y,width,height,fill=True):
306 """@DEPRECATED Moved to Screenlets class: Draws a circule""" 307 ctx.save() 308 ctx.translate(x, y) 309 ctx.arc(width/2,height/2,min(height,width)/2,0,2*math.pi) 310 if fill:ctx.fill() 311 else: ctx.stroke() 312 ctx.restore()
313
314 - def draw_line(self,ctx,start_x,start_y,end_x,end_y,line_width = 1,close=False,preserve=False):
315 """@DEPRECATED Moved to Screenlets class: Draws a line""" 316 ctx.save() 317 ctx.move_to(start_x, start_y) 318 ctx.set_line_width(line_width) 319 ctx.rel_line_to(end_x, end_y) 320 if close : ctx.close_path() 321 if preserve: ctx.stroke_preserve() 322 else: ctx.stroke() 323 ctx.restore()
324
325 - def draw_rectangle(self,ctx,x,y,width,height,fill=True):
326 """@DEPRECATED Moved to Screenlets class: Draws a rectangle""" 327 ctx.save() 328 ctx.translate(x, y) 329 ctx.rectangle (0,0,width,height) 330 if fill:ctx.fill() 331 else: ctx.stroke() 332 ctx.restore()
333
334 - def draw_rounded_rectangle(self,ctx,x,y,rounded_angle,width,height,fill=True):
335 """@DEPRECATED Moved to Screenlets class: Draws a rounded rectangle""" 336 ctx.save() 337 ctx.translate(x, y) 338 padding=0 # Padding from the edges of the window 339 rounded=rounded_angle # How round to make the edges 20 is ok 340 w = width 341 h = height 342 343 # Move to top corner 344 ctx.move_to(0+padding+rounded, 0+padding) 345 346 # Top right corner and round the edge 347 ctx.line_to(w-padding-rounded, 0+padding) 348 ctx.arc(w-padding-rounded, 0+padding+rounded, rounded, (math.pi/2 )+(math.pi) , 0) 349 350 # Bottom right corner and round the edge 351 ctx.line_to(w-padding, h-padding-rounded) 352 ctx.arc(w-padding-rounded, h-padding-rounded, rounded, 0, math.pi/2) 353 354 # Bottom left corner and round the edge. 355 ctx.line_to(0+padding+rounded, h-padding) 356 ctx.arc(0+padding+rounded, h-padding-rounded, rounded,math.pi/2, math.pi) 357 358 # Top left corner and round the edge 359 ctx.line_to(0+padding, 0+padding+rounded) 360 ctx.arc(0+padding+rounded, 0+padding+rounded, rounded, math.pi, (math.pi/2 )+(math.pi)) 361 362 # Fill in the shape. 363 if fill:ctx.fill() 364 else: ctx.stroke() 365 ctx.restore()
366
367 - def get_image_size(self,pix):
368 """@DEPRECATED Moved to Screenlets class: Gets a picture width and height""" 369 370 pixbuf = gtk.gdk.pixbuf_new_from_file(pix) 371 iw = pixbuf.get_width() 372 ih = pixbuf.get_height() 373 puxbuf = None 374 return iw,ih
375
376 - def draw_image(self,ctx,x,y, pix):
377 """@DEPRECATED Moved to Screenlets class: Draws a picture from specified path""" 378 379 ctx.save() 380 ctx.translate(x, y) 381 pixbuf = gtk.gdk.pixbuf_new_from_file(pix) 382 format = cairo.FORMAT_RGB24 383 if pixbuf.get_has_alpha(): 384 format = cairo.FORMAT_ARGB32 385 386 iw = pixbuf.get_width() 387 ih = pixbuf.get_height() 388 image = cairo.ImageSurface(format, iw, ih) 389 image = ctx.set_source_pixbuf(pixbuf, 0, 0) 390 391 ctx.paint() 392 puxbuf = None 393 image = None 394 ctx.restore()
395 396 397
398 - def draw_scaled_image(self,ctx,x,y, pix, w, h):
399 """@DEPRECATED Moved to Screenlets class: Draws a picture from specified path with a certain width and height""" 400 401 ctx.save() 402 ctx.translate(x, y) 403 pixbuf = gtk.gdk.pixbuf_new_from_file(pix).scale_simple(w,h,gtk.gdk.INTERP_HYPER) 404 format = cairo.FORMAT_RGB24 405 if pixbuf.get_has_alpha(): 406 format = cairo.FORMAT_ARGB32 407 408 iw = pixbuf.get_width() 409 ih = pixbuf.get_height() 410 image = cairo.ImageSurface(format, iw, ih) 411 412 matrix = cairo.Matrix(xx=iw/w, yy=ih/h) 413 image = ctx.set_source_pixbuf(pixbuf, 0, 0) 414 if image != None :image.set_matrix(matrix) 415 ctx.paint() 416 puxbuf = None 417 image = None 418 ctx.restore()
419
420 - def show_notification (self,text):
421 """@DEPRECATED Moved to Screenlets class: Show notification window at current mouse position.""" 422 if self.notify == None: 423 self.notify = Notify() 424 self.notify.text = text 425 self.notify.show()
426
427 - def hide_notification (self):
428 """@DEPRECATED Moved to Screenlets class: hide notification window""" 429 if self.notify != None: 430 self.notify.hide() 431 self.notify = None
432
433 - def show_tooltip (self,text,tooltipx,tooltipy):
434 """@DEPRECATED: Moved to Screenlets class: Show tooltip window at current mouse position.""" 435 if self.tooltip == None: 436 self.tooltip = Tooltip(300, 400) 437 self.tooltip.text = text 438 self.tooltip.x = tooltipx 439 self.tooltip.y = tooltipy 440 self.tooltip.show()
441
442 - def hide_tooltip (self):
443 """@DEPRECATED Moved to Screenlets class: hide tooltip window""" 444 if self.tooltip != None: 445 self.tooltip.hide() 446 self.tooltip = None
447
448 - def has_overrides (self):
449 """Check if this theme contains overrides for options.""" 450 return len(self.option_overrides) > 0
451
452 - def load_conf (self, filename):
453 """Load a config-file from this theme's dir and save vars in list.""" 454 ini = utils.IniReader() 455 if ini.load(filename): 456 if ini.has_section('Theme'): 457 self.__name__ = ini.get_option('name', section='Theme') 458 self.__author__ = ini.get_option('author', section='Theme') 459 self.__version__ = ini.get_option('version', section='Theme') 460 self.__info__ = ini.get_option('info', section='Theme') 461 if ini.has_section('Options'): 462 opts = ini.list_options(section='Options') 463 if opts: 464 for o in opts: 465 self.option_overrides[o[0]] = o[1] 466 print "Loaded theme config from:", filename 467 print "\tName: " + str(self.__name__) 468 print "\tAuthor: " +str(self.__author__) 469 print "\tVersion: " +str(self.__version__) 470 print "\tInfo: " +str(self.__info__) 471 else: 472 print "Failed to theme config from", filename
473 474
475 - def load_svg (self, filename):
476 """Load an SVG-file into this theme and reference it as ref_name.""" 477 if self.has_key(filename): 478 del self[filename] 479 try: 480 self[filename] = rsvg.Handle(self.path + "/" + filename) 481 self.svgs[filename[:-4]] = self[filename] 482 if self[filename] != None: 483 # set width/height 484 size=self[filename].get_dimension_data() 485 if size: 486 self.width = size[0] 487 self.height = size[1] 488 return True 489 except NameError, ex: 490 self[filename] = gtk.gdk.pixbuf_new_from_file(self.path + '/' + filename) 491 self.svgs[filename[:-4]] = self[filename] 492 if self[filename] != None: 493 # set width/height 494 self.width = self[filename].get_width() 495 self.height = self[filename].get_height() 496 print str(ex) 497 return True 498 499 else: 500 return False
501 #self[filename] = None 502
503 - def load_png (self, filename):
504 """Load a PNG-file into this theme and reference it as ref_name.""" 505 if self.has_key(filename): 506 del self[filename] 507 self[filename] = cairo.ImageSurface.create_from_png(self.path + 508 "/" + filename) 509 self.pngs[filename[:-4]] = self[filename] 510 if self[filename] != None: 511 return True 512 else: 513 return False
514 #self[filename] = None 515
516 - def __load_all (self):
517 """Load all files in the theme's path. Currently only loads SVGs and 518 PNGs.""" 519 # clear overrides 520 #self.__option_overrides = {} 521 # read dir 522 dirlst = glob.glob(self.path + '/*') 523 if len(dirlst)==0: 524 return False 525 plen = len(self.path) + 1 526 for file in dirlst: 527 fname = file[plen:] 528 if fname.endswith('.svg'): 529 # svg file 530 if self.load_svg(fname) == False: 531 return False 532 elif fname.endswith('.png'): 533 # svg file 534 if self.load_png(fname) == False: 535 return False 536 elif fname == "theme.conf": 537 print "theme.conf found! Loading option-overrides." 538 # theme.conf 539 if self.load_conf(file) == False: 540 return False 541 # print "Theme %s loaded from %s" % (self.__name__, self.path) 542 return True
543
544 - def reload (self):
545 """Re-Load all files in the theme's path.""" 546 self.free() 547 self.__load_all()
548 549 # TODO: fix function, rsvg handles are not freed properly
550 - def free (self):
551 """Deletes the Theme's contents and frees all rsvg-handles. 552 TODO: freeing rsvg-handles does NOT work for some reason""" 553 self.option_overrides.clear() 554 for filename in self: 555 try: 556 self[filename].free() 557 except AttributeError:pass 558 #self[filename].close() 559 del filename 560 self.clear()
561 562 # TEST: render-function 563 # should be used like "theme.render(context, 'notes-bg')" and then use 564 # either an svg or png image
565 - def render (self, ctx, name):
566 """Render an image from within this theme to the given context. This 567 function can EITHER use png OR svg images, so it is possible to 568 create themes using both image-formats when a Screenlet uses this 569 function for drawing its images. The image name has to be defined 570 without the extension and the function will automatically select 571 the available one (SVG is prefered over PNG).""" 572 573 ### Render Graphics even if rsvg is not available### 574 if os.path.isfile (self.path + '/' + name + '.svg'): 575 576 try: 577 self.svgs[name].render_cairo(ctx) 578 except: 579 try: 580 ctx.set_source_pixbuf(self.svgs[name], 0, 0) 581 582 ctx.paint() 583 pixbuf = None 584 except TypeError: 585 ctx.set_source_surface(self.pngs[name], 0, 0) 586 ctx.paint() 587 588 elif os.path.isfile (self.path + '/' + name + '.png'): 589 ctx.set_source_surface(self.pngs[name], 0, 0) 590 ctx.paint()
591 592 593
594 - def render_png_colorized(self, ctx, name,color):
595 # Scale the pixmap 596 ctx.set_source_rgba(color[0], color[1], color[2], color[3]) 597 ctx.set_source_surface(self.pngs[name], 0, 0) 598 ctx.mask_surface(image, 0, 0) 599 ctx.stroke()
600 601 602
603 -class Screenlet (gobject.GObject, EditableOptions, Drawing):
604 """A Screenlet is a (i.e. contains a) shaped gtk-window that is 605 fully invisible by default. Subclasses of Screenlet can render 606 their owner-drawn graphics on fully transparent background.""" 607 608 # default meta-info for Screenlets 609 __name__ = _('No name set for this Screenlet') 610 __version__ = '0.0' 611 __author__ = _('No author defined for this Screenlet') 612 __desc__ = _('No info set for this Screenlet') 613 __requires__ = [] 614 #__target_version__ = '0.0.0' 615 #__backend_version__ = '0.0.1' 616 617 # attributes (TODO: remove them here and add them to the constructor, 618 # because they only should exist per instance) 619 id = '' # id-attribute for handling instances 620 window = None # the gtk.Window behind the scenes 621 theme = None # the assigned ScreenletTheme 622 uses_theme = True # flag indicating whether Screenlet uses themes 623 draw_buttons = True 624 show_buttons = True 625 menu = None # the right-click gtk.Menu 626 is_dragged = False # TODO: make this work 627 quit_on_close = True # if True, closing this instance quits gtk 628 saving_enabled = True # if False, saving is disabled 629 dragging_over = False # true if something is dragged over 630 disable_updates = False # to temporarily avoid refresh/reshape 631 p_context = None # PangoContext 632 p_layout = None # PangoLayout 633 634 # default editable options, available for all Screenlets 635 x = 0 636 y = 0 637 mousex = 0 638 mousey = 0 639 mouse_is_over = False 640 width = 100 641 height = 100 642 scale = 1.0 643 opacity = 1.0 644 theme_name = "" 645 is_visible = True 646 is_sticky = False 647 is_widget = False 648 keep_above = True 649 keep_below = False 650 skip_pager = True 651 first_run = False 652 skip_taskbar = True 653 lock_position = False 654 allow_option_override = True # if False, overrides are ignored 655 ask_on_option_override = True # if True, overrides need confirmation 656 ignore_requirements = False # if True, DEB requirements are ignored 657 resize_on_scroll = True 658 has_started = False 659 has_focus = False 660 # internals (deprecated? we still don't get the end of a begin_move_drag) 661 gtk_icon_theme = None 662 __lastx = 0 663 __lasty = 0 664 p_fdesc = None 665 p_layout = None 666 tooltip = None 667 notify = None 668 # some menuitems (needed for checking/unchecking) 669 # DEPRECATED: remove - don't really work anyway ... (or fix the menu?) 670 __mi_keep_above = None 671 __mi_keep_below = None 672 __mi_widget = None 673 __mi_sticky = None 674 __mi_lock = None 675 # for custom signals (which aren't acutally used ... yet) 676 __gsignals__ = dict(screenlet_removed=(gobject.SIGNAL_RUN_FIRST, 677 gobject.TYPE_NONE, (gobject.TYPE_OBJECT,))) 678
679 - def __init__ (self, id='', width=100, height=100, parent_window=None, 680 show_window=True, is_widget=False, is_sticky=False, 681 uses_theme=True, draw_buttons=True,path=os.getcwd(), drag_drop=False, session=None, 682 enable_saving=True, service_class=services.ScreenletService, 683 uses_pango=False, is_sizable=True,resize_on_scroll=True, ask_on_option_override=False):
684 """Constructor - should only be subclassed""" 685 686 # call gobject and EditableOptions superclasses 687 super(Screenlet, self).__init__() 688 EditableOptions.__init__(self) 689 # init properties 690 self.id = id 691 self.session = session 692 self.service = None 693 # if we have an id and a service-class, register our service 694 if self.id and service_class: 695 self.register_service(service_class) 696 # notify service about adding this instance 697 self.service.instance_added(self.id) 698 self.width = width 699 self.height = height 700 self.is_dragged = False 701 self.__path__ = path 702 self.saving_enabled = enable_saving # used by session 703 # set some attributes without calling __setattr__ 704 self.__dict__['theme_name'] = "" 705 self.__dict__['is_widget'] = is_widget 706 self.__dict__['is_sticky'] = is_sticky 707 self.__dict__['draw_buttons'] = draw_buttons 708 self.resize_on_scroll = resize_on_scroll 709 self.__dict__['x'] = 0 710 self.__dict__['y'] = 0 711 # TEST: set scale relative to theme size (NOT WORKING) 712 #self.__dict__['scale'] = width/100.0 713 # /TEST 714 # shape bitmap 715 self.__shape_bitmap = None 716 self.__shape_bitmap_width = 0 717 self.__shape_bitmap_height = 0 718 # "editable" options, first create a group 719 self.add_options_group('Screenlet', 720 _('The basic settings for this Screenlet-instance.')) 721 # if this Screenlet uses themes, add theme-specific options 722 # (NOTE: this option became hidden with 0.0.9 and doesn't use 723 # get_available_themes anymore for showing the choices) 724 self.gtk_icon_theme = gtk.icon_theme_get_default() 725 self.load_buttons(None) 726 self.gtk_icon_theme.connect("changed", self.load_buttons) 727 if draw_buttons: self.draw_buttons = True 728 else: self.draw_buttons = False 729 if uses_theme: 730 self.uses_theme = True 731 self.add_option(StringOption('Screenlet', 'theme_name', 732 'default', '', '', hidden=True)) 733 # create/add options 734 self.add_option(IntOption('Screenlet', 'x', 735 0, _('X-Position'), _('The X-position of this Screenlet ...'), 736 min=0, max=gtk.gdk.screen_width())) 737 self.add_option(IntOption('Screenlet', 'y', 738 0, _('Y-Position'), _('The Y-position of this Screenlet ...'), 739 min=0, max=gtk.gdk.screen_height())) 740 self.add_option(IntOption('Screenlet', 'width', 741 width, _('Width'), _('The width of this Screenlet ...'), 742 min=16, max=1000, hidden=True)) 743 self.add_option(IntOption('Screenlet', 'height', 744 height, _('Height'), _('The height of this Screenlet ...'), 745 min=16, max=1000, hidden=True)) 746 self.add_option(FloatOption('Screenlet', 'scale', 747 self.scale, _('Scale'), _('The scale-factor of this Screenlet ...'), 748 min=0.1, max=10.0, digits=2, increment=0.1)) 749 self.add_option(FloatOption('Screenlet', 'opacity', 750 self.opacity, _('Opacity'), _('The opacity of the Screenlet window ...'), 751 min=0.1, max=1.0, digits=2, increment=0.1)) 752 self.add_option(BoolOption('Screenlet', 'is_sticky', 753 is_sticky, _('Stick to Desktop'), 754 _('Show this Screenlet on all workspaces ...'))) 755 self.add_option(BoolOption('Screenlet', 'is_widget', 756 is_widget, _('Treat as Widget'), 757 _('Treat this Screenlet as a "Widget" ...'))) 758 self.add_option(BoolOption('Screenlet', 'is_dragged', 759 self.is_dragged, "Is the screenlet dragged","Is the screenlet dragged", hidden=True)) 760 self.add_option(BoolOption('Screenlet', 'is_sizable', 761 is_sizable, "Can the screenlet be resized","is_sizable", hidden=True)) 762 self.add_option(BoolOption('Screenlet', 'is_visible', 763 self.is_visible, "Usefull to use screenlets as gnome panel applets","is_visible", hidden=True)) 764 self.add_option(BoolOption('Screenlet', 'lock_position', 765 self.lock_position, _('Lock position'), 766 _('Stop the screenlet from being moved...'))) 767 self.add_option(BoolOption('Screenlet', 'keep_above', 768 self.keep_above, _('Keep above'), 769 _('Keep this Screenlet above other windows ...'))) 770 self.add_option(BoolOption('Screenlet', 'keep_below', 771 self.keep_below, _('Keep below'), 772 _('Keep this Screenlet below other windows ...'))) 773 self.add_option(BoolOption('Screenlet', 'draw_buttons', 774 self.draw_buttons, _('Draw button controls'), 775 _('Draw buttons in top right corner'))) 776 self.add_option(BoolOption('Screenlet', 'skip_pager', 777 self.skip_pager, _('Skip Pager'), 778 _('Set this Screenlet to show/hide in pagers ...'))) 779 self.add_option(BoolOption('Screenlet', 'skip_taskbar', 780 self.skip_pager, _('Skip Taskbar'), 781 _('Set this Screenlet to show/hide in taskbars ...'))) 782 self.add_option(BoolOption('Screenlet', 'resize_on_scroll', 783 self.resize_on_scroll, _("Resize on mouse scroll"),"resize_on_scroll")) 784 self.add_option(BoolOption('Screenlet', 'ignore_requirements', 785 self.ignore_requirements, _('Ignore requirements'), 786 _('Set this Screenlet to ignore/demand DEB requirements ...'))) 787 if uses_theme: 788 self.ask_on_option_override = ask_on_option_override 789 self.add_option(BoolOption('Screenlet', 'allow_option_override', 790 self.allow_option_override, _('Allow overriding Options'), 791 _('Allow themes to override options in this screenlet ...'))) 792 self.add_option(BoolOption('Screenlet', 'ask_on_option_override', 793 self.ask_on_option_override, _('Ask on Override'), 794 _('Show a confirmation-dialog when a theme wants to override ')+\ 795 _('the current options of this Screenlet ...'))) 796 # disable width/height 797 self.disable_option('width') 798 self.disable_option('height') 799 # create window 800 self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) 801 if parent_window: 802 self.window.set_parent_window(parent_window) 803 self.window.set_transient_for(parent_window) 804 self.window.set_destroy_with_parent(True) 805 self.window.resize(width, height) 806 self.window.set_decorated(False) 807 self.window.set_app_paintable(True) 808 # create pango layout, if active 809 if uses_pango: 810 self.p_context = self.window.get_pango_context() 811 if self.p_context: 812 self.p_layout = pango.Layout(self.p_context) 813 self.p_layout.set_font_description(\ 814 pango.FontDescription("Sans 12")) 815 # set type hint 816 817 if str(sensors.sys_get_window_manager()).lower() == 'kwin': 818 print "WARNING - You are using kwin window manager , screenlets doesnt have full compatibility with this window manager" 819 #self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DOCK) 820 elif str(sensors.sys_get_window_manager()).lower() == 'sawfish': 821 print "WARNING - You are using kwin window manager , screenlets doesnt have full compatibility with this window manager" 822 else: 823 self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_TOOLBAR) 824 self.window.set_keep_above(True) 825 self.window.set_skip_taskbar_hint(True) 826 self.window.set_skip_pager_hint(True) 827 if is_sticky: 828 self.window.stick() 829 self.alpha_screen_changed(self.window) 830 self.update_shape() 831 #self.window.set_events(gtk.gdk.BUTTON_PRESS_MASK) 832 self.window.set_events(gtk.gdk.ALL_EVENTS_MASK) 833 self.window.connect("composited-changed", self.composite_changed) 834 self.window.connect("delete_event", self.delete_event) 835 self.window.connect("destroy", self.destroy) 836 self.window.connect("expose_event", self.expose) 837 self.window.connect("button-press-event", self.button_press) 838 self.window.connect("button-release-event", self.button_release) 839 self.window.connect("configure-event", self.configure_event) 840 self.window.connect("screen-changed", self.alpha_screen_changed) 841 self.window.connect("realize", self.realize_event) 842 self.window.connect("enter-notify-event", self.enter_notify_event) 843 self.window.connect("leave-notify-event", self.leave_notify_event) 844 self.window.connect("focus-in-event", self.focus_in_event) 845 self.window.connect("focus-out-event", self.focus_out_event) 846 self.window.connect("scroll-event", self.scroll_event) 847 self.window.connect("motion-notify-event",self.motion_notify_event) 848 self.window.connect("map-event", self.map_event) 849 self.window.connect("unmap-event", self.unmap_event) 850 # add key-handlers (TODO: use keyword-attrib to activate?) 851 self.window.connect("key-press-event", self.key_press) 852 # drag/drop support (NOTE: still experimental and incomplete) 853 if drag_drop: 854 self.window.drag_dest_set(gtk.DEST_DEFAULT_MOTION | 855 gtk.DEST_DEFAULT_DROP, #gtk.DEST_DEFAULT_ALL, 856 [("text/plain", 0, 0), 857 ("image", 0, 1), 858 ("text/uri-list", 0, 2)], 859 gtk.gdk.ACTION_COPY) 860 self.window.connect("drag_data_received", self.drag_data_received) 861 self.window.connect("drag-begin", self.drag_begin) 862 self.window.connect("drag-end", self.drag_end) 863 self.window.connect("drag-motion", self.drag_motion) 864 self.window.connect("drag-leave", self.drag_leave) 865 # create menu 866 self.menu = gtk.Menu() 867 # 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 868 869 870 if show_window: 871 self.window.show() 872 # print os.environ['HOME'] + '/.config/Screenlets/' + self.__name__[:-9] + '/default/'+ self.id 873 if not os.path.exists(os.environ['HOME'] + '/.config/Screenlets/' + self.__name__[:-9] + '/default/'+ self.id + '.ini'): 874 self.first_run = True 875 self.window.hide() 876 877 #Make opacity available only when composite is enabled 878 if not self.window.is_composited () : 879 self.disable_option('opacity')
880
881 - def __setattr__ (self, name, value):
882 # set the value in GObject (ESSENTIAL!!!!) 883 self.on_before_set_atribute(name, value) 884 gobject.GObject.__setattr__(self, name, value) 885 # And do other actions 886 if name=="x" or name=="y": 887 if self.has_started: 888 self.window.move(self.x, self.y) 889 elif name == 'opacity': 890 self.window.set_opacity(value) 891 elif name == 'scale': 892 self.window.resize(int(self.width * self.scale), 893 int(self.height * self.scale)) 894 # TODO: call on_resize-handler here !!!! 895 self.on_scale() 896 self.redraw_canvas() 897 self.update_shape() 898 899 900 elif name == "theme_name": 901 #self.__dict__ ['theme_name'] = value 902 #self.load_theme(self.get_theme_dir() + value) 903 # load theme 904 print "Theme set to: '%s'" % value 905 path = self.find_theme(value) 906 if path: 907 self.load_theme(path) 908 #self.load_first_theme(value) 909 self.redraw_canvas() 910 self.update_shape() 911 elif name in ("width", "height"): 912 #self.__dict__ [name] = value 913 if self.window: 914 self.window.resize(int(self.width*self.scale), int(self.height*self.scale)) 915 #self.redraw_canvas() 916 self.update_shape() 917 elif name == "is_widget": 918 if self.has_started: 919 self.set_is_widget(value) 920 elif name == "is_visible": 921 if self.has_started: 922 if value == True: 923 self.reshow() 924 else: 925 self.window.hide() 926 elif name == "is_sticky": 927 if value == True: 928 self.window.stick() 929 else: 930 self.window.unstick() 931 #if self.__mi_sticky: 932 # self.__mi_sticky.set_active(value) 933 elif name == "keep_above": 934 if self.has_started == True: 935 self.window.set_keep_above(bool(value)) 936 #self.__mi_keep_above.set_active(value) 937 elif name == "keep_below": 938 if self.has_started == True: 939 self.window.set_keep_below(bool(value)) 940 #self.__mi_keep_below.set_active(value) 941 elif name == "skip_pager": 942 if self.window.window: 943 self.window.window.set_skip_pager_hint(bool(value)) 944 elif name == "skip_taskbar": 945 if self.window.window: 946 self.window.window.set_skip_taskbar_hint(bool(value)) 947 # NOTE: This is the new recommended way of storing options in real-time 948 # (we access the backend through the session here) 949 if self.saving_enabled: 950 o = self.get_option_by_name(name) 951 if o != None: 952 self.session.backend.save_option(self.id, o.name, 953 o.on_export(value)) 954 self.on_after_set_atribute(name, value)
955 # /TEST 956 957 #----------------------------------------------------------------------- 958 # Screenlet's public functions 959 #----------------------------------------------------------------------- 960
961 - def check_requirements (self):
962 '''Checks if required DEB packages are installed''' 963 964 req_feedback = "" 965 fail = False 966 967 # operators=['>', '=', '<'] 968 969 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/"' 970 for req in self.__requires__: 971 operator = None 972 # req = req.replace(' ', '') 973 if req.find('(') != -1: 974 # package version is specified with an operator (no logical operators supported yet!) 975 pos = req.find('(') 976 package = req[:pos].strip() 977 version_str = req[pos+1:] 978 version_str = version_str[:version_str.find(')')] 979 while version_str.find(' ') != -1: 980 version_str = req.replace(' ', ' ') 981 res = version_str.split(' ') 982 version = res[1] 983 operator = res[0] 984 else: 985 # when only package name is specified 986 package = req 987 # version of the deb package if unspecified 988 version = _("?") 989 990 installed_version = os.popen(commandstr % package).readline().replace('\n', '') 991 992 if len(installed_version) < 1: 993 req_feedback += _("\n%(package)s %(version)s required, NOT INSTALLED!") % {"package":package, "version":version} 994 fail = True 995 else: 996 req_feedback += _("\n%(package)s %(version)s installed, req %(required)s.") % {"package":package, "version":installed_version, "required":version} 997 # will fail only if dpkg says that version is too old 998 # otherwise it's responsibility of developer to provide 999 # correct version id and operator (won't detect problems with these) 1000 if operator is not None: 1001 comp_command = "dpkg --compare-versions \"" + installed_version + "\" \"" + operator + "\" \"" + version + "\"" 1002 # print comp_command 1003 if subprocess.call(comp_command, shell=True) != 0: 1004 fail = True 1005 if fail: 1006 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")
1007
1009 """Appends the default menu-items to self.menu. You can add on OR'ed 1010 flag with DefaultMenuItems you want to add.""" 1011 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' 1012 1013 menu = self.menu 1014 1015 # children already exist? add separator 1016 if len(menu.get_children()) > 0: 1017 self.add_menuitem("", "-") 1018 # EXPERIMENTAL: 1019 if flags & DefaultMenuItem.XML: 1020 # create XML-menu from screenletpath/menu.xml 1021 xfile = self.get_screenlet_dir() + "/menu.xml" 1022 xmlmenu = screenlets.menu.create_menu_from_file(xfile, 1023 self.menuitem_callback) 1024 if xmlmenu: 1025 self.menu = xmlmenu 1026 # add size-selection 1027 if flags & DefaultMenuItem.SIZE: 1028 size_item = gtk.MenuItem(_("Size")) 1029 size_item.show() 1030 size_menu = gtk.Menu() 1031 menu.append(size_item) 1032 size_item.set_submenu(size_menu) 1033 #for i in xrange(10): 1034 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): 1035 s = str(int(i * 100)) 1036 item = gtk.MenuItem(s + " %") 1037 item.connect("activate", self.menuitem_callback, 1038 "scale:"+str(i)) 1039 item.show() 1040 size_menu.append(item) 1041 # create theme-selection menu 1042 if flags & DefaultMenuItem.THEMES: 1043 themes_item = gtk.MenuItem(_("Theme")) 1044 themes_item.show() 1045 themes_menu = gtk.Menu() 1046 menu.append(themes_item) 1047 themes_item.set_submenu(themes_menu) 1048 # create theme-list from theme-directory 1049 lst = self.get_available_themes() 1050 for tname in lst: 1051 item = gtk.MenuItem(tname) 1052 item.connect("activate", self.menuitem_callback, "theme:"+tname) 1053 item.show() 1054 themes_menu.append(item) 1055 1056 # add window-options menu 1057 if flags & DefaultMenuItem.WINDOW_MENU: 1058 winmenu_item = gtk.MenuItem(_("Window")) 1059 winmenu_item.show() 1060 winmenu_menu = gtk.Menu() 1061 menu.append(winmenu_item) 1062 winmenu_item.set_submenu(winmenu_menu) 1063 # add "lock"-menuitem 1064 self.__mi_lock = item = gtk.CheckMenuItem(_("Lock")) 1065 item.set_active(self.lock_position) 1066 item.connect("activate", self.menuitem_callback, 1067 "option:lock") 1068 item.show() 1069 winmenu_menu.append(item) 1070 # add "Sticky"-menuitem 1071 self.__mi_sticky = item = gtk.CheckMenuItem(_("Sticky")) 1072 item.set_active(self.is_sticky) 1073 item.connect("activate", self.menuitem_callback, 1074 "option:sticky") 1075 item.show() 1076 winmenu_menu.append(item) 1077 # add "Widget"-menuitem 1078 self.__mi_widget = item = gtk.CheckMenuItem(_("Widget")) 1079 item.set_active(self.is_widget) 1080 item.connect("activate", self.menuitem_callback, 1081 "option:widget") 1082 item.show() 1083 winmenu_menu.append(item) 1084 # add "Keep above"-menuitem 1085 self.__mi_keep_above = item = gtk.CheckMenuItem(_("Keep above")) 1086 item.set_active(self.keep_above) 1087 item.connect("activate", self.menuitem_callback, 1088 "option:keep_above") 1089 item.show() 1090 winmenu_menu.append(item) 1091 # add "Keep Below"-menuitem 1092 self.__mi_keep_below = item = gtk.CheckMenuItem(_("Keep below")) 1093 item.set_active(self.keep_below) 1094 item.connect("activate", self.menuitem_callback, 1095 "option:keep_below") 1096 item.show() 1097 winmenu_menu.append(item) 1098 1099 # add Settings item 1100 if flags & DefaultMenuItem.PROPERTIES: 1101 add_menuitem(menu, "-", self.menuitem_callback, "") 1102 add_menuitem(menu, _("Properties..."), self.menuitem_callback, "options") 1103 # add info item 1104 if flags & DefaultMenuItem.INFO: 1105 add_menuitem(menu, _("Info..."), self.menuitem_callback, "info") 1106 # add delete item 1107 if flags & DefaultMenuItem.ADD: 1108 add_menuitem(menu, "-", self.menuitem_callback, "") 1109 add_menuitem(menu, _("Add one more %s") % self.get_short_name(), self.menuitem_callback, "add") 1110 # add delete item 1111 if flags & DefaultMenuItem.DELETE: 1112 add_menuitem(menu, _("Delete this %s") % self.get_short_name(), self.menuitem_callback, "delete") 1113 # add Quit item 1114 if flags & DefaultMenuItem.QUIT: 1115 add_menuitem(menu, "-", self.menuitem_callback, "") 1116 add_menuitem(menu, _("Quit this %s") % self.get_short_name(), self.menuitem_callback, "quit_instance") 1117 # add Quit-all item 1118 if flags & DefaultMenuItem.QUIT_ALL: 1119 add_menuitem(menu, _("Quit all %ss") % self.get_short_name(), self.menuitem_callback, "quit")
1120
1121 - def add_menuitem (self, id, label, callback=None):
1122 """Simple way to add menuitems to a right-click menu. 1123 This function wraps screenlets.menu.add_menuitem. 1124 For backwards compatibility, the order of the parameters 1125 to this function is switched.""" 1126 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' 1127 if callback is None: 1128 callback = self.menuitem_callback 1129 # call menu.add_menuitem 1130 return add_menuitem(self.menu, label, callback, id)
1131
1132 - def add_submenuitem (self, id, label, lst, callback=None):
1133 """Simple way to add submenuitems to the right-click menu through a list.""" 1134 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' 1135 1136 submenu = gtk.MenuItem(label) 1137 submenu.show() 1138 sub_menu = gtk.Menu() 1139 self.menu.append(submenu) 1140 submenu.set_submenu(sub_menu) 1141 # create theme-list from theme-directory 1142 1143 for tname in lst: 1144 item = gtk.MenuItem(tname) 1145 item.connect("activate", self.menuitem_callback, 1146 tname) 1147 item.show() 1148 sub_menu.append(item) 1149 1150 return submenu
1151 1152 1153
1154 - def load_buttons(self, event):
1155 self.closeb = self.gtk_icon_theme.load_icon ("gtk-close", 16, 0) 1156 self.prop = self.gtk_icon_theme.load_icon ("gtk-properties", 16, 0)
1157
1158 - def create_buttons(self):
1159 1160 ctx = self.window.window.cairo_create() 1161 ctx.save() 1162 #ctx.set_source_rgba(0.5,0.5,0.5,0.6) 1163 #self.theme.draw_rounded_rectangle(ctx,(self.width*self.scale)-36,0,5,36,16) 1164 #close = theme1.load_icon ("gtk-close", 16, 0) 1165 #prop = theme1.load_icon ("gtk-properties", 16, 0) 1166 #zoom1 = theme1.load_icon ("gtk-zoom-in", 16, 0) 1167 #zoom2 = theme1.load_icon ("gtk-zoom-out", 16, 0) 1168 #close = gtk.image_new_from_stock(gtk.STOCK_CLOSE, 16) 1169 ctx.translate((self.width*self.scale)-16,0) 1170 ctx.set_source_pixbuf(self.closeb, 0, 0) 1171 ctx.paint() 1172 ctx.restore() 1173 ctx.save() 1174 ctx.translate((self.width*self.scale)-32,0) 1175 ctx.set_source_pixbuf(self.prop, 0, 0) 1176 ctx.paint() 1177 ctx.restore()
1178
1179 - def clear_cairo_context (self, ctx):
1180 """Fills the given cairo.Context with fully transparent white.""" 1181 ctx.save() 1182 ctx.set_source_rgba(1, 1, 1, 0) 1183 ctx.set_operator (cairo.OPERATOR_SOURCE) 1184 ctx.paint() 1185 ctx.restore()
1186
1187 - def close (self):
1188 """Close this Screenlet 1189 TODO: send close-notify instead of destroying window?""" 1190 #self.save_settings() 1191 self.window.unmap() 1192 self.window.destroy()
1193 #self.window.event(gtk.gdk.Event(gtk.gdk.DELETE)) 1194
1195 - def create_drag_icon (self):
1196 """Create drag-icon and -mask for drag-operation. Returns a 2-tuple 1197 with the icon and the mask. To supply your own icon you can use the 1198 on_create_drag_icon-handler and return the icon/mask as 2-tuple.""" 1199 w = self.width 1200 h = self.height 1201 icon, mask = self.on_create_drag_icon() 1202 if icon == None: 1203 # create icon 1204 icon = gtk.gdk.Pixmap(self.window.window, w, h) 1205 ctx = icon.cairo_create() 1206 self.clear_cairo_context(ctx) 1207 self.on_draw(ctx) 1208 if mask == None: 1209 # create mask 1210 mask = gtk.gdk.Pixmap(self.window.window, w, h) 1211 ctx = mask.cairo_create() 1212 self.clear_cairo_context(ctx) 1213 self.on_draw_shape(ctx) 1214 return (icon, mask)
1215
1216 - def enable_saving (self, enabled=True):
1217 """Enable/Disable realtime-saving of options.""" 1218 self.saving_enabled = enabled
1219
1220 - def find_theme (self, name):
1221 """Find the best occurence of a theme and return its global path.""" 1222 sn = self.get_short_name() 1223 utils.refresh_available_screenlet_paths() 1224 for p in SCREENLETS_PATH: 1225 fpath = p + '/' + sn + '/themes/' + name 1226 if os.path.isdir(fpath): 1227 return fpath 1228 return None
1229
1230 - def get_short_name (self):
1231 """Return the short name of this screenlet. This returns the classname 1232 of the screenlet without trailing "Screenlet". Please always use 1233 this function if you want to retrieve the short name of a Screenlet.""" 1234 return self.__class__.__name__[:-9]
1235
1236 - def get_screenlet_dir (self):
1237 """Return the name of this screenlet's personal directory.""" 1238 p = utils.find_first_screenlet_path(self.get_short_name()) 1239 if p: 1240 return p 1241 else: 1242 if self.__path__ != '': 1243 return self.__path__ 1244 else: 1245 return os.getcwd()
1246
1247 - def get_theme_dir (self):
1248 """Return the name of this screenlet's personal theme-dir. 1249 (Only returns the dir under the screenlet's location""" 1250 return self.get_screenlet_dir() + "/themes/"
1251
1252 - def get_available_themes (self):
1253 """Returns a list with the names of all available themes in this 1254 Screenlet's theme-directories.""" 1255 lst = [] 1256 utils.refresh_available_screenlet_paths() 1257 for p in SCREENLETS_PATH: 1258 d = p + '/' + self.get_short_name() + '/themes/' 1259 if os.path.isdir(d): 1260 #dirname = self.get_theme_dir() 1261 dirlst = glob.glob(d + '*') 1262 dirlst.sort() 1263 tdlen = len(d) 1264 for fname in dirlst: 1265 if os.path.isdir(fname): 1266 dname = fname[tdlen:] 1267 if not dname in lst: 1268 lst.append(dname) 1269 return lst
1270
1271 - def reshow(self):
1272 self.window.present() 1273 self.has_started = True 1274 self.is_dragged = False 1275 self.keep_above= self.keep_above 1276 self.keep_below= self.keep_below 1277 self.skip_taskbar = self.skip_taskbar 1278 self.window.set_skip_taskbar_hint(self.skip_taskbar) 1279 self.window.set_keep_above(self.keep_above) 1280 self.window.set_keep_below(self.keep_below) 1281 if self.is_widget: 1282 self.set_is_widget(True) 1283 self.has_focus = False
1284
1285 - def finish_loading(self):
1286 """Called when screenlet finishes loading""" 1287 1288 1289 self.window.present() 1290 1291 1292 # the keep above and keep bellow must be reset after the window is shown this is absolutly necessary 1293 self.window.hide() 1294 self.window.move(self.x, self.y) 1295 1296 if not self.ignore_requirements: 1297 self.check_requirements() 1298 1299 self.window.show() 1300 self.has_started = True 1301 self.is_dragged = False 1302 self.keep_above= self.keep_above 1303 self.keep_below= self.keep_below 1304 self.is_sticky = self.is_sticky 1305 self.skip_taskbar = self.skip_taskbar 1306 self.window.set_skip_taskbar_hint(self.skip_taskbar) 1307 self.window.set_keep_above(self.keep_above) 1308 self.window.set_keep_below(self.keep_below) 1309 1310 self.on_init() 1311 if self.is_widget: 1312 self.set_is_widget(True) 1313 self.has_focus = False 1314 ini = utils.IniReader() 1315 if ini.load (os.environ['HOME'] + '/.screenlets' + '/config.ini') and self.first_run: 1316 1317 if ini.get_option('Lock', section='Options') == 'True': 1318 self.lock_position = True 1319 elif ini.get_option('Lock', section='Options') == 'False': 1320 self.lock_position = False 1321 if ini.get_option('Sticky', section='Options') == 'True': 1322 self.is_sticky = True 1323 elif ini.get_option('Sticky', section='Options') == 'False': 1324 self.is_sticky = False 1325 if ini.get_option('Widget', section='Options') == 'True': 1326 self.is_widget = True 1327 elif ini.get_option('Widget', section='Options') == 'False': 1328 self.is_widget = False 1329 if ini.get_option('Keep_above', section='Options') == 'True': 1330 self.keep_above = True 1331 elif ini.get_option('Keep_above', section='Options') == 'False': 1332 self.keep_above = False 1333 if ini.get_option('Keep_below', section='Options') == 'True': 1334 self.keep_below = True 1335 elif ini.get_option('Keep_below', section='Options') == 'False': 1336 self.keep_below = False 1337 if ini.get_option('draw_buttons', section='Options') == 'True': 1338 self.draw_buttons = True 1339 elif ini.get_option('draw_buttons', section='Options') == 'False': 1340 self.draw_buttons = False
1341
1342 - def hide (self):
1343 """Hides this Screenlet's underlying gtk.Window""" 1344 self.window.hide() 1345 self.on_hide()
1346 1347 # EXPERIMENTAL: 1348 # NOTE: load_theme does NOT call redraw_canvas and update_shape!!!!! 1349 # To do all in one, set attribute self.theme_name instead
1350 - def load_theme (self, path):
1351 """Load a theme for this Screenlet from the given path. NOTE: 1352 load_theme does NOT call redraw_canvas and update_shape!!!!! To do all 1353 in one call, set the attribute self.theme_name instead.""" 1354 if self.theme: 1355 self.theme.free() 1356 del self.theme 1357 self.theme = ScreenletTheme(path) 1358 # check for errors 1359 if self.theme.loaded == False: 1360 print "Error while loading theme: " + path 1361 self.theme = None 1362 else: 1363 # call user-defined handler 1364 self.on_load_theme() 1365 # if override options is allowed, apply them 1366 if self.allow_option_override: 1367 if self.theme.has_overrides(): 1368 if self.ask_on_option_override==True and \ 1369 show_question(self, 1370 _('This theme wants to override your settings for this Screenlet. Do you want to allow that?')) == False: 1371 return 1372 self.theme.apply_option_overrides(self)
1373 # /EXPERIMENTAL 1374
1375 - def main (self):
1376 """If the Screenlet runs as stand-alone app, starts gtk.main()""" 1377 gtk.main()
1378
1379 - def register_service (self, service_classobj):
1380 """Register or create the given ScreenletService-(sub)class as the new 1381 service for this Screenlet. If self is not the first instance in the 1382 current session, the service from the first instance will be used 1383 instead and no new service is created.""" 1384 if self.session: 1385 if len(self.session.instances) == 0: 1386 # if it is the basic service, add name to call 1387 if service_classobj==services.ScreenletService:#BUG 1388 self.service = service_classobj(self, self.get_short_name()) 1389 else: 1390 # else only pass this screenlet 1391 self.service = service_classobj(self) 1392 else: 1393 self.service = self.session.instances[0].service 1394 # TODO: throw exception?? 1395 return True 1396 return False
1397
1398 - def set_is_widget (self, value):
1399 """Set this window to be treated as a Widget (only supported by 1400 compiz using the widget-plugin yet)""" 1401 if value==True: 1402 # set window type to utility 1403 #self.window.window.set_type_hint( 1404 # gtk.gdk.WINDOW_TYPE_HINT_UTILITY) 1405 # set _compiz_widget-property on window 1406 self.window.window.property_change("_COMPIZ_WIDGET", 1407 gtk.gdk.SELECTION_TYPE_WINDOW, 1408 32, gtk.gdk.PROP_MODE_REPLACE, (True,)) 1409 else: 1410 # set window type to normal 1411 #self.window.window.set_type_hint( 1412 # gtk.gdk.WINDOW_TYPE_HINT_NORMAL) 1413 # set _compiz_widget-property 1414 self.window.window.property_delete("_COMPIZ_WIDGET") 1415 # notify handler 1416 self.on_switch_widget_state(value)
1417
1418 - def show (self):
1419 """Show this Screenlet's underlying gtk.Window""" 1420 self.window.show() 1421 self.window.move(self.x, self.y) 1422 self.on_show()
1423
1424 - def show_settings_dialog (self):
1425 """Show the EditableSettingsDialog for this Screenlet.""" 1426 se = OptionsDialog(490, 450) 1427 img = gtk.Image() 1428 try: 1429 d = self.get_screenlet_dir() 1430 if os.path.isfile(d + '/icon.svg'): 1431 icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.svg') 1432 elif os.path.isfile(d + '/icon.png'): 1433 icn = gtk.gdk.pixbuf_new_from_file(d + '/icon.png') 1434 img.set_from_pixbuf(icn) 1435 except: 1436 img.set_from_stock(gtk.STOCK_PROPERTIES, 5) 1437 se.set_title(self.__name__) 1438 se.set_info(self.__name__, glib.markup_escape_text(self.__desc__), '(c) ' + glib.markup_escape_text(self.__author__), 1439 version='v' + self.__version__, icon=img) 1440 se.show_options_for_object(self) 1441 resp = se.run() 1442 if resp == gtk.RESPONSE_REJECT: # TODO!!!!! 1443 se.reset_to_defaults() 1444 else: 1445 self.update_shape() 1446 se.destroy()
1447
1448 - def redraw_canvas (self):
1449 """Redraw the entire Screenlet's window area. 1450 TODO: store window alloaction in class and change when size changes.""" 1451 # if updates are disabled, just exit 1452 if self.disable_updates: 1453 return 1454 if self.window: 1455 x, y, w, h = self.window.get_allocation() 1456 rect = gtk.gdk.Rectangle(x, y, w, h) 1457 if self.window.window: 1458 self.window.window.invalidate_rect(rect, True) 1459 self.window.window.process_updates(True)
1460 # if self.has_focus and self.draw_buttons and self.show_buttons: 1461 # self.create_buttons() 1462 1463
1464 - def redraw_canvas_area (self, x, y, width, height):
1465 """Redraw the given Rectangle (x, y, width, height) within the 1466 current Screenlet's window.""" 1467 # if updates are disabled, just exit 1468 if self.disable_updates: 1469 return 1470 if self.window: 1471 rect = gtk.gdk.Rectangle(x, y, width, height) 1472 if self.window.window: 1473 self.window.window.invalidate_rect(rect, True) 1474 self.window.window.process_updates(True)
1475
1476 - def remove_shape(self):
1477 """Removed shaped window , in case the nom composited shape has been set""" 1478 if self.window.window: 1479 self.window.window.shape_combine_mask(None,0,0)
1480
1481 - def update_shape (self):
1482 """Update window shape (only call this when shape has changed 1483 because it is very ressource intense if ran too often).""" 1484 # if updates are disabled, just exit 1485 if self.disable_updates: 1486 return 1487 #print "UPDATING SHAPE" 1488 # TODO: 1489 #if not self.window.is_composited(): 1490 # self.update_shape_non_composited() 1491 # calculate new width/height of shape bitmap 1492 w = int(self.width * self.scale) 1493 h = int(self.height * self.scale) 1494 # if 0 set it to 100 to avoid crashes and stay interactive 1495 if w==0: w = 100 1496 if h==0: h = 100 1497 # if size changed, recreate shape bitmap 1498 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height: 1499 data = ''.zfill(w*h) 1500 self.__shape_bitmap = gtk.gdk.bitmap_create_from_data(None, data, 1501 w, h) 1502 self.__shape_bitmap_width = w 1503 self.__shape_bitmap_height = h 1504 # create context and draw shape 1505 ctx = self.__shape_bitmap.cairo_create() 1506 self.clear_cairo_context(ctx) #TEST 1507 if self.has_focus and self.draw_buttons and self.show_buttons: 1508 ctx.save() 1509 #theme1 = gtk.icon_theme_get_default() 1510 #ctx.set_source_rgba(0.5,0.5,0.5,0.6) 1511 #self.theme.draw_rounded_rectangle(ctx,(self.width*self.scale)-36,0,5,36,16) 1512 #close = theme1.load_icon ("gtk-close", 16, 0) 1513 #prop = theme1.load_icon ("gtk-properties", 16, 0) 1514 #zoom1 = theme1.load_icon ("gtk-zoom-in", 16, 0) 1515 #zoom2 = theme1.load_icon ("gtk-zoom-out", 16, 0) 1516 #close = gtk.image_new_from_stock(gtk.STOCK_CLOSE, 16) 1517 ctx.translate((self.width*self.scale)-16,0) 1518 ctx.set_source_pixbuf(self.closeb, 0, 0) 1519 ctx.paint() 1520 ctx.restore() 1521 ctx.save() 1522 ctx.translate((self.width*self.scale)-32,0) 1523 ctx.set_source_pixbuf(self.prop, 0, 0) 1524 ctx.paint() 1525 ctx.restore() 1526 # shape the window acording if the window is composited or not 1527 1528 if self.window.is_composited(): 1529 1530 self.on_draw_shape(ctx) 1531 # and cut window with mask 1532 self.window.input_shape_combine_mask(self.__shape_bitmap, 0, 0) 1533 else: 1534 try: self.on_draw(ctx) #Works better then the shape method on non composited windows 1535 except: self.on_draw_shape(ctx) # if error on on_draw use standard shape method 1536 # and cut window with mask 1537 self.window.shape_combine_mask(self.__shape_bitmap,0,0) 1538 self.on_update_shape()
1539
1540 - def update_shape_non_composited (self):
1541 """TEST: This function is intended to shape the window whenever no 1542 composited environment can be found. (NOT WORKING YET!!!!)""" 1543 #pixbuf = gtk.gdk.GdkPixbuf.new_from_file) 1544 # calculate new width/height of shape bitmap 1545 w = int(self.width * self.scale) 1546 h = int(self.height * self.scale) 1547 # if 0 set it to 100 to avoid crashes and stay interactive 1548 if w==0: w = 100 1549 if h==0: h = 100 1550 # if size changed, recreate shape bitmap 1551 if w != self.__shape_bitmap_width or h != self.__shape_bitmap_height: 1552 data = ''.zfill(w*h) 1553 self.__shape_bitmap = gtk.gdk.pixbuf_new_from_data(data, 1554 gtk.gdk.COLORSPACE_RGB, True, 1, w, h, w) 1555 self.__shape_bitmap_width = w 1556 self.__shape_bitmap_height = h 1557 # and render window contents to it 1558 # TOOD!! 1559 if self.__shape_bitmap: 1560 # create new mask 1561 (pixmap,mask) = self.__shape_bitmap.render_pixmap_and_mask(255) 1562 # apply new mask to window 1563 self.window.shape_combine_mask(mask)
1564 1565 # ---------------------------------------------------------------------- 1566 # Screenlet's event-handler dummies 1567 # ---------------------------------------------------------------------- 1568
1569 - def on_delete (self):
1570 """Called when the Screenlet gets deleted. Return True to cancel. 1571 TODO: sometimes not properly called""" 1572 return not show_question(self, _("To quit all %s's, use 'Quit' instead. ") % self.__class__.__name__ +\ 1573 _('Really delete this %s and its settings?') % self.get_short_name()) 1574 """return not show_question(self, 'Deleting this instance of the '+\ 1575 self.__name__ + ' will also delete all your personal '+\ 1576 'changes you made to it!! If you just want to close the '+\ 1577 'application, use "Quit" instead. Are you sure you want to '+\ 1578 'delete this instance?') 1579 return False"""
1580 1581 # TODO: on_drag 1582 # TODO: on_drag_end 1583
1584 - def on_after_set_atribute(self,name, value):
1585 """Called after setting screenlet atributes""" 1586 pass
1587
1588 - def on_before_set_atribute(self,name, value):
1589 """Called before setting screenlet atributes""" 1590 pass
1591 1592
1593 - def on_create_drag_icon (self):
1594 """Called when the screenlet's drag-icon is created. You can supply 1595 your own icon and mask by returning them as a 2-tuple.""" 1596 return (None, None)
1597
1598 - def on_map(self):
1599 """Called when screenlet was mapped""" 1600 pass
1601
1602 - def on_unmap(self):
1603 """Called when screenlet was unmapped""" 1604 pass
1605
1606 - def on_composite_changed(self):
1607 """Called when composite state has changed""" 1608 pass
1609 1610
1611 - def on_drag_begin (self, drag_context):
1612 """Called when the Screenlet gets dragged.""" 1613 pass
1614
1615 - def on_drag_enter (self, drag_context, x, y, timestamp):
1616 """Called when something gets dragged into the Screenlets area.""" 1617 pass
1618
1619 - def on_drag_leave (self, drag_context, timestamp):
1620 """Called when something gets dragged out of the Screenlets area.""" 1621 pass
1622
1623 - def on_draw (self, ctx):
1624 """Callback for drawing the Screenlet's window - override 1625 in subclasses to implement your own drawing.""" 1626 pass
1627
1628 - def on_draw_shape (self, ctx):
1629 """Callback for drawing the Screenlet's shape - override 1630 in subclasses to draw the window's input-shape-mask.""" 1631 pass
1632
1633 - def on_drop (self, x, y, sel_data, timestamp):
1634 """Called when a selection is dropped on this Screenlet.""" 1635 return False
1636
1637 - def on_focus (self, event):
1638 """Called when the Screenlet's window receives focus.""" 1639 pass
1640
1641 - def on_hide (self):
1642 """Called when the Screenlet gets hidden.""" 1643 pass
1644
1645 - def on_init (self):
1646 """Called when the Screenlet's options have been applied and the 1647 screenlet finished its initialization. If you want to have your 1648 Screenlet do things on startup you should use this handler.""" 1649 pass
1650
1651 - def on_key_down (self, keycode, keyvalue, event=None):
1652 """Called when a key is pressed within the screenlet's window.""" 1653 pass
1654
1655 - def on_load_theme (self):
1656 """Called when the theme is reloaded (after loading, before redraw).""" 1657 pass
1658
1659 - def on_menuitem_select (self, id):
1660 """Called when a menuitem is selected.""" 1661 pass
1662
1663 - def on_mouse_down (self, event):
1664 """Called when a buttonpress-event occured in Screenlet's window. 1665 Returning True causes the event to be not further propagated.""" 1666 return False
1667
1668 - def on_mouse_enter (self, event):
1669 """Called when the mouse enters the Screenlet's window.""" 1670 pass
1671
1672 - def on_mouse_leave (self, event):
1673 """Called when the mouse leaves the Screenlet's window.""" 1674 pass
1675
1676 - def on_mouse_move(self, event):
1677 """Called when the mouse moves in the Screenlet's window.""" 1678 pass
1679
1680 - def on_mouse_up (self, event):
1681 """Called when a buttonrelease-event occured in Screenlet's window. 1682 Returning True causes the event to be not further propagated.""" 1683 return False
1684
1685 - def on_quit (self):
1686 """Callback for handling destroy-event. Perform your cleanup here!""" 1687 return True
1688
1689 - def on_realize (self):
1690 """"Callback for handling the realize-event."""
1691
1692 - def on_scale (self):
1693 """Called when Screenlet.scale is changed.""" 1694 pass
1695
1696 - def on_scroll_up (self):
1697 """Called when mousewheel is scrolled up (button4).""" 1698 pass
1699
1700 - def on_scroll_down (self):
1701 """Called when mousewheel is scrolled down (button5).""" 1702 pass
1703
1704 - def on_show (self):
1705 """Called when the Screenlet gets shown after being hidden.""" 1706 pass
1707
1708 - def on_switch_widget_state (self, state):
1709 """Called when the Screenlet enters/leaves "Widget"-state.""" 1710 pass
1711
1712 - def on_unfocus (self, event):
1713 """Called when the Screenlet's window loses focus.""" 1714 pass
1715
1716 - def on_update_shape(self):
1717 """Called when the Screenlet's window is updating shape""" 1718 pass
1719 # ---------------------------------------------------------------------- 1720 # Screenlet's event-handlers for GTK-events 1721 # ---------------------------------------------------------------------- 1722
1723 - def alpha_screen_changed (self, window, screen=None):
1724 """set colormap for window""" 1725 if screen==None: 1726 screen = window.get_screen() 1727 map = screen.get_rgba_colormap() 1728 if map: 1729 pass 1730 else: 1731 map = screen.get_rgb_colormap() 1732 window.set_colormap(map)
1733
1734 - def button_press (self, widget, event):
1735 1736 #print "Button press" 1737 # set flags for user-handler 1738 1739 1740 # call user-handler for onmousedownbegin_move_drag 1741 if self.on_mouse_down(event) == True: 1742 return True 1743 # unhandled? continue 1744 1745 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: 1746 if self.mousex >= self.width - (16/self.scale): 1747 self.menuitem_callback(widget,'quit_instance') 1748 elif self.mousex <= self.width -(16/self.scale): 1749 self.menuitem_callback(widget,'info') 1750 elif self.lock_position == False: 1751 if event.button == 1: 1752 self.is_dragged = True 1753 widget.begin_move_drag(event.button, int(event.x_root), 1754 int(event.y_root), event.time) 1755 1756 if event.button == 3: 1757 try: 1758 self.__mi_lock.set_active(self.lock_position) 1759 self.__mi_sticky.set_active(self.is_sticky) 1760 self.__mi_widget.set_active(self.is_widget) 1761 self.__mi_keep_above.set_active(self.keep_above) 1762 self.__mi_keep_below.set_active(self.keep_below) 1763 except : pass 1764 self.menu.popup(None, None, None, event.button, event.time) 1765 #elif event.button == 4: 1766 # print "MOUSEWHEEL" 1767 # self.scale -= 0.1 1768 #elif event.button == 5: 1769 # print "MOUSEWHEEL" 1770 # self.scale += 0.1 1771 return False
1772
1773 - def button_release (self, widget, event):
1774 print "Button release" 1775 if event.button==1: 1776 self.focus_in_event(self, None) 1777 self.is_dragged = False # doesn't work!!! we don't get an event when move_drag ends :( ... 1778 if self.on_mouse_up(event): 1779 return True 1780 return False
1781
1782 - def composite_changed(self,widget):
1783 #this handle is called when composition changed 1784 self.remove_shape() # removing previous set shape , this is absolutly necessary 1785 self.window.hide() # hiding the window and showing it again so the window can convert to the right composited state 1786 self.is_sticky = self.is_sticky #changing from non composited to composited makes the screenlets loose sticky state , this fixes that 1787 self.keep_above= self.keep_above 1788 self.keep_below= self.keep_below 1789 self.window.show() 1790 #print 'Compositing method changed to %s' % str(self.window.is_composited()) 1791 self.update_shape() 1792 self.redraw_canvas() 1793 1794 if not self.window.is_composited () : 1795 self.show_buttons = False 1796 self.disable_option("opacity") 1797 # print 'Warning - Buttons will not be shown until screenlet is restarted' 1798 1799 if self.window.is_composited () : 1800 self.enable_option("opacity") 1801 1802 self.is_sticky = self.is_sticky #and again ... 1803 self.keep_above= self.keep_above 1804 self.keep_below= self.keep_below 1805 self.window.set_keep_above(self.keep_above) 1806 self.window.set_keep_below(self.keep_below) 1807 self.on_composite_changed()
1808 1809 # NOTE: this should somehow handle the end of a move_drag-operation
1810 - def configure_event (self, widget, event):
1811 #print "onConfigure" 1812 #print event 1813 #if self.is_dragged == True: 1814 # set new position and cause a save of this Screenlet (not use 1815 # setattr to avoid conflicts with the window.move in __setattr__) 1816 if event.x != self.x: 1817 self.__dict__['x'] = event.x 1818 if self.session: 1819 self.session.backend.save_option(self.id, 'x', str(event.x)) 1820 # self.is_dragged = False 1821 if event.y != self.y: 1822 self.__dict__['y'] = event.y 1823 if self.session: 1824 self.session.backend.save_option(self.id, 'y', str(event.y)) 1825 # self.is_dragged = False 1826 return False
1827
1828 - def delete_event (self, widget, event, data=None):
1829 # cancel event? 1830 print "delete_event" 1831 if self.on_delete() == True: 1832 print "Cancel delete_event" 1833 return True 1834 else: 1835 self.close() 1836 return False
1837
1838 - def destroy (self, widget, data=None):
1839 # call user-defined on_quit-handler 1840 self.on_quit() 1841 #print "destroy signal occurred" 1842 self.emit("screenlet_removed", self) 1843 # close gtk? 1844 if self.quit_on_close: 1845 if self.session: # if we have a session, flush current data 1846 self.session.backend.flush() 1847 gtk.main_quit() 1848 else: 1849 del self # ??? does this really work???
1850
1851 - def drag_begin (self, widget, drag_context):
1852 print "Start drag" 1853 self.is_dragged = True 1854 self.on_drag_begin(drag_context)
1855 #return False 1856
1857 - def drag_data_received (self, widget, dc, x, y, sel_data, info, timestamp):
1858 return self.on_drop(x, y, sel_data, timestamp)
1859
1860 - def drag_end (self, widget, drag_context):
1861 print "End drag" 1862 self.is_dragged = False 1863 return False
1864
1865 - def drag_motion (self, widget, drag_context, x, y, timestamp):
1866 #print "Drag motion" 1867 if self.dragging_over == False: 1868 self.dragging_over = True 1869 self.on_drag_enter(drag_context, x, y, timestamp) 1870 return False
1871
1872 - def drag_leave (self, widget, drag_context, timestamp):
1873 self.dragging_over = False 1874 self.on_drag_leave(drag_context, timestamp) 1875 return
1876
1877 - def enter_notify_event (self, widget, event):
1878 #self.__mouse_inside = True 1879 self.__dict__['mouse_is_over'] = True 1880 self.on_mouse_enter(event)
1881 1882 #self.redraw_canvas() 1883
1884 - def expose (self, widget, event):
1885 ctx = widget.window.cairo_create() 1886 # clear context 1887 self.clear_cairo_context(ctx) 1888 # set a clip region for the expose event 1889 ctx.rectangle(event.area.x, event.area.y, 1890 event.area.width, event.area.height) 1891 ctx.clip() 1892 1893 # scale context 1894 #ctx.scale(self.scale, self.scale) 1895 # call drawing method 1896 self.on_draw(ctx) 1897 if self.show_buttons and self.draw_buttons and self.has_focus: 1898 self.create_buttons() 1899 # and delete context (needed?) 1900 del ctx 1901 return False
1902
1903 - def focus_in_event (self, widget, event):
1904 if self.skip_taskbar==False or self.skip_pager==False or self.is_dragged==True or event is None: 1905 #Screenlet always gets focus after being dragged so this is a good method 1906 #to control the end of a move_drag operation!!!!! 1907 #This code happens on the end of a move_drag 1908 self.is_dragged=False 1909 self.has_focus = True 1910 self.on_focus(event) 1911 self.update_shape() 1912 self.redraw_canvas()
1913 1914 1915 1916
1917 - def focus_out_event (self, widget, event):
1918 if self.is_dragged==False: 1919 self.has_focus = False 1920 self.on_unfocus(event) 1921 self.update_shape() 1922 self.redraw_canvas()
1923 1924 1925
1926 - def key_press (self, widget, event):
1927 """Handle keypress events, needed for in-place editing.""" 1928 self.on_key_down(event.keyval, event.string, event)
1929
1930 - def leave_notify_event (self, widget, event):
1931 #self.__mouse_inside = False 1932 #self.is_dragged = False 1933 self.__dict__['mouse_is_over'] = False 1934 self.on_mouse_leave(event)
1935 1936 #self.redraw_canvas() 1937
1938 - def menuitem_callback (self, widget, id):
1939 if id == "delete": 1940 if not self.on_delete(): 1941 # remove instance 1942 self.session.delete_instance (self.id) 1943 # notify about being rmeoved (does this get send???) 1944 self.service.instance_removed(self.id) 1945 elif id == "quit_instance": 1946 print 'Quitting current screenlet instance' 1947 self.session.quit_instance (self.id) 1948 self.service.instance_removed(self.id) 1949 elif id == "quit": 1950 self.close() 1951 elif id == "add": 1952 self.service.add("") 1953 elif id in ("info", "about", "settings", "options", "properties"): 1954 # show settings dialog 1955 self.show_settings_dialog() 1956 elif id.startswith('scale:'): 1957 self.scale = float(id[6:]) 1958 elif id[:5] == "size:": # DEPRECATED?? 1959 # set size and update shape (redraw is done by setting height) 1960 #self.__dict__['width'] = int(id[5:]) 1961 self.width = int(id[5:]) 1962 self.height = int(id[5:]) 1963 self.update_shape() 1964 elif id[:6]=="theme:": 1965 print "Screenlet: Set theme %s" % id[6:] 1966 # set theme 1967 self.theme_name = id[6:] 1968 elif id[:8] == "setting:": 1969 # set a boolean option to the opposite state 1970 try: 1971 if type(self.__dict__[id[8:]]) == bool: 1972 self.__dict__[id[8:]] = not self.__dict__[id[8:]] # UNSAFE!! 1973 except: 1974 print "Error: Cannot set missing or non-boolean value '"\ 1975 + id[8:] + "'" 1976 elif id[:7] == "option:": 1977 # NOTE: this part should be removed and XML-menus 1978 # should be used by default ... maybe 1979 # set option 1980 if id[7:]=="lock": 1981 if self.__mi_lock.get_active () != self.lock_position: 1982 self.lock_position = not self.lock_position 1983 elif id[7:]=="sticky": 1984 if self.__mi_sticky.get_active () != self.is_sticky: 1985 self.is_sticky = not self.is_sticky 1986 #widget.toggle() 1987 elif id[7:]=="widget": 1988 if self.__mi_widget.get_active () != self.is_widget: 1989 self.is_widget = not self.is_widget 1990 elif id[7:]=="keep_above": 1991 if self.__mi_keep_above.get_active () != self.keep_above: 1992 self.keep_above = not self.keep_above 1993 self.__mi_keep_above.set_active(self.keep_above) 1994 if self.keep_below and self.keep_above : 1995 self.keep_below = False 1996 self.__mi_keep_below.set_active(False) 1997 elif id[7:]=="keep_below": 1998 if self.__mi_keep_below.get_active () != self.keep_below: 1999 self.keep_below = not self.keep_below 2000 self.__mi_keep_below.set_active(self.keep_below) 2001 if self.keep_below and self.keep_above : 2002 self.keep_above = False 2003 self.__mi_keep_above.set_active(False) 2004 else: 2005 #print "Item: " + string 2006 pass 2007 # call user-handler 2008 self.on_menuitem_select(id) 2009 return False
2010
2011 - def map_event(self, widget, event):
2012 self.on_map()
2013
2014 - def unmap_event(self, widget, event):
2015 self.on_unmap()
2016
2017 - def motion_notify_event(self, widget, event):
2018 self.__dict__['mousex'] = event.x / self.scale 2019 self.__dict__['mousey'] = event.y / self.scale 2020 2021 self.on_mouse_move(event)
2022
2023 - def realize_event (self, widget):
2024 """called when window has been realized""" 2025 if self.window.window: 2026 self.window.window.set_back_pixmap(None, False) # needed? 2027 2028 self.on_realize()
2029
2030 - def scroll_event (self, widget, event):
2031 if event.direction == gtk.gdk.SCROLL_UP: 2032 if self.has_focus and self.is_sizable and self.resize_on_scroll: self.scale = self.scale +0.1 2033 self.on_scroll_up() 2034 elif event.direction == gtk.gdk.SCROLL_DOWN: 2035 if self.has_focus and self.is_sizable and self.resize_on_scroll: self.scale = self.scale -0.1 2036 self.on_scroll_down() 2037 return False
2038 2039
2040 - def show_notification (self,text):
2041 """Show notification window at current mouse position.""" 2042 if self.notify == None: 2043 self.notify = Notify() 2044 self.notify.text = text 2045 self.notify.show()
2046
2047 - def hide_notification (self):
2048 """hide notification window""" 2049 if self.notify != None: 2050 self.notify.hide() 2051 self.notify = None
2052
2053 - def show_tooltip (self,text,tooltipx,tooltipy):
2054 """Show tooltip window at current mouse position.""" 2055 if self.tooltip == None: 2056 self.tooltip = Tooltip(300, 400) 2057 self.tooltip.text = text 2058 self.tooltip.x = tooltipx 2059 self.tooltip.y = tooltipy 2060 self.tooltip.show() 2061 else: 2062 #self.tooltip = Tooltip(300, 400) 2063 self.tooltip.text = text 2064 self.tooltip.x = tooltipx 2065 self.tooltip.y = tooltipy
2066 #self.tooltip.show() 2067
2068 - def hide_tooltip (self):
2069 """hide tooltip window""" 2070 if self.tooltip != None: 2071 self.tooltip.hide() 2072 self.tooltip = None
2073 2074 # TEST!!!
2075 -class ShapedWidget (gtk.DrawingArea):
2076 """A simple base-class for creating owner-drawn gtk-widgets""" 2077 2078 __widget=None 2079 2080 mouse_inside = False 2081 width = 32 2082 height = 32 2083
2084 - def __init__ (self, width, height):
2085 # call superclass 2086 super(ShapedWidget, self).__init__() 2087 # create/setup widget 2088 #self.__widget = gtk.Widget() 2089 self.set_app_paintable(True) 2090 self.set_size_request(width, height) 2091 # connect handlers 2092 self.set_events(gtk.gdk.ALL_EVENTS_MASK) 2093 self.connect("expose-event", self.expose_event) 2094 self.connect("button-press-event", self.button_press) 2095 self.connect("button-release-event", self.button_release) 2096 self.connect("enter-notify-event", self.enter_notify) 2097 self.connect("leave-notify-event", self.leave_notify)
2098 2099 # EXPERIMENTAL: TODO: cache bitmap until size changes
2100 - def update_shape (self):
2101 """update widget's shape (only call this when shape has changed)""" 2102 data = "" 2103 for i in xrange(self.width*self.height): 2104 data += "0" 2105 bitmap = gtk.gdk.bitmap_create_from_data(None, 2106 data, self.width, self.height) 2107 ctx = bitmap.cairo_create() 2108 ctx.set_source_rgba(1, 1, 1, 0) 2109 ctx.set_operator (cairo.OPERATOR_SOURCE) 2110 ctx.paint() 2111 self.draw_shape(ctx) 2112 self.input_shape_combine_mask(bitmap, 0, 0) 2113 print "Updating shape."
2114
2115 - def button_press (self, widget, event):
2116 if event.button==1: 2117 print "left button pressed!" 2118 return False
2119
2120 - def button_release (self, widget, event):
2121 #if event.button==1: 2122 #print "left button release!" 2123 return False
2124
2125 - def enter_notify (self, widget, event):
2126 self.mouse_inside = True 2127 self.queue_draw()
2128 #print "mouse enter" 2129
2130 - def leave_notify (self, widget, event):
2131 self.mouse_inside = False 2132 self.queue_draw()
2133 #print "mouse leave" 2134
2135 - def draw (self, ctx):
2136 pass
2137
2138 - def draw_shape (self, ctx):
2139 self.draw(ctx)
2140
2141 - def expose_event (self, widget, event):
2142 ctx = widget.window.cairo_create() 2143 # set a clip region for the expose event 2144 ctx.rectangle(event.area.x, event.area.y, 2145 event.area.width, event.area.height) 2146 ctx.clip() 2147 # clear context 2148 ctx.set_source_rgba(1, 1, 1, 0) 2149 ctx.set_operator (cairo.OPERATOR_SOURCE) 2150 ctx.paint() 2151 # call drawing method 2152 self.draw(ctx) 2153 # and delete context 2154 del ctx 2155 return False
2156
2157 -class Tooltip(object):
2158 """A window that displays a text and serves as Tooltip (very basic yet).""" 2159 2160 # internals 2161 __timeout = None 2162 2163 # attribs 2164 text = '' 2165 font_name = 'FreeSans 9' 2166 width = 100 2167 height = 20 2168 x = 0 2169 y = 0 2170
2171 - def __init__ (self, width, height):
2172 object.__init__(self) 2173 # init 2174 self.__dict__['width'] = width 2175 self.__dict__['height'] = height 2176 self.window = gtk.Window() 2177 self.window.set_app_paintable(True) 2178 self.window.set_size_request(width, height) 2179 self.window.set_decorated(False) 2180 self.window.set_accept_focus(False) 2181 self.window.set_skip_pager_hint(True) 2182 self.window.set_skip_taskbar_hint(True) 2183 self.window.set_keep_above(True) 2184 self.screen_changed(self.window) 2185 self.window.connect("expose_event", self.expose) 2186 self.window.connect("screen-changed", self.screen_changed) 2187 #self.window.show() 2188 self.p_context = self.window.get_pango_context() 2189 self.p_layout = pango.Layout(self.p_context) 2190 self.p_layout.set_font_description(\ 2191 pango.FontDescription(self.font_name)) 2192 #self.p_layout.set_width(-1) 2193 self.p_layout.set_width(width * pango.SCALE - 6)
2194
2195 - def __setattr__ (self, name, value):
2196 self.__dict__[name] = value 2197 if name in ('width', 'height', 'text'): 2198 if name== 'width': 2199 self.p_layout.set_width(width) 2200 elif name == 'text': 2201 self.p_layout.set_markup(value) 2202 ink_rect, logical_rect = self.p_layout.get_pixel_extents() 2203 self.height = min(max(logical_rect[3], 16), 400) + 6 2204 self.window.set_size_request(self.width, self.height) 2205 self.window.queue_draw() 2206 elif name == 'x': 2207 self.window.move(int(value), int(self.y)) 2208 elif name == 'y': 2209 self.window.move(int(self.x), int(value))
2210
2211 - def show (self):
2212 """Show the Tooltip window.""" 2213 self.cancel_show() 2214 self.window.show() 2215 self.window.set_keep_above(True)
2216
2217 - def show_delayed (self, delay):
2218 """Show the Tooltip window after a given delay.""" 2219 self.cancel_show() 2220 self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
2221
2222 - def hide (self):
2223 """Hide the Tooltip window.""" 2224 self.cancel_show() 2225 self.window.destroy()
2226
2227 - def cancel_show (self):
2228 """Cancel showing of the Tooltip.""" 2229 if self.__timeout: 2230 gobject.source_remove(self.__timeout) 2231 self.p_context = None 2232 self.p_layout = None
2233
2234 - def __show_timeout (self):
2235 self.show()
2236
2237 - def screen_changed (self, window, screen=None):
2238 if screen == None: 2239 screen = window.get_screen() 2240 map = screen.get_rgba_colormap() 2241 if not map: 2242 map = screen.get_rgb_colormap() 2243 window.set_colormap(map)
2244
2245 - def expose (self, widget, event):
2246 ctx = self.window.window.cairo_create() 2247 ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ? 2248 # set a clip region for the expose event 2249 ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height) 2250 ctx.clip() 2251 # clear context 2252 ctx.set_source_rgba(1, 1, 1, 0) 2253 ctx.set_operator (cairo.OPERATOR_SOURCE) 2254 ctx.paint() 2255 # draw rectangle 2256 ctx.set_source_rgba(1, 1, 0.5, 1) 2257 ctx.rectangle(0, 0, self.width, self.height) 2258 ctx.fill() 2259 # draw text 2260 ctx.save() 2261 ctx.translate(3, 3) 2262 ctx.set_source_rgba(0, 0, 0, 1) 2263 ctx.show_layout(self.p_layout) 2264 ctx.fill() 2265 ctx.restore() 2266 ctx.rectangle(0, 0, self.width, self.height) 2267 ctx.set_source_rgba(0, 0, 0, 0.7) 2268 ctx.stroke()
2269
2270 -class Notify(object):
2271 """A window that displays a text and serves as Notification (very basic yet).""" 2272 2273 # internals 2274 __timeout = None 2275 2276 # attribs 2277 text = '' 2278 font_name = 'FreeSans 9' 2279 width = 200 2280 height = 100 2281 x = 0 2282 y = 0 2283 gradient = cairo.LinearGradient(0, 100,0, 0) 2284
2285 - def __init__ (self):
2286 object.__init__(self) 2287 # init 2288 self.window = gtk.Window() 2289 self.window.set_app_paintable(True) 2290 self.window.set_size_request(self.width, self.height) 2291 self.window.set_decorated(False) 2292 self.window.set_accept_focus(False) 2293 self.window.set_skip_pager_hint(True) 2294 self.window.set_skip_taskbar_hint(True) 2295 self.window.set_keep_above(True) 2296 self.screen_changed(self.window) 2297 self.window.connect("expose_event", self.expose) 2298 self.window.connect("screen-changed", self.screen_changed) 2299 #self.window.show() 2300 self.p_context = self.window.get_pango_context() 2301 self.p_layout = pango.Layout(self.p_context) 2302 self.p_layout.set_font_description(\ 2303 pango.FontDescription(self.font_name)) 2304 #self.p_layout.set_width(-1) 2305 self.p_layout.set_width(self.width * pango.SCALE - 6)
2306
2307 - def __setattr__ (self, name, value):
2308 self.__dict__[name] = value 2309 if name in ('text'): 2310 if name == 'text': 2311 self.p_layout.set_markup(value) 2312 ink_rect, logical_rect = self.p_layout.get_pixel_extents() 2313 self.window.queue_draw()
2314
2315 - def show (self):
2316 """Show the Notify window.""" 2317 self.window.move(gtk.gdk.screen_width() - self.width, gtk.gdk.screen_height() - self.height) 2318 self.cancel_show() 2319 self.window.show() 2320 self.window.set_keep_above(True)
2321
2322 - def show_delayed (self, delay):
2323 """Show the Notify window after a given delay.""" 2324 self.cancel_show() 2325 self.__timeout = gobject.timeout_add(delay, self.__show_timeout)
2326
2327 - def hide (self):
2328 """Hide the Notify window.""" 2329 self.cancel_show() 2330 self.window.destroy()
2331
2332 - def cancel_show (self):
2333 """Cancel showing of the Notify.""" 2334 if self.__timeout: 2335 gobject.source_remove(self.__timeout) 2336 self.p_context = None 2337 self.p_layout = None
2338
2339 - def __show_timeout (self):
2340 self.show()
2341
2342 - def screen_changed (self, window, screen=None):
2343 if screen == None: 2344 screen = window.get_screen() 2345 map = screen.get_rgba_colormap() 2346 if not map: 2347 map = screen.get_rgb_colormap() 2348 window.set_colormap(map)
2349
2350 - def expose (self, widget, event):
2351 ctx = self.window.window.cairo_create() 2352 ctx.set_antialias (cairo.ANTIALIAS_SUBPIXEL) # ? 2353 # set a clip region for the expose event 2354 ctx.rectangle(event.area.x, event.area.y,event.area.width, event.area.height) 2355 ctx.clip() 2356 # clear context 2357 ctx.set_source_rgba(1, 1, 1, 0) 2358 ctx.set_operator (cairo.OPERATOR_SOURCE) 2359 ctx.paint() 2360 # draw rectangle 2361 self.gradient.add_color_stop_rgba(1,0.3, 0.3, 0.3, 0.9) 2362 self.gradient.add_color_stop_rgba(0.3, 0, 0, 0, 0.9) 2363 ctx.set_source(self.gradient) 2364 ctx.rectangle(0, 0, self.width, self.height) 2365 ctx.fill() 2366 # draw text 2367 ctx.save() 2368 ctx.translate(3, 3) 2369 ctx.set_source_rgba(1, 1, 1, 1) 2370 ctx.show_layout(self.p_layout) 2371 ctx.fill() 2372 ctx.restore() 2373 ctx.rectangle(0, 0, self.width, self.height) 2374 ctx.set_source_rgba(0, 0, 0, 0.7) 2375 ctx.stroke()
2376 2377 # TEST (as the name implies) 2378 """class TestWidget(ShapedWidget): 2379 2380 def __init__(self, width, height): 2381 #ShapedWidget.__init__(self, width, height) 2382 super(TestWidget, self).__init__(width, height) 2383 2384 def draw(self, ctx): 2385 if self.mouse_inside: 2386 ctx.set_source_rgba(1, 0, 0, 0.8) 2387 else: 2388 ctx.set_source_rgba(1, 1, 0, 0.8) 2389 ctx.rectangle(0, 0, 32, 32) 2390 ctx.fill() 2391 """ 2392 2393 2394 # ------------------------------------------------------------------------------ 2395 # MODULE-FUNCTIONS 2396 # ------------------------------------------------------------------------------ 2397 2398 # the new recommended way of launching a screenlet from the "outside"
2399 -def launch_screenlet (name, debug=False):
2400 """Launch a screenlet, either through its service or by launching a new 2401 process of the given screenlet. Name has to be the name of the Screenlet's 2402 class without trailing 'Screenlet'. 2403 NOTE: we could only launch the file here""" 2404 # check for service 2405 if services.service_is_running(name): 2406 # add screenlet through service, if running 2407 srvc = services.get_service_by_name(name) 2408 if srvc: 2409 try: 2410 srvc.add('') # empty string for auto-creating ID 2411 return True 2412 except Exception, ex: 2413 print "Error while adding instance by service: %s" % ex 2414 # service not running or error? launch screenlet's file 2415 path = utils.find_first_screenlet_path(name) 2416 if path: 2417 # get full path of screenlet's file 2418 slfile = path + '/' + name + 'Screenlet.py' 2419 # launch screenlet as separate process 2420 print "Launching Screenlet from: %s" % slfile 2421 if debug: 2422 print "Logging output goes to: $HOME/.config/Screenlets/%sScreenlet.log" % name 2423 out = '$HOME/.config/Screenlets/%sScreenlet.log' % name 2424 else: 2425 out = '/dev/null' 2426 os.system('python -u %s > %s &' % (slfile, out)) 2427 return True 2428 else: 2429 print "Screenlet '%s' could not be launched." % name 2430 return False
2431
2432 -def show_message (screenlet, message, title=''):
2433 """Show a message for the given Screenlet (may contain Pango-Markup). 2434 If screenlet is None, this function can be used by other objects as well.""" 2435 if screenlet == None: 2436 md = gtk.MessageDialog(None, type=gtk.MESSAGE_INFO, 2437 buttons=gtk.BUTTONS_OK) 2438 md.set_title(title) 2439 else: 2440 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_INFO, 2441 buttons=gtk.BUTTONS_OK) 2442 md.set_title(screenlet.__name__) 2443 md.set_markup(message) 2444 md.run() 2445 md.destroy()
2446
2447 -def show_question (screenlet, message, title=''):
2448 """Show a question for the given Screenlet (may contain Pango-Markup).""" 2449 if screenlet == None: 2450 md = gtk.MessageDialog(None, type=gtk.MESSAGE_QUESTION, 2451 buttons=gtk.BUTTONS_YES_NO) 2452 md.set_title(title) 2453 else: 2454 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_QUESTION, 2455 buttons=gtk.BUTTONS_YES_NO) 2456 md.set_title(screenlet.__name__) 2457 md.set_markup(message) 2458 response = md.run() 2459 md.destroy() 2460 if response == gtk.RESPONSE_YES: 2461 return True 2462 return False
2463
2464 -def show_error (screenlet, message, title='Error'):
2465 """Show an error for the given Screenlet (may contain Pango-Markup).""" 2466 if screenlet == None: 2467 md = gtk.MessageDialog(None, type=gtk.MESSAGE_ERROR, 2468 buttons=gtk.BUTTONS_OK) 2469 md.set_title(title) 2470 else: 2471 md = gtk.MessageDialog(screenlet.window, type=gtk.MESSAGE_ERROR, 2472 buttons=gtk.BUTTONS_OK) 2473 md.set_title(screenlet.__name__) 2474 md.set_markup(message) 2475 md.run() 2476 md.destroy()
2477
2478 -def fatal_error (message):
2479 """Raise a fatal error to stdout and stderr and exit with an errorcode.""" 2480 import sys 2481 msg = 'FATAL ERROR: %s\n' % message 2482 sys.stdout.write(msg) 2483 sys.stderr.write(msg) 2484 sys.exit(1)
2485 2486 # LEGACY support: functions that are not used any longer (raise fatal error) 2487
2488 -def create_new_instance (name):
2489 fatal_error("This screenlet seems to be written for an older version of the framework. Please download a newer version of the %s." % name)
2490