#!/usr/bin/python
# -*- coding: UTF-8 -*-
"""
Copyright 2006-2007 Sebastian Kügler, Canonical Ltd, Luka Renko

Authors: 
    Sebastian Kügler <sebas@kde.org>
    Jonathan Riddell <jriddell@ubuntu.com>
    Luka Renko <lure@kubuntu.org>

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 2 of the License, or
(at your option) any later version.

"""

"""
A frontend to HAL's power features for KDE.
Supports screen brightness, battery level, plugged/unplugged notices, laptop lid closing actions
Specification at https://wiki.kubuntu.org/KubuntuPowerManagement

Issues:
    - We have to keep polling HAL rather than listening for signals because the Python DBUS bindings
      don't have Qt mainloop integration
    - Written in Python so will be slow to load up, will probably port to C++ Qt 4.2 in future
    - Should also handle UPS and bluetooth batteries
    - systray applet should be hidden if no battery, but then how do you suspend if no battery?  
      (ksmserver integration please)
    - Needs lots more testing
    - Use KUniqueApplication again as soon as dcop problem is sorted outc
    - dcop calls need patch to dcopexport.py, already submitted upstream
"""

import os
import sys
import subprocess
import dbus

from qt import *
from tdecore import *
from tdeui import *

from dcopext import DCOPClient, DCOPApp # used to lock the screen
from dcopexport import DCOPExObj

from guidance_power_manager_ui import PowerManagerUI
from notify import NotifyWidget
from tooltip import ToolTip

from powermanage import *

POLL_INTERVAL = 5000 # in milliseconds

class Notify(NotifyWidget):
    """ Pop up a passive notication windows. """

    def __init__(self,parent,msg,icon,caption=None):
        NotifyWidget.__init__(self,parent,"notify")
        self.setIcon(icon)
        self.setText(msg)
        if caption:
            self.Caption(caption)

    def setIcon(self,pixmap):
        """ Set an icon to be displayed in the notification. """
        if pixmap:
            self.Icon.setPixmap(pixmap)

    def setCaption(self,caption):
        """ Text to show in bold letters. """
        self.Caption.setText(QString("<b>")+caption+QString("</b>"))

    def setText(self,msg):
        """" Set actual notification message. """
        self.Text.setText(msg)


