#!/usr/bin/env python

# (c) 2007 Canonical Ltd.
#
# 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.
#
# 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, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

'''KDE user interface implementation.'''

import sys
import os.path
import re

from PyQt4.QtCore import SIGNAL
from PyQt4.QtCore import QTimer, QSize
import PyQt4.QtGui
from PyKDE4.kdecore import *
from PyKDE4.kdeui import *

import jockey.ui
from jockey.oslib import OSLib
import jockey.kdeui.ManagerWindowKDE4
import jockey.kdeui.ProgressDialog
import jockey.kdeui.LicenseDialog

class JockeyTreeWidgetItem(PyQt4.QtGui.QTreeWidgetItem):
    '''A subclassed QTreeWidgetItem that can store its handler.

    A workaround for the Model/View framework which will be implemented later
    on.
    '''
    def __init__(self, handler_id, parent=None):
        super(JockeyTreeWidgetItem, self).__init__(parent)
        self.handler_id = handler_id

class ManagerWindowKDE4(PyQt4.QtGui.QDialog, jockey.kdeui.ManagerWindowKDE4.Ui_manager_window):
    def __init__(self, parent=None):
        super(ManagerWindowKDE4, self).__init__(parent)
        self.setupUi(self)

class ProgressDialog(PyQt4.QtGui.QDialog, jockey.kdeui.ProgressDialog.Ui_progress_dialog):
    def __init__(self, parent=None):
        super(ProgressDialog, self).__init__(parent)
        self.setupUi(self)

class LicenseDialog(PyQt4.QtGui.QDialog, jockey.kdeui.LicenseDialog.Ui_dialog_licensetext):
    def __init__(self, parent=None):
        super(LicenseDialog, self).__init__(parent)
        self.setupUi(self)

