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

Source Code for Package screenlets

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