class PowerManager(PowerManagerUI):
    """ Our configuration dialog. """

    def __init__ (self, parent, name):
        PowerManagerUI.__init__(self, parent, name)        
        TDEGlobal.iconLoader().addAppDir("guidance")

        # The systray icon should show and hide the KDialogBase, not only this widget,
        # therefore, it gets our parent as parent.
        self.systray = KSystemTray(parent)
        self.icon = "battery-charging-100"
        self.systray.setPixmap(QPixmap(UserIcon(self.icon)))
        self.connect(self.systray, SIGNAL("quitSelected()"), self.quit)

        # Configuration filename
        self.config = TDEConfig("power-managerrc")

        self.powermanager = PowerManage()

    def prepare(self):
        """ Prepare UI. """
        self._initBrightness()
        self._initLid()
        self._initBattery()
        self.lastidlesec = 0

        self._initConfigKeywords()

        self._initUI(self.parent())

        self.configToUi()

        # Polling: evil.  can't receive signals in python-dbus unless we have a glib mainloop, 
        # so we need to poll
        self.pollTimer = QTimer(self)
        self.connect(self.pollTimer, SIGNAL("timeout()"), self.poll)
        self.pollTimer.start(POLL_INTERVAL) # 5 second poll, maybe make this configurable
        self.poll(False)

        # check CPU freq policy and notify if it was changed
        msg = self.checkCpuFreq()
        if msg != "":
            self.notify(msg)

        self.systray.show()

    def _initBrightness(self):
        """ Check for brightness support and disable widgets if it's not there. """
        if not self.powermanager.hasBrightness:
            self.PoweredBrightnessLabel.hide()
            self.PoweredBrightnessSlider.hide()
            self.BatteryBrightnessLabel.hide()
            self.BatteryBrightnessSlider.hide()

    def _initLid(self):
        """ Check for lid support and disable widgets if it's not there. """
        if not self.powermanager.hasLid:
            self.LaptopLidRadios.setEnabled(False)

    def _initCB(self, combo, options, values):
        """ Initialize QComboBox with proper values from provided options. """
        combo.clear() 
        for option in options:
            combo.insertItem(values[option])

    def _getCB(self, combo, options):
        """ Get current item from QComboBox from config file (string) value. """
        try:
            return options[combo.currentItem()]
        except IndexError:
            return ""

    def _setCB(self, combo, options, default, value):
        """ Set current item in QComboBox from string value. """
        try:
            num = options.index(value)
        except ValueError:
            num = default
            pass
        combo.setCurrentItem(num)

    def _getRB(self, radios, options):
        """ Get current item from QRadioButton from config file (string) value. """
        try:
            return options[radios.selectedId()]
        except IndexError:
            return ""

    def _setRB(self, radios, options, default, value):
        """ Set current item in QRadioButton from string value. """
        try:
            num = options.index(value)
        except ValueError:
            num = default
            pass
        radios.setButton(num)

    def _checkOldConfig(self, value, blank):
        """ Convert old numerical values to keywords. """
        try:
            num_val = int(value)
        except ValueError:
            return value
        if blank:
            if num_val == 0: return 'nothing'
            if num_val == 1: return 'blank'
            if num_val == 2: return 'suspend'
            if num_val == 3: return 'hibernate'
            if num_val == 4: return 'shutdown'
        else:
            if num_val == 0: return 'nothing'
            if num_val == 1: return 'suspend'
            if num_val == 2: return 'hibernate'
            if num_val == 3: return 'shutdown'
        return value


    def _initConfigKeywords(self):
        """ Define helper maps used with config file keywords. """
        # map action keyword to displayed name (l10n)
        self.act_name = {}  
        self.act_name['nothing'] = i18n("Do nothing")
        self.act_name['blank'] = i18n("Blank screen")
        self.act_name['suspend'] = i18n("Suspend")
        self.act_name['hibernate'] = i18n("Hibernate")
        self.act_name['shutdown'] = i18n("Shutdown")

        # map action keyword to action methods
        self.act_call = {}  
        self.act_call['nothing'] = None
        self.act_call['blank'] = self.blankScreen
        self.act_call['suspend'] = self.suspend
        self.act_call['hibernate'] = self.hibernate
        self.act_call['shutdown'] = self.shutdown

        # map action keyword to notification description (l10n)
        self.act_notify = {}  
        self.act_notify['nothing'] = i18n("doing nothing")
        self.act_notify['blank'] = i18n("blanking screen")
        self.act_notify['suspend'] = i18n("suspending")
        self.act_notify['hibernate'] = i18n("hibernating")
        self.act_notify['shutdown'] = i18n("shutting down")

        # map action keyword to action icon used in notification window
        self.act_icon = {}  
        self.act_icon['nothing'] = None
        self.act_icon['blank'] = None
        self.act_icon['suspend'] = SmallIcon("suspend")
        self.act_icon['hibernate'] = SmallIcon("hibernate")
        self.act_icon['shutdown'] = SmallIcon("exit")

        # map policy keyword to displayed name (l10n)
        self.freq_name = {}  
        self.freq_name['dynamic'] = i18n("Dynamic")
        self.freq_name['powersave'] = i18n("Powersave")
        self.freq_name['performance'] = i18n("Performance")

        # map policy keyword to policy change methods
        self.freq_call = {}  
        self.freq_call['dynamic'] = self.setCpuPolicyDynamic
        self.freq_call['powersave'] = self.setCpuPolicyPowersave
        self.freq_call['performance'] = self.setCpuPolicyPerformance


    def _initUI(self, parent):
        """ Build dynamic parts of the UI: context menu and tooltip. """
        self.canSuspend = self.powermanager.canSuspend and not self.config.readBoolEntry("disableSuspend", False)
        self.canHibernate = self.powermanager.canHibernate and not self.config.readBoolEntry("disableHibernate", False)

        # Connect some signals.  Updates in the dialogue apply instantly
        self.connect(self.PoweredBrightnessSlider, SIGNAL("valueChanged(int)"), self.changePoweredBrightness)
        self.connect(self.BatteryBrightnessSlider, SIGNAL("valueChanged(int)"), self.changeBatteryBrightness)

        #Add a blank tooltip, the tooltipgroup signals are then used for our KPassivePopup
        toolTipGroup = QToolTipGroup(self.systray)
        QToolTip.add(self.systray, "", toolTipGroup, "blah")
        self.connect(toolTipGroup, SIGNAL("showTip(const QString&)"), self.showTip)
        self.connect(toolTipGroup, SIGNAL("removeTip()"), self.hideTip)

        # Popup tooltip showing battery level
        self.popup = KPassivePopup(self.systray)
	
        self.tooltip = ToolTip(self.popup)

        self._addBatteryWidgets()

        self._addCpuWidgets()
        self.popup.setView(self.tooltip)

        # fill actions for LID
        self.lid_act = ['nothing', 'blank', 'suspend', 'hibernate', 'shutdown']
        self.lid_act_def = 0 
        # hide LID close actions that are not supported 
        if not self.canSuspend:
            self.laptopClosedSuspend.hide()
        if not self.canHibernate:
            self.laptopClosedHibernate.hide()

        # fill in only CPU policies that are supported by HW
        self.cb_freq = []       # list of supported cpu freq policies
        self.cb_freq_def = 0    # always use first policy as default
        if self.powermanager.hasCpuFreqGovernors:
            self.cb_freq = self.powermanager.getSupportedCpuPolicies()
        if len(self.cb_freq) > 0:
            self._initCB(self.PoweredFreqCombo, self.cb_freq, self.freq_name)
            self._initCB(self.BatteryFreqCombo, self.cb_freq, self.freq_name)
        else:
            self.PoweredFreqLabel.hide()
            self.PoweredFreqCombo.hide()
            self.BatteryFreqLabel.hide()
            self.BatteryFreqCombo.hide()

        # fill actions in Idle/Critical battery combo boxes
        self.cb_act = ['nothing']       # list of supported actions (keywords)
        self.cb_act_def_critical = 0    # default action when critical battery
        if self.canSuspend:
            self.cb_act.append('suspend')
        if self.canHibernate:
            self.cb_act.append('hibernate')
            self.cb_act_def_critical = len(self.cb_act) - 1 # hibernate
        self.cb_act.append('shutdown')
        if self.cb_act_def_critical == 0:
            self.cb_act_def_critical = len(self.cb_act) - 1  # shutdown
        self._initCB(self.PoweredIdleCombo, self.cb_act, self.act_name)
        self._initCB(self.BatteryIdleCombo, self.cb_act, self.act_name)
        self._initCB(self.BatteryCriticalCombo, self.cb_act, self.act_name)

        self.connect(self.PoweredIdleCombo,SIGNAL("activated(int)"),self.slotPoweredIdleActivated)
        self.connect(self.BatteryIdleCombo,SIGNAL("activated(int)"),self.slotBatteryIdleActivated)
        self.connect(self.BatteryCriticalCombo,SIGNAL("activated(int)"),self.slotBatteryCriticalActivated)

        # add suspend/hibernate to tray's context menu
        menu = self.systray.contextMenu()
        if self.canSuspend:
            action = TDEAction( i18n("Suspend"), TDEShortcut(), self.suspend, 
                              self.systray.actionCollection(), "suspend")
            action.setIcon("suspend")
            action.plug(menu)
        if self.canHibernate:
            action = TDEAction( i18n("Hibernate"), TDEShortcut(), self.hibernate, 
                              self.systray.actionCollection(), "hibernate")
            action.setIcon("hibernate")
            action.plug(menu)

        # add list of governators
        if self.powermanager.hasCpuFreqGovernors and len(self.cb_freq) > 0:
            submenu = TDEPopupMenu(menu)
            for policy in self.cb_freq:
                action = TDERadioAction(self.freq_name[policy], TDEShortcut(), 
                                      self.freq_call[policy], 
                                      self.systray.actionCollection(), policy)
                action.setExclusiveGroup("freqs")
                action.plug(submenu)
    
            policy = self.powermanager.getCpuPolicy()
            if policy in self.cb_freq:
                self.systray.actionCollection().action(policy).setChecked(True);
            menu.insertItem(i18n("CPU policy"), submenu)


        # TDEGlobalAccel crashes the application in pytde
        # see http://mats.gmd.de/pipermail/pytde/2006-May/013224.html
        #self.globalActions = TDEGlobalAccel(self)
        #self.suspendShortcut = TDEShortcut("XF86Sleep")
        #self.hibernateShortcut = TDEShortcut("XF86Standby")
        #self.hshutdownShortcut = TDEShortcut("XF86PowerOff")
        #self.globalActions.insert("suspend", i18n("Suspend"), i18n("what's this?"), self.suspendShortcut, #self.suspendShortcut, self.suspend)
        #self.globalActions.updateConnections()

    def _initBattery(self):
        """ Remove non-battery-related widgets if there's no battery bay. """
        if not self.powermanager.hasBattery:
            # Disable the Batterybox in the config dialogue,
            self.BatteryBox.setEnabled(False)
            # And change the icon in the systray, remove the restore option
            # This way, we're basically becoming a systray applet, you can 
            # hibernate and suspend from
            self.systray.setPixmap(QPixmap(UserIcon(self.icon)))
        if self.powermanager.hasAC:
            self.wasOnBattery = self.powermanager.onBattery()

    def configToUi(self):
        """ Setup the the values from the config file in the UI."""
        # brightness.
        if self.powermanager.hasBrightness:
            brightness_high = self.powermanager.brightness_levels
            self.BatteryBrightnessSlider.setMaxValue(self.powermanager.brightness_levels-1)
            self.PoweredBrightnessSlider.setMaxValue(self.powermanager.brightness_levels-1)
            self.BatteryBrightnessSlider.setValue(self.config.readNumEntry("batteryBrightness", int(brightness_high/2))) #default middle
            self.PoweredBrightnessSlider.setValue(self.config.readNumEntry("poweredBrightness", brightness_high)) #default highest

            tt_text = "Every step increases or decreases the brightness by %i%%" % int(100/brightness_high)
            QToolTip.add(self.BatteryBrightnessSlider, tt_text)
            QToolTip.add(self.PoweredBrightnessSlider, tt_text)

        self.lockScreenOnResume.setChecked(self.config.readBoolEntry("lockOnResume", True))

        # Idletime-related configuration
        self._setCB(self.PoweredIdleCombo, self.cb_act, 0, str(self.config.readEntry("poweredIdleAction"))) 
        self.PoweredIdleTime.setValue(self.config.readNumEntry("poweredIdleTime", 60)) 
        self._setCB(self.BatteryIdleCombo, self.cb_act, 0, str(self.config.readEntry("batteryIdleAction"))) 
        self.BatteryIdleTime.setValue(self.config.readNumEntry("batteryIdleTime", 10)) 

        self._setCB(self.PoweredFreqCombo, self.cb_freq, self.cb_freq_def, str(self.config.readEntry("poweredFreqPolicy"))) 
        self._setCB(self.BatteryFreqCombo, self.cb_freq, self.cb_freq_def, str(self.config.readEntry("batteryFreqPolicy")))

        self.BatteryIdleTime.setValue(self.config.readNumEntry("batteryIdleTime", 10)) # default Do nothing
        # battery critical and lid actions.
        self._setCB(self.BatteryCriticalCombo, self.cb_act, self.cb_act_def_critical, self._checkOldConfig(self.config.readEntry("batteryCriticalAction", ""), False)) 
        self._setRB(self.LaptopLidRadios, self.lid_act, self.lid_act_def, self._checkOldConfig(self.config.readEntry("laptopLidAction", ""), True))
        self.CriticalRemainTime.setValue(self.config.readNumEntry("criticalRemainTime", BATTERY_CRITICAL_MINUTES))
        self.criticalLevel = self.CriticalRemainTime.value()

        # Call some slots to disable various spinboxes if necessary
        self.slotBatteryCriticalActivated()
        self.slotPoweredIdleActivated()
        self.slotBatteryIdleActivated()


    def uiToConfig(self):
        """ Read all values from the UI and write them to the config file. """
        self.config.writeEntry("poweredBrightness", self.PoweredBrightnessSlider.value())
        self.config.writeEntry("batteryBrightness", self.BatteryBrightnessSlider.value())

        self.config.writeEntry("poweredIdleTime", self.PoweredIdleTime.value())
        self.config.writeEntry("poweredIdleAction", self._getCB(self.PoweredIdleCombo, self.cb_act))
        self.config.writeEntry("batteryIdleTime", self.BatteryIdleTime.value())
        self.config.writeEntry("batteryIdleAction", self._getCB(self.BatteryIdleCombo, self.cb_act))
        self.config.writeEntry("poweredFreqPolicy", self._getCB(self.PoweredFreqCombo, self.cb_freq))
        self.config.writeEntry("batteryFreqPolicy", self._getCB(self.BatteryFreqCombo, self.cb_freq))

        self.config.writeEntry("batteryCriticalAction", self._getCB(self.BatteryCriticalCombo, self.cb_act))
        self.config.writeEntry("criticalRemainTime", self.CriticalRemainTime.value())

        self.config.writeEntry("laptopLidAction", self._getRB(self.LaptopLidRadios, self.lid_act))
        self.config.writeEntry("lockOnResume", self.lockScreenOnResume.isChecked())

        self.criticalLevel = self.CriticalRemainTime.value()

        self.config.sync()

    def quit(self):
        """ Quit application. """
        kapp.quit()

    def showTip(self, text=""):
        """ Pop up the tooltip showing battery data and CPU frequencies. """
        self.popup.show()


    def showBrightnessPopup(self):
        if self.powermanager.onBattery():
            value=self.BatteryBrightnessSlider.value()*100/self.BatteryBrightnessSlider.maxValue()
        else:
            value=self.PoweredBrightnessSlider.value()*100/self.PoweredBrightnessSlider.maxValue()
        self.brightnessPopup = KPassivePopup.message('<b>Brightness:</b> '+str(value)+'%', self.systray)
        """pop.setTimeout(3000)"""
        self.brightnessPopup.show()	

    def setBrightnessUp(self):
        """Increments slider value by 10%"""
        if self.powermanager.onBattery():
            self.BatteryBrightnessSlider.setValue(float(self.BatteryBrightnessSlider.value())+max(float(self.BatteryBrightnessSlider.maxValue())/float(10),1))
        else:
            self.PoweredBrightnessSlider.setValue(float(self.PoweredBrightnessSlider.value())+max(float(self.PoweredBrightnessSlider.maxValue())/float(10),1))
        self.showBrightnessPopup()
            
    def setBrightnessDown(self):
        """Decrements slider value by 10%"""
        if self.powermanager.onBattery():
            self.BatteryBrightnessSlider.setValue(float(self.BatteryBrightnessSlider.value())-max(float(self.BatteryBrightnessSlider.maxValue())/float(10),1))
        else:
            self.PoweredBrightnessSlider.setValue(float(self.PoweredBrightnessSlider.value())-max(float(self.PoweredBrightnessSlider.maxValue())/float(10),1))
        self.showBrightnessPopup()

    def getBrightness(self):
      """Work with percentages - it's a bit nicer"""
      if self.powermanager.onBattery():
        value=self.BatteryBrightnessSlider.value()*100/self.BatteryBrightnessSlider.maxValue()
      else:
        value=self.PoweredBrightnessSlider.value()*100/self.PoweredBrightnessSlider.maxValue()
      return QString(str(value))

    def hideTip(self):
        """ Hide the tooltip."""
        self.popup.hide()

    def lockScreen(self):
        """ locks the screen using kdesktop """
        # create a new DCOP-Client:
        client = DCOPClient()
        # connect the client to the local DCOP-server:
        client.attach()
        # create a DCOP-Application-Object to talk to kdesktop:
        kdesktop = DCOPApp('kdesktop', client)
        # call a DCOP-function:
        try:
            ok, foo = kdesktop.KScreensaverIface.lock()
        except:
            print "Unable to lock the screen. The KDE Screensaver does not seem to be running."
    def suspend(self):
        """ Lock the screen and initiate a suspend to RAM (S3). """
        if self.config.readBoolEntry("lockOnResume", True):
            self.lockScreen()
        try:
            self.warningPopup.hide()
        except AttributeError:
            pass # No warningpopup, that's OK.        
        self.powermanager.suspend()
        self.powermanager.resetIdleSeconds()        

    def hibernate(self):
        """ Lock the screen and initiate a suspend to disk (S4). """
        if self.config.readBoolEntry("lockOnResume", True):
            self.lockScreen()
        try:
            self.warningPopup.hide()
        except AttributeError:
            pass # No warningpopup, that's OK.
        self.powermanager.hibernate()
        self.powermanager.resetIdleSeconds()

    def shutdown(self):
        """ Perform system shutdown. """
        self.powermanager.shutdown()
        
    def setCpuPolicyDynamic(self):
        """Change frequ for all cpu"""
        self.powermanager.setCpuPolicy('dynamic')
        self.notify(i18n("CPU frequency policy changed to %1.").arg(self.freq_name['dynamic']))
    
    def setCpuPolicyPerformance(self):
        """Change frequ for all cpu"""
        self.powermanager.setCpuPolicy('performance')
        self.notify(i18n("CPU frequency policy changed to %1.").arg(self.freq_name['performance']))

    def setCpuPolicyPowersave(self):
        """Change frequ for all cpu"""
        self.powermanager.setCpuPolicy('powersave')
        self.notify(i18n("CPU frequency policy changed to %1.").arg(self.freq_name['powersave']))

    def trySuspend(self):
        """ If supported, lock the screen and initiate a suspend to RAM (S3). """
        if self.canSuspend:
           self.suspend()
        else:
           print "Warning: DCOP suspend() called, but not supported."

    def tryHibernate(self):
        """ If supported, lock the screen and initiate a suspend to disk (S4). """
        if self.canHibernate:
           self.hibernate()
        else:
           print "Warning: DCOP hibernate() called, but not supported."

    def blankScreen(self):
        """ Lock and blank screen. """
        if self.config.readBoolEntry("lockOnResume", True):
            self.lockScreen()
        self.powermanager.blankScreen()

    def _getIcon(self):
        """ Set systray icon depending on battery status/level. """
        if self.powermanager.hasBattery:
            if self.batt_state == "not present":
                self.icon = "ac-adapter"
            if self.batt_state == "charged":
                self.icon = "battery-charging-100"
            elif self.batt_state == "discharging":
                if self.batt_level >= 95:
                    self.icon = "battery-discharging-100"
                elif self.batt_level < 95 and self.batt_level >= 85:
                    self.icon = "battery-discharging-090"
                elif self.batt_level < 85 and self.batt_level >= 75:
                    self.icon = "battery-discharging-070"
                elif self.batt_level < 75 and self.batt_level >= 60:
                    self.icon = "battery-discharging-060"
                elif self.batt_level < 65 and self.batt_level >= 45:
                    self.icon = "battery-discharging-050"
                elif self.batt_level < 45 and self.batt_level >= 30:
                    self.icon = "battery-discharging-040"
                elif self.batt_level < 30 and self.batt_level >= 20:
                    self.icon = "battery-discharging-030"
                elif self.batt_level < 20 and self.batt_level >= 10:
                    self.icon = "battery-discharging-020"
                elif self.batt_level < 10 and self.batt_level >= 5:
                    self.icon = "battery-discharging-010"
                else:
                    self.icon = "battery-discharging-000"
            elif self.batt_state == "charging":
                if self.batt_level >= 95:
                    self.icon = "battery-charging-100"
                elif self.batt_level < 95 and self.batt_level >= 85:
                    self.icon = "battery-charging-090"
                elif self.batt_level < 85 and self.batt_level >= 75:
                    self.icon = "battery-charging-070"
                elif self.batt_level < 75 and self.batt_level >= 60:
                    self.icon = "battery-charging-060"
                elif self.batt_level < 65 and self.batt_level >= 45:
                    self.icon = "battery-charging-050"
                elif self.batt_level < 45 and self.batt_level >= 30:
                    self.icon = "battery-charging-040"
                elif self.batt_level < 30 and self.batt_level >= 20:
                    self.icon = "battery-charging-030"
                elif self.batt_level < 20 and self.batt_level >= 10:
                    self.icon = "battery-charging-020"
                elif self.batt_level < 10 and self.batt_level >= 5:
                    self.icon = "battery-charging-010"
                else:
                    self.icon = "battery-charging-000"
        else:
            self.icon = "ac-adapter"
        return self.icon

    def getIcon(self):
        """ Return current icon."""
        return UserIcon(self.icon)

    def setIcon(self):
        """ Change the systray/tooltip icon."""
        oldIcon = self.icon
        self.icon = self._getIcon()
        if self.icon != oldIcon:
            self.systray.setPixmap(QPixmap(UserIcon(self.icon)))
            self.BattPixmap.setPixmap(QPixmap(UserIcon(self.icon)))

    def notify(self, msg, icon=None):
        """ Send a notification popup. """
        if icon:
            icon = QPixmap(icon)
        else:
            icon = QPixmap(SmallIcon("messagebox_info"))
        try: 
            del self.warningPopup
        except:
            pass
        self.warningPopup = KPassivePopup(self.systray)
        label = Notify(self.warningPopup, msg, icon)
        self.warningPopup.setView(label)
        position = QPoint(5,5)
        self.warningPopup.show(position)

    def poll(self,notify=True):
        """ Check for changes in plugged in status, battery status and laptop lid closed status. """
        debug( "------------ POLL ---------------")

        self.powermanager.checkHAL()
        # Battery stuff:
        # check for last state, and run plugged / unplugged message if the state changed.
        if self.powermanager.hasBattery:
            plugged_num = 0
            self.batt_state = "not present" # unknown yet
            self.batt_level = self.batt_remain = 0
            self.batt_rate = self.batt_charge = self.batt_full = 0
            for batt in self.powermanager.batteries:
                state, level, remain, rate, current, full = self.powermanager.getBatteryState(batt)
                self._updateBatteryWidget(batt, state, level, remain, rate)

                ## notify plugged/unplugged batteries
                if state == "not present":
                    if self.powermanager.batteryIsPresent[batt]:
                        self.notify(i18n("The battery has been removed."))
                        self.powermanager.batteryIsPresent[batt] = False
                else: # battery present
                    if not self.powermanager.batteryIsPresent[batt]:
                        self.notify(i18n("The battery has been inserted."))
                        self.powermanager.batteryIsPresent[batt] = True

                    ## get cumulative charge levels/rate 
                    self.batt_rate += rate
                    self.batt_charge += current
                    self.batt_full += full

                    ## calculate overall level (average of present batteries)
                    self.batt_remain += remain
                    self.batt_level += level
                    plugged_num += 1

                    ## calculate overall state (charging/discharging/charged)
                    if state in ("charging","discharging"):
                        self.batt_state = state
                    elif not self.batt_state in ("charging, discharging"):
                        self.batt_state = state

            # if we know charge and full -> recalculate overall level
            if self.batt_full > 0 and self.batt_charge > 0:
                self.batt_level = 100 * self.batt_charge / self.batt_full
            else:
                # if more than one battery present, we need to calculate average level
                if plugged_num > 1:
                    self.batt_level /= plugged_num

            # if rate is reported, calculate remaining time on our own
            if self.batt_rate > 0:
                if self.batt_state == "charging":
                    self.batt_remain = 3600 * (float(self.batt_full - self.batt_charge) / self.batt_rate)
                if self.batt_state == "discharging":
                    self.batt_remain = 3600 * (float(self.batt_charge) / self.batt_rate)

            remain_h = self.batt_remain/3600
            remain_m = (self.batt_remain/60)%60 

            blabel = i18n("<b>Battery:</b>")
            if self.batt_state == "charged":
                blabel += i18n(" fully charged")
            elif self.batt_state == "charging":
                blabel += i18n(" %i:%02ih to charge" % (remain_h,remain_m))
            elif self.batt_state == "discharging":
                blabel += i18n(" %i:%02ih remaining" % (remain_h,remain_m))
            self.BattMainLabel.setText(blabel)

            # update tray icon if needed
            self.setIcon()

            # check battery state
            self.checkBatteryCritical()

            # check Idletime
            self.checkIdletime()

        # CPU stuff
        self._updateCpuWidgets()

        if self.powermanager.hasBattery:
            on_battery = self.powermanager.onBattery()
            if self.powermanager.wasOnBattery != on_battery:
                self.powermanager.wasOnBattery = on_battery
                debug("poll: states differ")
                if not on_battery: 
                    debug("poll: Now on AC")
                    if notify:
                        self.powerHasBeenPlugged()
                else:
                    debug("poll: Now on battery")
                    if notify:
                        self.powerHasBeenUnplugged()
            else:
                debug("poll: state is the same")

        # Lid stuff
        if self.powermanager.hasLid:
            if self.powermanager.getLidClosedState():
                if not self.powermanager.lidClosedState:
                    self.powermanager.lidClosedState = True

                    action = self._getRB(self.LaptopLidRadios, self.lid_act)
                    if not self.act_name.has_key(action):
                        action = self.act_name[self.lid_act_def]

                    if self.act_call[action] != None:
                        note = i18n("Laptop lid is closed, %1 now.").arg(self.act_notify[action])
                        self.notify(note, self.act_icon[action])
                        QTimer.singleShot(2000, self.act_call[action])
            else:
                self.powermanager.lidClosedState = False

    def _addBatteryWidgets(self):
        """ Adds progressbars to show battery status to the tooltip."""
        BattLayout = QHBoxLayout(None,0,6,"BattLayout")

        self.BattPixmap = QLabel(self.tooltip,"BattLabLayout")
        self.BattPixmap.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed,0,0,self.BattPixmap.sizePolicy().hasHeightForWidth()))
        self.BattPixmap.setPixmap(QPixmap(UserIcon(self.icon)))
        self.BattPixmap.setScaledContents(1)
        BattLayout.addWidget(self.BattPixmap)
        self.BattMainLabel = QLabel(self.tooltip,"BattMainLabel")
        self.BattMainLabel.setText(i18n("<b>Battery:</b>"))
        BattLayout.addWidget(self.BattMainLabel)

        # Add to tooltip
        self.tooltip.layout().addLayout(BattLayout)

        # Create a progressbar and a label for every battery found, and add it to tooltip
        self.BattLabel = {}
        self.BattLayout = {}
        self.BattProgress = {}
        i = 1
        for batt in self.powermanager.batteries:
            self.BattLayout[batt] = QHBoxLayout(None,0,6,"BattBarLayout")
            self.BattLabel[batt] = QLabel(self.tooltip,"BattLabel")
            if len(self.powermanager.batteries) > 1:
                self.BattLabel[batt].setText(i18n("Battery %i" % i))
            self.BattLayout[batt].addWidget(self.BattLabel[batt])
            self.BattProgress[batt] = KProgress(self.tooltip,"BattProgress")
            self.BattProgress[batt].setMinimumSize(QSize(200,0))
            self.BattLayout[batt].addWidget(self.BattProgress[batt])
            self.tooltip.layout().addLayout(self.BattLayout[batt])
            i += 1


    def _updateBatteryWidget(self, batt, state, level, remain, rate):
        """ Retrieve battery information and update the related widgets accordingly. """
        self.BattProgress[batt].setEnabled(True)
        self.BattProgress[batt].setTotalSteps(100)
        self.BattProgress[batt].setProgress(level)
        if state == "not present":
            self.BattProgress[batt].setFormat(i18n("not present"))
        elif state == "charging":
            self.BattProgress[batt].setFormat(i18n("Charging (%p%)"))
        elif state == "discharging":
            if rate > 0:
                showrate = rate/1000
                self.BattProgress[batt].setFormat(i18n("Discharging (%p%)") + " - %.d W" % showrate)
            else:
                self.BattProgress[batt].setFormat(i18n("Discharging (%p%)"))
        else:
            self.BattProgress[batt].setFormat("%p%")

    def _addCpuWidgets(self):
        """ Adds progressbars to show CPU frequencies to the tooltip."""
        if not SHOW_CPUFREQ:
            return
        if len(self.powermanager.cpus) == 0:
            return

        LabelLayout = QHBoxLayout(None,0,6,"layout5")

        self.CpuPixmap = QLabel(self.tooltip,"CpuPixmap")
        self.CpuPixmap.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,QSizePolicy.Fixed,0,0,self.CpuPixmap.sizePolicy().hasHeightForWidth()))
        self.CpuPixmap.setPixmap(QPixmap(UserIcon("processor")))
        self.CpuPixmap.setScaledContents(1)
        LabelLayout.addWidget(self.CpuPixmap)
        self.CpuMainLabel = QLabel(self.tooltip,"CpuMainLabel")
        self.CpuMainLabel.setText(i18n("<b>CPU Frequency:</b>"))
        LabelLayout.addWidget(self.CpuMainLabel)

        # Add to tooltip
        self.tooltip.layout().addLayout(LabelLayout)

        # Create a progressbar and a label for every CPU found, and add it to tooltip
        self.CpuLabel = {}
        self.CpuLayout = {}
        self.CpuProgress = {}
        i = 1
        for cpu in self.powermanager.cpus:
            self.CpuLayout[cpu] = QHBoxLayout(None,0,6,"layout2")
            self.CpuLabel[cpu] = QLabel(self.tooltip,"CpuLabel")
            if len(self.powermanager.cpus) > 1:
                self.CpuLabel[cpu].setText(i18n("Processor %i" % i))
            self.CpuLayout[cpu].addWidget(self.CpuLabel[cpu])    
            self.CpuProgress[cpu] = KProgress(self.tooltip,"CpuProgress")
            self.CpuProgress[cpu].setFormat("%v MHz")
            self.CpuLayout[cpu].addWidget(self.CpuProgress[cpu])
            self.tooltip.layout().addLayout(self.CpuLayout[cpu])
            i += 1

    def slotPoweredIdleActivated(self, index=False):
        """ Signal slot for activated powered idle action. """
        if not index:
            index = self.PoweredIdleCombo.currentItem()
        self.PoweredIdleTime.setEnabled(index != 0)

    def slotBatteryIdleActivated(self, index=False):
        """ Signal slot for activated battery idle action. """
        if not index:
            index = self.BatteryIdleCombo.currentItem()
        self.BatteryIdleTime.setEnabled(index != 0)

    def slotBatteryCriticalActivated(self, index=False):
        """ Signal slot for activated battery critical action. """
        if not index:
            index = self.BatteryCriticalCombo.currentItem()
        self.CriticalRemainTime.setEnabled(index != 0)

    def _updateCpuWidgets(self):
        """ Retrieve CPU freq information and update the related widgets accordingly. """
        if not SHOW_CPUFREQ:
            return
        if len(self.powermanager.cpus) == 0:
            return

        clabel = i18n("<b>CPU Frequency:</b>") + " "
        policy = self.powermanager.getCpuPolicy()
        if self.freq_name.has_key(policy):
            clabel += self.freq_name[policy] # get l10n name
        else:
            clabel += policy
        self.CpuMainLabel.setText(clabel)

        for cpu in self.powermanager.cpus:
            cpustate = self.powermanager.getCpuState(cpu)
            if not cpustate['online']:
                self.CpuProgress[cpu].setEnabled(False)
            else:
                self.CpuProgress[cpu].setEnabled(True)
                self.CpuProgress[cpu].setTotalSteps(cpustate['max'])
                self.CpuProgress[cpu].setProgress(cpustate['cur'])
        if policy != "":
            self.systray.actionCollection().action(policy).setChecked(True)        
        if policy in self.cb_freq:
            self.systray.actionCollection().action(policy).setChecked(True)
            
    def changePoweredBrightness(self, level=None):
        """ Mains-powered brigthness slider has been moved. """
        # Check if the state applies and adjust brightness immediately.
        if not self.powermanager.onBattery() and self.powermanager.hasBrightness:
            if not level:
                level = self.PoweredBrightnessSlider.value()
            self.powermanager.adjustBrightness(level)

    def changeBatteryBrightness(self, level=None):
        """ Battery-powered brigthness slider has been moved. """
        # Check if the state applies and adjust brightness immediately.
        if self.powermanager.onBattery() and self.powermanager.hasBrightness:
            if not level:
                level = self.BatteryBrightnessSlider.value()
            self.powermanager.adjustBrightness(level)




    def checkCpuFreq(self):
        """ Adjust CPU frequency policy according to current state """
        if not self.powermanager.hasCpuFreqGovernors:
            return ""

        if self.powermanager.onBattery():
            policy = str(self.config.readEntry("batteryFreqPolicy"))
        else:
            policy = str(self.config.readEntry("poweredFreqPolicy"))
        if policy == "":
           policy = 'dynamic'

        # check if specified policy is supported by HW
        if not policy in self.cb_freq:
            print "Warning: policy from config file not supported: ", policy
            return ""

        current_policy = self.powermanager.getCpuPolicy()
        if current_policy != policy:
            debug("Switching CPU policy from %s to %s." % (current_policy, policy))
            self.powermanager.setCpuPolicy(policy)
            return i18n("CPU frequency policy changed to %1.").arg(self.freq_name[policy]) 
        elif current_policy == 'dynamic':
            debug("Dynamic policy -> update policy (conservative/ondemand)")
            self.powermanager.setCpuPolicy(policy)
        
        debug("CPU policy will stay %s" % current_policy)
        return ""

    def powerHasBeenUnplugged(self):
        """ Actions to perform when the plug has been pulled."""
        if self.powermanager.hasBrightness:
            self.powermanager.adjustBrightness(self.BatteryBrightnessSlider.value()) 
        self.powermanager.setPowerSave(True)
        self.checkBatteryCritical()
        self.changeBatteryBrightness()
        self.powermanager.setScreensaverBlankOnly(True)
        self.powermanager.resetIdleSeconds()
        msg = self.checkCpuFreq()
        if self.powermanager.hasAC:
            self.notify(i18n("The AC adapter has been unplugged, switching to battery mode.")+"\n"+msg, self.getIcon())

    def powerHasBeenPlugged(self):
        """ Actions to perform when AC adapter has been plugged in. """
        if self.powermanager.hasBrightness:
            self.powermanager.adjustBrightness(self.PoweredBrightnessSlider.value())
        self.powermanager.setPowerSave(False)
        self.changePoweredBrightness()
        self.powermanager.setScreensaverBlankOnly(False)
        msg = self.checkCpuFreq()
        self.powermanager.resetIdleSeconds()
        self.notify(i18n("The AC adapter has been plugged in, switching to AC mode.")+"\n"+msg, self.getIcon())

    def checkBatteryCritical(self):
        """ Check for warning and critical battery label and notify-warn or 
            initiate the configured action. """

        if not self.powermanager.hasBattery:
            return

        if self.batt_state == "discharging": 
            currentLevel = int(self.batt_remain/60)

            warningLevel = self.criticalLevel + 5 # warn five minutes before critical
            criticalLevel = self.criticalLevel

            debug("CurrentBat: %i, WarningBat: %i, CriticalBat: %i" % (currentLevel, warningLevel, criticalLevel))
            # We only want to suspend if the chargelevel is above a certain threshold, 
            # it sometimes takes some time for HAL to report remaining time correctly
            if currentLevel <= criticalLevel and self.batt_level < CHARGE_LEVEL_THRESHOLD:
                if not self.powermanager.criticalBatteryState and self.powermanager.onBattery():
                    self.powermanager.criticalBatteryState = True

                    action = str(self.config.readEntry("batteryCriticalAction"))
                    if not self.act_name.has_key(action):
                        action = self.act_name[self.cb_act_def_critical]

                    note = i18n("You are about to run out of battery power, %1 now.").arg(self.act_notify[action])
                    self.notify(note, self.act_icon[action])
                    if self.act_call[action] != None:
                        QTimer.singleShot(2000, self.act_call[action])
            else: 
                self.powermanager.criticalBatteryState = False
                if currentLevel <= warningLevel and self.batt_level < CHARGE_LEVEL_THRESHOLD:
                    if not self.powermanager.warningBatteryState:
                        self.powermanager.warningBatteryState = True
                        self.notify(i18n("You are low on battery power."), self.getIcon())
                else:
                    self.powermanager.warningBatteryState = False

    def checkIdletime(self):
        """ Reads the idle time and does some action. """
        idlesec = round(self.powermanager.getIdleSeconds()/60, 2)
        if self.powermanager.onBattery():
            idleTime = self.config.readNumEntry("batteryIdleTime", 10)
            action = str(self.config.readEntry("batteryIdleAction"))
        else:
            idleTime = self.config.readNumEntry("poweredIdleTime", 60)
            action = str(self.config.readEntry("poweredIdleAction"))
        if not self.act_name.has_key(action):
            action = 'nothing'

        if idlesec - self.lastidlesec > 100:
            debug("last: %u" % (idlesec - self.lastidlesec))
            return # probably bogus idleseconds right after suspend
        self.lastidlesec = idlesec
        if self.act_call[action] == None:
            return # doing nothing anyway
        if idlesec > idleTime:
            note = i18n("System idle for at least %1 minutes, %2 now.").arg(idleTime).arg(self.act_notify[action])
            self.notify(note, self.act_icon[action])
            QTimer.singleShot(2000, self.act_call[action])



