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

Source Code for Package screenlets

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