#!/usr/bin/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('(?' + title + '' + 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 = '
%s
' % text
if subtext:
text += subtext
text = text.replace('\n', '
')
# 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.'''
# TODO: this only works with a KDE PolicyKit, which does not exist yet;
# so work around by calling it through kdesu
# self.ui_show_main()
argv = ['/usr/lib/kde4/libexec/kdesu', sys.argv[0]]
try:
import subprocess
subprocess.call(argv, close_fds=True)
except OSError, e:
logging.error('could not execute %s: %s' % (str(argv), e.message))
sys.exit(0)
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('%s
%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) # Does someone need a qt-introduction? - this does *NOT* set current item
# (and it makes a stupid behavior - since it is not set. Clicking Active then just leaves the dialog useless)
self.mw.treeview_drivers.setCurrentItem(cur_item)
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('%s' % 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('%s' %
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())