class KDEUI(jockey.ui.AbstractUI):
    '''KDE user interface implementation.'''
    
    keyb_trans_re = re.compile('(?<! _) _ (?! _)', re.X)
    
    def convert_keybindings(self, str):
        return self.keyb_trans_re.sub('&', str)
        
    def ui_init(self):
        '''Initialize UI.'''
        
        self.mw = ManagerWindowKDE4()     

        # set icon 
        if os.path.exists('/usr/share/icons/hicolor/22x22/apps/jockey-kde.png') and os.path.exists('/usr/share/icons/hicolor/48x48/apps/jockey-kde.png'):
            icon = KIcon('jockey-kde.png')
            largeIcon = KIcon('jockey-kde.png')
        # Not a good idea to use KIcon for fallback since KIcon doesn't take 
        # absolute paths...
        elif os.path.exists('data/icons/22x22/apps/jockey-kde.png') and os.path.exists('data/icons/48x48/apps/jockey-kde.png'):
            icon = KIcon('data/icons/22x22/apps/jockey-kde.png')
            largeIcon = KIcon('data/icons/48x48/apps/jockey-kde.png')

        # load tray icon
        # not visible yet
        self.trayicon = KSystemTrayIcon(icon, self.mw)
        #print self.trayicon
        # connect the tray signals
        self.mw.connect(self.trayicon,
                        SIGNAL('activated(QSystemTrayIcon::ActivationReason)'),
                        self.open_app)

        self.mw.connect(self.trayicon,
                        SIGNAL('messageClicked()'), self.open_app)

        # Set window icon
        self.mw.setWindowIcon(KIcon(icon))
        
        # connect signals
        self.mw.connect(self.mw.buttonBox, SIGNAL('rejected()'), self.on_buttonBox_rejected)
        self.mw.connect(self.mw.buttonBox, SIGNAL('helpRequested()'),
                        self.on_buttonBox_helpRequested)
        
        self.mw.connect(self.mw.button_toggle, SIGNAL('clicked()'),
                          self.on_button_toggle_clicked)
        
        # Set Logo
        self.mw.logo_image.setPixmap(largeIcon.pixmap(48,48))

        # disable help button if help is not available
        if not OSLib.inst.ui_help_available(self):
            self.mw.buttonBox.setStandardButtons(KDialogButtonBox.Close)

        # load progress dialog UI
        self.progress_ui = ProgressDialog(self.mw)
        
        # load license dialog UI
        self.dialog_licensetext_ui = LicenseDialog(self.mw)
        self.dialog_licensetext_ui.setWindowTitle(self.string_license_dialog_title)
        
        # connect signals
        self.mw.connect(self.mw.linkbutton_licensetext, SIGNAL('leftClickedUrl()'),
                        self.on_linkbutton_licensetext_leftClickedUrl)
        
        
        # connect the signals
        self.progress_ui.connect(self.progress_ui.buttonCancel, SIGNAL('clicked()'),
                          self.on_buttonCancel_clicked)
        
        # we have to connect this signal here, but that still is a
        # bad solution.
        self.mw.connect(self.mw.treeview_drivers,
                        SIGNAL('currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem* )'),
                        self.on_treeview_drivers_currentItemChanged)
        
        self.mw.setWindowTitle(self.main_window_title())
        self.mw.label_license_label.setText(self.string_license_label)
        self.mw.linkbutton_licensetext.setText('(%s)' % self.string_details)
    
    def set_help_button_visible(self, buttonbox, is_visible):
        for myButton in buttonbox.buttons():
            # if it is a help button
            if buttonbox.buttonRole(myButton) == 4:
                myButton.setVisible(is_visible)
    
    def ui_show_main(self):
        '''Show the main window.'''

        # update tree model
        self.update_driver_info_ui(None)
        self.update_tree_model()
        
        # show help button?
        if not OSLib.inst.ui_help_available(self):
            self.set_help_button_visible(self.mw.buttonBox, False)
        
        self.mw.show()

    def ui_main_loop(self):
        '''Main loop for the user interface.'''

        KApplication.exec_() #MIGHT NEED TO INITIALISE KApp at some point

    def error_message(self, title, text):
        '''Present an error message box.'''

        text = '<h3>' + title + '</h3>' + text

        msgbox = KMessageBox.sorry(self.mw, text, title, KMessageBox.Notify)

        # KMessageBox returns nothing in this case, but when it closes we can return true (I think...)
        return True


    def confirm_action(self, title, text, subtext=None, action=None):
        '''Present a confirmation dialog.

        If action is given, it is used as button label instead of the default
        'Continue'.  Return True if the user confirms, False otherwise.
        '''
        text = '<h3>%s</h3>' % text
        if subtext:
            text += subtext
        text = text.replace('\n', '<br/>')
        # FIXME: action doesn't replace the default button label "continue".
        # The Hardy release doesn't either though...
        msgbox = KMessageBox.warningContinueCancel(self.mw, text, title)

        # If continue was clicked msgbox returns 5. It returns 2 when cancel is clicked.
        if msgbox == 5:
            return True
        elif msgbox == 2:
            return False

    def ui_notification(self, title, text):
        '''Present a notification popup.

        This should preferably create a tray icon. Clicking on the tray icon or
        notification should run the GUI.
        '''
        self.trayicon.show()
        self.ui_idle()
        KNotification.event('Drivers', '%s\n\n%s' % (title, text),
                KIcon("jockey-kde").pixmap(QSize(22,22)))

    def open_app(self, argument = None):
        '''Tray activation callback, launch the elevated app.'''

        self.ui_show_main()

    def ui_idle(self):
        '''Redraw app while external package manager progresses.'''

        KApplication.processEvents()    

    def ui_progress_start(self, title, description, total):
        '''Create a progress dialog.'''

        # hint: you achieve the indeterminate value by setting
        # both min and max to 0

        if(total == -1):
            total = 0

        self.progress_ui.progressBar.setRange(0, total)

        # set translations
        self.progress_ui.setWindowTitle(title)
        self.progress_ui.description.setText(description)

        self.progress_ui.show()
        self.cancel_progress = False 

    def ui_progress_update(self, current, total):
        '''Update status of current progress dialog.
        
        current/total specify the number of steps done and total steps to
        do, or -1 if it cannot be determined. In this case the dialog should
        display an indeterminated progress bar (bouncing back and forth).

        This should return True to cancel, and False otherwise.
        '''
        if current < 0 or total < 0:
            # indeterminated mode
            if self.progress_ui.progressBar.maximum() != 0:
                self.progress_ui.progressBar.setRange(0, 0)
        else:
            self.progress_ui.progressBar.setRange(0, total)
            self.progress_ui.progressBar.setValue(current)

        return self.cancel_progress

    def ui_progress_finish(self):
        '''Close the current progress dialog.'''

        self.progress_ui.close()

    # callbacks
    def on_buttonBox_rejected(self):
        '''Callback for clicked Close button.'''

        self.mw.close()
        # Ugly hack since pyqt doesn't seem to exit the main loop
        # without segfaulting since the class is not a subclass of
        # a QDialog or of something related to QT.
        if __name__ == '__main__':# don't break the tests
            sys.exit()
    
    def on_buttonBox_helpRequested(self):
        '''Callback for clicked Help button.'''

        OSLib.inst.ui_help(self)
        return True

    def on_button_toggle_clicked(self):    
        self.mw.treeview_drivers.setDisabled(True)
        
        item = self.mw.treeview_drivers.currentItem()
        
        if item:
            h_id = item.handler_id
                    
            if self.set_handler_enable(h_id, 'toggle', False):
                self.update_tree_model()
            self.mw.treeview_drivers.setDisabled(False)
        return True
   
    def on_treeview_drivers_currentItemChanged(self, old, new):
        if new:
            item = self.mw.treeview_drivers.currentItem()
            if item:
                h_id = item.handler_id
                self.update_driver_info_ui(h_id)
        return True
    
    def on_buttonCancel_clicked(self):
    
        '''Callback for cancelling the progress dialog.'''

        self.cancel_progress = True
        return True

    def update_tree_model(self):
        '''Updates the tree model with up to date information.

        Unfortunately, due to time contraints, the KDE frontend does not use a
        Model/View framework yet.'''

        # we have to workaround the model/view with a subclassed
        # QTreeWidgetItem
        
        self.mw.label_heading.setText('<b>%s</b><br/><br/>%s' % self.main_window_text())

        # keep current selection if we have one
        cur_item = self.mw.treeview_drivers.currentItem()
        if cur_item:
            cur_handler = cur_item.handler_id
            cur_item = None
        else:
            cur_handler = None

        # first clear the list
        self.mw.treeview_drivers.clear()

        # then repopulate
        ha = []
        parents = {}
        for h_id in self.get_displayed_handlers():
            info = self.get_ui_driver_info(h_id)
            if info['needs_reboot']:
                icon = KIcon('system-reboot')
            elif info['enabled']:
                icon = KIcon('jockey-enabled')
            else:
                icon = KIcon('jockey-disabled')

            # load the item
            # here we make use of the subclassed QTreeWidgetItem
            i = JockeyTreeWidgetItem(h_id, self.mw.treeview_drivers)
            i.setIcon(0, icon)
            i.setText(1, info['name'])
            if h_id == cur_handler:
                cur_item = i
            
        # Sort the items in alphabetical order
        self.mw.treeview_drivers.sortItems (1, PyQt4.QtCore.Qt.AscendingOrder)
        
        # We want to adjust the columns and expand the tree.
        self.mw.treeview_drivers.expandAll()
        self.mw.treeview_drivers.resizeColumnToContents(0)
        self.mw.treeview_drivers.resizeColumnToContents(1)

        # if we previously had a selection, restore it, otherwise select the
        # first one
        if not cur_item:
            cur_item = self.mw.treeview_drivers.topLevelItem(0)
        if cur_item:
            self.mw.treeview_drivers.setItemSelected(cur_item, True)
            self.update_driver_info_ui(cur_item.handler_id)
            
    def update_driver_info_ui(self, handler_id):
        '''Update UI elements which show the driver details.

        If handler_id is None, then no driver is selected, no information
        shown, and the appropriate controls are disabled.'''

