Subversion Repositories blueman

[/] [trunk/] [apps/] [blueman-applet] - Rev 600

Compare with Previous | Blame | Download | View Log

#!/usr/bin/python

# Copyright (C) 2008 Valmantas Paliksa <walmis at balticum-tv dot lt>
# Copyright (C) 2008 Tadas Dailyda <tadas at dailyda dot com>
#
# Licensed under the GNU General Public License Version 3
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 

import sys
from subprocess import *
import os.path
import os
import pynotify
import dbus
import dbus.glib
import gobject
import gtk
import gtk.gdk
import __builtin__
import traceback

#support running uninstalled
_dirname = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
if os.path.exists(os.path.join(_dirname,"ChangeLog")):
    sys.path.insert(0, _dirname)

from blueman.Constants import *
from blueman.Functions import *

from blueman.main.applet.DbusService import DbusService
from blueman.main.Config import Config

import blueman.plugins.applet
from blueman.plugins.AppletPlugin import AppletPlugin

import blueman.bluez as Bluez

from blueman.main.SignalTracker import SignalTracker

class StopException(Exception):
    pass
    
class LoadException(Exception):
    pass

__builtin__.StopException = StopException

class BluemanApplet(object):

    class Plugins(gobject.GObject):
        __gsignals__ = {
            'plugin-loaded' : (gobject.SIGNAL_NO_HOOKS, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
            'plugin-unloaded' : (gobject.SIGNAL_NO_HOOKS, gobject.TYPE_NONE, (gobject.TYPE_STRING,)),
        }
        
        def __init__(self, applet):
            gobject.GObject.__init__(self)
            self.__plugins = {}
            self.__classes = {}
            self.__deps = {}
            self.__cfls = {}
            self.__loaded = []
            self.applet = applet
            self.__config = Config()

            if self.__config.props.applet_plugins == None:
                self.__config.props.applet_plugins = []                 

            self.__config.connect('property-changed', self.on_property_changed)
        
        
        def on_property_changed(self, config, key, value):
            if key == "applet_plugins":
                if type(value) == list:
                    for item in value:
                        disable = item[0] == "!"
                        if disable:
                            item = item[1:]
                        try:
                            cls = self.__classes[item]
                            if not cls.__unloadable__ and disable:
                                print YELLOW("warning:"), item, "is not unloadable"
                            elif item in self.__loaded and disable:
                                self.Unload(item)
                            elif item not in self.__loaded and not disable:
                                self.Load(item)

                        except KeyError:
                            print YELLOW("warning:"), "Plugin %s not found" % item
                            continue
                            
        def GetClasses(self):
            return self.__classes
            
        def GetLoaded(self):
            return self.__loaded
            
        def GetDependencies(self):
            return self.__deps
            
        def GetConflicts(self):
            return self.__cfls
            
        def Load(self, name=None):
            if name:
                try:
                    self.__load_plugin(self.__classes[name])
                except LoadException:
                    pass
                    
                return 
                
            path = os.path.dirname(blueman.plugins.applet.__file__)
            plugins = []
            for root, dirs, files in os.walk(path):
                for f in files:
                    if f.endswith(".py") and not (f.endswith(".pyc") or f.endswith("_.py")):
                        plugins.append(f[0:-3])

            dprint(plugins)
            for plugin in plugins:
                try:
                    __import__("blueman.plugins.applet.%s" % plugin, None, None, [])
                except ImportError, e:
                    dprint("Unable to load plugin module %s\n%s" % (plugin, e))
            
            
            for cls in AppletPlugin.__subclasses__():
                self.__classes[cls.__name__] = cls
                if not cls.__name__ in self.__deps:
                    self.__deps[cls.__name__] = []
                
                if not cls.__name__ in self.__cfls:
                    self.__cfls[cls.__name__] = []              
                
                for c in cls.__depends__:
                    if not c in self.__deps:
                        self.__deps[c] = []
                    self.__deps[c].append(cls.__name__)

                for c in cls.__conflicts__:
                    if not c in self.__cfls:
                        self.__cfls[c] = []
                    self.__cfls[c].append(cls.__name__) 
                    if c not in self.__cfls[cls.__name__]:
                        self.__cfls[cls.__name__].append(c)
    
            c = self.__config.props.applet_plugins
            for name, cls in self.__classes.iteritems():
                for dep in self.__deps[name]:
                    #plugins that are required by not unloadable plugins are not unloadable too
                    if not self.__classes[dep].__unloadable__:
                        cls.__unloadable__ = False              
                
                if (cls.__autoload__ or cls.__name__ in c) and not (cls.__unloadable__ and "!"+cls.__name__ in c):
                    try:
                        self.__load_plugin(cls)
                    except LoadException:
                        pass
                        
        def Disabled(self, plugin):
            plugins = self.__config.props.applet_plugins
            return "!"+plugin in plugins
            
        def Enabled(self, plugin):
            plugins = self.__config.props.applet_plugins
            return plugin in plugins

        def __load_plugin(self, cls):
            if cls.__name__ in self.__loaded:
                return
                
            for dep in cls.__depends__:
                if not dep in self.__loaded:
                    if not dep in self.__classes:
                        raise "Could not satisfy dependency %s -> %s" % (cls.__name__, dep)
                    try:
                        self.__load_plugin(self.__classes[dep]) 
                    except Exception, e:
                        dprint(e)
                        return
                        
            for cfl in self.__cfls[cls.__name__]:
                if cfl in self.__classes:
                    if self.__classes[cfl].__priority__ > cls.__priority__ and not self.Disabled(cfl) and not self.Enabled(cls.__name__):
                        dprint("Not loading %s because it's conflict has higher priority" % cls.__name__)
                        return
                    
                if cfl in self.__loaded:
                    if cls.__priority__ > self.__classes[cfl].__priority__ and not self.Enabled(cfl):
                        self.Unload(cfl)
                    else:
                        raise LoadException("Not loading conflicting plugin %s due to lower priority" % cls.__name__)
            
            dprint("loading", cls)
            inst = cls(self.applet)
            try:
                inst._load(self.applet)
            except Exception, e:
                dprint("Failed to load %s\n%s" % (cls.__name__, e))
                if not cls.__unloadable__:
                    os._exit(1)
                
            else:
                self.__plugins[cls.__name__] = inst
            
                self.__loaded.append(cls.__name__)
                self.emit("plugin-loaded", cls.__name__)
            
        def __getattr__(self, key):
            try:
                return self.__plugins[key]
            except:
                return self.__dict__[key]

        def Unload(self, name):
            if self.__classes[name].__unloadable__:
                for d in self.__deps[name]:
                    self.Unload(d)
                    
                if name in self.__loaded:
                    dprint("Unloading %s" % name)
                    try:
                        inst = self.__plugins[name]
                        inst._unload()
                    except NotImplementedError:
                        print "Plugin cannot be unloaded"
                    else:
                        self.__loaded.remove(name)
                        del self.__plugins[name]
                        self.emit("plugin-unloaded", name)
                    
            else:
                raise Exception("Plugin %s is not unloadable" % name)
        
        def SetConfig(self, plugin, state):
            plugins = self.__config.props.applet_plugins 
            if plugin in plugins:
                plugins.remove(plugin)
            elif "!"+plugin in plugins:
                plugins.remove("!"+plugin)
                
            plugins.append("!"+plugin if not state else plugin)
            self.__config.props.applet_plugins = plugins
            
        def get_plugins(self):
            return self.__plugins
                        
        #executes a function on all plugin instances
        def Run(self, function, *args, **kwargs):
            rets = []
            for inst in AppletPlugin.instances:
                try:
                    ret = getattr(inst, function)(*args, **kwargs)
                    rets.append(ret)
                except Exception, e:
                    dprint("Function", function, "on", inst.__class__.__name__, "Failed")
                    traceback.print_exc()
                
            return rets
            
        #executes a function on all plugin instances, runs a callback after each plugin returns something
        def RunEx(self, function, callback, *args, **kwargs):
            for inst in AppletPlugin.instances:
                ret = getattr(inst, function)(*args, **kwargs)
                try:
                    ret = callback(inst, ret) 
                except StopException:
                    return
                except Exception, e:
                    dprint("Function", function, "on", inst.__class__.__name__, "Failed")
                    traceback.print_exc()
                    return
                    
                if ret != None:
                    args = ret

                    
    def __init__(self):
        setup_icon_path()
        if not pynotify.init("Blueman"):
            dprint("Error: Failed to init pynotify")
        
        check_single_instance("blueman-applet")


        self.Manager = None
        self.DbusSvc = DbusService(self)
        self.Signals = SignalTracker()
        
        self.Plugins = BluemanApplet.Plugins(self)
        self.Plugins.Load()
        

            
        self.Plugins.Run("on_plugins_loaded")
    

        self.bus = dbus.SystemBus()
        self.bus.watch_name_owner("org.bluez", self.on_dbus_name_owner_change)
        
        self.bus.add_signal_receiver(self.on_adapter_property_changed, "PropertyChanged", "org.bluez.Adapter", "org.bluez", path_keyword="path")


        gtk.main()
        
    
    def manager_init(self):
        try:

            self.Signals.DisconnectAll()
            self.Manager = Bluez.Manager("gobject")
            self.Plugins.Run("on_manager_state_changed", True)

            self.Signals.Handle("bluez", self.Manager, self.on_adapter_removed, "AdapterRemoved")
            self.Signals.Handle("bluez", self.Manager, self.on_adapter_added, "AdapterAdded")
        
        except dbus.exceptions.DBusException, e:
            dprint(e)
            self.manager_deinit()
            dprint("Bluez DBus API not available. Listening for DBus name ownership changes")
    
    def manager_deinit(self):
        self.Signals.DisconnectAll()
        self.Manager = None
        self.Plugins.Run("on_manager_state_changed", False)
        
    def on_dbus_name_owner_change(self, owner):
        dprint("org.bluez owner changed to", owner)
        if owner == "":
            self.manager_deinit()
        elif self.Manager == None:
            self.manager_init()
        
    def on_adapter_property_changed(self, key, value, path):
        self.Plugins.Run("on_adapter_property_changed", path, key, value)
    
    def on_adapter_added(self, path):
        dprint("Adapter added ", path)
        def on_activate():
            dprint("Adapter activated")
            self.Plugins.Run("on_adapter_added", path)

        adapter = Bluez.Adapter(path)
        wait_for_adapter(adapter, on_activate)
        
    def on_adapter_removed(self, path):
        dprint("Adapter removed ", path)
        self.Plugins.Run("on_adapter_removed", path)

BluemanApplet()

Compare with Previous | Blame | Download | View Log