def doDcop(kapp):
    """ Register kvandale in dcop, so it can be controlled from outside. """
    my_dcop = kapp.dcopClient()
    #my_dcop.attach()
    #my_dcop.registerAs("power-manager")


class DcopIface (DCOPExObj):
    """ Add some interface so we can use powermanager from the outside. """
    def __init__ (self, app, id='power-manager'):
        DCOPExObj.__init__ (self, id)
        #     addMethod (<signature>, <Python method>)
        #self.addMethod ('QString getQuery()', gvd.getZoekbegrip)

        # PM related.
        self.addMethod ('void suspend ()', app.trySuspend)
        self.addMethod ('void hibernate ()', app.tryHibernate)
        self.addMethod ('void shutdown ()', app.shutdown)

        # UI related.
        self.addMethod ('void showTip ()', app.showTip)
        #self.addMethod ('void show ()', app.parent().show)
        #self.addMethod ('void hide ()', app.parent().hide)

        #self.addMethod ('void plugged ()', app.powerHasBeenPlugged)
        #self.addMethod ('void unplugged ()', app.powerHasBeenUnplugged)
        self.addMethod ('bool onBattery ()', app.powermanager.onBattery)

	self.addMethod('void brightnessUp ()', app.setBrightnessUp)
	self.addMethod('void brightnessDown ()', app.setBrightnessDown)
	self.addMethod('QString getBrightness ()', app.getBrightness)
	
        #self.addMethod ('QString getCurrentResult()', gvd.getRawResult)