#        info = self.get_ui_driver_info(unicode(handler_id))
        info = self.get_ui_driver_info(handler_id)
        self.mw.label_drivername.setText('<b>%s</b>' % info['name'])
        self.current_driver_name = info['name']
        self.mw.textview_description.setText(info['description'])
        if info['certified'] == None:
            self.mw.image_certification.hide()
        elif info['certified']:
            self.mw.image_certification.show()
            
            if os.path.exists('/usr/share/icons/hicolor/scalable/actions/jockey-certified.svg'):
                certified_icon = KIcon('jockey-certified.svg')
                
                self.mw.image_certification.setPixmap(certified_icon.pixmap(16,16))
            elif os.path.exists('data/icons/scalable/actions/jockey-certified.svg'):
                certified_icon = PyQt4.QtGui.QIcon('data/icons/scalable/actions/jockey-certified.svg')
                
                self.mw.image_certification.setPixmap(certified_icon.pixmap(16,16))
        else:
            self.mw.image_certification.show()
            
            warning_icon = KIcon('dialog-warning')
            self.mw.image_certification.setPixmap(warning_icon.pixmap(16,16))
        
        self.mw.label_certification.setText(info['certification_label'])
        
        if info['free'] == None:
            self.mw.image_license.hide()
            self.mw.label_license_label.hide()
        elif info['free']:
            self.mw.image_license.show()
            self.mw.label_license_label.show()
            self.mw.image_license.setPixmap(KIcon('jockey-free').pixmap(16,16))
        else:
            self.mw.image_license.show()
            self.mw.label_license_label.show()
            
            if os.path.exists('/usr/share/icons/hicolor/scalable/actions/jockey-proprietary.svg'):
                proprietary_icon = KIcon('jockey-proprietary.svg')
                
                self.mw.image_license.setPixmap(proprietary_icon.pixmap(16,16))
            elif os.path.exists('data/icons/scalable/actions/jockey-proprietary.svg'):
                proprietary_icon = PyQt4.QtGui.QIcon('data/icons/scalable/actions/jockey-proprietary.svg')
                self.mw.image_license.setPixmap(proprietary_icon.pixmap(16,16))
                
        self.mw.label_license.setText(info['license_label'])
        
        if info['license_text']:
            self.mw.linkbutton_licensetext.show()
            self.current_license_text = info['license_text']
        else:
            self.mw.linkbutton_licensetext.hide()
            self.current_license_text = None
        if info['enabled'] == None:
            self.mw.image_enabled.hide()
        elif info['needs_reboot']:
            self.mw.image_enabled.show()
            self.mw.image_enabled.setPixmap(KIcon('system-reboot').pixmap(16,16))
        elif info['enabled']:
            self.mw.image_enabled.show()
            self.mw.image_enabled.setPixmap(KIcon('jockey-enabled').pixmap(16,16))
        else:
            self.mw.image_enabled.show()
            self.mw.image_enabled.setPixmap(KIcon('jockey-disabled').pixmap(16,16))
        self.mw.label_status.setText(info['status_label'])
        
        if not info['button_toggle_label']:
            self.mw.button_toggle.setText(self.string_button_enable)
            self.mw.button_toggle.setEnabled(False)
        else:
            self.mw.button_toggle.setEnabled(True)
            self.mw.button_toggle.setText(info['button_toggle_label'])
            if info['enabled']:
                icon = KIcon('jockey-disabled')
                self.mw.button_toggle.setIcon(icon)
            else:
                icon = KIcon('jockey-enabled')
                self.mw.button_toggle.setIcon(icon)

    def on_linkbutton_licensetext_leftClickedUrl(self):
        self.dialog_licensetext_ui.label_license_drivername.setText('<b>%s</b>' %
            self.current_driver_name)
        self.dialog_licensetext_ui.textview_license_text.setPlainText(self.current_license_text)
        self.dialog_licensetext_ui.show()
        return True

