screenlets

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
    1153 -        def add_default_menuitems (self, flags=DefaultMenuItem.STANDARD):
    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
    1741 -        def redraw_canvas_and_update_shape (self):
    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