class PowermanagerApp(KDialogBase):
    """ The KDialog providing the OK, Apply and Cancel buttons."""

    def __init__(self,parent=None,name=None):
        """ Initialise dialog and set mainwidget. """
        TDEGlobal.locale().insertCatalogue("guidance")
        TDEGlobal.iconLoader().addAppDir("guidance")

        # We would like to use a KUniqueApplication, but that breaks dcop due to some
        # strange bug. The following line is the revenge code for this bug, it is
        # intentionally ugly.
        if len(os.popen("dcop |grep guidance-").readlines()) > 1:
            print "There is already an instance of power manager running. Exiting."
            sys.exit(0)

        # Which buttons do we want?
        KDialogBase.__init__(self,KJanusWidget.Swallow,i18n("Power Manager"),
            KDialogBase.Ok|KDialogBase.Apply|KDialogBase.Cancel|KDialogBase.User1, KDialogBase.Close)
        self.pmwidget = PowerManager(self,name)
        self.setButtonText(KDialogBase.User1, i18n("About"))

        if not self.pmwidget.powermanager.isLaptop():
            print "This is not a laptop, quitting ... "
            sys.exit(1)

        self.pmwidget.prepare()

        self.setMainWidget(self.pmwidget)
        self.aboutus = TDEAboutApplication(self)

    def slotOk(self):
        """ The OK button has been pressed, save configuration and pass on do whatever 
            needs to be done by KDialog. """
        self.pmwidget.uiToConfig()
        self.pmwidget.checkCpuFreq()
        KDialogBase.slotOk(self)

    def slotApply(self):
        """ The Apply button has been pressed, save configuration and pass on do whatever 
            needs to be done by KDialog. """
        self.pmwidget.uiToConfig()
        self.pmwidget.checkCpuFreq()
        KDialogBase.slotApply(self)

    def slotCancel(self):
        """ The Cancel button has been pressed, reset some values and hide dialogue. """
        # In case brightness has changed, we reset it to the configured value.
        if self.pmwidget.powermanager.hasBrightness:
            brightness_high = self.pmwidget.powermanager.brightness_levels
            if not self.pmwidget.powermanager.onBattery():
                level = self.pmwidget.config.readNumEntry("poweredBrightness", brightness_high)
            else:
                level = self.pmwidget.config.readNumEntry("batteryBrightness", int(brightness_high/2))
            self.pmwidget.powermanager.adjustBrightness(level)
        self.pmwidget.configToUi()
        KDialogBase.slotCancel(self)

    def slotUser1(self):
        self.aboutus.show()