appName = 'jockey'
catalog = ''
programName = ki18n ('Jockey')
version = '1.0'
description = ki18n ('Jockey driver manager')
license = KAboutData.License_GPL
copyright = ki18n('(c) 2007, 2008 Canonical Ltd')
text = ki18n ('none')
homePage = 'https://launchpad.net/jockey'
#bugEmail = ""

#generates the about data entry from the provided information.
aboutData = KAboutData(appName, catalog, programName, version, description, license, copyright, text, homePage)

aboutData.addAuthor(ki18n('Martin Pitt'), ki18n('Main author'))
aboutData.addAuthor(ki18n('Jonathan Thomas'), ki18n('Work on PyQt4 to PyKDE4 port')) 
aboutData.addAuthor(ki18n('Alberto Milone'), ki18n('Work on PyQt4 to PyKDE4 port'))

if __name__ == '__main__':
    # We need a copy of sys.argv for ui.py to use
    argscopy = sys.argv
    # We don't want KCmdLineArgs to parse our cmd line args but it'll need 
    # something... Just give it nothing and let ui.py worry about args.
    sys.argv = [""]

    KCmdLineArgs.init(sys.argv, aboutData)
    kapp = KApplication()

    sys.argv = argscopy
    OSLib.inst = OSLib()
    u = KDEUI()
    sys.exit(u.run())