# There's a bug in KUniqueApplication that shows the pid in the dcop name,
# this fugly hack works around it.
class PMApp(TDEApplication):

    def name(self):
        return "power-manager"


if __name__ == "__main__":
    aboutdata = TDEAboutData("guidance", "Power Manager", "0.8.0", 
        "Handles battery, display and suspend modes for your computer.", TDEAboutData.License_GPL, 
        "(C) 2006-2007 Sebastian Kügler, Canonical Ltd, Luka Renko", 
        None, None, "jriddell@ubuntu.com")
    aboutdata.addAuthor("Sebastian Kügler", "Developer", "sebas@kde.org","http://vizZzion.org")
    aboutdata.addAuthor("Jonathan Riddell", "Developer", "jriddell@ubuntu.com")
    aboutdata.addAuthor("Luka Renko", "Developer", "lure@kubuntu.org")
    aboutdata.setProgramLogo(QImage("power-manager.png"))
    TDECmdLineArgs.init(sys.argv, aboutdata)
    #kapp = KUniqueApplication(True, True, False)
    #kapp = TDEApplication()
    kapp = PMApp(True, True)
    mainWindow = PowermanagerApp(None, "main window")
    doDcop(kapp)
    dcop_iface = DcopIface(mainWindow.pmwidget)
    """Start helper module / button listener"""
    try:
        helperPid = os.spawnl(os.P_NOWAIT, os.path.dirname(__file__)+'/gpmhelper.py', 'gpmhelper.py')
    except:
        """Non-fatal if this fails"""
        print "Unable to start button-listener"

    kapp.exec_loop()

    """Kill helper module / button listener"""
    os.system('kill '+str(helperPid))
