From da04465e41dbe89f88eb44e99382eec416f5eb80 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 27 Mar 2011 20:47:27 +0200 Subject: [PATCH 01/98] r1431 From 19138cd9e5b42652473e331cd866252cd3e4150b Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 29 Mar 2011 15:56:49 +0200 Subject: [PATCH 02/98] started reworking shortcuts --- openlp/core/lib/ui.py | 2 + openlp/core/ui/mainwindow.py | 63 ++++++++---------- openlp/core/ui/servicemanager.py | 2 +- openlp/core/ui/shortcutlistform.py | 95 +++++++++++++++++++-------- openlp/core/ui/slidecontroller.py | 3 +- openlp/core/utils/actions.py | 11 ++++ openlp/plugins/alerts/alertsplugin.py | 8 ++- 7 files changed, 116 insertions(+), 68 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index c1a9f8b35..6aa06bfcc 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -31,6 +31,7 @@ import logging from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon, Receiver, translate +from openlp.core.utils.actions import actionList log = logging.getLogger(__name__) @@ -265,6 +266,7 @@ def icon_action(parent, name, icon, checked=None): else: action = base_action(parent, name) action.setIcon(build_icon(icon)) + #actionList.add_action(action, name) return action def shortcut_action(parent, text, shortcuts, function): diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 594c6cc91..98144bfa3 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -38,7 +38,8 @@ from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \ ThemeManager, SlideController, PluginForm, MediaDockManager, \ ShortcutListForm, DisplayTagForm from openlp.core.utils import AppLocation, add_actions, LanguageManager, \ - ActionList, get_application_version + get_application_version +from openlp.core.utils.actions import actionList log = logging.getLogger(__name__) @@ -163,53 +164,52 @@ class Ui_MainWindow(object): # Create the menu items self.FileNewItem = icon_action(mainWindow, u'FileNewItem', u':/general/general_new.png') - mainWindow.actionList.add_action(self.FileNewItem, u'File') + actionList.add_action(self.FileNewItem, u'File') self.FileOpenItem = icon_action(mainWindow, u'FileOpenItem', u':/general/general_open.png') - mainWindow.actionList.add_action(self.FileOpenItem, u'File') + actionList.add_action(self.FileOpenItem, u'File') self.FileSaveItem = icon_action(mainWindow, u'FileSaveItem', u':/general/general_save.png') - mainWindow.actionList.add_action(self.FileSaveItem, u'File') + actionList.add_action(self.FileSaveItem, u'File') self.FileSaveAsItem = base_action(mainWindow, u'FileSaveAsItem') - mainWindow.actionList.add_action(self.FileSaveAsItem, u'File') + actionList.add_action(self.FileSaveAsItem, u'File') self.printServiceOrderItem = base_action( mainWindow, u'printServiceItem') - mainWindow.actionList.add_action( - self.printServiceOrderItem, u'Print Service Order') + actionList.add_action(self.printServiceOrderItem, u'File') self.FileExitItem = icon_action(mainWindow, u'FileExitItem', u':/system/system_exit.png') - mainWindow.actionList.add_action(self.FileExitItem, u'File') + actionList.add_action(self.FileExitItem, u'File') self.ImportThemeItem = base_action(mainWindow, u'ImportThemeItem') - mainWindow.actionList.add_action(self.ImportThemeItem, u'Import') + actionList.add_action(self.ImportThemeItem, u'Import') self.ImportLanguageItem = base_action(mainWindow, u'ImportLanguageItem') - mainWindow.actionList.add_action(self.ImportLanguageItem, u'Import') + actionList.add_action(self.ImportLanguageItem, u'Import') self.ExportThemeItem = base_action(mainWindow, u'ExportThemeItem') - mainWindow.actionList.add_action(self.ExportThemeItem, u'Export') + actionList.add_action(self.ExportThemeItem, u'Export') self.ExportLanguageItem = base_action(mainWindow, u'ExportLanguageItem') - mainWindow.actionList.add_action(self.ExportLanguageItem, u'Export') + actionList.add_action(self.ExportLanguageItem, u'Export') self.ViewMediaManagerItem = icon_action(mainWindow, u'ViewMediaManagerItem', u':/system/system_mediamanager.png', self.mediaManagerDock.isVisible()) self.ViewThemeManagerItem = icon_action(mainWindow, u'ViewThemeManagerItem', u':/system/system_thememanager.png', self.themeManagerDock.isVisible()) - mainWindow.actionList.add_action(self.ViewMediaManagerItem, u'View') + actionList.add_action(self.ViewMediaManagerItem, u'View') self.ViewServiceManagerItem = icon_action(mainWindow, u'ViewServiceManagerItem', u':/system/system_servicemanager.png', self.serviceManagerDock.isVisible()) - mainWindow.actionList.add_action(self.ViewServiceManagerItem, u'View') + actionList.add_action(self.ViewServiceManagerItem, u'View') self.ViewPreviewPanel = checkable_action(mainWindow, u'ViewPreviewPanel', previewVisible) - mainWindow.actionList.add_action(self.ViewPreviewPanel, u'View') + actionList.add_action(self.ViewPreviewPanel, u'View') self.ViewLivePanel = checkable_action(mainWindow, u'ViewLivePanel', liveVisible) - mainWindow.actionList.add_action(self.ViewLivePanel, u'View') + actionList.add_action(self.ViewLivePanel, u'View') self.ModeDefaultItem = checkable_action(mainWindow, u'ModeDefaultItem') - mainWindow.actionList.add_action(self.ModeDefaultItem, u'View Mode') + actionList.add_action(self.ModeDefaultItem, u'View Mode') self.ModeSetupItem = checkable_action(mainWindow, u'ModeLiveItem') - mainWindow.actionList.add_action(self.ModeSetupItem, u'View Mode') + actionList.add_action(self.ModeSetupItem, u'View Mode') self.ModeLiveItem = checkable_action(mainWindow, u'ModeLiveItem', True) - mainWindow.actionList.add_action(self.ModeLiveItem, u'View Mode') + actionList.add_action(self.ModeLiveItem, u'View Mode') self.ModeGroup = QtGui.QActionGroup(mainWindow) self.ModeGroup.addAction(self.ModeDefaultItem) self.ModeGroup.addAction(self.ModeSetupItem) @@ -217,18 +217,18 @@ class Ui_MainWindow(object): self.ModeDefaultItem.setChecked(True) self.ToolsAddToolItem = icon_action(mainWindow, u'ToolsAddToolItem', u':/tools/tools_add.png') - mainWindow.actionList.add_action(self.ToolsAddToolItem, u'Tools') + actionList.add_action(self.ToolsAddToolItem, u'Tools') self.ToolsOpenDataFolder = icon_action(mainWindow, u'ToolsOpenDataFolder', u':/general/general_open.png') - mainWindow.actionList.add_action(self.ToolsOpenDataFolder, u'Tools') + actionList.add_action(self.ToolsOpenDataFolder, u'Tools') self.settingsPluginListItem = icon_action(mainWindow, u'settingsPluginListItem', u':/system/settings_plugin_list.png') - mainWindow.actionList.add_action(self.settingsPluginListItem, + actionList.add_action(self.settingsPluginListItem, u'Settings') # i18n Language Items self.AutoLanguageItem = checkable_action(mainWindow, u'AutoLanguageItem', LanguageManager.auto_language) - mainWindow.actionList.add_action(self.AutoLanguageItem, u'Settings') + actionList.add_action(self.AutoLanguageItem, u'Settings') self.LanguageGroup = QtGui.QActionGroup(mainWindow) self.LanguageGroup.setExclusive(True) self.LanguageGroup.setObjectName(u'LanguageGroup') @@ -246,19 +246,19 @@ class Ui_MainWindow(object): u'DisplayTagItem', u':/system/tag_editor.png') self.SettingsConfigureItem = icon_action(mainWindow, u'SettingsConfigureItem', u':/system/system_settings.png') - mainWindow.actionList.add_action(self.SettingsShortcutsItem, + actionList.add_action(self.SettingsShortcutsItem, u'Settings') self.HelpDocumentationItem = icon_action(mainWindow, u'HelpDocumentationItem', u':/system/system_help_contents.png') self.HelpDocumentationItem.setEnabled(False) - mainWindow.actionList.add_action(self.HelpDocumentationItem, u'Help') + actionList.add_action(self.HelpDocumentationItem, u'Help') self.HelpAboutItem = icon_action(mainWindow, u'HelpAboutItem', u':/system/system_about.png') - mainWindow.actionList.add_action(self.HelpAboutItem, u'Help') + actionList.add_action(self.HelpAboutItem, u'Help') self.HelpOnlineHelpItem = base_action(mainWindow, u'HelpOnlineHelpItem') - mainWindow.actionList.add_action(self.HelpOnlineHelpItem, u'Help') + actionList.add_action(self.HelpOnlineHelpItem, u'Help') self.helpWebSiteItem = base_action(mainWindow, u'helpWebSiteItem') - mainWindow.actionList.add_action(self.helpWebSiteItem, u'Help') + actionList.add_action(self.helpWebSiteItem, u'Help') add_actions(self.FileImportMenu, (self.ImportThemeItem, self.ImportLanguageItem)) add_actions(self.FileExportMenu, @@ -301,7 +301,6 @@ class Ui_MainWindow(object): self.ToolsAddToolItem.setVisible(False) self.ImportLanguageItem.setVisible(False) self.ExportLanguageItem.setVisible(False) - self.SettingsShortcutsItem.setVisible(False) self.HelpDocumentationItem.setVisible(False) def retranslateUi(self, mainWindow): @@ -467,8 +466,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): """ log.info(u'MainWindow loaded') - actionList = ActionList() - def __init__(self, screens, application): """ This constructor sets up the interface, the various managers, and the @@ -476,7 +473,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): """ QtGui.QMainWindow.__init__(self) self.screens = screens - self.application = application # Set up settings sections for the main application # (not for use by plugins) @@ -485,7 +481,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.serviceSettingsSection = u'servicemanager' self.songsSettingsSection = u'songs' self.serviceNotSaved = False - self.actionList = ActionList() self.settingsmanager = SettingsManager(screens) self.aboutForm = AboutForm(self) self.settingsForm = SettingsForm(self.screens, self, self) @@ -777,7 +772,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): """ Show the shortcuts dialog """ - self.shortcutForm.exec_(self.actionList) + self.shortcutForm.exec_() def onModeDefaultItemClicked(self): """ diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 168ad8a8c..1d5321e01 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -40,6 +40,7 @@ from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm from openlp.core.ui.printserviceform import PrintServiceForm from openlp.core.utils import AppLocation, delete_file, file_is_unicode, \ split_filename +from openlp.core.utils.actions import actionList class ServiceManagerList(QtGui.QTreeWidget): """ @@ -315,7 +316,6 @@ class ServiceManager(QtGui.QWidget): self.configUpdated() def setServiceHotkeys(self): - actionList = self.mainwindow.actionList actionList.add_action(self.serviceManagerList.moveDown, u'Service') actionList.add_action(self.serviceManagerList.moveUp, u'Service') actionList.add_action(self.serviceManagerList.moveTop, u'Service') diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index e87ba3ada..a61dd41ed 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -30,6 +30,7 @@ import re from PyQt4 import QtCore, QtGui from openlp.core.utils import translate +from openlp.core.utils.actions import actionList from shortcutlistdialog import Ui_ShortcutListDialog REMOVE_AMPERSAND = re.compile(r'&{1}') @@ -40,21 +41,24 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): """ The shortcut list dialog """ - - def __init__(self, parent): - """ - Do some initialisation stuff - """ +#TODO: do not close on ESC +#TODO: ability to remove actions +#TODO: Save shortcuts +#TODO: doc + def __init__(self, parent=None): QtGui.QDialog.__init__(self, parent) self.setupUi(self) - self.actionList = None - self.captureShortcut = False + self.assingedShortcuts = [] + self.shortcutButton.setText(u'') QtCore.QObject.connect(self.shortcutButton, QtCore.SIGNAL(u'toggled(bool)'), self.onShortcutButtonClicked) + QtCore.QObject.connect(self.treeWidget, + QtCore.SIGNAL(u'itemPressed(QTreeWidgetItem*, int)'), + self.onItemPressed) def keyReleaseEvent(self, event): Qt = QtCore.Qt - if not self.captureShortcut: + if not self.shortcutButton.isChecked(): return key = event.key() if key == Qt.Key_Shift or key == Qt.Key_Control or \ @@ -68,45 +72,80 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): if event.modifiers() & Qt.ShiftModifier == Qt.ShiftModifier: key_string = u'Shift+' + key_string key_sequence = QtGui.QKeySequence(key_string) - existing_key = QtGui.QKeySequence(u'Ctrl+Shift+F8') - if key_sequence == existing_key: - QtGui.QMessageBox.warning( - self, + if key_sequence in self.assingedShortcuts: + QtGui.QMessageBox.warning(self, translate('OpenLP.ShortcutListDialog', 'Duplicate Shortcut'), unicode(translate('OpenLP.ShortcutListDialog', 'The shortcut ' - '"%s" is already assigned to another action, please ' - 'use a different shortcut.')) % key_sequence.toString(), + '"%s" is already assigned to another action, please ' + 'use a different shortcut.')) % key_sequence.toString(), QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok), QtGui.QMessageBox.Ok ) else: self.shortcutButton.setText(key_sequence.toString()) - self.shortcutButton.setChecked(False) - self.captureShortcut = False + self.shortcutButton.setChecked(False) - def exec_(self, actionList): - self.actionList = actionList - self.refreshActions() + def exec_(self): + # The dialog is opened the first time + if self.treeWidget.topLevelItemCount() == 0: + QtCore.QObject.connect(actionList.signal, + QtCore.SIGNAL(u'addedAction()'), self.initialiseActionList) + self.initialiseActionList() + self.refreshActionList() return QtGui.QDialog.exec_(self) - def refreshActions(self): - self.treeWidget.clear() - for category in self.actionList.categories: - item = QtGui.QTreeWidgetItem([category.name]) - for action in category.actions: + def refreshActionList(self): + self.assingedShortcuts = [] + iterator = QtGui.QTreeWidgetItemIterator(self.treeWidget) + while iterator.value(): + treewidgetItem = iterator.value() + action = treewidgetItem.data(0, QtCore.Qt.UserRole).toPyObject() + if action is not None: actionText = REMOVE_AMPERSAND.sub('', unicode(action.text())) - if (len(action.shortcuts()) == 2): + if len(action.shortcuts()) == 2: shortcutText = action.shortcuts()[0].toString() alternateText = action.shortcuts()[1].toString() else: shortcutText = action.shortcut().toString() alternateText = u'' - actionItem = QtGui.QTreeWidgetItem( - [actionText, shortcutText, alternateText]) + self.assingedShortcuts.extend(action.shortcuts()) + treewidgetItem.setText(0, actionText) + treewidgetItem.setText(1, shortcutText) + treewidgetItem.setText(2, alternateText) + iterator += 1 + + def initialiseActionList(self): + for category in actionList.categories: + item = QtGui.QTreeWidgetItem([category.name]) + for action in category.actions: + actionItem = QtGui.QTreeWidgetItem() actionItem.setIcon(0, action.icon()) + actionItem.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(action)) item.addChild(actionItem) item.setExpanded(True) self.treeWidget.addTopLevelItem(item) def onShortcutButtonClicked(self, toggled): - self.captureShortcut = toggled + if toggled: + return + item = self.treeWidget.currentItem() + action = item.data(0, QtCore.Qt.UserRole).toPyObject() + if action is None: + return + # TODO: Sort out which shortcuts should be kept. + action.setShortcuts(QtGui.QKeySequence(self.shortcutButton.text())) + self.refreshActionList() + + def onItemPressed(self, item, column): + item = self.treeWidget.currentItem() + action = item.data(0, QtCore.Qt.UserRole).toPyObject() + if action is None: + text = u'' + else: + if len(action.shortcuts()) == 0: + text = u'' + elif len(action.shortcuts()) == 2 and column == 2: + text = action.shortcuts()[1].toString() + else: + text = action.shortcuts()[0].toString() + self.shortcutButton.setText(text) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 232653326..1ae84e950 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -34,6 +34,7 @@ from openlp.core.lib import OpenLPToolbar, Receiver, resize_image, \ ItemCapabilities, translate from openlp.core.lib.ui import icon_action, UiStrings, shortcut_action from openlp.core.ui import HideMode, MainDisplay +from openlp.core.utils.actions import actionList log = logging.getLogger(__name__) @@ -362,14 +363,12 @@ class SlideController(QtGui.QWidget): QtCore.SIGNAL(u'config_screen_changed'), self.screenSizeChanged) def setPreviewHotkeys(self, parent=None): - actionList = self.parent.actionList self.previousItem.setShortcuts([QtCore.Qt.Key_Up, 0]) actionList.add_action(self.previousItem, u'Preview') self.nextItem.setShortcuts([QtCore.Qt.Key_Down, 0]) actionList.add_action(self.nextItem, u'Preview') def setLiveHotkeys(self, parent=None): - actionList = self.parent.actionList self.previousItem.setShortcuts([QtCore.Qt.Key_Up, QtCore.Qt.Key_PageUp]) self.previousItem.setShortcutContext( QtCore.Qt.WidgetWithChildrenShortcut) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 41ae41f4b..ba4a7b40d 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -27,6 +27,8 @@ The :mod:`~openlp.core.utils.actions` module provides action list classes used by the shortcuts system. """ +from PyQt4 import QtCore + class ActionCategory(object): """ The :class:`~openlp.core.utils.ActionCategory` class encapsulates a @@ -173,6 +175,7 @@ class ActionList(object): """ def __init__(self): self.categories = CategoryList() + self.signal = Emit() def add_action(self, action, category=u'Default', weight=None): if category not in self.categories: @@ -181,3 +184,11 @@ class ActionList(object): self.categories[category].actions.append(action) else: self.categories[category].actions.add(action, weight) + self.signal.emit(QtCore.SIGNAL(u'addedAction()')) + + +class Emit(QtCore.QObject): + def __init__(self): + QtCore.QObject.__init__(self) + +actionList = ActionList() diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index db0ba3b7e..2d22d2d5a 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -30,6 +30,8 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, StringContent, build_icon, translate from openlp.core.lib.db import Manager +from openlp.core.lib.ui import icon_action +from openlp.core.utils.actions import actionList from openlp.plugins.alerts.lib import AlertsManager, AlertsTab from openlp.plugins.alerts.lib.db import init_schema from openlp.plugins.alerts.forms import AlertForm @@ -58,9 +60,9 @@ class AlertsPlugin(Plugin): use it as their parent. """ log.info(u'add tools menu') - self.toolsAlertItem = QtGui.QAction(tools_menu) - self.toolsAlertItem.setIcon(build_icon(u':/plugins/plugin_alerts.png')) - self.toolsAlertItem.setObjectName(u'toolsAlertItem') + self.toolsAlertItem = icon_action(tools_menu, u'toolsAlertItem', + u':/plugins/plugin_alerts.png') + actionList.add_action(self.toolsAlertItem, u'Extra') self.toolsAlertItem.setText(translate('AlertsPlugin', '&Alert')) self.toolsAlertItem.setStatusTip( translate('AlertsPlugin', 'Show an alert message.')) From d9c2d92c8c4265975c7860bfc7fd5aba26c1bac1 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 29 Mar 2011 18:44:36 +0200 Subject: [PATCH 03/98] --- openlp/core/lib/ui.py | 24 +++--- openlp/core/ui/mainwindow.py | 104 +++++++++++--------------- openlp/core/ui/shortcutlistform.py | 15 +++- openlp/core/ui/slidecontroller.py | 23 +++--- openlp/core/utils/__init__.py | 6 +- openlp/core/utils/actions.py | 2 +- openlp/plugins/alerts/alertsplugin.py | 3 +- 7 files changed, 90 insertions(+), 87 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 6aa06bfcc..86def01d8 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -239,41 +239,47 @@ def create_up_down_push_button_set(parent): QtCore.SIGNAL(u'clicked()'), parent.onDownButtonClicked) return up_button, down_button -def base_action(parent, name): +def base_action(parent, name, category=None): """ Return the most basic action with the object name set. """ action = QtGui.QAction(parent) action.setObjectName(name) + if category is not None: + actionList.add_action(action, category) return action -def checkable_action(parent, name, checked=None): +def checkable_action(parent, name, checked=None, category=None): """ Return a standard action with the checkable attribute set. """ - action = base_action(parent, name) + action = base_action(parent, name, category) action.setCheckable(True) if checked is not None: action.setChecked(checked) return action -def icon_action(parent, name, icon, checked=None): +def icon_action(parent, name, icon, checked=None, category=None): """ Return a standard action with an icon. + + ``category`` + The category the action should be listed in the shortcut dialog. If you + not wish, that this action is added to the shortcut dialog, then do not + state any. """ if checked is not None: - action = checkable_action(parent, name, checked) + action = checkable_action(parent, name, checked, category) else: - action = base_action(parent, name) + action = base_action(parent, name, category) action.setIcon(build_icon(icon)) - #actionList.add_action(action, name) return action -def shortcut_action(parent, text, shortcuts, function): +def shortcut_action(parent, text, shortcuts, function, category=None): """ Return a shortcut enabled action. """ - action = QtGui.QAction(text, parent) + action = base_action(parent, text, category) action.setShortcuts(shortcuts) action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), function) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index ed06d3931..c37db0b97 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -39,7 +39,7 @@ from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \ ShortcutListForm, DisplayTagForm from openlp.core.utils import AppLocation, add_actions, LanguageManager, \ get_application_version -from openlp.core.utils.actions import actionList +#from openlp.core.utils.actions import actionList log = logging.getLogger(__name__) @@ -163,72 +163,60 @@ class Ui_MainWindow(object): self.themeManagerDock) # Create the menu items self.FileNewItem = icon_action(mainWindow, u'FileNewItem', - u':/general/general_new.png') - actionList.add_action(self.FileNewItem, u'File') + u':/general/general_new.png', category=u'File') self.FileOpenItem = icon_action(mainWindow, u'FileOpenItem', - u':/general/general_open.png') - actionList.add_action(self.FileOpenItem, u'File') + u':/general/general_open.png', category=u'File') self.FileSaveItem = icon_action(mainWindow, u'FileSaveItem', - u':/general/general_save.png') - actionList.add_action(self.FileSaveItem, u'File') - self.FileSaveAsItem = base_action(mainWindow, u'FileSaveAsItem') - actionList.add_action(self.FileSaveAsItem, u'File') + u':/general/general_save.png', category=u'File') + self.FileSaveAsItem = base_action( + mainWindow, u'FileSaveAsItem', u'File') self.printServiceOrderItem = base_action( - mainWindow, u'printServiceItem') - actionList.add_action(self.printServiceOrderItem, u'File') + mainWindow, u'printServiceItem', u'File') self.FileExitItem = icon_action(mainWindow, u'FileExitItem', - u':/system/system_exit.png') - actionList.add_action(self.FileExitItem, u'File') - self.ImportThemeItem = base_action(mainWindow, u'ImportThemeItem') - actionList.add_action(self.ImportThemeItem, u'Import') - self.ImportLanguageItem = base_action(mainWindow, u'ImportLanguageItem') - actionList.add_action(self.ImportLanguageItem, u'Import') - self.ExportThemeItem = base_action(mainWindow, u'ExportThemeItem') - actionList.add_action(self.ExportThemeItem, u'Export') - self.ExportLanguageItem = base_action(mainWindow, u'ExportLanguageItem') - actionList.add_action(self.ExportLanguageItem, u'Export') + u':/system/system_exit.png', category=u'File') + self.ImportThemeItem = base_action( + mainWindow, u'ImportThemeItem', u'Import') + self.ImportLanguageItem = base_action( + mainWindow, u'ImportLanguageItem', u'Import') + self.ExportThemeItem = base_action( + mainWindow, u'ExportThemeItem', u'Export') + self.ExportLanguageItem = base_action( + mainWindow, u'ExportLanguageItem', u'Export') self.ViewMediaManagerItem = icon_action(mainWindow, u'ViewMediaManagerItem', u':/system/system_mediamanager.png', - self.mediaManagerDock.isVisible()) + self.mediaManagerDock.isVisible(), u'View') self.ViewThemeManagerItem = icon_action(mainWindow, u'ViewThemeManagerItem', u':/system/system_thememanager.png', - self.themeManagerDock.isVisible()) - actionList.add_action(self.ViewMediaManagerItem, u'View') + self.themeManagerDock.isVisible(), u'View') self.ViewServiceManagerItem = icon_action(mainWindow, u'ViewServiceManagerItem', u':/system/system_servicemanager.png', - self.serviceManagerDock.isVisible()) - actionList.add_action(self.ViewServiceManagerItem, u'View') + self.serviceManagerDock.isVisible(), u'View') self.ViewPreviewPanel = checkable_action(mainWindow, - u'ViewPreviewPanel', previewVisible) - actionList.add_action(self.ViewPreviewPanel, u'View') - self.ViewLivePanel = checkable_action(mainWindow, u'ViewLivePanel', - liveVisible) - actionList.add_action(self.ViewLivePanel, u'View') - self.ModeDefaultItem = checkable_action(mainWindow, u'ModeDefaultItem') - actionList.add_action(self.ModeDefaultItem, u'View Mode') - self.ModeSetupItem = checkable_action(mainWindow, u'ModeLiveItem') - actionList.add_action(self.ModeSetupItem, u'View Mode') - self.ModeLiveItem = checkable_action(mainWindow, u'ModeLiveItem', True) - actionList.add_action(self.ModeLiveItem, u'View Mode') + u'ViewPreviewPanel', previewVisible, u'View') + self.ViewLivePanel = checkable_action( + mainWindow, u'ViewLivePanel', liveVisible, u'View') + self.ModeDefaultItem = checkable_action( + mainWindow, u'ModeDefaultItem', category=u'View Mode') + self.ModeSetupItem = checkable_action( + mainWindow, u'ModeLiveItem', category=u'View Mode') + self.ModeLiveItem = checkable_action( + mainWindow, u'ModeLiveItem', True, u'View Mode') self.ModeGroup = QtGui.QActionGroup(mainWindow) self.ModeGroup.addAction(self.ModeDefaultItem) self.ModeGroup.addAction(self.ModeSetupItem) self.ModeGroup.addAction(self.ModeLiveItem) self.ModeDefaultItem.setChecked(True) self.ToolsAddToolItem = icon_action(mainWindow, u'ToolsAddToolItem', - u':/tools/tools_add.png') - actionList.add_action(self.ToolsAddToolItem, u'Tools') - self.ToolsOpenDataFolder = icon_action(mainWindow, - u'ToolsOpenDataFolder', u':/general/general_open.png') - actionList.add_action(self.ToolsOpenDataFolder, u'Tools') + u':/tools/tools_add.png', category=u'Tools') + self.ToolsOpenDataFolder = icon_action(mainWindow, u'ToolsOpenDataFolder', + u':/general/general_open.png', category=u'Tools') self.settingsPluginListItem = icon_action(mainWindow, - u'settingsPluginListItem', u':/system/settings_plugin_list.png') - actionList.add_action(self.settingsPluginListItem, - u'Settings') + u'settingsPluginListItem', u':/system/settings_plugin_list.png', + category=u'Settings') # i18n Language Items self.AutoLanguageItem = checkable_action(mainWindow, u'AutoLanguageItem', LanguageManager.auto_language) - actionList.add_action(self.AutoLanguageItem, u'Settings') + #actionList.add_action(self.AutoLanguageItem, u'Settings') self.LanguageGroup = QtGui.QActionGroup(mainWindow) self.LanguageGroup.setExclusive(True) self.LanguageGroup.setObjectName(u'LanguageGroup') @@ -241,24 +229,22 @@ class Ui_MainWindow(object): add_actions(self.LanguageGroup, [languageItem]) self.SettingsShortcutsItem = icon_action(mainWindow, u'SettingsShortcutsItem', - u':/system/system_configure_shortcuts.png') + u':/system/system_configure_shortcuts.png', category=u'Settings') self.DisplayTagItem = icon_action(mainWindow, - u'DisplayTagItem', u':/system/tag_editor.png') + u'DisplayTagItem', u':/system/tag_editor.png', category=u'Settings') self.SettingsConfigureItem = icon_action(mainWindow, - u'SettingsConfigureItem', u':/system/system_settings.png') - actionList.add_action(self.SettingsShortcutsItem, - u'Settings') + u'SettingsConfigureItem', u':/system/system_settings.png', + category=u'Settings') self.HelpDocumentationItem = icon_action(mainWindow, - u'HelpDocumentationItem', u':/system/system_help_contents.png') + u'HelpDocumentationItem', u':/system/system_help_contents.png', + category=None)#u'Help') self.HelpDocumentationItem.setEnabled(False) - actionList.add_action(self.HelpDocumentationItem, u'Help') self.HelpAboutItem = icon_action(mainWindow, u'HelpAboutItem', - u':/system/system_about.png') - actionList.add_action(self.HelpAboutItem, u'Help') - self.HelpOnlineHelpItem = base_action(mainWindow, u'HelpOnlineHelpItem') - actionList.add_action(self.HelpOnlineHelpItem, u'Help') - self.helpWebSiteItem = base_action(mainWindow, u'helpWebSiteItem') - actionList.add_action(self.helpWebSiteItem, u'Help') + u':/system/system_about.png', category=None)#u'Help') + self.HelpOnlineHelpItem = base_action( + mainWindow, u'HelpOnlineHelpItem', category=u'Help') + self.helpWebSiteItem = base_action( + mainWindow, u'helpWebSiteItem', category=u'Help') add_actions(self.FileImportMenu, (self.ImportThemeItem, self.ImportLanguageItem)) add_actions(self.FileExportMenu, diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index a61dd41ed..071482f95 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -45,6 +45,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): #TODO: ability to remove actions #TODO: Save shortcuts #TODO: doc +#TODO: Fix Preview/Live controller (have the same shortcut) def __init__(self, parent=None): QtGui.QDialog.__init__(self, parent) self.setupUi(self) @@ -89,7 +90,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): # The dialog is opened the first time if self.treeWidget.topLevelItemCount() == 0: QtCore.QObject.connect(actionList.signal, - QtCore.SIGNAL(u'addedAction()'), self.initialiseActionList) + QtCore.SIGNAL(u'addedAction()'), self.asdf) self.initialiseActionList() self.refreshActionList() return QtGui.QDialog.exec_(self) @@ -125,6 +126,18 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): item.setExpanded(True) self.treeWidget.addTopLevelItem(item) + def asdf(self, action, category): + print action + for category in actionList.categories: + item = QtGui.QTreeWidgetItem([category.name]) + for action in category.actions: + actionItem = QtGui.QTreeWidgetItem() + actionItem.setIcon(0, action.icon()) + actionItem.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(action)) + item.addChild(actionItem) + item.setExpanded(True) + self.treeWidget.addTopLevelItem(item) + def onShortcutButtonClicked(self, toggled): if toggled: return diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 1ae84e950..cafbec198 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -156,15 +156,15 @@ class SlideController(QtGui.QWidget): self.hideMenu.setMenu(QtGui.QMenu( translate('OpenLP.SlideController', 'Hide'), self.toolbar)) self.blankScreen = icon_action(self.hideMenu, u'Blank Screen', - u':/slides/slide_blank.png', False) + u':/slides/slide_blank.png', False, u'Live Toolbar') self.blankScreen.setText( translate('OpenLP.SlideController', 'Blank Screen')) self.themeScreen = icon_action(self.hideMenu, u'Blank Theme', - u':/slides/slide_theme.png', False) + u':/slides/slide_theme.png', False, u'Live Toolbar') self.themeScreen.setText( translate('OpenLP.SlideController', 'Blank to Theme')) self.desktopScreen = icon_action(self.hideMenu, u'Desktop Screen', - u':/slides/slide_desktop.png', False) + u':/slides/slide_desktop.png', False, u'Live Toolbar') self.desktopScreen.setText( translate('OpenLP.SlideController', 'Show Desktop')) self.hideMenu.setDefaultAction(self.blankScreen) @@ -364,30 +364,27 @@ class SlideController(QtGui.QWidget): def setPreviewHotkeys(self, parent=None): self.previousItem.setShortcuts([QtCore.Qt.Key_Up, 0]) - actionList.add_action(self.previousItem, u'Preview') + actionList.add_action(self.previousItem, u'Preview Toolbar') self.nextItem.setShortcuts([QtCore.Qt.Key_Down, 0]) - actionList.add_action(self.nextItem, u'Preview') + actionList.add_action(self.nextItem, u'Preview Toolbar') def setLiveHotkeys(self, parent=None): self.previousItem.setShortcuts([QtCore.Qt.Key_Up, QtCore.Qt.Key_PageUp]) self.previousItem.setShortcutContext( QtCore.Qt.WidgetWithChildrenShortcut) - actionList.add_action(self.previousItem, u'Live') + actionList.add_action(self.previousItem, u'Live Toolbar') self.nextItem.setShortcuts([QtCore.Qt.Key_Down, QtCore.Qt.Key_PageDown]) self.nextItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) - actionList.add_action(self.nextItem, u'Live') + actionList.add_action(self.nextItem, u'Live Toolbar') self.previousService = shortcut_action(parent, translate('OpenLP.SlideController', 'Previous Service'), - [QtCore.Qt.Key_Left, 0], self.servicePrevious) - actionList.add_action(self.previousService, u'Live') + [QtCore.Qt.Key_Left, 0], self.servicePrevious, u'Live Toolbar') self.nextService = shortcut_action(parent, translate('OpenLP.SlideController', 'Next Service'), - [QtCore.Qt.Key_Right, 0], self.serviceNext) - actionList.add_action(self.nextService, u'Live') + [QtCore.Qt.Key_Right, 0], self.serviceNext, u'Live Toolbar') self.escapeItem = shortcut_action(parent, translate('OpenLP.SlideController', 'Escape Item'), - [QtCore.Qt.Key_Escape, 0], self.liveEscape) - actionList.add_action(self.escapeItem, u'Live') + [QtCore.Qt.Key_Escape, 0], self.liveEscape, u'Live Toolbar') def liveEscape(self): self.display.setVisible(False) diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 3c639297e..1bc7037ae 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -495,7 +495,7 @@ def get_uno_instance(resolver): from languagemanager import LanguageManager from actions import ActionList -__all__ = [u'AppLocation', u'check_latest_version', u'add_actions', - u'get_filesystem_encoding', u'LanguageManager', u'ActionList', - u'get_web_page', u'file_is_unicode', u'string_is_unicode', +__all__ = [u'AppLocation', u'get_application_version', u'check_latest_version', + u'add_actions', u'get_filesystem_encoding', u'LanguageManager', + u'ActionList', u'get_web_page', u'file_is_unicode', u'string_is_unicode', u'get_uno_command', u'get_uno_instance', u'delete_file'] diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index ba4a7b40d..b592c9a69 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -184,7 +184,7 @@ class ActionList(object): self.categories[category].actions.append(action) else: self.categories[category].actions.add(action, weight) - self.signal.emit(QtCore.SIGNAL(u'addedAction()')) + self.signal.emit(QtCore.SIGNAL(u'addedAction(QtGui.QAction, QString)'), action, QtCore.QString(category)) class Emit(QtCore.QObject): diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index 2d22d2d5a..c397fb9c4 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -62,7 +62,6 @@ class AlertsPlugin(Plugin): log.info(u'add tools menu') self.toolsAlertItem = icon_action(tools_menu, u'toolsAlertItem', u':/plugins/plugin_alerts.png') - actionList.add_action(self.toolsAlertItem, u'Extra') self.toolsAlertItem.setText(translate('AlertsPlugin', '&Alert')) self.toolsAlertItem.setStatusTip( translate('AlertsPlugin', 'Show an alert message.')) @@ -76,6 +75,7 @@ class AlertsPlugin(Plugin): log.info(u'Alerts Initialising') Plugin.initialise(self) self.toolsAlertItem.setVisible(True) + actionList.add_action(self.toolsAlertItem, u'Tools') self.liveController.alertTab = self.settings_tab def finalise(self): @@ -86,6 +86,7 @@ class AlertsPlugin(Plugin): self.manager.finalise() Plugin.finalise(self) self.toolsAlertItem.setVisible(False) + #TODO: remove the action from the actionList def toggleAlertsState(self): self.alertsActive = not self.alertsActive From b9b0cbbe9c3492f3aa6403f09cea64631d789455 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 29 Mar 2011 21:52:21 +0200 Subject: [PATCH 04/98] removed things, as this did not work --- openlp/core/ui/mainwindow.py | 6 +-- openlp/core/ui/shortcutlistdialog.py | 2 + openlp/core/ui/shortcutlistform.py | 58 +++++++++++++++------------- openlp/core/utils/actions.py | 9 ----- openlp/plugins/songs/songsplugin.py | 14 +++---- 5 files changed, 43 insertions(+), 46 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index c37db0b97..cf58dedd8 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -177,11 +177,11 @@ class Ui_MainWindow(object): self.ImportThemeItem = base_action( mainWindow, u'ImportThemeItem', u'Import') self.ImportLanguageItem = base_action( - mainWindow, u'ImportLanguageItem', u'Import') + mainWindow, u'ImportLanguageItem')#, u'Import') self.ExportThemeItem = base_action( mainWindow, u'ExportThemeItem', u'Export') self.ExportLanguageItem = base_action( - mainWindow, u'ExportLanguageItem', u'Export') + mainWindow, u'ExportLanguageItem')#, u'Export') self.ViewMediaManagerItem = icon_action(mainWindow, u'ViewMediaManagerItem', u':/system/system_mediamanager.png', self.mediaManagerDock.isVisible(), u'View') @@ -240,7 +240,7 @@ class Ui_MainWindow(object): category=None)#u'Help') self.HelpDocumentationItem.setEnabled(False) self.HelpAboutItem = icon_action(mainWindow, u'HelpAboutItem', - u':/system/system_about.png', category=None)#u'Help') + u':/system/system_about.png', category=u'Help') self.HelpOnlineHelpItem = base_action( mainWindow, u'HelpOnlineHelpItem', category=u'Help') self.helpWebSiteItem = base_action( diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index 4e5b9e270..4e0d7d144 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -30,6 +30,7 @@ from openlp.core.lib import translate, build_icon class Ui_ShortcutListDialog(object): def setupUi(self, shortcutListDialog): + shortcutListDialog.resize(440, 450) shortcutListDialog.setObjectName(u'shortcutListDialog') self.dialogLayout = QtGui.QVBoxLayout(shortcutListDialog) self.dialogLayout.setObjectName(u'dialogLayout') @@ -52,6 +53,7 @@ class Ui_ShortcutListDialog(object): build_icon(u':/system/system_configure_shortcuts.png')) self.shortcutButton.setCheckable(True) self.shortcutButton.setObjectName(u'shortcutButton') + self.shortcutButton.setFixedSize(150, 30) self.customLayout.addWidget(self.shortcutButton) self.clearShortcutButton = QtGui.QToolButton(shortcutListDialog) self.clearShortcutButton.setIcon( diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 83b5de84b..4d785cfc0 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -41,10 +41,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): """ The shortcut list dialog """ -#TODO: do not close on ESC -#TODO: ability to remove actions -#TODO: Save shortcuts -#TODO: doc +#TODO: do not close on ESC, ability to remove actions, save/load shortcuts, docs #TODO: Fix Preview/Live controller (have the same shortcut) def __init__(self, parent=None): QtGui.QDialog.__init__(self, parent) @@ -87,21 +84,17 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.shortcutButton.setChecked(False) def exec_(self): - # The dialog is opened the first time - if self.treeWidget.topLevelItemCount() == 0: - QtCore.QObject.connect(actionList.signal, - QtCore.SIGNAL(u'addedAction()'), self.initialiseActionList) - self.initialiseActionList() self.refreshActionList() return QtGui.QDialog.exec_(self) def refreshActionList(self): + # As refreshing does not work, the check does not work either. self.assingedShortcuts = [] - iterator = QtGui.QTreeWidgetItemIterator(self.treeWidget) - while iterator.value(): - treewidgetItem = iterator.value() - action = treewidgetItem.data(0, QtCore.Qt.UserRole).toPyObject() - if action is not None: + #self.treeWidget.clear() + for category in actionList.categories: + item = QtGui.QTreeWidgetItem([category.name]) + for action in category.actions: + self.assingedShortcuts.extend(action.shortcuts()) actionText = REMOVE_AMPERSAND.sub('', unicode(action.text())) if len(action.shortcuts()) == 2: shortcutText = action.shortcuts()[0].toString() @@ -109,19 +102,11 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): else: shortcutText = action.shortcut().toString() alternateText = u'' - self.assingedShortcuts.extend(action.shortcuts()) - treewidgetItem.setText(0, actionText) - treewidgetItem.setText(1, shortcutText) - treewidgetItem.setText(2, alternateText) - iterator += 1 - - def initialiseActionList(self): - for category in actionList.categories: - item = QtGui.QTreeWidgetItem([category.name]) - for action in category.actions: - actionItem = QtGui.QTreeWidgetItem() + actionItem = QtGui.QTreeWidgetItem( + [actionText, shortcutText, alternateText]) actionItem.setIcon(0, action.icon()) - actionItem.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(action)) + actionItem.setData(0, + QtCore.Qt.UserRole, QtCore.QVariant(action)) item.addChild(actionItem) item.setExpanded(True) self.treeWidget.addTopLevelItem(item) @@ -135,13 +120,16 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): return # TODO: Sort out which shortcuts should be kept. action.setShortcuts(QtGui.QKeySequence(self.shortcutButton.text())) + item.setText(1, self.shortcutButton.text()) self.refreshActionList() def onItemPressed(self, item, column): item = self.treeWidget.currentItem() action = item.data(0, QtCore.Qt.UserRole).toPyObject() + self.shortcutButton.setEnabled(action is not None) if action is None: text = u'' + self.shortcutButton.setChecked(False) else: if len(action.shortcuts()) == 0: text = u'' @@ -150,3 +138,21 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): else: text = action.shortcuts()[0].toString() self.shortcutButton.setText(text) + + def saveShortcuts(self): + """ + Save the shortcuts. + """ + settings = QtCore.QSettings() + settings.beginGroup(u'shortcuts') + # TODO: Save shortcuts + settings.endGroup() + + def loadShortcuts(self): + """ + Load the shortcuts. + """ + settings = QtCore.QSettings() + settings.beginGroup(u'shortcuts') + # TODO: Load shortcuts + settings.endGroup() diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index ba4a7b40d..69aeb14b4 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -27,8 +27,6 @@ The :mod:`~openlp.core.utils.actions` module provides action list classes used by the shortcuts system. """ -from PyQt4 import QtCore - class ActionCategory(object): """ The :class:`~openlp.core.utils.ActionCategory` class encapsulates a @@ -175,7 +173,6 @@ class ActionList(object): """ def __init__(self): self.categories = CategoryList() - self.signal = Emit() def add_action(self, action, category=u'Default', weight=None): if category not in self.categories: @@ -184,11 +181,5 @@ class ActionList(object): self.categories[category].actions.append(action) else: self.categories[category].actions.add(action, weight) - self.signal.emit(QtCore.SIGNAL(u'addedAction()')) - - -class Emit(QtCore.QObject): - def __init__(self): - QtCore.QObject.__init__(self) actionList = ActionList() diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 1260a832b..e1731bc01 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -33,7 +33,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, StringContent, build_icon, translate, \ Receiver from openlp.core.lib.db import Manager -from openlp.core.lib.ui import UiStrings +from openlp.core.lib.ui import UiStrings, base_action, icon_action from openlp.plugins.songs.lib import clean_song, SongMediaItem, SongsTab from openlp.plugins.songs.lib.db import init_schema, Song from openlp.plugins.songs.lib.importer import SongFormat @@ -78,8 +78,8 @@ class SongsPlugin(Plugin): use it as their parent. """ # Main song import menu item - will eventually be the only one - self.SongImportItem = QtGui.QAction(import_menu) - self.SongImportItem.setObjectName(u'SongImportItem') + self.SongImportItem = base_action( + import_menu, u'SongImportItem', u'Import') self.SongImportItem.setText(translate( 'SongsPlugin', '&Song')) self.SongImportItem.setToolTip(translate('SongsPlugin', @@ -99,8 +99,7 @@ class SongsPlugin(Plugin): use it as their parent. """ # Main song import menu item - will eventually be the only one - self.SongExportItem = QtGui.QAction(export_menu) - self.SongExportItem.setObjectName(u'SongExportItem') + self.SongExportItem = base_action(export_menu, u'SongExportItem', u'Export') self.SongExportItem.setText(translate( 'SongsPlugin', '&Song')) self.SongExportItem.setToolTip(translate('SongsPlugin', @@ -120,9 +119,8 @@ class SongsPlugin(Plugin): use it as their parent. """ log.info(u'add tools menu') - self.toolsReindexItem = QtGui.QAction(tools_menu) - self.toolsReindexItem.setIcon(build_icon(u':/plugins/plugin_songs.png')) - self.toolsReindexItem.setObjectName(u'toolsReindexItem') + self.toolsReindexItem = icon_action(tools_menu, u'toolsReindexItem', + u':/plugins/plugin_songs.png', category=u'Tools') self.toolsReindexItem.setText( translate('SongsPlugin', '&Re-index Songs')) self.toolsReindexItem.setStatusTip( From e20fd7d85d62707d3b6e78858aaa0174fc0ccba4 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 29 Mar 2011 21:58:04 +0200 Subject: [PATCH 05/98] clean up --- openlp/core/ui/mainwindow.py | 2 -- openlp/core/ui/shortcutlistform.py | 6 ++++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index cf58dedd8..f4cec4ae7 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -39,7 +39,6 @@ from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \ ShortcutListForm, DisplayTagForm from openlp.core.utils import AppLocation, add_actions, LanguageManager, \ get_application_version -#from openlp.core.utils.actions import actionList log = logging.getLogger(__name__) @@ -216,7 +215,6 @@ class Ui_MainWindow(object): # i18n Language Items self.AutoLanguageItem = checkable_action(mainWindow, u'AutoLanguageItem', LanguageManager.auto_language) - #actionList.add_action(self.AutoLanguageItem, u'Settings') self.LanguageGroup = QtGui.QActionGroup(mainWindow) self.LanguageGroup.setExclusive(True) self.LanguageGroup.setObjectName(u'LanguageGroup') diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 4d785cfc0..2dbb57f2d 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -41,8 +41,10 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): """ The shortcut list dialog """ -#TODO: do not close on ESC, ability to remove actions, save/load shortcuts, docs -#TODO: Fix Preview/Live controller (have the same shortcut) +#TODO: do not close on ESC, ability to remove actions (e. g. reindex tool) +#TODO: save/load shortcuts, docs +#TODO: Fix Preview/Live controller (have the same shortcut), make sure + def __init__(self, parent=None): QtGui.QDialog.__init__(self, parent) self.setupUi(self) From 9b939bb9624eca001999b65b87adcc9987ecf070 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 30 Mar 2011 12:12:39 +0200 Subject: [PATCH 06/98] fixed reloading --- openlp/core/ui/shortcutlistform.py | 5 ++--- openlp/core/utils/actions.py | 12 +++++++----- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 2dbb57f2d..b524c5d06 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -90,12 +90,11 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): return QtGui.QDialog.exec_(self) def refreshActionList(self): - # As refreshing does not work, the check does not work either. self.assingedShortcuts = [] - #self.treeWidget.clear() + self.treeWidget.clear() for category in actionList.categories: item = QtGui.QTreeWidgetItem([category.name]) - for action in category.actions: + for action, default in category.actions: self.assingedShortcuts.extend(action.shortcuts()) actionText = REMOVE_AMPERSAND.sub('', unicode(action.text())) if len(action.shortcuts()) == 2: diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 69aeb14b4..cbfa3a488 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -27,9 +27,9 @@ The :mod:`~openlp.core.utils.actions` module provides action list classes used by the shortcuts system. """ -class ActionCategory(object): +class Category(object): """ - The :class:`~openlp.core.utils.ActionCategory` class encapsulates a + The :class:`~openlp.core.utils.Category` class encapsulates a category for the :class:`~openlp.core.utils.CategoryList` class. """ def __init__(self, name, weight=0): @@ -67,10 +67,11 @@ class CategoryActionList(object): Python 3 "next" method. """ if self.index >= len(self.actions): + self.index = 0 raise StopIteration else: self.index += 1 - return self.actions[self.index - 1][1] + return self.actions[self.index - 1][1:] def next(self): """ @@ -91,7 +92,7 @@ class CategoryActionList(object): self.add(name, weight) def add(self, action, weight=0): - self.actions.append((weight, action)) + self.actions.append((weight, action, action.shortcuts())) self.actions.sort(key=lambda act: act[0]) @@ -126,6 +127,7 @@ class CategoryList(object): Python 3 "next" method for iterator. """ if self.index >= len(self.categories): + self.index = 0 raise StopIteration else: self.index += 1 @@ -153,7 +155,7 @@ class CategoryList(object): self.add(name, weight) def add(self, name, weight=0, actions=None): - category = ActionCategory(name, weight) + category = Category(name, weight) if actions: for action in actions: if isinstance(action, tuple): From fe92842c6b656610190791221b99c87d31898f1a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 30 Mar 2011 12:27:27 +0200 Subject: [PATCH 07/98] added ability to remove actions --- openlp/core/ui/shortcutlistform.py | 4 ++-- openlp/core/utils/actions.py | 17 ++++++++++++++--- openlp/plugins/alerts/alertsplugin.py | 2 +- openlp/plugins/songs/songsplugin.py | 14 ++++++++++---- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index b524c5d06..fd5a53813 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -41,7 +41,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): """ The shortcut list dialog """ -#TODO: do not close on ESC, ability to remove actions (e. g. reindex tool) +#TODO: do not close on ESC #TODO: save/load shortcuts, docs #TODO: Fix Preview/Live controller (have the same shortcut), make sure @@ -94,7 +94,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.treeWidget.clear() for category in actionList.categories: item = QtGui.QTreeWidgetItem([category.name]) - for action, default in category.actions: + for action in category.actions: self.assingedShortcuts.extend(action.shortcuts()) actionText = REMOVE_AMPERSAND.sub('', unicode(action.text())) if len(action.shortcuts()) == 2: diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index cbfa3a488..8887a7e30 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -71,7 +71,7 @@ class CategoryActionList(object): raise StopIteration else: self.index += 1 - return self.actions[self.index - 1][1:] + return self.actions[self.index - 1][1] def next(self): """ @@ -92,9 +92,15 @@ class CategoryActionList(object): self.add(name, weight) def add(self, action, weight=0): - self.actions.append((weight, action, action.shortcuts())) + self.actions.append((weight, action)) self.actions.sort(key=lambda act: act[0]) + def remove(self, remove_action): + for action in self.actions: + if action[1] == remove_action: + self.actions.remove(action) + return + class CategoryList(object): """ @@ -176,7 +182,7 @@ class ActionList(object): def __init__(self): self.categories = CategoryList() - def add_action(self, action, category=u'Default', weight=None): + def add_action(self, action, category, weight=None): if category not in self.categories: self.categories.append(category) if weight is None: @@ -184,4 +190,9 @@ class ActionList(object): else: self.categories[category].actions.add(action, weight) + def remove_action(self, action, category): + if category not in self.categories: + return + self.categories[category].actions.remove(action) + actionList = ActionList() diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index c397fb9c4..a10d56a20 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -86,7 +86,7 @@ class AlertsPlugin(Plugin): self.manager.finalise() Plugin.finalise(self) self.toolsAlertItem.setVisible(False) - #TODO: remove the action from the actionList + actionList.remove_action(self.toolsAlertItem, u'Tools') def toggleAlertsState(self): self.alertsActive = not self.alertsActive diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index e1731bc01..e5ce25906 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -34,6 +34,7 @@ from openlp.core.lib import Plugin, StringContent, build_icon, translate, \ Receiver from openlp.core.lib.db import Manager from openlp.core.lib.ui import UiStrings, base_action, icon_action +from openlp.core.utils.actions import actionList from openlp.plugins.songs.lib import clean_song, SongMediaItem, SongsTab from openlp.plugins.songs.lib.db import init_schema, Song from openlp.plugins.songs.lib.importer import SongFormat @@ -65,6 +66,9 @@ class SongsPlugin(Plugin): log.info(u'Songs Initialising') Plugin.initialise(self) self.toolsReindexItem.setVisible(True) + actionList.add_action(self.SongImportItem, u'Import') + actionList.add_action(self.SongExportItem, u'Export') + actionList.add_action(self.toolsReindexItem, u'Tools') self.mediaItem.displayResultsSong( self.manager.get_all_objects(Song, order_by_ref=Song.search_title)) @@ -78,8 +82,7 @@ class SongsPlugin(Plugin): use it as their parent. """ # Main song import menu item - will eventually be the only one - self.SongImportItem = base_action( - import_menu, u'SongImportItem', u'Import') + self.SongImportItem = base_action(import_menu, u'SongImportItem') self.SongImportItem.setText(translate( 'SongsPlugin', '&Song')) self.SongImportItem.setToolTip(translate('SongsPlugin', @@ -99,7 +102,7 @@ class SongsPlugin(Plugin): use it as their parent. """ # Main song import menu item - will eventually be the only one - self.SongExportItem = base_action(export_menu, u'SongExportItem', u'Export') + self.SongExportItem = base_action(export_menu, u'SongExportItem') self.SongExportItem.setText(translate( 'SongsPlugin', '&Song')) self.SongExportItem.setToolTip(translate('SongsPlugin', @@ -120,7 +123,7 @@ class SongsPlugin(Plugin): """ log.info(u'add tools menu') self.toolsReindexItem = icon_action(tools_menu, u'toolsReindexItem', - u':/plugins/plugin_songs.png', category=u'Tools') + u':/plugins/plugin_songs.png') self.toolsReindexItem.setText( translate('SongsPlugin', '&Re-index Songs')) self.toolsReindexItem.setStatusTip( @@ -257,4 +260,7 @@ class SongsPlugin(Plugin): log.info(u'Songs Finalising') self.manager.finalise() self.toolsReindexItem.setVisible(False) + actionList.remove_action(self.SongImportItem, u'Import') + actionList.remove_action(self.SongExportItem, u'Export') + actionList.remove_action(self.toolsReindexItem, u'Tools') Plugin.finalise(self) From 5ef0cc12c09cf523b8fa3563d41fc9f3261e692a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 30 Mar 2011 18:34:40 +0200 Subject: [PATCH 08/98] started working on saving/loading shortcuts --- openlp/core/ui/mainwindow.py | 3 +- openlp/core/ui/shortcutlistdialog.py | 11 ------ openlp/core/ui/shortcutlistform.py | 55 ++++++++++++++++++++-------- openlp/core/utils/actions.py | 7 ++-- 4 files changed, 45 insertions(+), 31 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index f4cec4ae7..d0f6338e3 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -761,7 +761,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): """ Show the shortcuts dialog """ - self.shortcutForm.exec_() + if self.shortcutForm.exec_(): + self.shortcutForm.saveShortcuts() def onModeDefaultItemClicked(self): """ diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index 4e0d7d144..467fb0534 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -39,15 +39,8 @@ class Ui_ShortcutListDialog(object): self.treeWidget.setObjectName(u'treeWidget') self.treeWidget.setColumnCount(3) self.dialogLayout.addWidget(self.treeWidget) - self.defaultButton = QtGui.QRadioButton(shortcutListDialog) - self.defaultButton.setChecked(True) - self.defaultButton.setObjectName(u'defaultButton') - self.dialogLayout.addWidget(self.defaultButton) self.customLayout = QtGui.QHBoxLayout() self.customLayout.setObjectName(u'customLayout') - self.customButton = QtGui.QRadioButton(shortcutListDialog) - self.customButton.setObjectName(u'customButton') - self.customLayout.addWidget(self.customButton) self.shortcutButton = QtGui.QPushButton(shortcutListDialog) self.shortcutButton.setIcon( build_icon(u':/system/system_configure_shortcuts.png')) @@ -82,9 +75,5 @@ class Ui_ShortcutListDialog(object): translate('OpenLP.ShortcutListDialog', 'Action'), translate('OpenLP.ShortcutListDialog', 'Shortcut'), translate('OpenLP.ShortcutListDialog', 'Alternate')]) - self.defaultButton.setText( - translate('OpenLP.ShortcutListDialog', 'Default: %s')) - self.customButton.setText( - translate('OpenLP.ShortcutListDialog', 'Custom:')) self.shortcutButton.setText( translate('OpenLP.ShortcutListDialog', 'None')) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index fd5a53813..127569f24 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -49,6 +49,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): QtGui.QDialog.__init__(self, parent) self.setupUi(self) self.assingedShortcuts = [] + self.column = -1 self.shortcutButton.setText(u'') QtCore.QObject.connect(self.shortcutButton, QtCore.SIGNAL(u'toggled(bool)'), self.onShortcutButtonClicked) @@ -119,25 +120,40 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): action = item.data(0, QtCore.Qt.UserRole).toPyObject() if action is None: return - # TODO: Sort out which shortcuts should be kept. - action.setShortcuts(QtGui.QKeySequence(self.shortcutButton.text())) - item.setText(1, self.shortcutButton.text()) - self.refreshActionList() + shortcuts = [] + if self.column == 1: + # We are changing the primary shortcut. + shortcuts.append(QtGui.QKeySequence(self.shortcutButton.text())) + if len(action.shortcuts()) == 2: + shortcuts.append(action.shortcuts()[1]) + else: + shortcuts.append(0) + item.setText(1, self.shortcutButton.text()) + elif self.column == 2: + # We are changing the secondary shortcut. + if len(action.shortcuts()) == 1: + shortcuts.append(action.shortcuts()[0]) + else: + shortcuts.append(0) + shortcuts.append(QtGui.QKeySequence(self.shortcutButton.text())) + item.setText(2, self.shortcutButton.text()) + else: + return + action.setShortcuts(shortcuts) def onItemPressed(self, item, column): + self.column = column item = self.treeWidget.currentItem() action = item.data(0, QtCore.Qt.UserRole).toPyObject() - self.shortcutButton.setEnabled(action is not None) - if action is None: - text = u'' - self.shortcutButton.setChecked(False) - else: - if len(action.shortcuts()) == 0: - text = u'' - elif len(action.shortcuts()) == 2 and column == 2: - text = action.shortcuts()[1].toString() - else: - text = action.shortcuts()[0].toString() + self.shortcutButton.setEnabled(True) + text = u'' + if action is None or column not in [1, 2] or \ + len(action.shortcuts()) == 0: + self.shortcutButton.setEnabled(False) + elif column == 1: + text = action.shortcuts()[0].toString() + elif len(action.shortcuts()) == 2: + text = action.shortcuts()[1].toString() self.shortcutButton.setText(text) def saveShortcuts(self): @@ -146,7 +162,12 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): """ settings = QtCore.QSettings() settings.beginGroup(u'shortcuts') - # TODO: Save shortcuts + for category in actionList.categories: + break + for action in category.actions: + if action.defaultShortcuts != action.shortcuts(): + settings.setValue(action.text(), + QtCore.QVariant(action.shortcuts())) settings.endGroup() def loadShortcuts(self): @@ -155,5 +176,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): """ settings = QtCore.QSettings() settings.beginGroup(u'shortcuts') + for shortcut in settings.allKeys(): + pass # TODO: Load shortcuts settings.endGroup() diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 8887a7e30..d94ee1511 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -27,9 +27,9 @@ The :mod:`~openlp.core.utils.actions` module provides action list classes used by the shortcuts system. """ -class Category(object): +class ActionCategory(object): """ - The :class:`~openlp.core.utils.Category` class encapsulates a + The :class:`~openlp.core.utils.ActionCategory` class encapsulates a category for the :class:`~openlp.core.utils.CategoryList` class. """ def __init__(self, name, weight=0): @@ -161,7 +161,7 @@ class CategoryList(object): self.add(name, weight) def add(self, name, weight=0, actions=None): - category = Category(name, weight) + category = ActionCategory(name, weight) if actions: for action in actions: if isinstance(action, tuple): @@ -185,6 +185,7 @@ class ActionList(object): def add_action(self, action, category, weight=None): if category not in self.categories: self.categories.append(category) + action.defaultShortcuts = action.shortcuts() if weight is None: self.categories[category].actions.append(action) else: From 772bd8eb66daa57a0c9b8759cbad4b11e22f9282 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 30 Mar 2011 19:50:36 +0200 Subject: [PATCH 09/98] basic saving and loading of shortcuts --- openlp/core/ui/mainwindow.py | 2 +- openlp/core/ui/shortcutlistform.py | 33 +++++++++--------------------- openlp/core/utils/actions.py | 12 +++++++++++ 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index d0f6338e3..1fdf0fd76 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -762,7 +762,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): Show the shortcuts dialog """ if self.shortcutForm.exec_(): - self.shortcutForm.saveShortcuts() + self.shortcutForm.save() def onModeDefaultItemClicked(self): """ diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 127569f24..15c6c53e5 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -87,10 +87,10 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.shortcutButton.setChecked(False) def exec_(self): - self.refreshActionList() + self.reloadActionList() return QtGui.QDialog.exec_(self) - def refreshActionList(self): + def reloadActionList(self): self.assingedShortcuts = [] self.treeWidget.clear() for category in actionList.categories: @@ -147,36 +147,23 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): action = item.data(0, QtCore.Qt.UserRole).toPyObject() self.shortcutButton.setEnabled(True) text = u'' - if action is None or column not in [1, 2] or \ - len(action.shortcuts()) == 0: + if action is None or column not in [1, 2]: self.shortcutButton.setEnabled(False) - elif column == 1: + elif column == 1 and len(action.shortcuts()) != 0: text = action.shortcuts()[0].toString() - elif len(action.shortcuts()) == 2: + elif len(action.shortcuts()) == 2 and len(action.shortcuts()) != 0: text = action.shortcuts()[1].toString() self.shortcutButton.setText(text) - def saveShortcuts(self): + def save(self): """ - Save the shortcuts. + Save the shortcuts. **Note**, that we do not have to load the shortcuts, + as they are loaded in :class:`~openlp.core.utils.ActionList`. """ settings = QtCore.QSettings() settings.beginGroup(u'shortcuts') for category in actionList.categories: - break for action in category.actions: - if action.defaultShortcuts != action.shortcuts(): - settings.setValue(action.text(), - QtCore.QVariant(action.shortcuts())) - settings.endGroup() - - def loadShortcuts(self): - """ - Load the shortcuts. - """ - settings = QtCore.QSettings() - settings.beginGroup(u'shortcuts') - for shortcut in settings.allKeys(): - pass - # TODO: Load shortcuts + settings.setValue( + action.objectName(), QtCore.QVariant(action.shortcuts())) settings.endGroup() diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index d94ee1511..6b7df6817 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -27,6 +27,10 @@ The :mod:`~openlp.core.utils.actions` module provides action list classes used by the shortcuts system. """ +from PyQt4 import QtCore, QtGui + +from openlp.core.lib import translate + class ActionCategory(object): """ The :class:`~openlp.core.utils.ActionCategory` class encapsulates a @@ -190,6 +194,14 @@ class ActionList(object): self.categories[category].actions.append(action) else: self.categories[category].actions.add(action, weight) + # Load the shortcut from the config. + settings = QtCore.QSettings() + settings.beginGroup(u'shortcuts') + shortcuts = settings.value(action.objectName(), + QtCore.QVariant(action.shortcuts())).toStringList() + action.setShortcuts( + [QtGui.QKeySequence(shortcut) for shortcut in shortcuts]) + settings.endGroup() def remove_action(self, action, category): if category not in self.categories: From bf2a9d338b25dbce73ed0c3f4ec2a2724e9a2b96 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 30 Mar 2011 20:12:05 +0200 Subject: [PATCH 10/98] clean up --- openlp/core/lib/ui.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 86def01d8..44bf7c0dd 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -242,6 +242,11 @@ def create_up_down_push_button_set(parent): def base_action(parent, name, category=None): """ Return the most basic action with the object name set. + + ``category`` + The category the action should be listed in the shortcut dialog. If you + not wish, that this action is added to the shortcut dialog, then do not + state any. """ action = QtGui.QAction(parent) action.setObjectName(name) @@ -262,11 +267,6 @@ def checkable_action(parent, name, checked=None, category=None): def icon_action(parent, name, icon, checked=None, category=None): """ Return a standard action with an icon. - - ``category`` - The category the action should be listed in the shortcut dialog. If you - not wish, that this action is added to the shortcut dialog, then do not - state any. """ if checked is not None: action = checkable_action(parent, name, checked, category) From fb78350a44f421e3bb8b4b3184e51eeb2194fc61 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 30 Mar 2011 20:27:33 +0200 Subject: [PATCH 11/98] added bible actions --- openlp/plugins/bibles/bibleplugin.py | 16 +++++++++------- openlp/plugins/songs/songsplugin.py | 6 ++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 4f677f211..86d751e9a 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -29,6 +29,8 @@ import logging from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, StringContent, build_icon, translate +from openlp.core.lib.ui import base_action +from openlp.core.utils.actions import actionList from openlp.plugins.bibles.lib import BibleManager, BiblesTab, BibleMediaItem log = logging.getLogger(__name__) @@ -50,6 +52,9 @@ class BiblePlugin(Plugin): self.manager = BibleManager(self) Plugin.initialise(self) self.importBibleItem.setVisible(True) + actionList.add_action(self.importBibleItem, u'Import') + # Do not add the action to the list yet. + #actionList.add_action(self.exportBibleItem, u'Export') # Set to invisible until we can export bibles self.exportBibleItem.setVisible(False) @@ -64,21 +69,18 @@ class BiblePlugin(Plugin): self.exportBibleItem.setVisible(False) def addImportMenuItem(self, import_menu): - self.importBibleItem = QtGui.QAction(import_menu) - self.importBibleItem.setObjectName(u'importBibleItem') + self.importBibleItem = base_action(import_menu, u'importBibleItem') + self.importBibleItem.setText(translate('BiblesPlugin', '&Bible')) import_menu.addAction(self.importBibleItem) - self.importBibleItem.setText( - translate('BiblesPlugin', '&Bible')) # signals and slots QtCore.QObject.connect(self.importBibleItem, QtCore.SIGNAL(u'triggered()'), self.onBibleImportClick) self.importBibleItem.setVisible(False) def addExportMenuItem(self, export_menu): - self.exportBibleItem = QtGui.QAction(export_menu) - self.exportBibleItem.setObjectName(u'exportBibleItem') - export_menu.addAction(self.exportBibleItem) + self.exportBibleItem = base_action(export_menu, u'exportBibleItem') self.exportBibleItem.setText(translate('BiblesPlugin', '&Bible')) + export_menu.addAction(self.exportBibleItem) self.exportBibleItem.setVisible(False) def onBibleImportClick(self): diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index e5ce25906..12462428e 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -83,8 +83,7 @@ class SongsPlugin(Plugin): """ # Main song import menu item - will eventually be the only one self.SongImportItem = base_action(import_menu, u'SongImportItem') - self.SongImportItem.setText(translate( - 'SongsPlugin', '&Song')) + self.SongImportItem.setText(translate('SongsPlugin', '&Song')) self.SongImportItem.setToolTip(translate('SongsPlugin', 'Import songs using the import wizard.')) import_menu.addAction(self.SongImportItem) @@ -103,8 +102,7 @@ class SongsPlugin(Plugin): """ # Main song import menu item - will eventually be the only one self.SongExportItem = base_action(export_menu, u'SongExportItem') - self.SongExportItem.setText(translate( - 'SongsPlugin', '&Song')) + self.SongExportItem.setText(translate('SongsPlugin', '&Song')) self.SongExportItem.setToolTip(translate('SongsPlugin', 'Exports songs using the export wizard.')) export_menu.addAction(self.SongExportItem) From bba2f2c604245e959dd27d336950624e095454e9 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 30 Mar 2011 20:52:21 +0200 Subject: [PATCH 12/98] docs --- openlp/core/ui/shortcutlistform.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 15c6c53e5..97d21bebf 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -42,9 +42,9 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): The shortcut list dialog """ #TODO: do not close on ESC -#TODO: save/load shortcuts, docs -#TODO: Fix Preview/Live controller (have the same shortcut), make sure - +#TODO: Fix Preview/Live controller (have the same shortcut) +#TODO: double click event +#TODO: refresh self.assingedShortcuts def __init__(self, parent=None): QtGui.QDialog.__init__(self, parent) self.setupUi(self) @@ -91,6 +91,9 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): return QtGui.QDialog.exec_(self) def reloadActionList(self): + """ + Reload the ``treeWidget`` list to add new and remove old actions. + """ self.assingedShortcuts = [] self.treeWidget.clear() for category in actionList.categories: @@ -114,6 +117,9 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.treeWidget.addTopLevelItem(item) def onShortcutButtonClicked(self, toggled): + """ + Save the new shortcut to the action if the button is unchanged. + """ if toggled: return item = self.treeWidget.currentItem() @@ -142,6 +148,10 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): action.setShortcuts(shortcuts) def onItemPressed(self, item, column): + """ + A item has been pressed. We adjust the button's text to the action's + shortcut which is encapsulate in the item. + """ self.column = column item = self.treeWidget.currentItem() action = item.data(0, QtCore.Qt.UserRole).toPyObject() From 4ca738e9b3bfd8753a8d78a31c86ea5d182ff1d8 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 31 Mar 2011 10:43:19 +0200 Subject: [PATCH 13/98] staticmethod for adding/removing actions --- openlp/core/lib/ui.py | 4 ++-- openlp/core/ui/servicemanager.py | 20 ++++++++++---------- openlp/core/ui/shortcutlistform.py | 6 +++--- openlp/core/ui/slidecontroller.py | 10 +++++----- openlp/core/utils/actions.py | 23 +++++++++++------------ openlp/plugins/alerts/alertsplugin.py | 6 +++--- openlp/plugins/bibles/bibleplugin.py | 6 +++--- openlp/plugins/songs/songsplugin.py | 14 +++++++------- 8 files changed, 44 insertions(+), 45 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 44bf7c0dd..a298d803b 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -31,7 +31,7 @@ import logging from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon, Receiver, translate -from openlp.core.utils.actions import actionList +from openlp.core.utils.actions import ActionList log = logging.getLogger(__name__) @@ -251,7 +251,7 @@ def base_action(parent, name, category=None): action = QtGui.QAction(parent) action.setObjectName(name) if category is not None: - actionList.add_action(action, category) + ActionList.add_action(action, category) return action def checkable_action(parent, name, checked=None, category=None): diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 1d5321e01..4bdd647ed 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -40,7 +40,7 @@ from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm from openlp.core.ui.printserviceform import PrintServiceForm from openlp.core.utils import AppLocation, delete_file, file_is_unicode, \ split_filename -from openlp.core.utils.actions import actionList +from openlp.core.utils.actions import ActionList class ServiceManagerList(QtGui.QTreeWidget): """ @@ -316,15 +316,15 @@ class ServiceManager(QtGui.QWidget): self.configUpdated() def setServiceHotkeys(self): - actionList.add_action(self.serviceManagerList.moveDown, u'Service') - actionList.add_action(self.serviceManagerList.moveUp, u'Service') - actionList.add_action(self.serviceManagerList.moveTop, u'Service') - actionList.add_action(self.serviceManagerList.moveBottom, u'Service') - actionList.add_action(self.serviceManagerList.makeLive, u'Service') - actionList.add_action(self.serviceManagerList.up, u'Service') - actionList.add_action(self.serviceManagerList.down, u'Service') - actionList.add_action(self.serviceManagerList.expand, u'Service') - actionList.add_action(self.serviceManagerList.collapse, u'Service') + ActionList.add_action(self.serviceManagerList.moveDown, u'Service') + ActionList.add_action(self.serviceManagerList.moveUp, u'Service') + ActionList.add_action(self.serviceManagerList.moveTop, u'Service') + ActionList.add_action(self.serviceManagerList.moveBottom, u'Service') + ActionList.add_action(self.serviceManagerList.makeLive, u'Service') + ActionList.add_action(self.serviceManagerList.up, u'Service') + ActionList.add_action(self.serviceManagerList.down, u'Service') + ActionList.add_action(self.serviceManagerList.expand, u'Service') + ActionList.add_action(self.serviceManagerList.collapse, u'Service') def setModified(self, modified=True): diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 97d21bebf..8ba78a876 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -30,7 +30,7 @@ import re from PyQt4 import QtCore, QtGui from openlp.core.utils import translate -from openlp.core.utils.actions import actionList +from openlp.core.utils.actions import ActionList from shortcutlistdialog import Ui_ShortcutListDialog REMOVE_AMPERSAND = re.compile(r'&{1}') @@ -96,7 +96,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): """ self.assingedShortcuts = [] self.treeWidget.clear() - for category in actionList.categories: + for category in ActionList.categories: item = QtGui.QTreeWidgetItem([category.name]) for action in category.actions: self.assingedShortcuts.extend(action.shortcuts()) @@ -172,7 +172,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): """ settings = QtCore.QSettings() settings.beginGroup(u'shortcuts') - for category in actionList.categories: + for category in ActionList.categories: for action in category.actions: settings.setValue( action.objectName(), QtCore.QVariant(action.shortcuts())) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index cafbec198..bc478463c 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -34,7 +34,7 @@ from openlp.core.lib import OpenLPToolbar, Receiver, resize_image, \ ItemCapabilities, translate from openlp.core.lib.ui import icon_action, UiStrings, shortcut_action from openlp.core.ui import HideMode, MainDisplay -from openlp.core.utils.actions import actionList +from openlp.core.utils.actions import ActionList log = logging.getLogger(__name__) @@ -364,18 +364,18 @@ class SlideController(QtGui.QWidget): def setPreviewHotkeys(self, parent=None): self.previousItem.setShortcuts([QtCore.Qt.Key_Up, 0]) - actionList.add_action(self.previousItem, u'Preview Toolbar') + ActionList.add_action(self.previousItem, u'Preview Toolbar') self.nextItem.setShortcuts([QtCore.Qt.Key_Down, 0]) - actionList.add_action(self.nextItem, u'Preview Toolbar') + ActionList.add_action(self.nextItem, u'Preview Toolbar') def setLiveHotkeys(self, parent=None): self.previousItem.setShortcuts([QtCore.Qt.Key_Up, QtCore.Qt.Key_PageUp]) self.previousItem.setShortcutContext( QtCore.Qt.WidgetWithChildrenShortcut) - actionList.add_action(self.previousItem, u'Live Toolbar') + ActionList.add_action(self.previousItem, u'Live Toolbar') self.nextItem.setShortcuts([QtCore.Qt.Key_Down, QtCore.Qt.Key_PageDown]) self.nextItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) - actionList.add_action(self.nextItem, u'Live Toolbar') + ActionList.add_action(self.nextItem, u'Live Toolbar') self.previousService = shortcut_action(parent, translate('OpenLP.SlideController', 'Previous Service'), [QtCore.Qt.Key_Left, 0], self.servicePrevious, u'Live Toolbar') diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 6b7df6817..e71aee672 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -183,17 +183,17 @@ class ActionList(object): has a weight by which it is sorted when iterating through the list of actions or categories. """ - def __init__(self): - self.categories = CategoryList() + categories = CategoryList() - def add_action(self, action, category, weight=None): - if category not in self.categories: - self.categories.append(category) + @staticmethod + def add_action(action, category, weight=None): + if category not in ActionList.categories: + ActionList.categories.append(category) action.defaultShortcuts = action.shortcuts() if weight is None: - self.categories[category].actions.append(action) + ActionList.categories[category].actions.append(action) else: - self.categories[category].actions.add(action, weight) + ActionList.categories[category].actions.add(action, weight) # Load the shortcut from the config. settings = QtCore.QSettings() settings.beginGroup(u'shortcuts') @@ -203,9 +203,8 @@ class ActionList(object): [QtGui.QKeySequence(shortcut) for shortcut in shortcuts]) settings.endGroup() - def remove_action(self, action, category): - if category not in self.categories: + @staticmethod + def remove_action(action, category): + if category not in ActionList.categories: return - self.categories[category].actions.remove(action) - -actionList = ActionList() + ActionList.categories[category].actions.remove(action) diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index a10d56a20..45e1969f4 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -31,7 +31,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, StringContent, build_icon, translate from openlp.core.lib.db import Manager from openlp.core.lib.ui import icon_action -from openlp.core.utils.actions import actionList +from openlp.core.utils.actions import ActionList from openlp.plugins.alerts.lib import AlertsManager, AlertsTab from openlp.plugins.alerts.lib.db import init_schema from openlp.plugins.alerts.forms import AlertForm @@ -75,7 +75,7 @@ class AlertsPlugin(Plugin): log.info(u'Alerts Initialising') Plugin.initialise(self) self.toolsAlertItem.setVisible(True) - actionList.add_action(self.toolsAlertItem, u'Tools') + ActionList.add_action(self.toolsAlertItem, u'Tools') self.liveController.alertTab = self.settings_tab def finalise(self): @@ -86,7 +86,7 @@ class AlertsPlugin(Plugin): self.manager.finalise() Plugin.finalise(self) self.toolsAlertItem.setVisible(False) - actionList.remove_action(self.toolsAlertItem, u'Tools') + ActionList.remove_action(self.toolsAlertItem, u'Tools') def toggleAlertsState(self): self.alertsActive = not self.alertsActive diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 86d751e9a..1396d4550 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -30,7 +30,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, StringContent, build_icon, translate from openlp.core.lib.ui import base_action -from openlp.core.utils.actions import actionList +from openlp.core.utils.actions import ActionList from openlp.plugins.bibles.lib import BibleManager, BiblesTab, BibleMediaItem log = logging.getLogger(__name__) @@ -52,9 +52,9 @@ class BiblePlugin(Plugin): self.manager = BibleManager(self) Plugin.initialise(self) self.importBibleItem.setVisible(True) - actionList.add_action(self.importBibleItem, u'Import') + ActionList.add_action(self.importBibleItem, u'Import') # Do not add the action to the list yet. - #actionList.add_action(self.exportBibleItem, u'Export') + #ActionList.add_action(self.exportBibleItem, u'Export') # Set to invisible until we can export bibles self.exportBibleItem.setVisible(False) diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 12462428e..de0714d5e 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -34,7 +34,7 @@ from openlp.core.lib import Plugin, StringContent, build_icon, translate, \ Receiver from openlp.core.lib.db import Manager from openlp.core.lib.ui import UiStrings, base_action, icon_action -from openlp.core.utils.actions import actionList +from openlp.core.utils.actions import ActionList from openlp.plugins.songs.lib import clean_song, SongMediaItem, SongsTab from openlp.plugins.songs.lib.db import init_schema, Song from openlp.plugins.songs.lib.importer import SongFormat @@ -66,9 +66,9 @@ class SongsPlugin(Plugin): log.info(u'Songs Initialising') Plugin.initialise(self) self.toolsReindexItem.setVisible(True) - actionList.add_action(self.SongImportItem, u'Import') - actionList.add_action(self.SongExportItem, u'Export') - actionList.add_action(self.toolsReindexItem, u'Tools') + ActionList.add_action(self.SongImportItem, u'Import') + ActionList.add_action(self.SongExportItem, u'Export') + ActionList.add_action(self.toolsReindexItem, u'Tools') self.mediaItem.displayResultsSong( self.manager.get_all_objects(Song, order_by_ref=Song.search_title)) @@ -258,7 +258,7 @@ class SongsPlugin(Plugin): log.info(u'Songs Finalising') self.manager.finalise() self.toolsReindexItem.setVisible(False) - actionList.remove_action(self.SongImportItem, u'Import') - actionList.remove_action(self.SongExportItem, u'Export') - actionList.remove_action(self.toolsReindexItem, u'Tools') + ActionList.remove_action(self.SongImportItem, u'Import') + ActionList.remove_action(self.SongExportItem, u'Export') + ActionList.remove_action(self.toolsReindexItem, u'Tools') Plugin.finalise(self) From 0c9b839e916e1b310be7e78e150be48f16bfdd52 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 31 Mar 2011 14:50:38 +0200 Subject: [PATCH 14/98] check if shortcut is already assigned, refreshing list, double click event, started resetting the selected action --- openlp/core/ui/shortcutlistform.py | 86 ++++++++++++++++++++++-------- 1 file changed, 63 insertions(+), 23 deletions(-) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 8ba78a876..4746ae3f7 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -42,9 +42,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): The shortcut list dialog """ #TODO: do not close on ESC -#TODO: Fix Preview/Live controller (have the same shortcut) -#TODO: double click event -#TODO: refresh self.assingedShortcuts +#TODO: Fix Preview/Live controller (have the same shortcut def __init__(self, parent=None): QtGui.QDialog.__init__(self, parent) self.setupUi(self) @@ -56,6 +54,11 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): QtCore.QObject.connect(self.treeWidget, QtCore.SIGNAL(u'itemPressed(QTreeWidgetItem*, int)'), self.onItemPressed) + QtCore.QObject.connect(self.treeWidget, + QtCore.SIGNAL(u'itemDoubleClicked(QTreeWidgetItem*, int)'), + self.onItemDoubleClicked) + QtCore.QObject.connect(self.clearShortcutButton, + QtCore.SIGNAL(u'clicked(bool)'), self.onClearShortcutButtonClicked) def keyReleaseEvent(self, event): Qt = QtCore.Qt @@ -87,34 +90,50 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.shortcutButton.setChecked(False) def exec_(self): - self.reloadActionList() + self.reloadShortcutList() return QtGui.QDialog.exec_(self) - def reloadActionList(self): + def reloadShortcutList(self): """ Reload the ``treeWidget`` list to add new and remove old actions. """ - self.assingedShortcuts = [] self.treeWidget.clear() for category in ActionList.categories: item = QtGui.QTreeWidgetItem([category.name]) for action in category.actions: - self.assingedShortcuts.extend(action.shortcuts()) actionText = REMOVE_AMPERSAND.sub('', unicode(action.text())) - if len(action.shortcuts()) == 2: - shortcutText = action.shortcuts()[0].toString() - alternateText = action.shortcuts()[1].toString() - else: - shortcutText = action.shortcut().toString() - alternateText = u'' - actionItem = QtGui.QTreeWidgetItem( - [actionText, shortcutText, alternateText]) + actionItem = QtGui.QTreeWidgetItem([actionText]) actionItem.setIcon(0, action.icon()) actionItem.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(action)) item.addChild(actionItem) item.setExpanded(True) self.treeWidget.addTopLevelItem(item) + self.refreshShortcutList() + + def refreshShortcutList(self): + """ + This refreshes the item's shortcuts shown in the list. Note, this + neither adds new actions nor removes old actions. + """ + self.assingedShortcuts = [] + iterator = QtGui.QTreeWidgetItemIterator(self.treeWidget) + while iterator.value(): + item = iterator.value() + iterator += 1 + action = item.data(0, QtCore.Qt.UserRole).toPyObject() + if action is None: + continue + self.assingedShortcuts.extend(action.shortcuts()) + if len(action.shortcuts()) == 0: + item.setText(1, u'') + item.setText(2, u'') + elif len(action.shortcuts()) == 1: + item.setText(1, action.shortcuts()[0].toString()) + item.setText(2, u'') + else: + item.setText(1, action.shortcuts()[0].toString()) + item.setText(2, action.shortcuts()[1].toString()) def onShortcutButtonClicked(self, toggled): """ @@ -127,25 +146,30 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): if action is None: return shortcuts = [] + # We are changing the primary shortcut. if self.column == 1: - # We are changing the primary shortcut. shortcuts.append(QtGui.QKeySequence(self.shortcutButton.text())) if len(action.shortcuts()) == 2: shortcuts.append(action.shortcuts()[1]) - else: - shortcuts.append(0) - item.setText(1, self.shortcutButton.text()) + # We are changing the secondary shortcut. elif self.column == 2: - # We are changing the secondary shortcut. if len(action.shortcuts()) == 1: shortcuts.append(action.shortcuts()[0]) - else: - shortcuts.append(0) shortcuts.append(QtGui.QKeySequence(self.shortcutButton.text())) - item.setText(2, self.shortcutButton.text()) else: return action.setShortcuts(shortcuts) + self.refreshShortcutList() + + def onItemDoubleClicked(self, item, column): + """ + """ + item = self.treeWidget.currentItem() + action = item.data(0, QtCore.Qt.UserRole).toPyObject() + self.shortcutButton.setChecked(True) + if action is None or column not in [1, 2]: + self.shortcutButton.setChecked(False) + self.onItemPressed(item, column) def onItemPressed(self, item, column): """ @@ -165,6 +189,22 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): text = action.shortcuts()[1].toString() self.shortcutButton.setText(text) + def onClearShortcutButtonClicked(self, toggled): + """ + Restore the defaults of this + """ + item = self.treeWidget.currentItem() + if item is None: + return + action = item.data(0, QtCore.Qt.UserRole).toPyObject() + if action is None: + return + #FIXME: defaultShortcuts + action.setShortcuts(action.defaultShortcuts) + self.shortcutButton.setChecked(False) + self.shortcutButton.setText(u'') + self.refreshShortcutList() + def save(self): """ Save the shortcuts. **Note**, that we do not have to load the shortcuts, From b17ad266728d9032af9ae42529d6007b86a6f063 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 31 Mar 2011 20:53:50 +0200 Subject: [PATCH 15/98] make sure we save the default shortcut --- openlp/core/lib/toolbar.py | 15 ++--- openlp/core/lib/ui.py | 17 ++++- openlp/core/ui/mainwindow.py | 103 ++++++++++++------------------ openlp/core/ui/servicemanager.py | 53 +++++++-------- openlp/core/ui/slidecontroller.py | 27 ++++---- openlp/core/utils/actions.py | 2 - 6 files changed, 107 insertions(+), 110 deletions(-) diff --git a/openlp/core/lib/toolbar.py b/openlp/core/lib/toolbar.py index d5d2fa4f8..44149cd85 100644 --- a/openlp/core/lib/toolbar.py +++ b/openlp/core/lib/toolbar.py @@ -51,8 +51,7 @@ class OpenLPToolbar(QtGui.QToolBar): log.debug(u'Init done for %s' % parent.__class__.__name__) def addToolbarButton(self, title, icon, tooltip=None, slot=None, - checkable=False, shortcut=0, alternate=0, - context=QtCore.Qt.WidgetShortcut): + checkable=False, shortcuts=None, context=QtCore.Qt.WidgetShortcut): """ A method to help developers easily add a button to the toolbar. @@ -74,11 +73,8 @@ class OpenLPToolbar(QtGui.QToolBar): If *True* the button has two, *off* and *on*, states. Default is *False*, which means the buttons has only one state. - ``shortcut`` - The primary shortcut for this action - - ``alternate`` - The alternate shortcut for this action + ``shortcuts`` + The list of shortcuts for this action ``context`` Specify the context in which this shortcut is valid @@ -103,8 +99,9 @@ class OpenLPToolbar(QtGui.QToolBar): QtCore.QObject.connect(newAction, QtCore.SIGNAL(u'toggled(bool)'), slot) self.actions[title] = newAction - newAction.setShortcuts([shortcut, alternate]) - newAction.setShortcutContext(context) + if shortcuts is not None: + newAction.setShortcuts(shortcuts) + newAction.setShortcutContext(context) return newAction def addToolbarSeparator(self, handle): diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index a298d803b..c6f7dd070 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -275,13 +275,26 @@ def icon_action(parent, name, icon, checked=None, category=None): action.setIcon(build_icon(icon)) return action -def shortcut_action(parent, text, shortcuts, function, category=None): +def shortcut_action(parent, name, shortcuts, function, icon=None, checked=None, + category=None): """ Return a shortcut enabled action. """ - action = base_action(parent, text, category) + # We cannot use the base_action, icon_action and the like, as we must not + # add the action to the actionList before the shortcut has been set + # (when we add the action to the list, the default shortcut is saved, to be + # able restore the shortcut). + action = QtGui.QAction(parent) + action.setObjectName(name) + if icon is not None: + action.setIcon(build_icon(icon)) + if checked is not None: + action.setCheckable(True) + action.setChecked(checked) action.setShortcuts(shortcuts) action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) + if category is not None: + ActionList.add_action(action, category) QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), function) return action diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 1fdf0fd76..14bed4712 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -33,7 +33,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import RenderManager, build_icon, OpenLPDockWidget, \ SettingsManager, PluginManager, Receiver, translate from openlp.core.lib.ui import UiStrings, base_action, checkable_action, \ - icon_action + icon_action, shortcut_action from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \ ThemeManager, SlideController, PluginForm, MediaDockManager, \ ShortcutListForm, DisplayTagForm @@ -161,18 +161,32 @@ class Ui_MainWindow(object): mainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.themeManagerDock) # Create the menu items - self.FileNewItem = icon_action(mainWindow, u'FileNewItem', + self.FileNewItem = shortcut_action(mainWindow, u'FileNewItem', + [QtGui.QKeySequence(u'Ctrl+N')], + self.ServiceManagerContents.onNewServiceClicked, u':/general/general_new.png', category=u'File') - self.FileOpenItem = icon_action(mainWindow, u'FileOpenItem', + self.FileNewItem.setShortcutContext(QtCore.Qt.WindowShortcut) + self.FileOpenItem = shortcut_action(mainWindow, u'FileOpenItem', + [QtGui.QKeySequence(u'Ctrl+O')], + self.ServiceManagerContents.onLoadServiceClicked, u':/general/general_open.png', category=u'File') - self.FileSaveItem = icon_action(mainWindow, u'FileSaveItem', + self.FileOpenItem.setShortcutContext(QtCore.Qt.WindowShortcut) + self.FileSaveItem = shortcut_action(mainWindow, u'FileSaveItem', + [QtGui.QKeySequence(u'Ctrl+S')], + self.ServiceManagerContents.saveFile, u':/general/general_save.png', category=u'File') - self.FileSaveAsItem = base_action( - mainWindow, u'FileSaveAsItem', u'File') - self.printServiceOrderItem = base_action( - mainWindow, u'printServiceItem', u'File') - self.FileExitItem = icon_action(mainWindow, u'FileExitItem', + self.FileSaveItem.setShortcutContext(QtCore.Qt.WindowShortcut) + self.FileSaveAsItem = shortcut_action(mainWindow, u'FileSaveAsItem', + [QtGui.QKeySequence(u'Ctrl+Shift+S')], + self.ServiceManagerContents.saveFileAs, category=u'File') + self.FileSaveAsItem.setShortcutContext(QtCore.Qt.WindowShortcut) + self.printServiceOrderItem = shortcut_action(mainWindow, + u'printServiceItem', [QtGui.QKeySequence(u'Ctrl+P')], + self.ServiceManagerContents.printServiceOrder, category=u'File') + self.FileExitItem = shortcut_action(mainWindow, u'FileExitItem', + [QtGui.QKeySequence(u'Alt+F4')], mainWindow.close, u':/system/system_exit.png', category=u'File') + self.FileSaveAsItem.setShortcutContext(QtCore.Qt.WindowShortcut) self.ImportThemeItem = base_action( mainWindow, u'ImportThemeItem', u'Import') self.ImportLanguageItem = base_action( @@ -181,15 +195,21 @@ class Ui_MainWindow(object): mainWindow, u'ExportThemeItem', u'Export') self.ExportLanguageItem = base_action( mainWindow, u'ExportLanguageItem')#, u'Export') - self.ViewMediaManagerItem = icon_action(mainWindow, - u'ViewMediaManagerItem', u':/system/system_mediamanager.png', + self.ViewMediaManagerItem = shortcut_action(mainWindow, + u'ViewMediaManagerItem', [QtGui.QKeySequence(u'F8')], + self.toggleMediaManager, u':/system/system_mediamanager.png', self.mediaManagerDock.isVisible(), u'View') - self.ViewThemeManagerItem = icon_action(mainWindow, - u'ViewThemeManagerItem', u':/system/system_thememanager.png', + self.FileSaveAsItem.setShortcutContext(QtCore.Qt.WindowShortcut) + self.ViewThemeManagerItem = shortcut_action(mainWindow, + u'ViewThemeManagerItem', [QtGui.QKeySequence(u'F9')], + self.toggleThemeManager, u':/system/system_thememanager.png', self.themeManagerDock.isVisible(), u'View') - self.ViewServiceManagerItem = icon_action(mainWindow, - u'ViewServiceManagerItem', u':/system/system_servicemanager.png', + self.FileSaveAsItem.setShortcutContext(QtCore.Qt.WindowShortcut) + self.ViewServiceManagerItem = shortcut_action(mainWindow, + u'ViewServiceManagerItem', [QtGui.QKeySequence(u'F10')], + self.toggleServiceManager, u':/system/system_servicemanager.png', self.serviceManagerDock.isVisible(), u'View') + self.FileSaveAsItem.setShortcutContext(QtCore.Qt.WindowShortcut) self.ViewPreviewPanel = checkable_action(mainWindow, u'ViewPreviewPanel', previewVisible, u'View') self.ViewLivePanel = checkable_action( @@ -278,8 +298,6 @@ class Ui_MainWindow(object): # Connect up some signals and slots QtCore.QObject.connect(self.FileMenu, QtCore.SIGNAL(u'aboutToShow()'), self.updateFileMenu) - QtCore.QObject.connect(self.FileExitItem, - QtCore.SIGNAL(u'triggered()'), mainWindow.close) QtCore.QMetaObject.connectSlotsByName(mainWindow) # Hide the entry, as it does not have any functionality yet. self.ToolsAddToolItem.setVisible(False) @@ -312,36 +330,27 @@ class Ui_MainWindow(object): self.FileNewItem.setText(translate('OpenLP.MainWindow', '&New')) self.FileNewItem.setToolTip(UiStrings.NewService) self.FileNewItem.setStatusTip(UiStrings.CreateService) - self.FileNewItem.setShortcut(translate('OpenLP.MainWindow', 'Ctrl+N')) self.FileOpenItem.setText(translate('OpenLP.MainWindow', '&Open')) self.FileOpenItem.setToolTip(UiStrings.OpenService) self.FileOpenItem.setStatusTip( translate('OpenLP.MainWindow', 'Open an existing service.')) - self.FileOpenItem.setShortcut(translate('OpenLP.MainWindow', 'Ctrl+O')) self.FileSaveItem.setText(translate('OpenLP.MainWindow', '&Save')) self.FileSaveItem.setToolTip(UiStrings.SaveService) self.FileSaveItem.setStatusTip( translate('OpenLP.MainWindow', 'Save the current service to disk.')) - self.FileSaveItem.setShortcut(translate('OpenLP.MainWindow', 'Ctrl+S')) self.FileSaveAsItem.setText( translate('OpenLP.MainWindow', 'Save &As...')) self.FileSaveAsItem.setToolTip( translate('OpenLP.MainWindow', 'Save Service As')) self.FileSaveAsItem.setStatusTip(translate('OpenLP.MainWindow', 'Save the current service under a new name.')) - self.FileSaveAsItem.setShortcut( - translate('OpenLP.MainWindow', 'Ctrl+Shift+S')) self.printServiceOrderItem.setText(UiStrings.PrintServiceOrder) self.printServiceOrderItem.setStatusTip(translate('OpenLP.MainWindow', 'Print the current Service Order.')) - self.printServiceOrderItem.setShortcut( - translate('OpenLP.MainWindow', 'Ctrl+P')) self.FileExitItem.setText( translate('OpenLP.MainWindow', 'E&xit')) self.FileExitItem.setStatusTip( translate('OpenLP.MainWindow', 'Quit OpenLP')) - self.FileExitItem.setShortcut( - translate('OpenLP.MainWindow', 'Alt+F4')) self.ImportThemeItem.setText( translate('OpenLP.MainWindow', '&Theme')) self.ImportLanguageItem.setText( @@ -362,24 +371,18 @@ class Ui_MainWindow(object): translate('OpenLP.MainWindow', 'Toggle Media Manager')) self.ViewMediaManagerItem.setStatusTip(translate('OpenLP.MainWindow', 'Toggle the visibility of the media manager.')) - self.ViewMediaManagerItem.setShortcut( - translate('OpenLP.MainWindow', 'F8')) self.ViewThemeManagerItem.setText( translate('OpenLP.MainWindow', '&Theme Manager')) self.ViewThemeManagerItem.setToolTip( translate('OpenLP.MainWindow', 'Toggle Theme Manager')) self.ViewThemeManagerItem.setStatusTip(translate('OpenLP.MainWindow', 'Toggle the visibility of the theme manager.')) - self.ViewThemeManagerItem.setShortcut( - translate('OpenLP.MainWindow', 'F10')) self.ViewServiceManagerItem.setText( translate('OpenLP.MainWindow', '&Service Manager')) self.ViewServiceManagerItem.setToolTip( translate('OpenLP.MainWindow', 'Toggle Service Manager')) self.ViewServiceManagerItem.setStatusTip(translate('OpenLP.MainWindow', 'Toggle the visibility of the service manager.')) - self.ViewServiceManagerItem.setShortcut( - translate('OpenLP.MainWindow', 'F9')) self.ViewPreviewPanel.setText( translate('OpenLP.MainWindow', '&Preview Panel')) self.ViewPreviewPanel.setToolTip( @@ -409,6 +412,7 @@ class Ui_MainWindow(object): translate('OpenLP.MainWindow', 'More information about OpenLP')) self.HelpAboutItem.setShortcut( translate('OpenLP.MainWindow', 'Ctrl+F1')) + print self.HelpAboutItem.shortcuts() self.HelpOnlineHelpItem.setText( translate('OpenLP.MainWindow', '&Online Help')) # Uncomment after 1.9.5 beta string freeze @@ -490,12 +494,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): QtCore.QObject.connect(self.ExportThemeItem, QtCore.SIGNAL(u'triggered()'), self.themeManagerContents.onExportTheme) - QtCore.QObject.connect(self.ViewMediaManagerItem, - QtCore.SIGNAL(u'triggered(bool)'), self.toggleMediaManager) - QtCore.QObject.connect(self.ViewServiceManagerItem, - QtCore.SIGNAL(u'triggered(bool)'), self.toggleServiceManager) - QtCore.QObject.connect(self.ViewThemeManagerItem, - QtCore.SIGNAL(u'triggered(bool)'), self.toggleThemeManager) QtCore.QObject.connect(self.ViewPreviewPanel, QtCore.SIGNAL(u'toggled(bool)'), self.setPreviewPanelVisibility) QtCore.QObject.connect(self.ViewLivePanel, @@ -525,20 +523,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): QtCore.SIGNAL(u'triggered()'), self.onSettingsConfigureItemClicked) QtCore.QObject.connect(self.SettingsShortcutsItem, QtCore.SIGNAL(u'triggered()'), self.onSettingsShortcutsItemClicked) - QtCore.QObject.connect(self.FileNewItem, QtCore.SIGNAL(u'triggered()'), - self.ServiceManagerContents.onNewServiceClicked) - QtCore.QObject.connect(self.FileOpenItem, - QtCore.SIGNAL(u'triggered()'), - self.ServiceManagerContents.onLoadServiceClicked) - QtCore.QObject.connect(self.FileSaveItem, - QtCore.SIGNAL(u'triggered()'), - self.ServiceManagerContents.saveFile) - QtCore.QObject.connect(self.FileSaveAsItem, - QtCore.SIGNAL(u'triggered()'), - self.ServiceManagerContents.saveFileAs) - QtCore.QObject.connect(self.printServiceOrderItem, - QtCore.SIGNAL(u'triggered()'), - self.ServiceManagerContents.printServiceOrder) # i18n set signals for languages self.LanguageGroup.triggered.connect(LanguageManager.set_language) QtCore.QObject.connect(self.ModeDefaultItem, @@ -909,17 +893,14 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): unicode(translate('OpenLP.MainWindow', 'Default Theme: %s')) % theme) - def toggleMediaManager(self, visible): - if self.mediaManagerDock.isVisible() != visible: - self.mediaManagerDock.setVisible(visible) + def toggleMediaManager(self): + self.mediaManagerDock.setVisible(not self.mediaManagerDock.isVisible()) - def toggleServiceManager(self, visible): - if self.serviceManagerDock.isVisible() != visible: - self.serviceManagerDock.setVisible(visible) + def toggleServiceManager(self): + self.serviceManagerDock.setVisible(not self.serviceManagerDock.isVisible()) - def toggleThemeManager(self, visible): - if self.themeManagerDock.isVisible() != visible: - self.themeManagerDock.setVisible(visible) + def toggleThemeManager(self): + self.themeManagerDock.setVisible(not self.themeManagerDock.isVisible()) def setPreviewPanelVisibility(self, visible): """ diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 4bdd647ed..3ed477485 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -165,38 +165,50 @@ class ServiceManager(QtGui.QWidget): u':/services/service_top.png', translate('OpenLP.ServiceManager', 'Move item to the top of the service.'), - self.onServiceTop, shortcut=QtCore.Qt.Key_Home) + self.onServiceTop, shortcuts=[QtCore.Qt.Key_Home]) + self.serviceManagerList.moveTop.setObjectName(u'moveTop') + ActionList.add_action(self.serviceManagerList.moveTop, u'Service') self.serviceManagerList.moveUp = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move &up'), u':/services/service_up.png', translate('OpenLP.ServiceManager', 'Move item up one position in the service.'), - self.onServiceUp, shortcut=QtCore.Qt.Key_PageUp) + self.onServiceUp, shortcuts=[QtCore.Qt.Key_PageUp]) + self.serviceManagerList.moveUp.setObjectName(u'moveUp') + ActionList.add_action(self.serviceManagerList.moveUp, u'Service') self.serviceManagerList.moveDown = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move &down'), u':/services/service_down.png', translate('OpenLP.ServiceManager', 'Move item down one position in the service.'), - self.onServiceDown, shortcut=QtCore.Qt.Key_PageDown) + self.onServiceDown, shortcuts=[QtCore.Qt.Key_PageDown]) + self.serviceManagerList.moveDown.setObjectName(u'moveDown') + ActionList.add_action(self.serviceManagerList.moveDown, u'Service') self.serviceManagerList.moveBottom = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move to &bottom'), u':/services/service_bottom.png', translate('OpenLP.ServiceManager', 'Move item to the end of the service.'), - self.onServiceEnd, shortcut=QtCore.Qt.Key_End) + self.onServiceEnd, shortcuts=[QtCore.Qt.Key_End]) + self.serviceManagerList.moveBottom.setObjectName(u'moveBottom') + ActionList.add_action(self.serviceManagerList.moveBottom, u'Service') self.serviceManagerList.down = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move &down'), None, translate('OpenLP.ServiceManager', 'Moves the selection down the window.'), - self.onMoveSelectionDown, shortcut=QtCore.Qt.Key_Down) + self.onMoveSelectionDown, shortcuts=[QtCore.Qt.Key_Down]) + self.serviceManagerList.down.setObjectName(u'down') + ActionList.add_action(self.serviceManagerList.down, u'Service') self.serviceManagerList.down.setVisible(False) self.serviceManagerList.up = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move up'), None, translate('OpenLP.ServiceManager', 'Moves the selection up the window.'), - self.onMoveSelectionUp, shortcut=QtCore.Qt.Key_Up) + self.onMoveSelectionUp, shortcuts=[QtCore.Qt.Key_Up]) + self.serviceManagerList.up.setObjectName(u'up') + ActionList.add_action(self.serviceManagerList.up, u'Service') self.serviceManagerList.up.setVisible(False) self.orderToolbar.addSeparator() self.serviceManagerList.delete = self.orderToolbar.addToolbarButton( @@ -211,22 +223,26 @@ class ServiceManager(QtGui.QWidget): u':/services/service_expand_all.png', translate('OpenLP.ServiceManager', 'Expand all the service items.'), - self.onExpandAll, shortcut=QtCore.Qt.Key_Plus) + self.onExpandAll, shortcuts=[QtCore.Qt.Key_Plus]) + self.serviceManagerList.expand.setObjectName(u'expand') + ActionList.add_action(self.serviceManagerList.expand, u'Service') self.serviceManagerList.collapse = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', '&Collapse all'), u':/services/service_collapse_all.png', translate('OpenLP.ServiceManager', 'Collapse all the service items.'), - self.onCollapseAll, shortcut=QtCore.Qt.Key_Minus) + self.onCollapseAll, shortcuts=[QtCore.Qt.Key_Minus]) + self.serviceManagerList.collapse.setObjectName(u'collapse') + ActionList.add_action(self.serviceManagerList.collapse, u'Service') self.orderToolbar.addSeparator() self.serviceManagerList.makeLive = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Go Live'), u':/general/general_live.png', translate('OpenLP.ServiceManager', - 'Send the selected item to Live.'), - self.makeLive, shortcut=QtCore.Qt.Key_Enter, - alternate=QtCore.Qt.Key_Return) - self.orderToolbar.setObjectName(u'orderToolbar') + 'Send the selected item to Live.'), self.makeLive, + shortcuts=[QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return]) + self.serviceManagerList.makeLive.setObjectName(u'orderToolbar') + ActionList.add_action(self.serviceManagerList.makeLive, u'Service') self.layout.addWidget(self.orderToolbar) # Connect up our signals and slots QtCore.QObject.connect(self.themeComboBox, @@ -301,7 +317,6 @@ class ServiceManager(QtGui.QWidget): self.themeMenu = QtGui.QMenu( translate('OpenLP.ServiceManager', '&Change Item Theme')) self.menu.addMenu(self.themeMenu) - self.setServiceHotkeys() self.serviceManagerList.addActions( [self.serviceManagerList.moveDown, self.serviceManagerList.moveUp, @@ -315,18 +330,6 @@ class ServiceManager(QtGui.QWidget): ]) self.configUpdated() - def setServiceHotkeys(self): - ActionList.add_action(self.serviceManagerList.moveDown, u'Service') - ActionList.add_action(self.serviceManagerList.moveUp, u'Service') - ActionList.add_action(self.serviceManagerList.moveTop, u'Service') - ActionList.add_action(self.serviceManagerList.moveBottom, u'Service') - ActionList.add_action(self.serviceManagerList.makeLive, u'Service') - ActionList.add_action(self.serviceManagerList.up, u'Service') - ActionList.add_action(self.serviceManagerList.down, u'Service') - ActionList.add_action(self.serviceManagerList.expand, u'Service') - ActionList.add_action(self.serviceManagerList.collapse, u'Service') - - def setModified(self, modified=True): """ Setter for property "modified". Sets whether or not the current service diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index bc478463c..e9d748ad5 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -142,11 +142,13 @@ class SlideController(QtGui.QWidget): u':/slides/slide_previous.png', translate('OpenLP.SlideController', 'Move to previous'), self.onSlideSelectedPrevious) + self.previousItem.setObjectName(u'previousItem') self.nextItem = self.toolbar.addToolbarButton( translate('OpenLP.SlideController', 'Next Slide'), u':/slides/slide_next.png', translate('OpenLP.SlideController', 'Move to next'), self.onSlideSelectedNext) + self.nextItem.setObjectName(u'nextItem') self.toolbar.addToolbarSeparator(u'Close Separator') if self.isLive: self.hideMenu = QtGui.QToolButton(self.toolbar) @@ -363,9 +365,9 @@ class SlideController(QtGui.QWidget): QtCore.SIGNAL(u'config_screen_changed'), self.screenSizeChanged) def setPreviewHotkeys(self, parent=None): - self.previousItem.setShortcuts([QtCore.Qt.Key_Up, 0]) + self.previousItem.setShortcuts([QtCore.Qt.Key_Up]) ActionList.add_action(self.previousItem, u'Preview Toolbar') - self.nextItem.setShortcuts([QtCore.Qt.Key_Down, 0]) + self.nextItem.setShortcuts([QtCore.Qt.Key_Down]) ActionList.add_action(self.nextItem, u'Preview Toolbar') def setLiveHotkeys(self, parent=None): @@ -376,15 +378,18 @@ class SlideController(QtGui.QWidget): self.nextItem.setShortcuts([QtCore.Qt.Key_Down, QtCore.Qt.Key_PageDown]) self.nextItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) ActionList.add_action(self.nextItem, u'Live Toolbar') - self.previousService = shortcut_action(parent, - translate('OpenLP.SlideController', 'Previous Service'), - [QtCore.Qt.Key_Left, 0], self.servicePrevious, u'Live Toolbar') - self.nextService = shortcut_action(parent, - translate('OpenLP.SlideController', 'Next Service'), - [QtCore.Qt.Key_Right, 0], self.serviceNext, u'Live Toolbar') - self.escapeItem = shortcut_action(parent, - translate('OpenLP.SlideController', 'Escape Item'), - [QtCore.Qt.Key_Escape, 0], self.liveEscape, u'Live Toolbar') + self.previousService = shortcut_action(parent, u'previousService', + [QtCore.Qt.Key_Left], self.servicePrevious, u'Live Toolbar') + self.previousService.setText( + translate('OpenLP.SlideController', 'Previous Service')) + self.nextService = shortcut_action(parent, 'nextService', + [QtCore.Qt.Key_Right], self.serviceNext, u'Live Toolbar') + self.nextService.setText( + translate('OpenLP.SlideController', 'Next Service')) + self.escapeItem = shortcut_action(parent, 'escapeItem', + [QtCore.Qt.Key_Escape], self.liveEscape, u'Live Toolbar') + self.escapeItem.setText( + translate('OpenLP.SlideController', 'Escape Item')) def liveEscape(self): self.display.setVisible(False) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index e71aee672..34bc72173 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -29,8 +29,6 @@ by the shortcuts system. """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import translate - class ActionCategory(object): """ The :class:`~openlp.core.utils.ActionCategory` class encapsulates a From df4de23df09486908a5daf69b454f48be0fe3395 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 1 Apr 2011 16:28:25 +0200 Subject: [PATCH 16/98] Added Theme, Blank, Desktop shortcuts, further clean ups Fixes: https://launchpad.net/bugs/739779 --- openlp/core/lib/searchedit.py | 3 +- openlp/core/lib/ui.py | 22 ++++---- openlp/core/ui/mainwindow.py | 59 ++++++++------------- openlp/core/ui/shortcutlistform.py | 47 +++++++++++----- openlp/core/ui/slidecontroller.py | 39 ++++++++------ openlp/core/utils/actions.py | 8 +++ openlp/plugins/songusage/songusageplugin.py | 30 +++++------ 7 files changed, 113 insertions(+), 95 deletions(-) diff --git a/openlp/core/lib/searchedit.py b/openlp/core/lib/searchedit.py index 4841d76dc..41699256f 100644 --- a/openlp/core/lib/searchedit.py +++ b/openlp/core/lib/searchedit.py @@ -29,6 +29,7 @@ import logging from PyQt4 import QtCore, QtGui from openlp.core.lib import build_icon +from openlp.core.lib.ui import icon_action log = logging.getLogger(__name__) @@ -132,7 +133,7 @@ class SearchEdit(QtGui.QLineEdit): menu = QtGui.QMenu(self) first = None for identifier, icon, title in items: - action = QtGui.QAction(build_icon(icon), title, menu) + action = icon_action(menu, title, icon) action.setData(QtCore.QVariant(identifier)) menu.addAction(action) QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered(bool)'), diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index c6f7dd070..a02041cad 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -280,21 +280,17 @@ def shortcut_action(parent, name, shortcuts, function, icon=None, checked=None, """ Return a shortcut enabled action. """ - # We cannot use the base_action, icon_action and the like, as we must not - # add the action to the actionList before the shortcut has been set - # (when we add the action to the list, the default shortcut is saved, to be - # able restore the shortcut). - action = QtGui.QAction(parent) - action.setObjectName(name) if icon is not None: - action.setIcon(build_icon(icon)) - if checked is not None: - action.setCheckable(True) - action.setChecked(checked) + action = icon_action(parent, name, icon, checked, category) + elif checked is not None: + action = checkable_action(parent, name, checked, category) + else: + action = base_action(parent, name, category) action.setShortcuts(shortcuts) - action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) - if category is not None: - ActionList.add_action(action, category) + action.setShortcutContext(QtCore.Qt.WindowShortcut) + # We have to save the default shortcut again, as the action's shortcut was + # set after adding the shortcut to the action list. + action.defaultShortcuts = action.shortcuts() QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), function) return action diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 14bed4712..458e8814f 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -165,28 +165,23 @@ class Ui_MainWindow(object): [QtGui.QKeySequence(u'Ctrl+N')], self.ServiceManagerContents.onNewServiceClicked, u':/general/general_new.png', category=u'File') - self.FileNewItem.setShortcutContext(QtCore.Qt.WindowShortcut) self.FileOpenItem = shortcut_action(mainWindow, u'FileOpenItem', [QtGui.QKeySequence(u'Ctrl+O')], self.ServiceManagerContents.onLoadServiceClicked, u':/general/general_open.png', category=u'File') - self.FileOpenItem.setShortcutContext(QtCore.Qt.WindowShortcut) self.FileSaveItem = shortcut_action(mainWindow, u'FileSaveItem', [QtGui.QKeySequence(u'Ctrl+S')], self.ServiceManagerContents.saveFile, u':/general/general_save.png', category=u'File') - self.FileSaveItem.setShortcutContext(QtCore.Qt.WindowShortcut) self.FileSaveAsItem = shortcut_action(mainWindow, u'FileSaveAsItem', [QtGui.QKeySequence(u'Ctrl+Shift+S')], self.ServiceManagerContents.saveFileAs, category=u'File') - self.FileSaveAsItem.setShortcutContext(QtCore.Qt.WindowShortcut) self.printServiceOrderItem = shortcut_action(mainWindow, u'printServiceItem', [QtGui.QKeySequence(u'Ctrl+P')], self.ServiceManagerContents.printServiceOrder, category=u'File') self.FileExitItem = shortcut_action(mainWindow, u'FileExitItem', [QtGui.QKeySequence(u'Alt+F4')], mainWindow.close, u':/system/system_exit.png', category=u'File') - self.FileSaveAsItem.setShortcutContext(QtCore.Qt.WindowShortcut) self.ImportThemeItem = base_action( mainWindow, u'ImportThemeItem', u'Import') self.ImportLanguageItem = base_action( @@ -199,21 +194,20 @@ class Ui_MainWindow(object): u'ViewMediaManagerItem', [QtGui.QKeySequence(u'F8')], self.toggleMediaManager, u':/system/system_mediamanager.png', self.mediaManagerDock.isVisible(), u'View') - self.FileSaveAsItem.setShortcutContext(QtCore.Qt.WindowShortcut) self.ViewThemeManagerItem = shortcut_action(mainWindow, - u'ViewThemeManagerItem', [QtGui.QKeySequence(u'F9')], + u'ViewThemeManagerItem', [QtGui.QKeySequence(u'F10')], self.toggleThemeManager, u':/system/system_thememanager.png', self.themeManagerDock.isVisible(), u'View') - self.FileSaveAsItem.setShortcutContext(QtCore.Qt.WindowShortcut) self.ViewServiceManagerItem = shortcut_action(mainWindow, - u'ViewServiceManagerItem', [QtGui.QKeySequence(u'F10')], + u'ViewServiceManagerItem', [QtGui.QKeySequence(u'F9')], self.toggleServiceManager, u':/system/system_servicemanager.png', self.serviceManagerDock.isVisible(), u'View') - self.FileSaveAsItem.setShortcutContext(QtCore.Qt.WindowShortcut) - self.ViewPreviewPanel = checkable_action(mainWindow, - u'ViewPreviewPanel', previewVisible, u'View') - self.ViewLivePanel = checkable_action( - mainWindow, u'ViewLivePanel', liveVisible, u'View') + self.ViewPreviewPanel = shortcut_action(mainWindow, + u'ViewPreviewPanel', [QtGui.QKeySequence(u'F11')], + self.setPreviewPanelVisibility, checked=previewVisible, category=u'View') + self.ViewLivePanel = shortcut_action(mainWindow, u'ViewLivePanel', + [QtGui.QKeySequence(u'F12')], self.setLivePanelVisibility, + checked=liveVisible, category=u'View') self.ModeDefaultItem = checkable_action( mainWindow, u'ModeDefaultItem', category=u'View Mode') self.ModeSetupItem = checkable_action( @@ -229,8 +223,9 @@ class Ui_MainWindow(object): u':/tools/tools_add.png', category=u'Tools') self.ToolsOpenDataFolder = icon_action(mainWindow, u'ToolsOpenDataFolder', u':/general/general_open.png', category=u'Tools') - self.settingsPluginListItem = icon_action(mainWindow, - u'settingsPluginListItem', u':/system/settings_plugin_list.png', + self.settingsPluginListItem = shortcut_action(mainWindow, + u'settingsPluginListItem', [QtGui.QKeySequence(u'Alt+F7')], + self.onPluginItemClicked, u':/system/settings_plugin_list.png', category=u'Settings') # i18n Language Items self.AutoLanguageItem = checkable_action(mainWindow, @@ -257,7 +252,8 @@ class Ui_MainWindow(object): u'HelpDocumentationItem', u':/system/system_help_contents.png', category=None)#u'Help') self.HelpDocumentationItem.setEnabled(False) - self.HelpAboutItem = icon_action(mainWindow, u'HelpAboutItem', + self.HelpAboutItem = shortcut_action(mainWindow, u'HelpAboutItem', + [QtGui.QKeySequence(u'Ctrl+F1')], self.onHelpAboutItemClicked, u':/system/system_about.png', category=u'Help') self.HelpOnlineHelpItem = base_action( mainWindow, u'HelpOnlineHelpItem', category=u'Help') @@ -389,30 +385,21 @@ class Ui_MainWindow(object): translate('OpenLP.MainWindow', 'Toggle Preview Panel')) self.ViewPreviewPanel.setStatusTip(translate('OpenLP.MainWindow', 'Toggle the visibility of the preview panel.')) - self.ViewPreviewPanel.setShortcut( - translate('OpenLP.MainWindow', 'F11')) self.ViewLivePanel.setText( translate('OpenLP.MainWindow', '&Live Panel')) self.ViewLivePanel.setToolTip( translate('OpenLP.MainWindow', 'Toggle Live Panel')) self.ViewLivePanel.setStatusTip(translate('OpenLP.MainWindow', 'Toggle the visibility of the live panel.')) - self.ViewLivePanel.setShortcut( - translate('OpenLP.MainWindow', 'F12')) self.settingsPluginListItem.setText(translate('OpenLP.MainWindow', '&Plugin List')) self.settingsPluginListItem.setStatusTip( translate('OpenLP.MainWindow', 'List the Plugins')) - self.settingsPluginListItem.setShortcut( - translate('OpenLP.MainWindow', 'Alt+F7')) self.HelpDocumentationItem.setText( translate('OpenLP.MainWindow', '&User Guide')) self.HelpAboutItem.setText(translate('OpenLP.MainWindow', '&About')) self.HelpAboutItem.setStatusTip( translate('OpenLP.MainWindow', 'More information about OpenLP')) - self.HelpAboutItem.setShortcut( - translate('OpenLP.MainWindow', 'Ctrl+F1')) - print self.HelpAboutItem.shortcuts() self.HelpOnlineHelpItem.setText( translate('OpenLP.MainWindow', '&Online Help')) # Uncomment after 1.9.5 beta string freeze @@ -494,10 +481,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): QtCore.QObject.connect(self.ExportThemeItem, QtCore.SIGNAL(u'triggered()'), self.themeManagerContents.onExportTheme) - QtCore.QObject.connect(self.ViewPreviewPanel, - QtCore.SIGNAL(u'toggled(bool)'), self.setPreviewPanelVisibility) - QtCore.QObject.connect(self.ViewLivePanel, - QtCore.SIGNAL(u'toggled(bool)'), self.setLivePanelVisibility) QtCore.QObject.connect(self.mediaManagerDock, QtCore.SIGNAL(u'visibilityChanged(bool)'), self.ViewMediaManagerItem.setChecked) @@ -511,12 +494,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): QtCore.SIGNAL(u'triggered()'), self.onHelpWebSiteClicked) QtCore.QObject.connect(self.HelpOnlineHelpItem, QtCore.SIGNAL(u'triggered()'), self.onHelpOnLineHelpClicked) - QtCore.QObject.connect(self.HelpAboutItem, - QtCore.SIGNAL(u'triggered()'), self.onHelpAboutItemClicked) QtCore.QObject.connect(self.ToolsOpenDataFolder, QtCore.SIGNAL(u'triggered()'), self.onToolsOpenDataFolderClicked) - QtCore.QObject.connect(self.settingsPluginListItem, - QtCore.SIGNAL(u'triggered()'), self.onPluginItemClicked) QtCore.QObject.connect(self.DisplayTagItem, QtCore.SIGNAL(u'triggered()'), self.onDisplayTagItemClicked) QtCore.QObject.connect(self.SettingsConfigureItem, @@ -902,7 +881,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): def toggleThemeManager(self): self.themeManagerDock.setVisible(not self.themeManagerDock.isVisible()) - def setPreviewPanelVisibility(self, visible): + def setPreviewPanelVisibility(self, visible=None): """ Sets the visibility of the preview panel including saving the setting and updating the menu. @@ -912,12 +891,14 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): True - Visible False - Hidden """ + if visible is None: + visible = self.ViewPreviewPanel.isVisible() self.previewController.panel.setVisible(visible) QtCore.QSettings().setValue(u'user interface/preview panel', QtCore.QVariant(visible)) self.ViewPreviewPanel.setChecked(visible) - def setLivePanelVisibility(self, visible): + def setLivePanelVisibility(self, visible=None): """ Sets the visibility of the live panel including saving the setting and updating the menu. @@ -927,6 +908,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): True - Visible False - Hidden """ + if visible is None: + visible = self.ViewLivePanel.isVisible() self.liveController.panel.setVisible(visible) QtCore.QSettings().setValue(u'user interface/live panel', QtCore.QVariant(visible)) @@ -984,8 +967,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.FileMenu.addSeparator() for fileId, filename in enumerate(recentFilesToDisplay): log.debug('Recent file name: %s', filename) - action = QtGui.QAction(u'&%d %s' % (fileId + 1, - QtCore.QFileInfo(filename).fileName()), self) + action = base_action(self, u'&%d %s' % (fileId + 1, + QtCore.QFileInfo(filename).fileName())) action.setData(QtCore.QVariant(filename)) self.connect(action, QtCore.SIGNAL(u'triggered()'), self.ServiceManagerContents.onRecentServiceClicked) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 4746ae3f7..e350009cd 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -42,13 +42,13 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): The shortcut list dialog """ #TODO: do not close on ESC -#TODO: Fix Preview/Live controller (have the same shortcut +#TODO: Fix Preview/Live controller (have the same shortcut) def __init__(self, parent=None): QtGui.QDialog.__init__(self, parent) self.setupUi(self) - self.assingedShortcuts = [] self.column = -1 self.shortcutButton.setText(u'') + self.shortcutButton.setEnabled(False) QtCore.QObject.connect(self.shortcutButton, QtCore.SIGNAL(u'toggled(bool)'), self.onShortcutButtonClicked) QtCore.QObject.connect(self.treeWidget, @@ -76,7 +76,27 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): if event.modifiers() & Qt.ShiftModifier == Qt.ShiftModifier: key_string = u'Shift+' + key_string key_sequence = QtGui.QKeySequence(key_string) - if key_sequence in self.assingedShortcuts: + # The item/action we are attempting to change. + changing_item = self.treeWidget.currentItem() + changing_action = changing_item.data(0, QtCore.Qt.UserRole).toPyObject() + shortcut_valid = True + for category in ActionList.categories: + for action in category.actions: + shortcuts = action.shortcuts() + if key_sequence not in shortcuts: + continue + if action is changing_action: + continue + # Have the same parentWidget, thus they cannot have the same + # shortcut. + #TODO: Does not fully work right now. + if action.parentWidget() is changing_action.parentWidget(): + shortcut_valid = False + if action.shortcutContext() == QtCore.Qt.WindowShortcut: + shortcut_valid = False + if changing_action.shortcutContext() == QtCore.Qt.WindowShortcut: + shortcut_valid = False + if not shortcut_valid: QtGui.QMessageBox.warning(self, translate('OpenLP.ShortcutListDialog', 'Duplicate Shortcut'), unicode(translate('OpenLP.ShortcutListDialog', 'The shortcut ' @@ -91,6 +111,9 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): def exec_(self): self.reloadShortcutList() + self.shortcutButton.setChecked(False) + self.shortcutButton.setEnabled(False) + self.shortcutButton.setText(u'') return QtGui.QDialog.exec_(self) def reloadShortcutList(self): @@ -116,7 +139,6 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): This refreshes the item's shortcuts shown in the list. Note, this neither adds new actions nor removes old actions. """ - self.assingedShortcuts = [] iterator = QtGui.QTreeWidgetItemIterator(self.treeWidget) while iterator.value(): item = iterator.value() @@ -124,7 +146,6 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): action = item.data(0, QtCore.Qt.UserRole).toPyObject() if action is None: continue - self.assingedShortcuts.extend(action.shortcuts()) if len(action.shortcuts()) == 0: item.setText(1, u'') item.setText(2, u'') @@ -142,6 +163,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): if toggled: return item = self.treeWidget.currentItem() + if item is None: + return action = item.data(0, QtCore.Qt.UserRole).toPyObject() if action is None: return @@ -163,12 +186,13 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): def onItemDoubleClicked(self, item, column): """ + A item has been double clicked. ``The shortcutButton`` will be checked + and the item's shortcut will be displayed. """ - item = self.treeWidget.currentItem() action = item.data(0, QtCore.Qt.UserRole).toPyObject() + if action is None: + return self.shortcutButton.setChecked(True) - if action is None or column not in [1, 2]: - self.shortcutButton.setChecked(False) self.onItemPressed(item, column) def onItemPressed(self, item, column): @@ -177,11 +201,11 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): shortcut which is encapsulate in the item. """ self.column = column - item = self.treeWidget.currentItem() action = item.data(0, QtCore.Qt.UserRole).toPyObject() self.shortcutButton.setEnabled(True) text = u'' if action is None or column not in [1, 2]: + self.shortcutButton.setChecked(False) self.shortcutButton.setEnabled(False) elif column == 1 and len(action.shortcuts()) != 0: text = action.shortcuts()[0].toString() @@ -194,16 +218,15 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): Restore the defaults of this """ item = self.treeWidget.currentItem() + self.shortcutButton.setChecked(False) if item is None: return action = item.data(0, QtCore.Qt.UserRole).toPyObject() if action is None: return - #FIXME: defaultShortcuts action.setShortcuts(action.defaultShortcuts) - self.shortcutButton.setChecked(False) - self.shortcutButton.setText(u'') self.refreshShortcutList() + self.onItemPressed(item, self.column) def save(self): """ diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index e9d748ad5..750ada7cd 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -157,16 +157,20 @@ class SlideController(QtGui.QWidget): self.toolbar.addToolbarWidget(u'Hide Menu', self.hideMenu) self.hideMenu.setMenu(QtGui.QMenu( translate('OpenLP.SlideController', 'Hide'), self.toolbar)) - self.blankScreen = icon_action(self.hideMenu, u'Blank Screen', + self.blankScreen = shortcut_action(self.hideMenu, u'blankScreen', + [QtCore.Qt.Key_Period], self.onBlankDisplay, u':/slides/slide_blank.png', False, u'Live Toolbar') self.blankScreen.setText( translate('OpenLP.SlideController', 'Blank Screen')) - self.themeScreen = icon_action(self.hideMenu, u'Blank Theme', + self.themeScreen = shortcut_action(self.hideMenu, u'themeScreen', + [QtGui.QKeySequence(u'T')], self.onThemeDisplay, u':/slides/slide_theme.png', False, u'Live Toolbar') self.themeScreen.setText( translate('OpenLP.SlideController', 'Blank to Theme')) - self.desktopScreen = icon_action(self.hideMenu, u'Desktop Screen', - u':/slides/slide_desktop.png', False, u'Live Toolbar') + self.desktopScreen = shortcut_action(self.hideMenu, + u'desktopScreen', [QtGui.QKeySequence(u'D')], + self.onHideDisplay, u':/slides/slide_desktop.png', False, + u'Live Toolbar') self.desktopScreen.setText( translate('OpenLP.SlideController', 'Show Desktop')) self.hideMenu.setDefaultAction(self.blankScreen) @@ -294,12 +298,6 @@ class SlideController(QtGui.QWidget): QtCore.QObject.connect(self.previewListWidget, QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected) if self.isLive: - QtCore.QObject.connect(self.blankScreen, - QtCore.SIGNAL(u'triggered(bool)'), self.onBlankDisplay) - QtCore.QObject.connect(self.themeScreen, - QtCore.SIGNAL(u'triggered(bool)'), self.onThemeDisplay) - QtCore.QObject.connect(self.desktopScreen, - QtCore.SIGNAL(u'triggered(bool)'), self.onHideDisplay) QtCore.QObject.connect(self.volumeSlider, QtCore.SIGNAL(u'sliderReleased()'), self.mediaVolume) QtCore.QObject.connect(Receiver.get_receiver(), @@ -380,14 +378,17 @@ class SlideController(QtGui.QWidget): ActionList.add_action(self.nextItem, u'Live Toolbar') self.previousService = shortcut_action(parent, u'previousService', [QtCore.Qt.Key_Left], self.servicePrevious, u'Live Toolbar') + self.previousService.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) self.previousService.setText( translate('OpenLP.SlideController', 'Previous Service')) self.nextService = shortcut_action(parent, 'nextService', [QtCore.Qt.Key_Right], self.serviceNext, u'Live Toolbar') + self.nextService.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) self.nextService.setText( translate('OpenLP.SlideController', 'Next Service')) self.escapeItem = shortcut_action(parent, 'escapeItem', [QtCore.Qt.Key_Escape], self.liveEscape, u'Live Toolbar') + self.escapeItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) self.escapeItem.setText( translate('OpenLP.SlideController', 'Escape Item')) @@ -742,10 +743,12 @@ class SlideController(QtGui.QWidget): """ self.onBlankDisplay(False) - def onBlankDisplay(self, checked): + def onBlankDisplay(self, checked=None): """ Handle the blank screen button actions """ + if checked is None: + checked = self.blankScreen.isChecked() log.debug(u'onBlankDisplay %s' % checked) self.hideMenu.setDefaultAction(self.blankScreen) self.blankScreen.setChecked(checked) @@ -763,10 +766,12 @@ class SlideController(QtGui.QWidget): self.blankPlugin(checked) self.updatePreview() - def onThemeDisplay(self, checked): + def onThemeDisplay(self, checked=None): """ Handle the Theme screen button """ + if checked is None: + checked = self.themeScreen.isChecked() log.debug(u'onThemeDisplay %s' % checked) self.hideMenu.setDefaultAction(self.themeScreen) self.blankScreen.setChecked(False) @@ -784,10 +789,12 @@ class SlideController(QtGui.QWidget): self.blankPlugin(checked) self.updatePreview() - def onHideDisplay(self, checked): + def onHideDisplay(self, checked=None): """ Handle the Hide screen button """ + if checked is None: + checked = self.desktopScreen.isChecked() log.debug(u'onHideDisplay %s' % checked) self.hideMenu.setDefaultAction(self.desktopScreen) self.blankScreen.setChecked(False) @@ -1102,11 +1109,11 @@ class SlideController(QtGui.QWidget): screen hide attributes """ blank = None - if self.blankScreen.isChecked: + if self.blankScreen.isChecked(): blank = self.blankScreen - if self.themeScreen.isChecked: + if self.themeScreen.isChecked(): blank = self.themeScreen - if self.desktopScreen.isChecked: + if self.desktopScreen.isChecked(): blank = self.desktopScreen if blank: blank.setChecked(False) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 34bc72173..7421db30e 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -173,6 +173,11 @@ class CategoryList(object): self.categories.append(category) self.categories.sort(key=lambda cat: cat.weight) + def remove(self, name): + for category in self.categories: + if category.name == name: + self.categories.remove(category) + class ActionList(object): """ @@ -206,3 +211,6 @@ class ActionList(object): if category not in ActionList.categories: return ActionList.categories[category].actions.remove(action) + # Remove empty categories. + if len(ActionList.categories[category].actions) == 0: + ActionList.categories.remove(category) diff --git a/openlp/plugins/songusage/songusageplugin.py b/openlp/plugins/songusage/songusageplugin.py index 8ff40373a..2e55a23c0 100644 --- a/openlp/plugins/songusage/songusageplugin.py +++ b/openlp/plugins/songusage/songusageplugin.py @@ -32,6 +32,8 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, StringContent, Receiver, build_icon, \ translate from openlp.core.lib.db import Manager +from openlp.core.lib.ui import base_action, shortcut_action +from openlp.core.utils.actions import ActionList from openlp.plugins.songusage.forms import SongUsageDetailForm, \ SongUsageDeleteForm from openlp.plugins.songusage.lib.db import init_schema, SongUsageItem @@ -63,30 +65,25 @@ class SongUsagePlugin(Plugin): self.SongUsageMenu.setObjectName(u'SongUsageMenu') self.SongUsageMenu.setTitle(translate( 'SongUsagePlugin', '&Song Usage Tracking')) - #SongUsage Delete - self.SongUsageDelete = QtGui.QAction(tools_menu) + # SongUsage Delete + self.SongUsageDelete = base_action(tools_menu, u'SongUsageDelete') self.SongUsageDelete.setText(translate('SongUsagePlugin', '&Delete Tracking Data')) self.SongUsageDelete.setStatusTip(translate('SongUsagePlugin', 'Delete song usage data up to a specified date.')) - self.SongUsageDelete.setObjectName(u'SongUsageDelete') - #SongUsage Report - self.SongUsageReport = QtGui.QAction(tools_menu) + # SongUsage Report + self.SongUsageReport = base_action(tools_menu, u'SongUsageReport') self.SongUsageReport.setText( translate('SongUsagePlugin', '&Extract Tracking Data')) self.SongUsageReport.setStatusTip( translate('SongUsagePlugin', 'Generate a report on song usage.')) - self.SongUsageReport.setObjectName(u'SongUsageReport') - #SongUsage activation - self.SongUsageStatus = QtGui.QAction(tools_menu) - self.SongUsageStatus.setCheckable(True) - self.SongUsageStatus.setChecked(False) + # SongUsage activation + self.SongUsageStatus = shortcut_action(tools_menu, u'SongUsageStatus', + [QtCore.Qt.Key_F4], self.toggleSongUsageState, checked=False) self.SongUsageStatus.setText(translate( 'SongUsagePlugin', 'Toggle Tracking')) self.SongUsageStatus.setStatusTip(translate('SongUsagePlugin', 'Toggle the tracking of song usage.')) - self.SongUsageStatus.setShortcut(u'F4') - self.SongUsageStatus.setObjectName(u'SongUsageStatus') #Add Menus together self.toolsMenu.addAction(self.SongUsageMenu.menuAction()) self.SongUsageMenu.addAction(self.SongUsageStatus) @@ -97,9 +94,6 @@ class SongUsagePlugin(Plugin): QtCore.QObject.connect(self.SongUsageStatus, QtCore.SIGNAL(u'visibilityChanged(bool)'), self.SongUsageStatus.setChecked) - QtCore.QObject.connect(self.SongUsageStatus, - QtCore.SIGNAL(u'triggered(bool)'), - self.toggleSongUsageState) QtCore.QObject.connect(self.SongUsageDelete, QtCore.SIGNAL(u'triggered()'), self.onSongUsageDelete) QtCore.QObject.connect(self.SongUsageReport, @@ -116,6 +110,9 @@ class SongUsagePlugin(Plugin): self.settingsSection + u'/active', QtCore.QVariant(False)).toBool() self.SongUsageStatus.setChecked(self.SongUsageActive) + ActionList.add_action(self.SongUsageDelete, u'Song Usage') + ActionList.add_action(self.SongUsageReport, u'Song Usage') + ActionList.add_action(self.SongUsageStatus, u'Song Usage') if self.manager is None: self.manager = Manager(u'songusage', init_schema) self.SongUsagedeleteform = SongUsageDeleteForm(self.manager, @@ -131,6 +128,9 @@ class SongUsagePlugin(Plugin): self.manager.finalise() Plugin.finalise(self) self.SongUsageMenu.menuAction().setVisible(False) + ActionList.remove_action(self.SongUsageDelete, u'Song Usage') + ActionList.remove_action(self.SongUsageReport, u'Song Usage') + ActionList.remove_action(self.SongUsageStatus, u'Song Usage') #stop any events being processed self.SongUsageActive = False From 1a493b1fc8da490d0cd7783188f79c02caeb83cd Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 1 Apr 2011 18:53:39 +0200 Subject: [PATCH 17/98] possible fix for not working 'move selection down' --- openlp/core/ui/slidecontroller.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 750ada7cd..9f486518b 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -32,7 +32,7 @@ from PyQt4.phonon import Phonon from openlp.core.lib import OpenLPToolbar, Receiver, resize_image, \ ItemCapabilities, translate -from openlp.core.lib.ui import icon_action, UiStrings, shortcut_action +from openlp.core.lib.ui import UiStrings, shortcut_action from openlp.core.ui import HideMode, MainDisplay from openlp.core.utils.actions import ActionList @@ -143,12 +143,17 @@ class SlideController(QtGui.QWidget): translate('OpenLP.SlideController', 'Move to previous'), self.onSlideSelectedPrevious) self.previousItem.setObjectName(u'previousItem') + self.previousItem.setShortcuts([QtCore.Qt.Key_Up, QtCore.Qt.Key_PageUp]) + self.previousItem.setShortcutContext( + QtCore.Qt.WidgetWithChildrenShortcut) self.nextItem = self.toolbar.addToolbarButton( translate('OpenLP.SlideController', 'Next Slide'), u':/slides/slide_next.png', translate('OpenLP.SlideController', 'Move to next'), self.onSlideSelectedNext) self.nextItem.setObjectName(u'nextItem') + self.nextItem.setShortcuts([QtCore.Qt.Key_Down, QtCore.Qt.Key_PageDown]) + self.nextItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) self.toolbar.addToolbarSeparator(u'Close Separator') if self.isLive: self.hideMenu = QtGui.QToolButton(self.toolbar) @@ -363,18 +368,11 @@ class SlideController(QtGui.QWidget): QtCore.SIGNAL(u'config_screen_changed'), self.screenSizeChanged) def setPreviewHotkeys(self, parent=None): - self.previousItem.setShortcuts([QtCore.Qt.Key_Up]) ActionList.add_action(self.previousItem, u'Preview Toolbar') - self.nextItem.setShortcuts([QtCore.Qt.Key_Down]) ActionList.add_action(self.nextItem, u'Preview Toolbar') def setLiveHotkeys(self, parent=None): - self.previousItem.setShortcuts([QtCore.Qt.Key_Up, QtCore.Qt.Key_PageUp]) - self.previousItem.setShortcutContext( - QtCore.Qt.WidgetWithChildrenShortcut) ActionList.add_action(self.previousItem, u'Live Toolbar') - self.nextItem.setShortcuts([QtCore.Qt.Key_Down, QtCore.Qt.Key_PageDown]) - self.nextItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) ActionList.add_action(self.nextItem, u'Live Toolbar') self.previousService = shortcut_action(parent, u'previousService', [QtCore.Qt.Key_Left], self.servicePrevious, u'Live Toolbar') From 4badce0205e1f29d69f2074bbd2bb30d934cf9d7 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 2 Apr 2011 15:08:54 +0200 Subject: [PATCH 18/98] fixed actions which did not have a parent which caused the check to fail --- openlp/core/lib/toolbar.py | 3 +-- openlp/core/ui/shortcutlistform.py | 11 ++++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/openlp/core/lib/toolbar.py b/openlp/core/lib/toolbar.py index 44149cd85..d2b37df51 100644 --- a/openlp/core/lib/toolbar.py +++ b/openlp/core/lib/toolbar.py @@ -79,7 +79,6 @@ class OpenLPToolbar(QtGui.QToolBar): ``context`` Specify the context in which this shortcut is valid """ - newAction = None if icon: actionIcon = build_icon(icon) if slot and not checkable: @@ -88,7 +87,7 @@ class OpenLPToolbar(QtGui.QToolBar): newAction = self.addAction(actionIcon, title) self.icons[title] = actionIcon else: - newAction = QtGui.QAction(title, newAction) + newAction = QtGui.QAction(title, self) self.addAction(newAction) QtCore.QObject.connect(newAction, QtCore.SIGNAL(u'triggered()'), slot) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index e350009cd..bd4fb8ab6 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -42,7 +42,6 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): The shortcut list dialog """ #TODO: do not close on ESC -#TODO: Fix Preview/Live controller (have the same shortcut) def __init__(self, parent=None): QtGui.QDialog.__init__(self, parent) self.setupUi(self) @@ -89,12 +88,13 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): continue # Have the same parentWidget, thus they cannot have the same # shortcut. - #TODO: Does not fully work right now. - if action.parentWidget() is changing_action.parentWidget(): + if action.parent() is changing_action.parent(): shortcut_valid = False - if action.shortcutContext() == QtCore.Qt.WindowShortcut: + if action.shortcutContext() in [QtCore.Qt.WindowShortcut, + QtCore.Qt.ApplicationShortcut]: shortcut_valid = False - if changing_action.shortcutContext() == QtCore.Qt.WindowShortcut: + if changing_action.shortcutContext() in \ + [QtCore.Qt.WindowShortcut, QtCore.Qt.ApplicationShortcut]: shortcut_valid = False if not shortcut_valid: QtGui.QMessageBox.warning(self, @@ -193,6 +193,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): if action is None: return self.shortcutButton.setChecked(True) + self.shortcutButton.setFocus(QtCore.Qt.OtherFocusReason) self.onItemPressed(item, column) def onItemPressed(self, item, column): From 7260f91825cd8a9c8bf2567dcd7d88b789b9fc1c Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 2 Apr 2011 16:22:08 +0200 Subject: [PATCH 19/98] restore butto nnow works --- openlp/core/ui/shortcutlistdialog.py | 2 +- openlp/core/ui/shortcutlistform.py | 32 +++++++++++++++++++++++++++- openlp/core/ui/slidecontroller.py | 15 ++++++------- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index 467fb0534..5f8936628 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -58,7 +58,7 @@ class Ui_ShortcutListDialog(object): self.dialogLayout.addLayout(self.customLayout) self.buttonBox = QtGui.QDialogButtonBox(shortcutListDialog) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel | - QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Reset) + QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.RestoreDefaults) self.buttonBox.setObjectName(u'buttonBox') self.dialogLayout.addWidget(self.buttonBox) self.retranslateUi(shortcutListDialog) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index bd4fb8ab6..14aa1d009 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -41,7 +41,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): """ The shortcut list dialog """ -#TODO: do not close on ESC + def __init__(self, parent=None): QtGui.QDialog.__init__(self, parent) self.setupUi(self) @@ -58,6 +58,16 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.onItemDoubleClicked) QtCore.QObject.connect(self.clearShortcutButton, QtCore.SIGNAL(u'clicked(bool)'), self.onClearShortcutButtonClicked) + QtCore.QObject.connect(self.buttonBox, + QtCore.SIGNAL(u'clicked(QAbstractButton*)'), + self.onRestoreDefaultsClicked) + + def keyPressEvent(self, event): + if self.shortcutButton.isChecked(): + event.ignore() + elif event.key() == QtCore.Qt.Key_Escape: + event.accept() + self.close() def keyReleaseEvent(self, event): Qt = QtCore.Qt @@ -229,6 +239,26 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.refreshShortcutList() self.onItemPressed(item, self.column) + def onRestoreDefaultsClicked(self, button): + """ + Restores all default shortcuts. + """ + if self.buttonBox.buttonRole(button) != QtGui.QDialogButtonBox.ResetRole: + return + if QtGui.QMessageBox.question(self, + translate('OpenLP.ShortcutListDialog', 'Restore Default Shortcuts'), + translate('OpenLP.ShortcutListDialog', 'Do you want to restore all ' + 'shortcuts to their defaults?'), QtGui.QMessageBox.StandardButtons( + QtGui.QMessageBox.Yes | + QtGui.QMessageBox.No)) == QtGui.QMessageBox.No: + return + self.shortcutButton.setChecked(False) + self.shortcutButton.setText(u'') + for category in ActionList.categories: + for action in category.actions: + action.setShortcuts(action.defaultShortcuts) + self.refreshShortcutList() + def save(self): """ Save the shortcuts. **Note**, that we do not have to load the shortcuts, diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 9f486518b..1ec2e1772 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -141,19 +141,18 @@ class SlideController(QtGui.QWidget): translate('OpenLP.SlideController', 'Previous Slide'), u':/slides/slide_previous.png', translate('OpenLP.SlideController', 'Move to previous'), - self.onSlideSelectedPrevious) - self.previousItem.setObjectName(u'previousItem') - self.previousItem.setShortcuts([QtCore.Qt.Key_Up, QtCore.Qt.Key_PageUp]) - self.previousItem.setShortcutContext( - QtCore.Qt.WidgetWithChildrenShortcut) + self.onSlideSelectedPrevious, + shortcuts=[QtCore.Qt.Key_Up, QtCore.Qt.Key_PageUp], + context=QtCore.Qt.WidgetWithChildrenShortcut) + self.previousItem.setObjectName(u'previousItem') self.nextItem = self.toolbar.addToolbarButton( translate('OpenLP.SlideController', 'Next Slide'), u':/slides/slide_next.png', translate('OpenLP.SlideController', 'Move to next'), - self.onSlideSelectedNext) + self.onSlideSelectedNext, + shortcuts=[QtCore.Qt.Key_Down, QtCore.Qt.Key_PageDown], + context=QtCore.Qt.WidgetWithChildrenShortcut) self.nextItem.setObjectName(u'nextItem') - self.nextItem.setShortcuts([QtCore.Qt.Key_Down, QtCore.Qt.Key_PageDown]) - self.nextItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) self.toolbar.addToolbarSeparator(u'Close Separator') if self.isLive: self.hideMenu = QtGui.QToolButton(self.toolbar) From e3d822f07ad30f72b99f21410039438639faf56a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 2 Apr 2011 17:37:14 +0200 Subject: [PATCH 20/98] fixed strange save bug --- openlp/core/lib/ui.py | 17 +++++++++-------- openlp/core/ui/slidecontroller.py | 6 ++++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index a02041cad..9f24da96c 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -280,17 +280,18 @@ def shortcut_action(parent, name, shortcuts, function, icon=None, checked=None, """ Return a shortcut enabled action. """ + action = QtGui.QAction(parent) + action.setObjectName(name) if icon is not None: - action = icon_action(parent, name, icon, checked, category) - elif checked is not None: - action = checkable_action(parent, name, checked, category) - else: - action = base_action(parent, name, category) + action.setIcon(build_icon(icon)) + if checked is not None: + action.setCheckable(True) + action.setChecked(checked) + action.setCheckable(True) action.setShortcuts(shortcuts) action.setShortcutContext(QtCore.Qt.WindowShortcut) - # We have to save the default shortcut again, as the action's shortcut was - # set after adding the shortcut to the action list. - action.defaultShortcuts = action.shortcuts() + if category is not None: + ActionList.add_action(action, category) QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), function) return action diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 1ec2e1772..3fe27f4d9 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -144,7 +144,6 @@ class SlideController(QtGui.QWidget): self.onSlideSelectedPrevious, shortcuts=[QtCore.Qt.Key_Up, QtCore.Qt.Key_PageUp], context=QtCore.Qt.WidgetWithChildrenShortcut) - self.previousItem.setObjectName(u'previousItem') self.nextItem = self.toolbar.addToolbarButton( translate('OpenLP.SlideController', 'Next Slide'), u':/slides/slide_next.png', @@ -152,7 +151,6 @@ class SlideController(QtGui.QWidget): self.onSlideSelectedNext, shortcuts=[QtCore.Qt.Key_Down, QtCore.Qt.Key_PageDown], context=QtCore.Qt.WidgetWithChildrenShortcut) - self.nextItem.setObjectName(u'nextItem') self.toolbar.addToolbarSeparator(u'Close Separator') if self.isLive: self.hideMenu = QtGui.QToolButton(self.toolbar) @@ -367,10 +365,14 @@ class SlideController(QtGui.QWidget): QtCore.SIGNAL(u'config_screen_changed'), self.screenSizeChanged) def setPreviewHotkeys(self, parent=None): + self.previousItem.setObjectName(u'previousItemPreview') + self.nextItem.setObjectName(u'nextItemPreview') ActionList.add_action(self.previousItem, u'Preview Toolbar') ActionList.add_action(self.nextItem, u'Preview Toolbar') def setLiveHotkeys(self, parent=None): + self.previousItem.setObjectName(u'previousItemLive') + self.nextItem.setObjectName(u'nextItemLive') ActionList.add_action(self.previousItem, u'Live Toolbar') ActionList.add_action(self.nextItem, u'Live Toolbar') self.previousService = shortcut_action(parent, u'previousService', From 958155e7d75d8a86a90f0e4de1e33f9641520c37 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 2 Apr 2011 17:45:00 +0200 Subject: [PATCH 21/98] fixed alternate shortcut --- openlp/core/ui/shortcutlistform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 14aa1d009..80b9652a0 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -186,7 +186,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): shortcuts.append(action.shortcuts()[1]) # We are changing the secondary shortcut. elif self.column == 2: - if len(action.shortcuts()) == 1: + if len(action.shortcuts()) != 0: shortcuts.append(action.shortcuts()[0]) shortcuts.append(QtGui.QKeySequence(self.shortcutButton.text())) else: From 61068e26a6dce8bc7f577b716777cf3c56ef23eb Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 2 Apr 2011 18:14:43 +0200 Subject: [PATCH 22/98] removed cancel button --- openlp/core/ui/shortcutlistdialog.py | 4 ++-- openlp/core/ui/shortcutlistform.py | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index 5f8936628..a4bcc359e 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -57,8 +57,8 @@ class Ui_ShortcutListDialog(object): self.customLayout.addStretch() self.dialogLayout.addLayout(self.customLayout) self.buttonBox = QtGui.QDialogButtonBox(shortcutListDialog) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel | - QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.RestoreDefaults) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok | + QtGui.QDialogButtonBox.RestoreDefaults) self.buttonBox.setObjectName(u'buttonBox') self.dialogLayout.addWidget(self.buttonBox) self.retranslateUi(shortcutListDialog) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 80b9652a0..84aea006a 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -63,11 +63,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.onRestoreDefaultsClicked) def keyPressEvent(self, event): - if self.shortcutButton.isChecked(): - event.ignore() - elif event.key() == QtCore.Qt.Key_Escape: - event.accept() - self.close() + event.ignore() def keyReleaseEvent(self, event): Qt = QtCore.Qt From 28b83aadaed1c66eecb628152956414a379f0f19 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 3 Apr 2011 13:36:43 +0200 Subject: [PATCH 23/98] translate categories --- openlp/core/lib/ui.py | 8 +++ openlp/core/ui/mainwindow.py | 63 +++++++++++---------- openlp/core/ui/servicemanager.py | 23 +++++--- openlp/core/ui/slidecontroller.py | 20 +++---- openlp/core/utils/actions.py | 2 + openlp/plugins/alerts/alertsplugin.py | 4 +- openlp/plugins/bibles/bibleplugin.py | 8 ++- openlp/plugins/songs/songsplugin.py | 12 ++-- openlp/plugins/songusage/songusageplugin.py | 20 ++++--- 9 files changed, 94 insertions(+), 66 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 9f24da96c..15309edc9 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -56,8 +56,10 @@ class UiStrings(object): EmptyField = translate('OpenLP.Ui', 'Empty Field') Error = translate('OpenLP.Ui', 'Error') Export = translate('OpenLP.Ui', 'Export') + File = translate('OpenLP.Ui', 'File') FontSizePtUnit = translate('OpenLP.Ui', 'pt', 'Abbreviated font pointsize unit') + Help = translate('OpenLP.Ui', 'Help') Hours = translate('OpenLP.Ui', 'h', 'The abbreviated unit for hours') Image = translate('OpenLP.Ui', 'Image') Import = translate('OpenLP.Ui', 'Import') @@ -65,6 +67,7 @@ class UiStrings(object): Live = translate('OpenLP.Ui', 'Live') LiveBGError = translate('OpenLP.Ui', 'Live Background Error') LivePanel = translate('OpenLP.Ui', 'Live Panel') + LiveToolbar = translate('OpenLP.Ui', 'Live Toolbar') Load = translate('OpenLP.Ui', 'Load') Minutes = translate('OpenLP.Ui', 'm', 'The abbreviated unit for minutes') Middle = translate('OpenLP.Ui', 'Middle') @@ -82,6 +85,7 @@ class UiStrings(object): OpenService = translate('OpenLP.Ui', 'Open Service') Preview = translate('OpenLP.Ui', 'Preview') PreviewPanel = translate('OpenLP.Ui', 'Preview Panel') + PreviewToolbar = translate('OpenLP.Ui', 'Preview Toolbar') PrintServiceOrder = translate('OpenLP.Ui', 'Print Service Order') ReplaceBG = translate('OpenLP.Ui', 'Replace Background') ReplaceLiveBG = translate('OpenLP.Ui', 'Replace Live Background') @@ -92,13 +96,17 @@ class UiStrings(object): Search = translate('OpenLP.Ui', 'Search') SelectDelete = translate('OpenLP.Ui', 'You must select an item to delete.') SelectEdit = translate('OpenLP.Ui', 'You must select an item to edit.') + Settings = translate('OpenLP.Ui', 'Settings') SaveService = translate('OpenLP.Ui', 'Save Service') Service = translate('OpenLP.Ui', 'Service') StartTimeCode = unicode(translate('OpenLP.Ui', 'Start %s')) Theme = translate('OpenLP.Ui', 'Theme', 'Singular') Themes = translate('OpenLP.Ui', 'Themes', 'Plural') + Tools = translate('OpenLP.Ui', 'Tools') Top = translate('OpenLP.Ui', 'Top') Version = translate('OpenLP.Ui', 'Version') + View = translate('OpenLP.Ui', 'View') + ViewMode = translate('OpenLP.Ui', 'View Model') def add_welcome_page(parent, image): """ diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 458e8814f..bb6d105b9 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -164,69 +164,72 @@ class Ui_MainWindow(object): self.FileNewItem = shortcut_action(mainWindow, u'FileNewItem', [QtGui.QKeySequence(u'Ctrl+N')], self.ServiceManagerContents.onNewServiceClicked, - u':/general/general_new.png', category=u'File') + u':/general/general_new.png', category=UiStrings.File) self.FileOpenItem = shortcut_action(mainWindow, u'FileOpenItem', [QtGui.QKeySequence(u'Ctrl+O')], self.ServiceManagerContents.onLoadServiceClicked, - u':/general/general_open.png', category=u'File') + u':/general/general_open.png', category=UiStrings.File) self.FileSaveItem = shortcut_action(mainWindow, u'FileSaveItem', [QtGui.QKeySequence(u'Ctrl+S')], self.ServiceManagerContents.saveFile, - u':/general/general_save.png', category=u'File') + u':/general/general_save.png', category=UiStrings.File) self.FileSaveAsItem = shortcut_action(mainWindow, u'FileSaveAsItem', [QtGui.QKeySequence(u'Ctrl+Shift+S')], - self.ServiceManagerContents.saveFileAs, category=u'File') + self.ServiceManagerContents.saveFileAs, category=UiStrings.File) self.printServiceOrderItem = shortcut_action(mainWindow, u'printServiceItem', [QtGui.QKeySequence(u'Ctrl+P')], - self.ServiceManagerContents.printServiceOrder, category=u'File') + self.ServiceManagerContents.printServiceOrder, + category=UiStrings.File) self.FileExitItem = shortcut_action(mainWindow, u'FileExitItem', [QtGui.QKeySequence(u'Alt+F4')], mainWindow.close, - u':/system/system_exit.png', category=u'File') + u':/system/system_exit.png', category=UiStrings.File) self.ImportThemeItem = base_action( - mainWindow, u'ImportThemeItem', u'Import') + mainWindow, u'ImportThemeItem', UiStrings.Import) self.ImportLanguageItem = base_action( - mainWindow, u'ImportLanguageItem')#, u'Import') + mainWindow, u'ImportLanguageItem')#, UiStrings.Import) self.ExportThemeItem = base_action( - mainWindow, u'ExportThemeItem', u'Export') + mainWindow, u'ExportThemeItem', UiStrings.Export) self.ExportLanguageItem = base_action( - mainWindow, u'ExportLanguageItem')#, u'Export') + mainWindow, u'ExportLanguageItem')#, UiStrings.Export) self.ViewMediaManagerItem = shortcut_action(mainWindow, u'ViewMediaManagerItem', [QtGui.QKeySequence(u'F8')], self.toggleMediaManager, u':/system/system_mediamanager.png', - self.mediaManagerDock.isVisible(), u'View') + self.mediaManagerDock.isVisible(), UiStrings.View) self.ViewThemeManagerItem = shortcut_action(mainWindow, u'ViewThemeManagerItem', [QtGui.QKeySequence(u'F10')], self.toggleThemeManager, u':/system/system_thememanager.png', - self.themeManagerDock.isVisible(), u'View') + self.themeManagerDock.isVisible(), UiStrings.View) self.ViewServiceManagerItem = shortcut_action(mainWindow, u'ViewServiceManagerItem', [QtGui.QKeySequence(u'F9')], self.toggleServiceManager, u':/system/system_servicemanager.png', - self.serviceManagerDock.isVisible(), u'View') + self.serviceManagerDock.isVisible(), UiStrings.View) self.ViewPreviewPanel = shortcut_action(mainWindow, u'ViewPreviewPanel', [QtGui.QKeySequence(u'F11')], - self.setPreviewPanelVisibility, checked=previewVisible, category=u'View') + self.setPreviewPanelVisibility, checked=previewVisible, + category=UiStrings.View) self.ViewLivePanel = shortcut_action(mainWindow, u'ViewLivePanel', [QtGui.QKeySequence(u'F12')], self.setLivePanelVisibility, - checked=liveVisible, category=u'View') + checked=liveVisible, category=UiStrings.View) self.ModeDefaultItem = checkable_action( - mainWindow, u'ModeDefaultItem', category=u'View Mode') + mainWindow, u'ModeDefaultItem', category=UiStrings.ViewMode) self.ModeSetupItem = checkable_action( - mainWindow, u'ModeLiveItem', category=u'View Mode') + mainWindow, u'ModeLiveItem', category=UiStrings.ViewMode) self.ModeLiveItem = checkable_action( - mainWindow, u'ModeLiveItem', True, u'View Mode') + mainWindow, u'ModeLiveItem', True, UiStrings.ViewMode) self.ModeGroup = QtGui.QActionGroup(mainWindow) self.ModeGroup.addAction(self.ModeDefaultItem) self.ModeGroup.addAction(self.ModeSetupItem) self.ModeGroup.addAction(self.ModeLiveItem) self.ModeDefaultItem.setChecked(True) self.ToolsAddToolItem = icon_action(mainWindow, u'ToolsAddToolItem', - u':/tools/tools_add.png', category=u'Tools') - self.ToolsOpenDataFolder = icon_action(mainWindow, u'ToolsOpenDataFolder', - u':/general/general_open.png', category=u'Tools') + u':/tools/tools_add.png', category=UiStrings.Tools) + self.ToolsOpenDataFolder = icon_action(mainWindow, + u'ToolsOpenDataFolder', u':/general/general_open.png', + category=UiStrings.Tools) self.settingsPluginListItem = shortcut_action(mainWindow, u'settingsPluginListItem', [QtGui.QKeySequence(u'Alt+F7')], self.onPluginItemClicked, u':/system/settings_plugin_list.png', - category=u'Settings') + category=UiStrings.Settings) # i18n Language Items self.AutoLanguageItem = checkable_action(mainWindow, u'AutoLanguageItem', LanguageManager.auto_language) @@ -242,23 +245,25 @@ class Ui_MainWindow(object): add_actions(self.LanguageGroup, [languageItem]) self.SettingsShortcutsItem = icon_action(mainWindow, u'SettingsShortcutsItem', - u':/system/system_configure_shortcuts.png', category=u'Settings') + u':/system/system_configure_shortcuts.png', + category=UiStrings.Settings) self.DisplayTagItem = icon_action(mainWindow, - u'DisplayTagItem', u':/system/tag_editor.png', category=u'Settings') + u'DisplayTagItem', u':/system/tag_editor.png', + category=UiStrings.Settings) self.SettingsConfigureItem = icon_action(mainWindow, u'SettingsConfigureItem', u':/system/system_settings.png', - category=u'Settings') + category=UiStrings.Settings) self.HelpDocumentationItem = icon_action(mainWindow, u'HelpDocumentationItem', u':/system/system_help_contents.png', - category=None)#u'Help') + category=None)#UiStrings.Help) self.HelpDocumentationItem.setEnabled(False) self.HelpAboutItem = shortcut_action(mainWindow, u'HelpAboutItem', [QtGui.QKeySequence(u'Ctrl+F1')], self.onHelpAboutItemClicked, - u':/system/system_about.png', category=u'Help') + u':/system/system_about.png', category=UiStrings.Help) self.HelpOnlineHelpItem = base_action( - mainWindow, u'HelpOnlineHelpItem', category=u'Help') + mainWindow, u'HelpOnlineHelpItem', category=UiStrings.Help) self.helpWebSiteItem = base_action( - mainWindow, u'helpWebSiteItem', category=u'Help') + mainWindow, u'helpWebSiteItem', category=UiStrings.Help) add_actions(self.FileImportMenu, (self.ImportThemeItem, self.ImportLanguageItem)) add_actions(self.FileExportMenu, diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 8d6062ab1..9daa1d8a2 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -167,7 +167,8 @@ class ServiceManager(QtGui.QWidget): 'Move item to the top of the service.'), self.onServiceTop, shortcuts=[QtCore.Qt.Key_Home]) self.serviceManagerList.moveTop.setObjectName(u'moveTop') - ActionList.add_action(self.serviceManagerList.moveTop, u'Service') + ActionList.add_action( + self.serviceManagerList.moveTop, UiStrings.Service) self.serviceManagerList.moveUp = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move &up'), u':/services/service_up.png', @@ -175,7 +176,7 @@ class ServiceManager(QtGui.QWidget): 'Move item up one position in the service.'), self.onServiceUp, shortcuts=[QtCore.Qt.Key_PageUp]) self.serviceManagerList.moveUp.setObjectName(u'moveUp') - ActionList.add_action(self.serviceManagerList.moveUp, u'Service') + ActionList.add_action(self.serviceManagerList.moveUp, UiStrings.Service) self.serviceManagerList.moveDown = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move &down'), u':/services/service_down.png', @@ -183,7 +184,8 @@ class ServiceManager(QtGui.QWidget): 'Move item down one position in the service.'), self.onServiceDown, shortcuts=[QtCore.Qt.Key_PageDown]) self.serviceManagerList.moveDown.setObjectName(u'moveDown') - ActionList.add_action(self.serviceManagerList.moveDown, u'Service') + ActionList.add_action( + self.serviceManagerList.moveDown, UiStrings.Service) self.serviceManagerList.moveBottom = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move to &bottom'), u':/services/service_bottom.png', @@ -191,7 +193,8 @@ class ServiceManager(QtGui.QWidget): 'Move item to the end of the service.'), self.onServiceEnd, shortcuts=[QtCore.Qt.Key_End]) self.serviceManagerList.moveBottom.setObjectName(u'moveBottom') - ActionList.add_action(self.serviceManagerList.moveBottom, u'Service') + ActionList.add_action( + self.serviceManagerList.moveBottom, UiStrings.Service) self.serviceManagerList.down = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move &down'), None, @@ -199,7 +202,7 @@ class ServiceManager(QtGui.QWidget): 'Moves the selection down the window.'), self.onMoveSelectionDown, shortcuts=[QtCore.Qt.Key_Down]) self.serviceManagerList.down.setObjectName(u'down') - ActionList.add_action(self.serviceManagerList.down, u'Service') + ActionList.add_action(self.serviceManagerList.down, UiStrings.Service) self.serviceManagerList.down.setVisible(False) self.serviceManagerList.up = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move up'), @@ -208,7 +211,7 @@ class ServiceManager(QtGui.QWidget): 'Moves the selection up the window.'), self.onMoveSelectionUp, shortcuts=[QtCore.Qt.Key_Up]) self.serviceManagerList.up.setObjectName(u'up') - ActionList.add_action(self.serviceManagerList.up, u'Service') + ActionList.add_action(self.serviceManagerList.up, UiStrings.Service) self.serviceManagerList.up.setVisible(False) self.orderToolbar.addSeparator() self.serviceManagerList.delete = self.orderToolbar.addToolbarButton( @@ -225,7 +228,7 @@ class ServiceManager(QtGui.QWidget): 'Expand all the service items.'), self.onExpandAll, shortcuts=[QtCore.Qt.Key_Plus]) self.serviceManagerList.expand.setObjectName(u'expand') - ActionList.add_action(self.serviceManagerList.expand, u'Service') + ActionList.add_action(self.serviceManagerList.expand, UiStrings.Service) self.serviceManagerList.collapse = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', '&Collapse all'), u':/services/service_collapse_all.png', @@ -233,7 +236,8 @@ class ServiceManager(QtGui.QWidget): 'Collapse all the service items.'), self.onCollapseAll, shortcuts=[QtCore.Qt.Key_Minus]) self.serviceManagerList.collapse.setObjectName(u'collapse') - ActionList.add_action(self.serviceManagerList.collapse, u'Service') + ActionList.add_action( + self.serviceManagerList.collapse, UiStrings.Service) self.orderToolbar.addSeparator() self.serviceManagerList.makeLive = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Go Live'), @@ -242,7 +246,8 @@ class ServiceManager(QtGui.QWidget): 'Send the selected item to Live.'), self.makeLive, shortcuts=[QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return]) self.serviceManagerList.makeLive.setObjectName(u'orderToolbar') - ActionList.add_action(self.serviceManagerList.makeLive, u'Service') + ActionList.add_action( + self.serviceManagerList.makeLive, UiStrings.Service) self.layout.addWidget(self.orderToolbar) # Connect up our signals and slots QtCore.QObject.connect(self.themeComboBox, diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 3fe27f4d9..af94eb425 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -161,18 +161,18 @@ class SlideController(QtGui.QWidget): translate('OpenLP.SlideController', 'Hide'), self.toolbar)) self.blankScreen = shortcut_action(self.hideMenu, u'blankScreen', [QtCore.Qt.Key_Period], self.onBlankDisplay, - u':/slides/slide_blank.png', False, u'Live Toolbar') + u':/slides/slide_blank.png', False, UiStrings.LiveToolbar) self.blankScreen.setText( translate('OpenLP.SlideController', 'Blank Screen')) self.themeScreen = shortcut_action(self.hideMenu, u'themeScreen', [QtGui.QKeySequence(u'T')], self.onThemeDisplay, - u':/slides/slide_theme.png', False, u'Live Toolbar') + u':/slides/slide_theme.png', False, UiStrings.LiveToolbar) self.themeScreen.setText( translate('OpenLP.SlideController', 'Blank to Theme')) self.desktopScreen = shortcut_action(self.hideMenu, u'desktopScreen', [QtGui.QKeySequence(u'D')], self.onHideDisplay, u':/slides/slide_desktop.png', False, - u'Live Toolbar') + UiStrings.LiveToolbar) self.desktopScreen.setText( translate('OpenLP.SlideController', 'Show Desktop')) self.hideMenu.setDefaultAction(self.blankScreen) @@ -367,26 +367,26 @@ class SlideController(QtGui.QWidget): def setPreviewHotkeys(self, parent=None): self.previousItem.setObjectName(u'previousItemPreview') self.nextItem.setObjectName(u'nextItemPreview') - ActionList.add_action(self.previousItem, u'Preview Toolbar') - ActionList.add_action(self.nextItem, u'Preview Toolbar') + ActionList.add_action(self.previousItem, UiStrings.PreviewToolbar) + ActionList.add_action(self.nextItem, UiStrings.PreviewToolbar) def setLiveHotkeys(self, parent=None): self.previousItem.setObjectName(u'previousItemLive') self.nextItem.setObjectName(u'nextItemLive') - ActionList.add_action(self.previousItem, u'Live Toolbar') - ActionList.add_action(self.nextItem, u'Live Toolbar') + ActionList.add_action(self.previousItem, UiStrings.LiveToolbar) + ActionList.add_action(self.nextItem, UiStrings.LiveToolbar) self.previousService = shortcut_action(parent, u'previousService', - [QtCore.Qt.Key_Left], self.servicePrevious, u'Live Toolbar') + [QtCore.Qt.Key_Left], self.servicePrevious, UiStrings.LiveToolbar) self.previousService.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) self.previousService.setText( translate('OpenLP.SlideController', 'Previous Service')) self.nextService = shortcut_action(parent, 'nextService', - [QtCore.Qt.Key_Right], self.serviceNext, u'Live Toolbar') + [QtCore.Qt.Key_Right], self.serviceNext, UiStrings.LiveToolbar) self.nextService.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) self.nextService.setText( translate('OpenLP.SlideController', 'Next Service')) self.escapeItem = shortcut_action(parent, 'escapeItem', - [QtCore.Qt.Key_Escape], self.liveEscape, u'Live Toolbar') + [QtCore.Qt.Key_Escape], self.liveEscape, UiStrings.LiveToolbar) self.escapeItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) self.escapeItem.setText( translate('OpenLP.SlideController', 'Escape Item')) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 7421db30e..04cb1f091 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -190,6 +190,7 @@ class ActionList(object): @staticmethod def add_action(action, category, weight=None): + category = unicode(category) if category not in ActionList.categories: ActionList.categories.append(category) action.defaultShortcuts = action.shortcuts() @@ -208,6 +209,7 @@ class ActionList(object): @staticmethod def remove_action(action, category): + category = unicode(category) if category not in ActionList.categories: return ActionList.categories[category].actions.remove(action) diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index 45e1969f4..d564a9754 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -30,7 +30,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, StringContent, build_icon, translate from openlp.core.lib.db import Manager -from openlp.core.lib.ui import icon_action +from openlp.core.lib.ui import icon_action, UiStrings from openlp.core.utils.actions import ActionList from openlp.plugins.alerts.lib import AlertsManager, AlertsTab from openlp.plugins.alerts.lib.db import init_schema @@ -75,7 +75,7 @@ class AlertsPlugin(Plugin): log.info(u'Alerts Initialising') Plugin.initialise(self) self.toolsAlertItem.setVisible(True) - ActionList.add_action(self.toolsAlertItem, u'Tools') + ActionList.add_action(self.toolsAlertItem, UiStrings.Tools) self.liveController.alertTab = self.settings_tab def finalise(self): diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 1396d4550..60be388ba 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -29,7 +29,7 @@ import logging from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, StringContent, build_icon, translate -from openlp.core.lib.ui import base_action +from openlp.core.lib.ui import base_action, UiStrings from openlp.core.utils.actions import ActionList from openlp.plugins.bibles.lib import BibleManager, BiblesTab, BibleMediaItem @@ -52,9 +52,9 @@ class BiblePlugin(Plugin): self.manager = BibleManager(self) Plugin.initialise(self) self.importBibleItem.setVisible(True) - ActionList.add_action(self.importBibleItem, u'Import') + ActionList.add_action(self.importBibleItem, UiStrings.Import) # Do not add the action to the list yet. - #ActionList.add_action(self.exportBibleItem, u'Export') + #ActionList.add_action(self.exportBibleItem, UiStrings.Export) # Set to invisible until we can export bibles self.exportBibleItem.setVisible(False) @@ -65,7 +65,9 @@ class BiblePlugin(Plugin): log.info(u'Plugin Finalise') self.manager.finalise() Plugin.finalise(self) + ActionList.remove_action(self.importBibleItem, UiStrings.Import) self.importBibleItem.setVisible(False) + #ActionList.remove_action(self.exportBibleItem, UiStrings.Export) self.exportBibleItem.setVisible(False) def addImportMenuItem(self, import_menu): diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index de0714d5e..a3230ff7f 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -66,9 +66,9 @@ class SongsPlugin(Plugin): log.info(u'Songs Initialising') Plugin.initialise(self) self.toolsReindexItem.setVisible(True) - ActionList.add_action(self.SongImportItem, u'Import') - ActionList.add_action(self.SongExportItem, u'Export') - ActionList.add_action(self.toolsReindexItem, u'Tools') + ActionList.add_action(self.SongImportItem, UiStrings.Import) + ActionList.add_action(self.SongExportItem, UiStrings.Export) + ActionList.add_action(self.toolsReindexItem, UiStrings.Tools) self.mediaItem.displayResultsSong( self.manager.get_all_objects(Song, order_by_ref=Song.search_title)) @@ -258,7 +258,7 @@ class SongsPlugin(Plugin): log.info(u'Songs Finalising') self.manager.finalise() self.toolsReindexItem.setVisible(False) - ActionList.remove_action(self.SongImportItem, u'Import') - ActionList.remove_action(self.SongExportItem, u'Export') - ActionList.remove_action(self.toolsReindexItem, u'Tools') + ActionList.remove_action(self.SongImportItem, UiStrings.Import) + ActionList.remove_action(self.SongExportItem, UiStrings.Export) + ActionList.remove_action(self.toolsReindexItem, UiStrings.Tools) Plugin.finalise(self) diff --git a/openlp/plugins/songusage/songusageplugin.py b/openlp/plugins/songusage/songusageplugin.py index 2e55a23c0..b0a545ad8 100644 --- a/openlp/plugins/songusage/songusageplugin.py +++ b/openlp/plugins/songusage/songusageplugin.py @@ -32,7 +32,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Plugin, StringContent, Receiver, build_icon, \ translate from openlp.core.lib.db import Manager -from openlp.core.lib.ui import base_action, shortcut_action +from openlp.core.lib.ui import base_action, shortcut_action, UiStrings from openlp.core.utils.actions import ActionList from openlp.plugins.songusage.forms import SongUsageDetailForm, \ SongUsageDeleteForm @@ -110,9 +110,12 @@ class SongUsagePlugin(Plugin): self.settingsSection + u'/active', QtCore.QVariant(False)).toBool() self.SongUsageStatus.setChecked(self.SongUsageActive) - ActionList.add_action(self.SongUsageDelete, u'Song Usage') - ActionList.add_action(self.SongUsageReport, u'Song Usage') - ActionList.add_action(self.SongUsageStatus, u'Song Usage') + ActionList.add_action(self.SongUsageDelete, + translate('SongUsagePlugin', 'Song Usage')) + ActionList.add_action(self.SongUsageReport, + translate('SongUsagePlugin', 'Song Usage')) + ActionList.add_action(self.SongUsageStatus, + translate('SongUsagePlugin', 'Song Usage')) if self.manager is None: self.manager = Manager(u'songusage', init_schema) self.SongUsagedeleteform = SongUsageDeleteForm(self.manager, @@ -128,9 +131,12 @@ class SongUsagePlugin(Plugin): self.manager.finalise() Plugin.finalise(self) self.SongUsageMenu.menuAction().setVisible(False) - ActionList.remove_action(self.SongUsageDelete, u'Song Usage') - ActionList.remove_action(self.SongUsageReport, u'Song Usage') - ActionList.remove_action(self.SongUsageStatus, u'Song Usage') + ActionList.remove_action(self.SongUsageDelete, + translate('SongUsagePlugin', 'Song Usage')) + ActionList.remove_action(self.SongUsageReport, + translate('SongUsagePlugin', 'Song Usage')) + ActionList.remove_action(self.SongUsageStatus, + translate('SongUsagePlugin', 'Song Usage')) #stop any events being processed self.SongUsageActive = False From 0a5b3b2c2d460f06eacd6bb3debbc17fc3b4f5fc Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 3 Apr 2011 15:24:51 +0200 Subject: [PATCH 24/98] --- openlp/core/lib/ui.py | 1 - openlp/core/ui/shortcutlistdialog.py | 17 +++++-- openlp/core/ui/shortcutlistform.py | 70 ++++++++++++++++++---------- 3 files changed, 59 insertions(+), 29 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 15309edc9..029bb34df 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -295,7 +295,6 @@ def shortcut_action(parent, name, shortcuts, function, icon=None, checked=None, if checked is not None: action.setCheckable(True) action.setChecked(checked) - action.setCheckable(True) action.setShortcuts(shortcuts) action.setShortcutContext(QtCore.Qt.WindowShortcut) if category is not None: diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index a4bcc359e..1004014d6 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -34,6 +34,9 @@ class Ui_ShortcutListDialog(object): shortcutListDialog.setObjectName(u'shortcutListDialog') self.dialogLayout = QtGui.QVBoxLayout(shortcutListDialog) self.dialogLayout.setObjectName(u'dialogLayout') + self.descriptionLabel = QtGui.QLabel(shortcutListDialog); + self.descriptionLabel.setObjectName(u'descriptionLabel') + self.dialogLayout.addWidget(self.descriptionLabel) self.treeWidget = QtGui.QTreeWidget(shortcutListDialog) self.treeWidget.setAlternatingRowColors(True) self.treeWidget.setObjectName(u'treeWidget') @@ -57,8 +60,8 @@ class Ui_ShortcutListDialog(object): self.customLayout.addStretch() self.dialogLayout.addLayout(self.customLayout) self.buttonBox = QtGui.QDialogButtonBox(shortcutListDialog) - self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok | - QtGui.QDialogButtonBox.RestoreDefaults) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel | + QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.RestoreDefaults) self.buttonBox.setObjectName(u'buttonBox') self.dialogLayout.addWidget(self.buttonBox) self.retranslateUi(shortcutListDialog) @@ -71,9 +74,15 @@ class Ui_ShortcutListDialog(object): def retranslateUi(self, shortcutListDialog): shortcutListDialog.setWindowTitle( translate('OpenLP.ShortcutListDialog', 'Customize Shortcuts')) + self.descriptionLabel.setText(translate('OpenLP.ShortcutListDialog', + 'Select an action and click the button below to start capturing ' + 'a new shortcut.')) self.treeWidget.setHeaderLabels([ translate('OpenLP.ShortcutListDialog', 'Action'), translate('OpenLP.ShortcutListDialog', 'Shortcut'), translate('OpenLP.ShortcutListDialog', 'Alternate')]) - self.shortcutButton.setText( - translate('OpenLP.ShortcutListDialog', 'None')) + self.shortcutButton.setToolTip( + translate('OpenLP.ShortcutListDialog', 'Capture shortcut.')) + self.clearShortcutButton.setToolTip( + translate('OpenLP.ShortcutListDialog', + 'Restore the default shortcut(s) of this action.')) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 84aea006a..58b42a351 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -46,6 +46,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): QtGui.QDialog.__init__(self, parent) self.setupUi(self) self.column = -1 + self.changedActions = {} self.shortcutButton.setText(u'') self.shortcutButton.setEnabled(False) QtCore.QObject.connect(self.shortcutButton, @@ -63,7 +64,11 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.onRestoreDefaultsClicked) def keyPressEvent(self, event): - event.ignore() + if self.shortcutButton.isChecked(): + event.ignore() + elif event.key() == QtCore.Qt.Key_Escape: + event.accept() + self.close() def keyReleaseEvent(self, event): Qt = QtCore.Qt @@ -87,7 +92,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): shortcut_valid = True for category in ActionList.categories: for action in category.actions: - shortcuts = action.shortcuts() + shortcuts = self._actionShortcuts(action) if key_sequence not in shortcuts: continue if action is changing_action: @@ -116,6 +121,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.shortcutButton.setChecked(False) def exec_(self): + self.changedActions = {} self.reloadShortcutList() self.shortcutButton.setChecked(False) self.shortcutButton.setEnabled(False) @@ -152,15 +158,16 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): action = item.data(0, QtCore.Qt.UserRole).toPyObject() if action is None: continue - if len(action.shortcuts()) == 0: + shortcuts = self._actionShortcuts(action) + if len(shortcuts) == 0: item.setText(1, u'') item.setText(2, u'') - elif len(action.shortcuts()) == 1: - item.setText(1, action.shortcuts()[0].toString()) + elif len(shortcuts) == 1: + item.setText(1, shortcuts[0].toString()) item.setText(2, u'') else: - item.setText(1, action.shortcuts()[0].toString()) - item.setText(2, action.shortcuts()[1].toString()) + item.setText(1, shortcuts[0].toString()) + item.setText(2, shortcuts[1].toString()) def onShortcutButtonClicked(self, toggled): """ @@ -174,20 +181,21 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): action = item.data(0, QtCore.Qt.UserRole).toPyObject() if action is None: return - shortcuts = [] + shortcuts = self._actionShortcuts(action) + new_shortcuts = [] # We are changing the primary shortcut. - if self.column == 1: - shortcuts.append(QtGui.QKeySequence(self.shortcutButton.text())) - if len(action.shortcuts()) == 2: - shortcuts.append(action.shortcuts()[1]) + if self.column in [0, 1]: + new_shortcuts.append(QtGui.QKeySequence(self.shortcutButton.text())) + if len(shortcuts) == 2: + new_shortcuts.append(shortcuts[1]) # We are changing the secondary shortcut. elif self.column == 2: - if len(action.shortcuts()) != 0: - shortcuts.append(action.shortcuts()[0]) - shortcuts.append(QtGui.QKeySequence(self.shortcutButton.text())) + if len(shortcuts) != 0: + new_shortcuts.append(shortcuts[0]) + new_shortcuts.append(QtGui.QKeySequence(self.shortcutButton.text())) else: return - action.setShortcuts(shortcuts) + self.changedActions[action] = new_shortcuts self.refreshShortcutList() def onItemDoubleClicked(self, item, column): @@ -209,15 +217,17 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): """ self.column = column action = item.data(0, QtCore.Qt.UserRole).toPyObject() - self.shortcutButton.setEnabled(True) text = u'' - if action is None or column not in [1, 2]: + if action is None:# or column not in [1, 2]: self.shortcutButton.setChecked(False) self.shortcutButton.setEnabled(False) - elif column == 1 and len(action.shortcuts()) != 0: - text = action.shortcuts()[0].toString() - elif len(action.shortcuts()) == 2 and len(action.shortcuts()) != 0: - text = action.shortcuts()[1].toString() + else: + self.shortcutButton.setEnabled(True) + shortcuts = self._actionShortcuts(action) + if column != 2 and len(shortcuts) != 0: + text = shortcuts[0].toString() + elif len(shortcuts) == 2: + text = shortcuts[1].toString() self.shortcutButton.setText(text) def onClearShortcutButtonClicked(self, toggled): @@ -231,7 +241,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): action = item.data(0, QtCore.Qt.UserRole).toPyObject() if action is None: return - action.setShortcuts(action.defaultShortcuts) + self.changedActions[action] = action.defaultShortcuts self.refreshShortcutList() self.onItemPressed(item, self.column) @@ -252,7 +262,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.shortcutButton.setText(u'') for category in ActionList.categories: for action in category.actions: - action.setShortcuts(action.defaultShortcuts) + self.changedActions[action] = action.defaultShortcuts self.refreshShortcutList() def save(self): @@ -264,6 +274,18 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): settings.beginGroup(u'shortcuts') for category in ActionList.categories: for action in category.actions: + if self.changedActions .has_key(action): + action.setShortcuts(self.changedActions[action]) settings.setValue( action.objectName(), QtCore.QVariant(action.shortcuts())) settings.endGroup() + + def _actionShortcuts(self, action): + """ + This returns the shortcuts for the given ``action``, which also includes + those shortcuts which are not yet assigned to an action (as changes are + applied when closing the dialog). + """ + if self.changedActions.has_key(action): + return self.changedActions[action] + return action.shortcuts() From 65f134ab1b6ce8993359b2346a8fe1d5c7d46c06 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 3 Apr 2011 15:36:59 +0200 Subject: [PATCH 25/98] removed fix (I'll deal with this in another branch) --- openlp/core/ui/slidecontroller.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index af94eb425..66c26da3b 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -1108,11 +1108,11 @@ class SlideController(QtGui.QWidget): screen hide attributes """ blank = None - if self.blankScreen.isChecked(): + if self.blankScreen.isChecked: blank = self.blankScreen - if self.themeScreen.isChecked(): + if self.themeScreen.isChecked: blank = self.themeScreen - if self.desktopScreen.isChecked(): + if self.desktopScreen.isChecked: blank = self.desktopScreen if blank: blank.setChecked(False) From dfc2eed1f4206fd57529351574188445c85c3317 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 3 Apr 2011 16:20:55 +0200 Subject: [PATCH 26/98] adapted latest merge --- openlp/core/lib/__init__.py | 4 +++- openlp/core/ui/servicemanager.py | 4 ++-- openlp/core/ui/shortcutlistform.py | 8 +++++++- openlp/core/utils/actions.py | 30 ++++++++++++++++++++++++++---- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index e62cb3e44..0f0fcca5e 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -186,8 +186,9 @@ def context_menu_action(base, icon, text, slot, shortcuts=None): if icon: action.setIcon(build_icon(icon)) QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), slot) - if shortcuts: + if shortcuts is not None: action.setShortcuts(shortcuts) + ActionList.add_action(action) return action def context_menu(base, icon, text): @@ -343,3 +344,4 @@ from dockwidget import OpenLPDockWidget from renderer import Renderer from rendermanager import RenderManager from mediamanageritem import MediaManagerItem +from openlp.core.utils.actions import ActionList diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 3ea3f1a6b..fc20d0f2d 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -202,7 +202,7 @@ class ServiceManager(QtGui.QWidget): 'Moves the selection down the window.'), self.onMoveSelectionDown, shortcuts=[QtCore.Qt.Key_Down]) self.serviceManagerList.down.setObjectName(u'down') - ActionList.add_action(self.serviceManagerList.down, UiStrings.Service) + ActionList.add_action(self.serviceManagerList.down) self.serviceManagerList.down.setVisible(False) self.serviceManagerList.up = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move up'), @@ -211,7 +211,7 @@ class ServiceManager(QtGui.QWidget): 'Moves the selection up the window.'), self.onMoveSelectionUp, shortcuts=[QtCore.Qt.Key_Up]) self.serviceManagerList.up.setObjectName(u'up') - ActionList.add_action(self.serviceManagerList.up, UiStrings.Service) + ActionList.add_action(self.serviceManagerList.up) self.serviceManagerList.up.setVisible(False) self.orderToolbar.addSeparator() self.serviceManagerList.delete = self.orderToolbar.addToolbarButton( diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 58b42a351..5251ac314 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -134,6 +134,9 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): """ self.treeWidget.clear() for category in ActionList.categories: + # Check if the category is for internal use only. + if category.name is None: + continue item = QtGui.QTreeWidgetItem([category.name]) for action in category.actions: actionText = REMOVE_AMPERSAND.sub('', unicode(action.text())) @@ -218,7 +221,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.column = column action = item.data(0, QtCore.Qt.UserRole).toPyObject() text = u'' - if action is None:# or column not in [1, 2]: + if action is None: self.shortcutButton.setChecked(False) self.shortcutButton.setEnabled(False) else: @@ -273,6 +276,9 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): settings = QtCore.QSettings() settings.beginGroup(u'shortcuts') for category in ActionList.categories: + # Check if the category is for internal use only. + if category.name is None: + continue for action in category.actions: if self.changedActions .has_key(action): action.setShortcuts(self.changedActions[action]) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 04cb1f091..efac7b1bd 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -189,8 +189,26 @@ class ActionList(object): categories = CategoryList() @staticmethod - def add_action(action, category, weight=None): - category = unicode(category) + def add_action(action, category=None, weight=None): + """ + Add an action to the list of actions. + + ``action`` + The action to add (QAction). + + ``category`` + The category this action belongs to. The category can be a QString + or python unicode string. **Note**, if the category is ``None``, the + category and its actions are being hidden in the shortcut dialog. + However, if they are added, it is possible to avoid assigning + shortcuts twice, which is important. + + ``weight`` + The weight specifies how important a category is. However, this only + has an impact on the order the categories are displayed. + """ + if category is not None: + category = unicode(category) if category not in ActionList.categories: ActionList.categories.append(category) action.defaultShortcuts = action.shortcuts() @@ -198,6 +216,9 @@ class ActionList(object): ActionList.categories[category].actions.append(action) else: ActionList.categories[category].actions.add(action, weight) + if category is None: + # Stop here, as this action is not configurable. + return # Load the shortcut from the config. settings = QtCore.QSettings() settings.beginGroup(u'shortcuts') @@ -208,8 +229,9 @@ class ActionList(object): settings.endGroup() @staticmethod - def remove_action(action, category): - category = unicode(category) + def remove_action(action, category=None): + if category is not None: + category = unicode(category) if category not in ActionList.categories: return ActionList.categories[category].actions.remove(action) From 84605fb43054fadcd4c9690147bf8b62f9f8d9bd Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 4 Apr 2011 16:38:17 +0200 Subject: [PATCH 27/98] fixed wrong context of shortcuts; moved functions --- openlp/core/lib/__init__.py | 53 --------------------- openlp/core/lib/mediamanageritem.py | 23 +++++---- openlp/core/lib/ui.py | 72 +++++++++++++++++++++++++++-- openlp/core/ui/servicemanager.py | 8 ++-- 4 files changed, 85 insertions(+), 71 deletions(-) diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 0f0fcca5e..491f3e652 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -166,59 +166,6 @@ def build_icon(icon): QtGui.QIcon.Normal, QtGui.QIcon.Off) return button_icon -def context_menu_action(base, icon, text, slot, shortcuts=None): - """ - Utility method to help build context menus for plugins - - ``base`` - The parent menu to add this menu item to - - ``icon`` - An icon for this action - - ``text`` - The text to display for this action - - ``slot`` - The code to run when this action is triggered - """ - action = QtGui.QAction(text, base) - if icon: - action.setIcon(build_icon(icon)) - QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), slot) - if shortcuts is not None: - action.setShortcuts(shortcuts) - ActionList.add_action(action) - return action - -def context_menu(base, icon, text): - """ - Utility method to help build context menus for plugins - - ``base`` - The parent object to add this menu to - - ``icon`` - An icon for this menu - - ``text`` - The text to display for this menu - """ - action = QtGui.QMenu(text, base) - action.setIcon(build_icon(icon)) - return action - -def context_menu_separator(base): - """ - Add a separator to a context menu - - ``base`` - The menu object to add the separator to - """ - action = QtGui.QAction(u'', base) - action.setSeparator(True) - return action - def image_to_byte(image): """ Resize an image to fit on the current screen for the web and returns diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 63132b141..13277876d 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -31,10 +31,10 @@ import os from PyQt4 import QtCore, QtGui -from openlp.core.lib import context_menu_action, context_menu_separator, \ - SettingsManager, OpenLPToolbar, ServiceItem, StringContent, build_icon, \ - translate, Receiver, ListWidgetWithDnD -from openlp.core.lib.ui import UiStrings +from openlp.core.lib import SettingsManager, OpenLPToolbar, ServiceItem, \ + StringContent, build_icon, translate, Receiver, ListWidgetWithDnD +from openlp.core.lib.ui import UiStrings, context_menu_action, \ + context_menu_separator log = logging.getLogger(__name__) @@ -260,39 +260,42 @@ class MediaManagerItem(QtGui.QWidget): context_menu_action( self.listView, u':/general/general_edit.png', self.plugin.getString(StringContent.Edit)[u'title'], - self.onEditClick)) + self.onEditClick, context=QtCore.Qt.WidgetShortcut)) self.listView.addAction(context_menu_separator(self.listView)) if self.hasDeleteIcon: self.listView.addAction( context_menu_action( self.listView, u':/general/general_delete.png', self.plugin.getString(StringContent.Delete)[u'title'], - self.onDeleteClick, [QtCore.Qt.Key_Delete])) + self.onDeleteClick, [QtCore.Qt.Key_Delete], + context=QtCore.Qt.WidgetShortcut)) self.listView.addAction(context_menu_separator(self.listView)) self.listView.addAction( context_menu_action( self.listView, u':/general/general_preview.png', self.plugin.getString(StringContent.Preview)[u'title'], - self.onPreviewClick, [QtCore.Qt.Key_Enter])) + self.onPreviewClick, [QtCore.Qt.Key_Enter], + context=QtCore.Qt.WidgetShortcut)) self.listView.addAction( context_menu_action( self.listView, u':/general/general_live.png', self.plugin.getString(StringContent.Live)[u'title'], self.onLiveClick, [QtCore.Qt.ShiftModifier + \ QtCore.Qt.Key_Enter, QtCore.Qt.ShiftModifier + \ - QtCore.Qt.Key_Return])) + QtCore.Qt.Key_Return], context=QtCore.Qt.WidgetShortcut)) self.listView.addAction( context_menu_action( self.listView, u':/general/general_add.png', self.plugin.getString(StringContent.Service)[u'title'], - self.onAddClick, [QtCore.Qt.Key_Plus, QtCore.Qt.Key_Equal])) + self.onAddClick, [QtCore.Qt.Key_Plus, QtCore.Qt.Key_Equal], + context=QtCore.Qt.WidgetShortcut)) if self.addToServiceItem: self.listView.addAction( context_menu_action( self.listView, u':/general/general_add.png', translate('OpenLP.MediaManagerItem', '&Add to selected Service Item'), - self.onAddEditClick)) + self.onAddEditClick, context=QtCore.Qt.WidgetShortcut)) QtCore.QObject.connect(self.listView, QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.onClickPressed) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 029bb34df..35ea23497 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -284,7 +284,7 @@ def icon_action(parent, name, icon, checked=None, category=None): return action def shortcut_action(parent, name, shortcuts, function, icon=None, checked=None, - category=None): + category=None, context=QtCore.Qt.WindowShortcut): """ Return a shortcut enabled action. """ @@ -296,12 +296,76 @@ def shortcut_action(parent, name, shortcuts, function, icon=None, checked=None, action.setCheckable(True) action.setChecked(checked) action.setShortcuts(shortcuts) - action.setShortcutContext(QtCore.Qt.WindowShortcut) - if category is not None: - ActionList.add_action(action, category) + action.setShortcutContext(context) + ActionList.add_action(action, category) QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), function) return action +def context_menu_action(base, icon, text, slot, shortcuts=None, category=None, + context=QtCore.Qt.WindowShortcut): + """ + Utility method to help build context menus for plugins + + ``base`` + The parent menu to add this menu item to + + ``icon`` + An icon for this action + + ``text`` + The text to display for this action + + ``slot`` + The code to run when this action is triggered + + ``shortcuts`` + The action's shortcuts. + + ``category`` + The category the shortcut should be listed in the shortcut dialog. If + left to None, then the action will be hidden in the shortcut dialog. + + ``context`` + The context the shortcut is valid. + """ + action = QtGui.QAction(text, base) + if icon: + action.setIcon(build_icon(icon)) + QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), slot) + if shortcuts is not None: + action.setShortcuts(shortcuts) + action.setShortcutContext(context) + ActionList.add_action(action) + return action + +def context_menu(base, icon, text): + """ + Utility method to help build context menus for plugins + + ``base`` + The parent object to add this menu to + + ``icon`` + An icon for this menu + + ``text`` + The text to display for this menu + """ + action = QtGui.QMenu(text, base) + action.setIcon(build_icon(icon)) + return action + +def context_menu_separator(base): + """ + Add a separator to a context menu + + ``base`` + The menu object to add the separator to + """ + action = QtGui.QAction(u'', base) + action.setSeparator(True) + return action + def add_widget_completer(cache, widget): """ Adds a text autocompleter to a widget. diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index fc20d0f2d..caecaca50 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -32,10 +32,10 @@ log = logging.getLogger(__name__) from PyQt4 import QtCore, QtGui -from openlp.core.lib import OpenLPToolbar, ServiceItem, context_menu_action, \ - Receiver, build_icon, ItemCapabilities, SettingsManager, translate +from openlp.core.lib import OpenLPToolbar, ServiceItem, Receiver, build_icon, \ + ItemCapabilities, SettingsManager, translate from openlp.core.lib.theme import ThemeLevel -from openlp.core.lib.ui import UiStrings, critical_error_message_box +from openlp.core.lib.ui import UiStrings, critical_error_message_box, context_menu_action from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm from openlp.core.ui.printserviceform import PrintServiceForm from openlp.core.utils import AppLocation, delete_file, file_is_unicode, \ @@ -1267,7 +1267,7 @@ class ServiceManager(QtGui.QWidget): for theme in theme_list: self.themeComboBox.addItem(theme) action = context_menu_action(self.serviceManagerList, None, theme, - self.onThemeChangeAction) + self.onThemeChangeAction, context=QtCore.Qt.WidgetShortcut) self.themeMenu.addAction(action) index = self.themeComboBox.findText(self.service_theme, QtCore.Qt.MatchExactly) From 4a6be382ff3689cd52da8c330536293055ec5be5 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 4 Apr 2011 18:15:38 +0200 Subject: [PATCH 28/98] fixed empty texts --- openlp/core/lib/searchedit.py | 3 ++- openlp/core/ui/mainwindow.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/openlp/core/lib/searchedit.py b/openlp/core/lib/searchedit.py index 41699256f..d32961ef9 100644 --- a/openlp/core/lib/searchedit.py +++ b/openlp/core/lib/searchedit.py @@ -133,7 +133,8 @@ class SearchEdit(QtGui.QLineEdit): menu = QtGui.QMenu(self) first = None for identifier, icon, title in items: - action = icon_action(menu, title, icon) + action = icon_action(menu, u'', icon) + action.setText(title) action.setData(QtCore.QVariant(identifier)) menu.addAction(action) QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered(bool)'), diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index bb6d105b9..ab712a57d 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -972,8 +972,9 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.FileMenu.addSeparator() for fileId, filename in enumerate(recentFilesToDisplay): log.debug('Recent file name: %s', filename) - action = base_action(self, u'&%d %s' % (fileId + 1, - QtCore.QFileInfo(filename).fileName())) + action = base_action(self, u'') + action.setText(u'&%d %s' % + (fileId + 1, QtCore.QFileInfo(filename).fileName())) action.setData(QtCore.QVariant(filename)) self.connect(action, QtCore.SIGNAL(u'triggered()'), self.ServiceManagerContents.onRecentServiceClicked) From 38c939362455d330663da881762f6b94198ea3bf Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 4 Apr 2011 19:33:42 +0200 Subject: [PATCH 29/98] expand categories --- openlp/core/ui/shortcutlistform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 5251ac314..a99b7c3bb 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -145,8 +145,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): actionItem.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(action)) item.addChild(actionItem) - item.setExpanded(True) self.treeWidget.addTopLevelItem(item) + item.setExpanded(True) self.refreshShortcutList() def refreshShortcutList(self): From 3bfe9ec77c8e430018d2dcafa34b20ff1eb1e9aa Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 6 Apr 2011 21:14:02 +0200 Subject: [PATCH 30/98] --- openlp/core/ui/printservicedialog.py | 4 + openlp/core/ui/printserviceform.py | 107 ++++++++++++++++++--------- 2 files changed, 77 insertions(+), 34 deletions(-) diff --git a/openlp/core/ui/printservicedialog.py b/openlp/core/ui/printservicedialog.py index 97f4b4060..b669c83ec 100644 --- a/openlp/core/ui/printservicedialog.py +++ b/openlp/core/ui/printservicedialog.py @@ -132,6 +132,8 @@ class Ui_PrintServiceDialog(object): self.groupLayout = QtGui.QVBoxLayout() self.slideTextCheckBox = QtGui.QCheckBox() self.groupLayout.addWidget(self.slideTextCheckBox) + self.pageBreakAfterText = QtGui.QCheckBox() + self.groupLayout.addWidget(self.pageBreakAfterText) self.notesCheckBox = QtGui.QCheckBox() self.groupLayout.addWidget(self.notesCheckBox) self.metaDataCheckBox = QtGui.QCheckBox() @@ -149,6 +151,8 @@ class Ui_PrintServiceDialog(object): printServiceDialog.setWindowTitle(UiStrings.PrintServiceOrder) self.slideTextCheckBox.setText(translate('OpenLP.PrintServiceForm', 'Include slide text if available')) + self.pageBreakAfterText.setText(translate('OpenLP.PrintServiceForm', + 'break')) self.notesCheckBox.setText(translate('OpenLP.PrintServiceForm', 'Include service item notes')) self.metaDataCheckBox.setText(translate('OpenLP.PrintServiceForm', diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index 4e0f018a4..03febc067 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -26,6 +26,7 @@ import datetime from PyQt4 import QtCore, QtGui +from lxml import html from openlp.core.lib import translate from openlp.core.lib.ui import UiStrings @@ -50,6 +51,10 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): settings.beginGroup(u'advanced') self.slideTextCheckBox.setChecked(settings.value( u'print slide text', QtCore.QVariant(False)).toBool()) + self.pageBreakAfterText.setChecked(settings.value( + u'enable page break', QtCore.QVariant(False)).toBool()) + if not self.slideTextCheckBox.isChecked(): + self.pageBreakAfterText.setDisabled(True) self.metaDataCheckBox.setChecked(settings.value( u'print file meta data', QtCore.QVariant(False)).toBool()) self.notesCheckBox.setChecked(settings.value( @@ -76,6 +81,9 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): QtCore.SIGNAL(u'triggered()'), self.copyText) QtCore.QObject.connect(self.htmlCopy, QtCore.SIGNAL(u'triggered()'), self.copyHtmlText) + QtCore.QObject.connect(self.slideTextCheckBox, + QtCore.SIGNAL(u'stateChanged(int)'), + self.onSlideTextCheckBoxChanged) self.updatePreviewText() def toggleOptions(self, checked): @@ -93,57 +101,80 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): """ Creates the html text and updates the html of *self.document*. """ - text = u'' - if self.titleLineEdit.text(): - text += u'

%s

' % unicode(self.titleLineEdit.text()) - for item in self.serviceManager.serviceItems: + html_data = html.fromstring( + u'%s' % unicode(self.titleLineEdit.text())) + html_data.append(html.Element(u'body')) + html_data.body.append(html.fromstring( + u'

%s

' % unicode(self.titleLineEdit.text()))) + for index, item in enumerate(self.serviceManager.serviceItems): item = item[u'service_item'] + div = html.Element(u'div') # Add the title of the service item. - text += u'

%s

' % (item.icon, - item.get_display_title()) - # Add slide text of the service item. + item_title = html.Element(u'h3') + icon = html.Element(u'img') + icon.set(u'src', item.icon) + item_title.append(icon) + item_title.append( + html.fromstring(u' %s' % item.get_display_title())) + div.append(item_title) if self.slideTextCheckBox.isChecked(): + # Add the text of the service item. if item.is_text(): - # Add the text of the service item. - verse = None + verse_def = None for slide in item.get_frames(): - if not verse: - text += u'

' + slide[u'html'] - verse = slide[u'verseTag'] - elif verse != slide[u'verseTag']: - text += u'<\p>

' + slide[u'html'] - verse = slide[u'verseTag'] + if not verse_def or verse_def != slide[u'verseTag']: + p = html.Element(u'p') + div.append(p) else: - text += u'
' + slide[u'html'] - text += u'

' + p.append(html.Element(u'br')) + p.append(html.fromstring( + u'%s' % slide[u'html'])) + verse_def = slide[u'verseTag'] + # Break the page before the div element. + if index != 0 and self.pageBreakAfterText.isChecked(): + div.set(u'style', u'page-break-before:always') + # Add the image names of the service item. elif item.is_image(): - # Add the image names of the service item. - text += u'
    ' + ol = html.Element(u'ol') for slide in range(len(item.get_frames())): - text += u'
  1. %s

  2. ' % \ - item.get_frame_title(slide) - text += u'
' + li = html.Element(u'li') + li.text = item.get_frame_title(slide) + ol.append(li) + div.append(ol) + # add footer if item.foot_text: - # add footer - text += u'

%s

' % item.foot_text + div.append(html.fromstring(item.foot_text)) # Add service items' notes. if self.notesCheckBox.isChecked(): if item.notes: - text += u'

%s

%s' % (translate( - 'OpenLP.ServiceManager', 'Notes:'), - item.notes.replace(u'\n', u'
')) + p = html.Element(u'p') + strong = html.Element(u'strong') + strong.text = unicode( + translate('OpenLP.ServiceManager', 'Notes:')) + p.append(strong) + p.append(html.fromstring(u'%s' % + item.notes.replace(u'\n', u'
'))) + div.append(p) # Add play length of media files. if item.is_media() and self.metaDataCheckBox.isChecked(): tme = item.media_length if item.end_time > 0: tme = item.end_time - item.start_time - text += u'

%s %s

' % (translate( - 'OpenLP.ServiceManager', u'Playing time:'), - unicode(datetime.timedelta(seconds=tme))) - if self.footerTextEdit.toPlainText(): - text += u'

%s

%s' % (translate('OpenLP.ServiceManager', - u'Custom Service Notes:'), self.footerTextEdit.toPlainText()) - self.document.setHtml(text) + p = html.fromstring(u'

%s

' % + translate('OpenLP.ServiceManager', 'Playing time:')) + p.append(html.fromstring(u'%s' % + unicode(datetime.timedelta(seconds=tme)))) + div.append(p) + html_data.body.append(div) + # Add the custom service notes: + if self.footerTextEdit.toPlainText(): + html_data.append(html.fromstring(u'

%

' % + translate('OpenLP.ServiceManager', u'Custom Service Notes:'))) + html_data.append(html.fromstring( + u'%s' % self.footerTextEdit.toPlainText())) + for div in html_data.body: + print len(div), html.tostring(div), u'\n' + self.document.setHtml(html.tostring(html_data)) self.previewWidget.updatePreview() def paintRequested(self, printer): @@ -232,6 +263,12 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): else: self.copyTextButton.setText(UiStrings.CopyToText) + def onSlideTextCheckBoxChanged(self, state): + """ + The ``slideTextCheckBox`` checkbox was either checked or unchecked. + """ + self.pageBreakAfterText.setDisabled(state == QtCore.Qt.Unchecked) + def saveOptions(self): """ Save the settings and close the dialog. @@ -241,6 +278,8 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): settings.beginGroup(u'advanced') settings.setValue(u'print slide text', QtCore.QVariant(self.slideTextCheckBox.isChecked())) + settings.setValue(u'enable page break', + QtCore.QVariant(self.pageBreakAfterText.isChecked())) settings.setValue(u'print file meta data', QtCore.QVariant(self.metaDataCheckBox.isChecked())) settings.setValue(u'print notes', From 9b04385f347bed26d50d61586679b10cf5185bf7 Mon Sep 17 00:00:00 2001 From: Jonathan Corwin Date: Wed, 6 Apr 2011 23:54:47 +0100 Subject: [PATCH 31/98] Blanking and switching between song and ppt and back --- openlp/core/ui/slidecontroller.py | 39 ++++++++----------- openlp/plugins/bibles/lib/mediaitem.py | 1 + .../presentations/lib/messagelistener.py | 18 ++++++--- 3 files changed, 30 insertions(+), 28 deletions(-) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 232653326..eec5d3a19 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -569,19 +569,18 @@ class SlideController(QtGui.QWidget): self.onStopLoop() # If old item was a command tell it to stop if self.serviceItem: - if self.serviceItem.is_command(): + oldItem = self.serviceItem + self.serviceItem = None + if oldItem.is_command(): Receiver.send_message(u'%s_stop' % - self.serviceItem.name.lower(), [serviceItem, self.isLive]) - if self.serviceItem.is_media(): + oldItem.name.lower(), [oldItem, self.isLive]) + if oldItem.is_media(): self.onMediaClose() - if self.isLive: - if serviceItem.is_capable(ItemCapabilities.ProvidesOwnDisplay): - self._forceUnblank() - blanked = self.blankScreen.isChecked() - else: - blanked = False + if self.isLive and oldItem.is_capable( + ItemCapabilities.ProvidesOwnDisplay): + self._resetBlank() Receiver.send_message(u'%s_start' % serviceItem.name.lower(), - [serviceItem, self.isLive, blanked, slideno]) + [serviceItem, self.isLive, self.display.hideMode, slideno]) self.slideList = {} width = self.parent.controlSplitter.sizes()[self.split] self.serviceItem = serviceItem @@ -1095,20 +1094,14 @@ class SlideController(QtGui.QWidget): self.slidePreview.clear() self.slidePreview.show() - def _forceUnblank(self): + def _resetBlank(self): """ Used by command items which provide their own displays to reset the screen hide attributes """ - blank = None - if self.blankScreen.isChecked: - blank = self.blankScreen - if self.themeScreen.isChecked: - blank = self.themeScreen - if self.desktopScreen.isChecked: - blank = self.desktopScreen - if blank: - blank.setChecked(False) - self.hideMenu.setDefaultAction(blank) - QtCore.QSettings().remove( - self.parent.generalSettingsSection + u'/screen blank') + if self.blankScreen.isChecked(): + self.onBlankDisplay(True) + elif self.themeScreen.isChecked(): + self.onThemeDisplay(True) + elif self.desktopScreen.isChecked(): + self.onHideDisplay(True) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index a9694fd0c..9c45b85f8 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -58,6 +58,7 @@ class BibleMediaItem(MediaManagerItem): MediaManagerItem.__init__(self, parent, plugin, icon) # Place to store the search results for both bibles. self.settings = self.parent.settings_tab + self.quickPreviewAllowed = True self.search_results = {} self.second_search_results = {} QtCore.QObject.connect(Receiver.get_receiver(), diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index b86219f42..728a154f8 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -49,7 +49,7 @@ class Controller(object): self.doc = None log.info(u'%s controller loaded' % live) - def add_handler(self, controller, file, is_blank): + def add_handler(self, controller, file, hide_mode): """ Add a handler, which is an instance of a presentation and slidecontroller combination. If the slidecontroller has a display @@ -66,9 +66,17 @@ class Controller(object): return if self.is_live: self.doc.start_presentation() - if is_blank: + if hide_mode == HideMode.Screen: + Receiver.send_message(u'maindisplay_hide', HideMode.Screen) + self.stop() + elif hide_mode == HideMode.Theme: + Receiver.send_message(u'maindisplay_hide', HideMode.Theme) self.blank() - Receiver.send_message(u'maindisplay_hide', HideMode.Screen) + elif hide_mode == HideMode.Blank: + Receiver.send_message(u'maindisplay_hide', HideMode.Screen) + self.blank() + else: + Receiver.send_message(u'maindisplay_hide', HideMode.Screen) self.doc.slidenumber = 0 def activate(self): @@ -261,7 +269,7 @@ class MessageListener(object): is_live = message[1] item = message[0] log.debug(u'Startup called with message %s' % message) - is_blank = message[2] + hide_mode = message[2] file = os.path.join(item.get_frame_path(), item.get_frame_title()) self.handler = item.title @@ -273,7 +281,7 @@ class MessageListener(object): controller = self.live_handler else: controller = self.preview_handler - controller.add_handler(self.controllers[self.handler], file, is_blank) + controller.add_handler(self.controllers[self.handler], file, hide_mode) def slide(self, message): """ From 9b84319c249cf09b66e90d7f28c7eb38c5afe134 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 7 Apr 2011 16:26:29 +0200 Subject: [PATCH 32/98] added new dialog from superfly + few own changes --- openlp/core/ui/shortcutlistdialog.py | 109 ++++++++++++++++---------- openlp/core/ui/shortcutlistform.py | 62 ++++++++------- resources/forms/shortcutlistdialog.ui | 80 +++++++++++++------ 3 files changed, 158 insertions(+), 93 deletions(-) diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index 83f897bd8..6531a1a19 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -30,44 +30,71 @@ from openlp.core.lib import translate, build_icon class Ui_ShortcutListDialog(object): def setupUi(self, shortcutListDialog): - shortcutListDialog.resize(440, 450) - shortcutListDialog.setObjectName(u'shortcutListDialog') - self.dialogLayout = QtGui.QVBoxLayout(shortcutListDialog) - self.dialogLayout.setObjectName(u'dialogLayout') - self.descriptionLabel = QtGui.QLabel(shortcutListDialog) - self.descriptionLabel.setObjectName(u'descriptionLabel') - self.descriptionLabel.setWordWrap(True) - self.dialogLayout.addWidget(self.descriptionLabel) + shortcutListDialog.setObjectName("shortcutListDialog") + shortcutListDialog.resize(500, 438) + self.shortcutListLayout = QtGui.QVBoxLayout(shortcutListDialog) + self.shortcutListLayout.setSpacing(8) + self.shortcutListLayout.setMargin(8) + self.shortcutListLayout.setObjectName("shortcutListLayout") self.treeWidget = QtGui.QTreeWidget(shortcutListDialog) self.treeWidget.setAlternatingRowColors(True) - self.treeWidget.setObjectName(u'treeWidget') + self.treeWidget.setObjectName("treeWidget") self.treeWidget.setColumnCount(3) - self.dialogLayout.addWidget(self.treeWidget) - self.customLayout = QtGui.QHBoxLayout() - self.customLayout.setObjectName(u'customLayout') - self.shortcutButtonLabel = QtGui.QLabel(shortcutListDialog) - self.shortcutButtonLabel.setObjectName(u'descriptionLabel') - self.customLayout.addWidget(self.shortcutButtonLabel) - self.shortcutButton = QtGui.QPushButton(shortcutListDialog) - self.shortcutButton.setIcon( - build_icon(u':/system/system_configure_shortcuts.png')) - self.shortcutButton.setCheckable(True) - self.shortcutButton.setObjectName(u'shortcutButton') - self.shortcutButton.setFixedSize(150, 30) - self.customLayout.addWidget(self.shortcutButton) - self.clearShortcutButton = QtGui.QToolButton(shortcutListDialog) - self.clearShortcutButton.setIcon( - build_icon(u':/system/clear_shortcut.png')) - self.clearShortcutButton.setAutoRaise(True) - self.clearShortcutButton.setObjectName(u'clearShortcutButton') - self.customLayout.addWidget(self.clearShortcutButton) - self.customLayout.addStretch() - self.dialogLayout.addLayout(self.customLayout) + self.shortcutListLayout.addWidget(self.treeWidget) + self.detailsLayout = QtGui.QGridLayout() + self.detailsLayout.setSpacing(8) + self.detailsLayout.setContentsMargins(-1, 0, -1, -1) + self.detailsLayout.setObjectName("detailsLayout") + self.defaultRadioButton = QtGui.QRadioButton(shortcutListDialog) + self.defaultRadioButton.setChecked(True) + self.defaultRadioButton.setObjectName("defaultRadioButton") + self.detailsLayout.addWidget(self.defaultRadioButton, 0, 0, 1, 1) + self.customRadioButton = QtGui.QRadioButton(shortcutListDialog) + self.customRadioButton.setObjectName("customRadioButton") + self.detailsLayout.addWidget(self.customRadioButton, 1, 0, 1, 1) + self.primaryLayout = QtGui.QHBoxLayout() + self.primaryLayout.setSpacing(8) + self.primaryLayout.setObjectName("primaryLayout") + self.primaryPushButton = QtGui.QPushButton(shortcutListDialog) + self.primaryPushButton.setMinimumSize(QtCore.QSize(84, 0)) + self.primaryPushButton.setIcon(build_icon(u':/system/system_configure_shortcuts.png')) + self.primaryPushButton.setCheckable(True) + self.primaryPushButton.setChecked(False) + self.primaryPushButton.setObjectName("primaryPushButton") + self.primaryLayout.addWidget(self.primaryPushButton) + self.clearPrimaryButton = QtGui.QToolButton(shortcutListDialog) + self.clearPrimaryButton.setMinimumSize(QtCore.QSize(0, 16)) + self.clearPrimaryButton.setText("") + self.clearPrimaryButton.setIcon(build_icon(u':/system/clear_shortcut.png')) + self.clearPrimaryButton.setObjectName("clearPrimaryButton") + self.primaryLayout.addWidget(self.clearPrimaryButton) + self.detailsLayout.addLayout(self.primaryLayout, 1, 1, 1, 1) + self.alternateLayout = QtGui.QHBoxLayout() + self.alternateLayout.setSpacing(8) + self.alternateLayout.setObjectName("alternateLayout") + self.alternatePushButton = QtGui.QPushButton(shortcutListDialog) + self.alternatePushButton.setIcon(build_icon(u':/system/system_configure_shortcuts.png')) + self.alternatePushButton.setObjectName("alternatePushButton") + self.alternateLayout.addWidget(self.alternatePushButton) + self.clearAlternateButton = QtGui.QToolButton(shortcutListDialog) + self.clearAlternateButton.setText("") + self.clearAlternateButton.setIcon(build_icon(u':/system/clear_shortcut.png')) + self.clearAlternateButton.setObjectName("clearAlternateButton") + self.alternateLayout.addWidget(self.clearAlternateButton) + self.detailsLayout.addLayout(self.alternateLayout, 1, 2, 1, 1) + self.primaryLabel = QtGui.QLabel(shortcutListDialog) + self.primaryLabel.setObjectName("primaryLabel") + self.detailsLayout.addWidget(self.primaryLabel, 0, 1, 1, 1) + self.alternateLabel = QtGui.QLabel(shortcutListDialog) + self.alternateLabel.setObjectName("alternateLabel") + self.detailsLayout.addWidget(self.alternateLabel, 0, 2, 1, 1) + self.shortcutListLayout.addLayout(self.detailsLayout) self.buttonBox = QtGui.QDialogButtonBox(shortcutListDialog) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel | - QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.RestoreDefaults) - self.buttonBox.setObjectName(u'buttonBox') - self.dialogLayout.addWidget(self.buttonBox) + QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Reset) + self.buttonBox.setObjectName("buttonBox") + self.shortcutListLayout.addWidget(self.buttonBox) self.retranslateUi(shortcutListDialog) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'accepted()'), shortcutListDialog.accept) @@ -78,15 +105,17 @@ class Ui_ShortcutListDialog(object): def retranslateUi(self, shortcutListDialog): shortcutListDialog.setWindowTitle( translate('OpenLP.ShortcutListDialog', 'Customize Shortcuts')) - self.descriptionLabel.setText(translate('OpenLP.ShortcutListDialog', - 'Select an action and click the button below to start capturing ' - 'a new shortcut.')) + #self.descriptionLabel.setText(translate('OpenLP.ShortcutListDialog', + #'Select an action and click the button below to start capturing ' + #'a new shortcut.')) self.treeWidget.setHeaderLabels([ translate('OpenLP.ShortcutListDialog', 'Action'), translate('OpenLP.ShortcutListDialog', 'Shortcut'), translate('OpenLP.ShortcutListDialog', 'Alternate')]) - self.shortcutButtonLabel.setText( + self.primaryPushButton.setText( translate('OpenLP.ShortcutListDialog', 'Capture shortcut:')) - self.clearShortcutButton.setToolTip( - translate('OpenLP.ShortcutListDialog', - 'Restore the default shortcut(s) of this action.')) + self.alternatePushButton.setText( + translate('OpenLP.ShortcutListDialog', 'Capture shortcut:')) + #self.clearShortcutButton.setToolTip( + #translate('OpenLP.ShortcutListDialog', + #'Restore the default shortcut(s) of this action.')) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index a99b7c3bb..00e216c2a 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -47,24 +47,28 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.setupUi(self) self.column = -1 self.changedActions = {} - self.shortcutButton.setText(u'') - self.shortcutButton.setEnabled(False) - QtCore.QObject.connect(self.shortcutButton, - QtCore.SIGNAL(u'toggled(bool)'), self.onShortcutButtonClicked) + self.primaryPushButton.setText(u'') + self.alternatePushButton.setText(u'') + self.primaryPushButton.setEnabled(False) + self.alternatePushButton.setEnabled(False) + QtCore.QObject.connect(self.primaryPushButton, + QtCore.SIGNAL(u'toggled(bool)'), self.onPrimaryPushButtonClicked) QtCore.QObject.connect(self.treeWidget, QtCore.SIGNAL(u'itemPressed(QTreeWidgetItem*, int)'), self.onItemPressed) QtCore.QObject.connect(self.treeWidget, QtCore.SIGNAL(u'itemDoubleClicked(QTreeWidgetItem*, int)'), self.onItemDoubleClicked) - QtCore.QObject.connect(self.clearShortcutButton, - QtCore.SIGNAL(u'clicked(bool)'), self.onClearShortcutButtonClicked) + QtCore.QObject.connect(self.clearPrimaryButton, + QtCore.SIGNAL(u'clicked(bool)'), self.onClearPrimaryButtonClicked) +# QtCore.QObject.connect(self.clearShortcutButton, +# QtCore.SIGNAL(u'clicked(bool)'), self.onClearPrimaryButtonClicked) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'clicked(QAbstractButton*)'), self.onRestoreDefaultsClicked) def keyPressEvent(self, event): - if self.shortcutButton.isChecked(): + if self.primaryPushButton.isChecked(): event.ignore() elif event.key() == QtCore.Qt.Key_Escape: event.accept() @@ -72,7 +76,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): def keyReleaseEvent(self, event): Qt = QtCore.Qt - if not self.shortcutButton.isChecked(): + if not self.primaryPushButton.isChecked(): return key = event.key() if key == Qt.Key_Shift or key == Qt.Key_Control or \ @@ -117,15 +121,15 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): QtGui.QMessageBox.Ok ) else: - self.shortcutButton.setText(key_sequence.toString()) - self.shortcutButton.setChecked(False) + self.primaryPushButton.setText(key_sequence.toString()) + self.primaryPushButton.setChecked(False) def exec_(self): self.changedActions = {} self.reloadShortcutList() - self.shortcutButton.setChecked(False) - self.shortcutButton.setEnabled(False) - self.shortcutButton.setText(u'') + self.primaryPushButton.setChecked(False) + self.primaryPushButton.setEnabled(False) + self.primaryPushButton.setText(u'') return QtGui.QDialog.exec_(self) def reloadShortcutList(self): @@ -172,7 +176,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): item.setText(1, shortcuts[0].toString()) item.setText(2, shortcuts[1].toString()) - def onShortcutButtonClicked(self, toggled): + def onPrimaryPushButtonClicked(self, toggled): """ Save the new shortcut to the action if the button is unchanged. """ @@ -188,14 +192,16 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): new_shortcuts = [] # We are changing the primary shortcut. if self.column in [0, 1]: - new_shortcuts.append(QtGui.QKeySequence(self.shortcutButton.text())) + new_shortcuts.append( + QtGui.QKeySequence(self.primaryPushButton.text())) if len(shortcuts) == 2: new_shortcuts.append(shortcuts[1]) # We are changing the secondary shortcut. elif self.column == 2: if len(shortcuts) != 0: new_shortcuts.append(shortcuts[0]) - new_shortcuts.append(QtGui.QKeySequence(self.shortcutButton.text())) + new_shortcuts.append( + QtGui.QKeySequence(self.primaryPushButton.text())) else: return self.changedActions[action] = new_shortcuts @@ -203,14 +209,14 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): def onItemDoubleClicked(self, item, column): """ - A item has been double clicked. ``The shortcutButton`` will be checked - and the item's shortcut will be displayed. + A item has been double clicked. ``The primaryPushButton`` will be + checked and the item's shortcut will be displayed. """ action = item.data(0, QtCore.Qt.UserRole).toPyObject() if action is None: return - self.shortcutButton.setChecked(True) - self.shortcutButton.setFocus(QtCore.Qt.OtherFocusReason) + self.primaryPushButton.setChecked(True) + self.primaryPushButton.setFocus(QtCore.Qt.OtherFocusReason) self.onItemPressed(item, column) def onItemPressed(self, item, column): @@ -222,23 +228,23 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): action = item.data(0, QtCore.Qt.UserRole).toPyObject() text = u'' if action is None: - self.shortcutButton.setChecked(False) - self.shortcutButton.setEnabled(False) + self.primaryPushButton.setChecked(False) + self.primaryPushButton.setEnabled(False) else: - self.shortcutButton.setEnabled(True) + self.primaryPushButton.setEnabled(True) shortcuts = self._actionShortcuts(action) if column != 2 and len(shortcuts) != 0: text = shortcuts[0].toString() elif len(shortcuts) == 2: text = shortcuts[1].toString() - self.shortcutButton.setText(text) + self.primaryPushButton.setText(text) - def onClearShortcutButtonClicked(self, toggled): + def onClearPrimaryButtonClicked(self, toggled): """ Restore the defaults of this """ item = self.treeWidget.currentItem() - self.shortcutButton.setChecked(False) + self.primaryPushButton.setChecked(False) if item is None: return action = item.data(0, QtCore.Qt.UserRole).toPyObject() @@ -261,8 +267,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)) == QtGui.QMessageBox.No: return - self.shortcutButton.setChecked(False) - self.shortcutButton.setText(u'') + self.primaryPushButton.setChecked(False) + self.primaryPushButton.setText(u'') for category in ActionList.categories: for action in category.actions: self.changedActions[action] = action.defaultShortcuts diff --git a/resources/forms/shortcutlistdialog.ui b/resources/forms/shortcutlistdialog.ui index 519925560..9a5c599d1 100644 --- a/resources/forms/shortcutlistdialog.ui +++ b/resources/forms/shortcutlistdialog.ui @@ -41,35 +41,35 @@ - + + + 0 + 8 - - 0 - - + - Default: None + Default: true - - + + + + Custom: + + + + + 8 - - - - Custom: - - - @@ -83,7 +83,7 @@ - :/system/system_settings.png:/system/system_settings.png + :/system/system_configure_shortcuts.png:/system/system_configure_shortcuts.png true @@ -110,21 +110,51 @@ + + + + + + 8 + - - - Qt::Horizontal + + + None - - - 40 - 20 - + + + :/system/system_configure_shortcuts.png:/system/system_configure_shortcuts.png - + + + + + + + + + + :/system/clear_shortcut.png:/system/clear_shortcut.png + + + + + + Ctrl+V + + + + + + + Shift+Ins + + + From dab699371cea47a8cbc2f6fb4ec9c926e4ac195c Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 7 Apr 2011 17:37:07 +0200 Subject: [PATCH 33/98] integrated second button(s), clean ups --- openlp/core/ui/shortcutlistdialog.py | 74 +++++------ openlp/core/ui/shortcutlistform.py | 175 +++++++++++++++++---------- 2 files changed, 150 insertions(+), 99 deletions(-) diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index 6531a1a19..b5dffd936 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -30,70 +30,67 @@ from openlp.core.lib import translate, build_icon class Ui_ShortcutListDialog(object): def setupUi(self, shortcutListDialog): - shortcutListDialog.setObjectName("shortcutListDialog") + shortcutListDialog.setObjectName(u'shortcutListDialog') shortcutListDialog.resize(500, 438) self.shortcutListLayout = QtGui.QVBoxLayout(shortcutListDialog) - self.shortcutListLayout.setSpacing(8) - self.shortcutListLayout.setMargin(8) - self.shortcutListLayout.setObjectName("shortcutListLayout") + self.shortcutListLayout.setObjectName(u'shortcutListLayout') self.treeWidget = QtGui.QTreeWidget(shortcutListDialog) + self.treeWidget.setObjectName(u'treeWidget') self.treeWidget.setAlternatingRowColors(True) - self.treeWidget.setObjectName("treeWidget") self.treeWidget.setColumnCount(3) self.shortcutListLayout.addWidget(self.treeWidget) self.detailsLayout = QtGui.QGridLayout() - self.detailsLayout.setSpacing(8) + self.detailsLayout.setObjectName(u'detailsLayout') self.detailsLayout.setContentsMargins(-1, 0, -1, -1) - self.detailsLayout.setObjectName("detailsLayout") self.defaultRadioButton = QtGui.QRadioButton(shortcutListDialog) + self.defaultRadioButton.setObjectName(u'defaultRadioButton') self.defaultRadioButton.setChecked(True) - self.defaultRadioButton.setObjectName("defaultRadioButton") self.detailsLayout.addWidget(self.defaultRadioButton, 0, 0, 1, 1) self.customRadioButton = QtGui.QRadioButton(shortcutListDialog) - self.customRadioButton.setObjectName("customRadioButton") + self.customRadioButton.setObjectName(u'customRadioButton') self.detailsLayout.addWidget(self.customRadioButton, 1, 0, 1, 1) self.primaryLayout = QtGui.QHBoxLayout() - self.primaryLayout.setSpacing(8) - self.primaryLayout.setObjectName("primaryLayout") + self.primaryLayout.setObjectName(u'primaryLayout') self.primaryPushButton = QtGui.QPushButton(shortcutListDialog) + self.primaryPushButton.setObjectName(u'primaryPushButton') self.primaryPushButton.setMinimumSize(QtCore.QSize(84, 0)) - self.primaryPushButton.setIcon(build_icon(u':/system/system_configure_shortcuts.png')) + self.primaryPushButton.setIcon( + build_icon(u':/system/system_configure_shortcuts.png')) self.primaryPushButton.setCheckable(True) - self.primaryPushButton.setChecked(False) - self.primaryPushButton.setObjectName("primaryPushButton") self.primaryLayout.addWidget(self.primaryPushButton) self.clearPrimaryButton = QtGui.QToolButton(shortcutListDialog) + self.clearPrimaryButton.setObjectName(u'clearPrimaryButton') self.clearPrimaryButton.setMinimumSize(QtCore.QSize(0, 16)) - self.clearPrimaryButton.setText("") - self.clearPrimaryButton.setIcon(build_icon(u':/system/clear_shortcut.png')) - self.clearPrimaryButton.setObjectName("clearPrimaryButton") + self.clearPrimaryButton.setIcon( + build_icon(u':/system/clear_shortcut.png')) self.primaryLayout.addWidget(self.clearPrimaryButton) self.detailsLayout.addLayout(self.primaryLayout, 1, 1, 1, 1) self.alternateLayout = QtGui.QHBoxLayout() - self.alternateLayout.setSpacing(8) - self.alternateLayout.setObjectName("alternateLayout") + self.alternateLayout.setObjectName(u'alternateLayout') self.alternatePushButton = QtGui.QPushButton(shortcutListDialog) - self.alternatePushButton.setIcon(build_icon(u':/system/system_configure_shortcuts.png')) - self.alternatePushButton.setObjectName("alternatePushButton") + self.alternatePushButton.setObjectName(u'alternatePushButton') + self.alternatePushButton.setCheckable(True) + self.alternatePushButton.setIcon( + build_icon(u':/system/system_configure_shortcuts.png')) self.alternateLayout.addWidget(self.alternatePushButton) self.clearAlternateButton = QtGui.QToolButton(shortcutListDialog) - self.clearAlternateButton.setText("") - self.clearAlternateButton.setIcon(build_icon(u':/system/clear_shortcut.png')) - self.clearAlternateButton.setObjectName("clearAlternateButton") + self.clearAlternateButton.setObjectName(u'clearAlternateButton') + self.clearAlternateButton.setIcon( + build_icon(u':/system/clear_shortcut.png')) self.alternateLayout.addWidget(self.clearAlternateButton) self.detailsLayout.addLayout(self.alternateLayout, 1, 2, 1, 1) self.primaryLabel = QtGui.QLabel(shortcutListDialog) - self.primaryLabel.setObjectName("primaryLabel") + self.primaryLabel.setObjectName(u'primaryLabel') self.detailsLayout.addWidget(self.primaryLabel, 0, 1, 1, 1) self.alternateLabel = QtGui.QLabel(shortcutListDialog) - self.alternateLabel.setObjectName("alternateLabel") + self.alternateLabel.setObjectName(u'alternateLabel') self.detailsLayout.addWidget(self.alternateLabel, 0, 2, 1, 1) self.shortcutListLayout.addLayout(self.detailsLayout) self.buttonBox = QtGui.QDialogButtonBox(shortcutListDialog) + self.buttonBox.setObjectName(u'buttonBox') self.buttonBox.setOrientation(QtCore.Qt.Horizontal) self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel | - QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Reset) - self.buttonBox.setObjectName("buttonBox") + QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.RestoreDefaults) self.shortcutListLayout.addWidget(self.buttonBox) self.retranslateUi(shortcutListDialog) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'accepted()'), @@ -112,10 +109,17 @@ class Ui_ShortcutListDialog(object): translate('OpenLP.ShortcutListDialog', 'Action'), translate('OpenLP.ShortcutListDialog', 'Shortcut'), translate('OpenLP.ShortcutListDialog', 'Alternate')]) - self.primaryPushButton.setText( - translate('OpenLP.ShortcutListDialog', 'Capture shortcut:')) - self.alternatePushButton.setText( - translate('OpenLP.ShortcutListDialog', 'Capture shortcut:')) - #self.clearShortcutButton.setToolTip( - #translate('OpenLP.ShortcutListDialog', - #'Restore the default shortcut(s) of this action.')) + self.defaultRadioButton.setText( + translate('OpenLP.ShortcutListDialog', 'Default')) + self.customRadioButton.setText( + translate('OpenLP.ShortcutListDialog', 'Custom')) + self.primaryPushButton.setToolTip( + translate('OpenLP.ShortcutListDialog', 'Capture shortcut.')) + self.alternatePushButton.setToolTip( + translate('OpenLP.ShortcutListDialog', 'Capture shortcut.')) + self.clearPrimaryButton.setToolTip( + translate('OpenLP.ShortcutListDialog', + 'Restore the default shortcut(s) of this action.')) + self.clearAlternateButton.setToolTip( + translate('OpenLP.ShortcutListDialog', + 'Restore the default shortcut(s) of this action.')) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 00e216c2a..cda8cfc5d 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -45,14 +45,11 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): def __init__(self, parent=None): QtGui.QDialog.__init__(self, parent) self.setupUi(self) - self.column = -1 self.changedActions = {} - self.primaryPushButton.setText(u'') - self.alternatePushButton.setText(u'') - self.primaryPushButton.setEnabled(False) - self.alternatePushButton.setEnabled(False) QtCore.QObject.connect(self.primaryPushButton, QtCore.SIGNAL(u'toggled(bool)'), self.onPrimaryPushButtonClicked) + QtCore.QObject.connect(self.alternatePushButton, + QtCore.SIGNAL(u'toggled(bool)'), self.onAlternatePushButtonClicked) QtCore.QObject.connect(self.treeWidget, QtCore.SIGNAL(u'itemPressed(QTreeWidgetItem*, int)'), self.onItemPressed) @@ -61,14 +58,15 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.onItemDoubleClicked) QtCore.QObject.connect(self.clearPrimaryButton, QtCore.SIGNAL(u'clicked(bool)'), self.onClearPrimaryButtonClicked) -# QtCore.QObject.connect(self.clearShortcutButton, -# QtCore.SIGNAL(u'clicked(bool)'), self.onClearPrimaryButtonClicked) + QtCore.QObject.connect(self.clearAlternateButton, + QtCore.SIGNAL(u'clicked(bool)'), self.onClearAlternateButtonClicked) QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'clicked(QAbstractButton*)'), self.onRestoreDefaultsClicked) def keyPressEvent(self, event): - if self.primaryPushButton.isChecked(): + if self.primaryPushButton.isChecked() or \ + self.alternatePushButton.isChecked(): event.ignore() elif event.key() == QtCore.Qt.Key_Escape: event.accept() @@ -76,7 +74,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): def keyReleaseEvent(self, event): Qt = QtCore.Qt - if not self.primaryPushButton.isChecked(): + if not self.primaryPushButton.isChecked() and \ + not self.alternatePushButton.isChecked(): return key = event.key() if key == Qt.Key_Shift or key == Qt.Key_Control or \ @@ -90,9 +89,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): if event.modifiers() & Qt.ShiftModifier == Qt.ShiftModifier: key_string = u'Shift+' + key_string key_sequence = QtGui.QKeySequence(key_string) - # The item/action we are attempting to change. - changing_item = self.treeWidget.currentItem() - changing_action = changing_item.data(0, QtCore.Qt.UserRole).toPyObject() + # The action we are attempting to change. + changing_action = self._currentItemAction() shortcut_valid = True for category in ActionList.categories: for action in category.actions: @@ -100,11 +98,18 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): if key_sequence not in shortcuts: continue if action is changing_action: - continue - # Have the same parentWidget, thus they cannot have the same - # shortcut. + if self.primaryPushButton.isChecked() and \ + shortcuts.index(key_sequence) == 0: + continue + if self.alternatePushButton.isChecked() and \ + shortcuts.index(key_sequence) == 1: + continue + # Have the same parent, thus they cannot have the same shortcut. if action.parent() is changing_action.parent(): shortcut_valid = False + # The new shortcut is already assigned, but if both shortcuts + # are only valid in a different widget the new shortcut is + # vaild, because they will not interfere. if action.shortcutContext() in [QtCore.Qt.WindowShortcut, QtCore.Qt.ApplicationShortcut]: shortcut_valid = False @@ -121,8 +126,12 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): QtGui.QMessageBox.Ok ) else: - self.primaryPushButton.setText(key_sequence.toString()) - self.primaryPushButton.setChecked(False) + if self.primaryPushButton.isChecked(): + self.primaryPushButton.setText(key_sequence.toString()) + self.primaryPushButton.setChecked(False) + elif self.alternatePushButton.isChecked(): + self.alternatePushButton.setText(key_sequence.toString()) + self.alternatePushButton.setChecked(False) def exec_(self): self.changedActions = {} @@ -130,6 +139,9 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.primaryPushButton.setChecked(False) self.primaryPushButton.setEnabled(False) self.primaryPushButton.setText(u'') + self.alternatePushButton.setChecked(False) + self.alternatePushButton.setEnabled(False) + self.alternatePushButton.setText(u'') return QtGui.QDialog.exec_(self) def reloadShortcutList(self): @@ -162,7 +174,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): while iterator.value(): item = iterator.value() iterator += 1 - action = item.data(0, QtCore.Qt.UserRole).toPyObject() + action = self._currentItemAction(item) if action is None: continue shortcuts = self._actionShortcuts(action) @@ -181,29 +193,35 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): Save the new shortcut to the action if the button is unchanged. """ if toggled: + self.alternatePushButton.setChecked(False) return - item = self.treeWidget.currentItem() - if item is None: - return - action = item.data(0, QtCore.Qt.UserRole).toPyObject() + action = self._currentItemAction() if action is None: return shortcuts = self._actionShortcuts(action) new_shortcuts = [] - # We are changing the primary shortcut. - if self.column in [0, 1]: - new_shortcuts.append( - QtGui.QKeySequence(self.primaryPushButton.text())) - if len(shortcuts) == 2: - new_shortcuts.append(shortcuts[1]) - # We are changing the secondary shortcut. - elif self.column == 2: - if len(shortcuts) != 0: - new_shortcuts.append(shortcuts[0]) - new_shortcuts.append( - QtGui.QKeySequence(self.primaryPushButton.text())) - else: + new_shortcuts.append( + QtGui.QKeySequence(self.primaryPushButton.text())) + if len(shortcuts) == 2: + new_shortcuts.append(shortcuts[1]) + self.changedActions[action] = new_shortcuts + self.refreshShortcutList() + + def onAlternatePushButtonClicked(self, toggled): + """ + """ + if toggled: + self.primaryPushButton.setChecked(False) return + action = self._currentItemAction() + if action is None: + return + shortcuts = self._actionShortcuts(action) + new_shortcuts = [] + if len(shortcuts) != 0: + new_shortcuts.append(shortcuts[0]) + new_shortcuts.append( + QtGui.QKeySequence(self.alternatePushButton.text())) self.changedActions[action] = new_shortcuts self.refreshShortcutList() @@ -212,47 +230,39 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): A item has been double clicked. ``The primaryPushButton`` will be checked and the item's shortcut will be displayed. """ - action = item.data(0, QtCore.Qt.UserRole).toPyObject() + action = self._currentItemAction(item) if action is None: return - self.primaryPushButton.setChecked(True) - self.primaryPushButton.setFocus(QtCore.Qt.OtherFocusReason) - self.onItemPressed(item, column) + self.primaryPushButton.setChecked(column in [0, 1]) + self.alternatePushButton.setChecked(column not in [0, 1]) + if column in [0, 1]: + self.primaryPushButton.setFocus(QtCore.Qt.OtherFocusReason) + else: + self.alternatePushButton.setFocus(QtCore.Qt.OtherFocusReason) + self.onItemPressed() - def onItemPressed(self, item, column): + def onItemPressed(self, item=None, column=-1): """ A item has been pressed. We adjust the button's text to the action's shortcut which is encapsulate in the item. """ - self.column = column - action = item.data(0, QtCore.Qt.UserRole).toPyObject() - text = u'' + action = self._currentItemAction(item) + self.primaryPushButton.setEnabled(action is not None) + self.alternatePushButton.setEnabled(action is not None) + primary_text = u'' + alternate_text = u'' if action is None: self.primaryPushButton.setChecked(False) - self.primaryPushButton.setEnabled(False) + self.alternatePushButton.setChecked(False) else: - self.primaryPushButton.setEnabled(True) shortcuts = self._actionShortcuts(action) - if column != 2 and len(shortcuts) != 0: - text = shortcuts[0].toString() + if len(shortcuts) == 1: + primary_text = shortcuts[0].toString() elif len(shortcuts) == 2: - text = shortcuts[1].toString() - self.primaryPushButton.setText(text) - - def onClearPrimaryButtonClicked(self, toggled): - """ - Restore the defaults of this - """ - item = self.treeWidget.currentItem() - self.primaryPushButton.setChecked(False) - if item is None: - return - action = item.data(0, QtCore.Qt.UserRole).toPyObject() - if action is None: - return - self.changedActions[action] = action.defaultShortcuts - self.refreshShortcutList() - self.onItemPressed(item, self.column) + primary_text = shortcuts[0].toString() + alternate_text = shortcuts[1].toString() + self.primaryPushButton.setText(primary_text) + self.alternatePushButton.setText(alternate_text) def onRestoreDefaultsClicked(self, button): """ @@ -269,6 +279,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): return self.primaryPushButton.setChecked(False) self.primaryPushButton.setText(u'') + self.alternatePushButton.setChecked(False) + self.alternatePushButton.setText(u'') for category in ActionList.categories: for action in category.actions: self.changedActions[action] = action.defaultShortcuts @@ -292,6 +304,30 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): action.objectName(), QtCore.QVariant(action.shortcuts())) settings.endGroup() + def onClearPrimaryButtonClicked(self, toggled): + """ + Restore the defaults of this action. + """ + self._clearButtonClicked(self.primaryPushButton) + + def onClearAlternateButtonClicked(self, toggled): + """ + Restore the defaults of this action. + """ + self._clearButtonClicked(self.alternatePushButton) + + def _clearButtonClicked(self, button): + """ + Restore the defaults of this action. The given button will be unchecked. + """ + button.setChecked(False) + action = self._currentItemAction() + if action is None: + return + self.changedActions[action] = action.defaultShortcuts + self.refreshShortcutList() + self.onItemPressed() + def _actionShortcuts(self, action): """ This returns the shortcuts for the given ``action``, which also includes @@ -301,3 +337,14 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): if self.changedActions.has_key(action): return self.changedActions[action] return action.shortcuts() + + def _currentItemAction(self, item=None): + """ + Returns the action of the current item if no item is given, otherwise + we return the action of the given item. + """ + if item is None: + item = self.treeWidget.currentItem() + if item is None: + return + return item.data(0, QtCore.Qt.UserRole).toPyObject() From 78a9c89025d09cb4fbca38ae5a75266d6dff74e2 Mon Sep 17 00:00:00 2001 From: Jonathan Corwin Date: Thu, 7 Apr 2011 23:57:12 +0100 Subject: [PATCH 34/98] Fix stuttering powerpoint start --- openlp/core/ui/slidecontroller.py | 6 +++--- .../plugins/presentations/lib/messagelistener.py | 14 +++++++++----- .../presentations/lib/powerpointcontroller.py | 11 +++++------ 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index eec5d3a19..ccdb1f17c 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -646,7 +646,7 @@ class SlideController(QtGui.QWidget): self.display.buildHtml(self.serviceItem) if serviceItem.is_media(): self.onMediaStart(serviceItem) - self.onSlideSelected() + self.onSlideSelected(True) self.previewListWidget.setFocus() Receiver.send_message(u'slidecontroller_%s_started' % self.typePrefix, [serviceItem]) @@ -833,7 +833,7 @@ class SlideController(QtGui.QWidget): % self.serviceItem.name.lower(), [self.serviceItem, self.isLive]) - def onSlideSelected(self): + def onSlideSelected(self, start=False): """ Generate the preview when you click on a slide. if this is the Live Controller also display on the screen @@ -842,7 +842,7 @@ class SlideController(QtGui.QWidget): self.selectedRow = 0 if row > -1 and row < self.previewListWidget.rowCount(): if self.serviceItem.is_command(): - if self.isLive: + if self.isLive and not start: Receiver.send_message( u'%s_slide' % self.serviceItem.name.lower(), [self.serviceItem, self.isLive, row]) diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index 728a154f8..4f9268b3e 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -49,7 +49,7 @@ class Controller(object): self.doc = None log.info(u'%s controller loaded' % live) - def add_handler(self, controller, file, hide_mode): + def add_handler(self, controller, file, hide_mode, slide_no): """ Add a handler, which is an instance of a presentation and slidecontroller combination. If the slidecontroller has a display @@ -64,8 +64,8 @@ class Controller(object): # Display error message to user # Inform slidecontroller that the action failed? return + self.doc.slidenumber = slide_no if self.is_live: - self.doc.start_presentation() if hide_mode == HideMode.Screen: Receiver.send_message(u'maindisplay_hide', HideMode.Screen) self.stop() @@ -73,11 +73,14 @@ class Controller(object): Receiver.send_message(u'maindisplay_hide', HideMode.Theme) self.blank() elif hide_mode == HideMode.Blank: - Receiver.send_message(u'maindisplay_hide', HideMode.Screen) + Receiver.send_message(u'maindisplay_hide', HideMode.Blank) self.blank() else: + self.doc.start_presentation() Receiver.send_message(u'maindisplay_hide', HideMode.Screen) - self.doc.slidenumber = 0 + self.doc.slidenumber = 0 + if slide_no > 1: + self.slide(slide_no) def activate(self): """ @@ -281,7 +284,8 @@ class MessageListener(object): controller = self.live_handler else: controller = self.preview_handler - controller.add_handler(self.controllers[self.handler], file, hide_mode) + controller.add_handler(self.controllers[self.handler], file, hide_mode, + message[3]) def slide(self, message): """ diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index 793b0b75d..a02bc841a 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -251,14 +251,13 @@ class PowerpointDocument(PresentationDocument): win32ui.GetForegroundWindow().GetDC().GetDeviceCaps(88) except win32ui.error: dpi = 96 - self.presentation.SlideShowSettings.Run() - self.presentation.SlideShowWindow.View.GotoSlide(1) rendermanager = self.controller.plugin.renderManager rect = rendermanager.screens.current[u'size'] - self.presentation.SlideShowWindow.Top = rect.y() * 72 / dpi - self.presentation.SlideShowWindow.Height = rect.height() * 72 / dpi - self.presentation.SlideShowWindow.Left = rect.x() * 72 / dpi - self.presentation.SlideShowWindow.Width = rect.width() * 72 / dpi + ppt_window = self.presentation.SlideShowSettings.Run() + ppt_window.Top = rect.y() * 72 / dpi + ppt_window.Height = rect.height() * 72 / dpi + ppt_window.Left = rect.x() * 72 / dpi + ppt_window.Width = rect.width() * 72 / dpi def get_slide_number(self): """ From ea5e0d65cc5209db530012416f867e1920d8d879 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 8 Apr 2011 08:25:02 +0200 Subject: [PATCH 35/98] --- openlp/core/ui/shortcutlistdialog.py | 4 +- openlp/core/ui/shortcutlistform.py | 68 ++++++++++++++++++---------- 2 files changed, 45 insertions(+), 27 deletions(-) diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index b5dffd936..a5050ab25 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -119,7 +119,7 @@ class Ui_ShortcutListDialog(object): translate('OpenLP.ShortcutListDialog', 'Capture shortcut.')) self.clearPrimaryButton.setToolTip( translate('OpenLP.ShortcutListDialog', - 'Restore the default shortcut(s) of this action.')) + 'Restore the default shortcut of this action.')) self.clearAlternateButton.setToolTip( translate('OpenLP.ShortcutListDialog', - 'Restore the default shortcut(s) of this action.')) + 'Restore the default shortcut of this action.')) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index cda8cfc5d..9b20fd9d2 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -127,21 +127,17 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): ) else: if self.primaryPushButton.isChecked(): - self.primaryPushButton.setText(key_sequence.toString()) - self.primaryPushButton.setChecked(False) + self._adjustButton(self.primaryPushButton, + checked=False, text=key_sequence.toString()) elif self.alternatePushButton.isChecked(): - self.alternatePushButton.setText(key_sequence.toString()) - self.alternatePushButton.setChecked(False) + self._adjustButton(self.alternatePushButton, + checked=False, text=key_sequence.toString()) def exec_(self): self.changedActions = {} self.reloadShortcutList() - self.primaryPushButton.setChecked(False) - self.primaryPushButton.setEnabled(False) - self.primaryPushButton.setText(u'') - self.alternatePushButton.setChecked(False) - self.alternatePushButton.setEnabled(False) - self.alternatePushButton.setText(u'') + self._adjustButton(self.primaryPushButton, False, False, u'') + self._adjustButton(self.alternatePushButton, False, False, u'') return QtGui.QDialog.exec_(self) def reloadShortcutList(self): @@ -227,7 +223,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): def onItemDoubleClicked(self, item, column): """ - A item has been double clicked. ``The primaryPushButton`` will be + A item has been double clicked. The ``primaryPushButton`` will be checked and the item's shortcut will be displayed. """ action = self._currentItemAction(item) @@ -251,18 +247,30 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.alternatePushButton.setEnabled(action is not None) primary_text = u'' alternate_text = u'' + primary_label_text = u'' + alternate_label_text = u'' if action is None: self.primaryPushButton.setChecked(False) self.alternatePushButton.setChecked(False) else: + if len(action.defaultShortcuts) != 0: + primary_label_text = action.defaultShortcuts[0].toString() + if len(action.defaultShortcuts) == 2: + alternate_label_text = action.defaultShortcuts[1].toString() shortcuts = self._actionShortcuts(action) - if len(shortcuts) == 1: - primary_text = shortcuts[0].toString() - elif len(shortcuts) == 2: - primary_text = shortcuts[0].toString() - alternate_text = shortcuts[1].toString() + if shortcuts != action.defaultShortcuts: + self.customRadioButton.setChecked(True) + if len(shortcuts) == 1: + primary_text = shortcuts[0].toString() + elif len(shortcuts) == 2: + primary_text = shortcuts[0].toString() + alternate_text = shortcuts[1].toString() + else: + self.defaultRadioButton.setChecked(True) self.primaryPushButton.setText(primary_text) self.alternatePushButton.setText(alternate_text) + self.primaryLabel.setText(primary_label_text) + self.alternateLabel.setText(alternate_label_text) def onRestoreDefaultsClicked(self, button): """ @@ -277,10 +285,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)) == QtGui.QMessageBox.No: return - self.primaryPushButton.setChecked(False) - self.primaryPushButton.setText(u'') - self.alternatePushButton.setChecked(False) - self.alternatePushButton.setText(u'') + self._adjustButton(self.primaryPushButton, False, text=u'') + self._adjustButton(self.alternatePushButton, False, text=u'') for category in ActionList.categories: for action in category.actions: self.changedActions[action] = action.defaultShortcuts @@ -318,7 +324,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): def _clearButtonClicked(self, button): """ - Restore the defaults of this action. The given button will be unchecked. + Restore the defaults of this action. The given ``button`` will be + unchecked. """ button.setChecked(False) action = self._currentItemAction() @@ -331,8 +338,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): def _actionShortcuts(self, action): """ This returns the shortcuts for the given ``action``, which also includes - those shortcuts which are not yet assigned to an action (as changes are - applied when closing the dialog). + those shortcuts which are not saved yet but already assigned (as changes + are applied when closing the dialog). """ if self.changedActions.has_key(action): return self.changedActions[action] @@ -340,11 +347,22 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): def _currentItemAction(self, item=None): """ - Returns the action of the current item if no item is given, otherwise - we return the action of the given item. + Returns the action of the given ``item``. If no item is given, we return + the action of the current item of the ``treeWidget``. """ if item is None: item = self.treeWidget.currentItem() if item is None: return return item.data(0, QtCore.Qt.UserRole).toPyObject() + + def _adjustButton(self, button, checked=None, enabled=None, text=None): + """ + Can be called to adjust more properties of the given ``button`` at once. + """ + if checked is not None: + button.setChecked(checked) + if enabled is not None: + button.setEnabled(enabled) + if text is not None: + button.setText(text) From 283171e296ca2aeb1c6d9475d2548ff0ff353c81 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 9 Apr 2011 16:50:44 +0200 Subject: [PATCH 36/98] use weight to sort categories --- openlp/core/ui/mainwindow.py | 9 +++++++ openlp/core/ui/servicemanager.py | 1 + openlp/core/ui/slidecontroller.py | 2 ++ openlp/core/utils/actions.py | 42 ++++++++++++++++++++++++++----- 4 files changed, 48 insertions(+), 6 deletions(-) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index ab712a57d..63c5aa777 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -39,6 +39,7 @@ from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \ ShortcutListForm, DisplayTagForm from openlp.core.utils import AppLocation, add_actions, LanguageManager, \ get_application_version +from openlp.core.utils.actions import ActionList log = logging.getLogger(__name__) @@ -161,6 +162,7 @@ class Ui_MainWindow(object): mainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.themeManagerDock) # Create the menu items + ActionList.add_category(UiStrings.File, 100) self.FileNewItem = shortcut_action(mainWindow, u'FileNewItem', [QtGui.QKeySequence(u'Ctrl+N')], self.ServiceManagerContents.onNewServiceClicked, @@ -183,14 +185,17 @@ class Ui_MainWindow(object): self.FileExitItem = shortcut_action(mainWindow, u'FileExitItem', [QtGui.QKeySequence(u'Alt+F4')], mainWindow.close, u':/system/system_exit.png', category=UiStrings.File) + ActionList.add_category(UiStrings.Import, 95) self.ImportThemeItem = base_action( mainWindow, u'ImportThemeItem', UiStrings.Import) self.ImportLanguageItem = base_action( mainWindow, u'ImportLanguageItem')#, UiStrings.Import) + ActionList.add_category(UiStrings.Export, 90) self.ExportThemeItem = base_action( mainWindow, u'ExportThemeItem', UiStrings.Export) self.ExportLanguageItem = base_action( mainWindow, u'ExportLanguageItem')#, UiStrings.Export) + ActionList.add_category(UiStrings.View, 85) self.ViewMediaManagerItem = shortcut_action(mainWindow, u'ViewMediaManagerItem', [QtGui.QKeySequence(u'F8')], self.toggleMediaManager, u':/system/system_mediamanager.png', @@ -210,6 +215,7 @@ class Ui_MainWindow(object): self.ViewLivePanel = shortcut_action(mainWindow, u'ViewLivePanel', [QtGui.QKeySequence(u'F12')], self.setLivePanelVisibility, checked=liveVisible, category=UiStrings.View) + ActionList.add_category(UiStrings.ViewMode, 80) self.ModeDefaultItem = checkable_action( mainWindow, u'ModeDefaultItem', category=UiStrings.ViewMode) self.ModeSetupItem = checkable_action( @@ -221,11 +227,13 @@ class Ui_MainWindow(object): self.ModeGroup.addAction(self.ModeSetupItem) self.ModeGroup.addAction(self.ModeLiveItem) self.ModeDefaultItem.setChecked(True) + ActionList.add_category(UiStrings.Tools, 75) self.ToolsAddToolItem = icon_action(mainWindow, u'ToolsAddToolItem', u':/tools/tools_add.png', category=UiStrings.Tools) self.ToolsOpenDataFolder = icon_action(mainWindow, u'ToolsOpenDataFolder', u':/general/general_open.png', category=UiStrings.Tools) + ActionList.add_category(UiStrings.Settings, 70) self.settingsPluginListItem = shortcut_action(mainWindow, u'settingsPluginListItem', [QtGui.QKeySequence(u'Alt+F7')], self.onPluginItemClicked, u':/system/settings_plugin_list.png', @@ -253,6 +261,7 @@ class Ui_MainWindow(object): self.SettingsConfigureItem = icon_action(mainWindow, u'SettingsConfigureItem', u':/system/system_settings.png', category=UiStrings.Settings) + ActionList.add_category(UiStrings.Help, 65) self.HelpDocumentationItem = icon_action(mainWindow, u'HelpDocumentationItem', u':/system/system_help_contents.png', category=None)#UiStrings.Help) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index caecaca50..528b642ce 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -167,6 +167,7 @@ class ServiceManager(QtGui.QWidget): 'Move item to the top of the service.'), self.onServiceTop, shortcuts=[QtCore.Qt.Key_Home]) self.serviceManagerList.moveTop.setObjectName(u'moveTop') + ActionList.add_category(UiStrings.Service, 45) ActionList.add_action( self.serviceManagerList.moveTop, UiStrings.Service) self.serviceManagerList.moveUp = self.orderToolbar.addToolbarButton( diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 66c26da3b..204dada75 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -367,12 +367,14 @@ class SlideController(QtGui.QWidget): def setPreviewHotkeys(self, parent=None): self.previousItem.setObjectName(u'previousItemPreview') self.nextItem.setObjectName(u'nextItemPreview') + ActionList.add_category(UiStrings.PreviewToolbar, 55) ActionList.add_action(self.previousItem, UiStrings.PreviewToolbar) ActionList.add_action(self.nextItem, UiStrings.PreviewToolbar) def setLiveHotkeys(self, parent=None): self.previousItem.setObjectName(u'previousItemLive') self.nextItem.setObjectName(u'nextItemLive') + ActionList.add_category(UiStrings.LiveToolbar, 60) ActionList.add_action(self.previousItem, UiStrings.LiveToolbar) ActionList.add_action(self.nextItem, UiStrings.LiveToolbar) self.previousService = shortcut_action(parent, u'previousService', diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index efac7b1bd..214a42249 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -154,13 +154,10 @@ class CategoryList(object): return False def append(self, name, actions=None): - weight = 0 - if len(self.categories) > 0: - weight = self.categories[-1].weight + 1 if actions: - self.add(name, weight, actions) + self.add(name, actions=actions) else: - self.add(name, weight) + self.add(name) def add(self, name, weight=0, actions=None): category = ActionCategory(name, weight) @@ -171,7 +168,7 @@ class CategoryList(object): else: category.actions.append(action) self.categories.append(category) - self.categories.sort(key=lambda cat: cat.weight) + self.categories.sort(key=lambda cat: cat.weight, reverse=True) def remove(self, name): for category in self.categories: @@ -230,6 +227,17 @@ class ActionList(object): @staticmethod def remove_action(action, category=None): + """ + This removes an action from its category. Empty categories are + automatically removed. + + ``action`` + The QAction object to be removed. + + ``category`` + The name (unicode string) of the category, which contains the + action. Defaults to None. + """ if category is not None: category = unicode(category) if category not in ActionList.categories: @@ -238,3 +246,25 @@ class ActionList(object): # Remove empty categories. if len(ActionList.categories[category].actions) == 0: ActionList.categories.remove(category) + + @staticmethod + def add_category(name, weight): + """ + Add an empty category to the list of categories. This is ony convenient + for categories with a given weight. + + ``name`` + The category's name. + + ``weight`` + The category's weight (int). + """ + if name in ActionList.categories: + # Only change the weight and resort the categories again. + for category in ActionList.categories: + if category.name == name: + category.weight = weight + ActionList.categories.categories.sort( + key=lambda cat: cat.weight, reverse=True) + return + ActionList.categories.add(name, weight) From 9011c85a6beedb1ba623374b34029c7cdc122556 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 9 Apr 2011 18:11:02 +0200 Subject: [PATCH 37/98] removed static methods --- openlp/core/lib/ui.py | 9 ++-- openlp/core/ui/mainwindow.py | 19 ++++---- openlp/core/ui/servicemanager.py | 23 +++++----- openlp/core/ui/shortcutlistform.py | 9 ++-- openlp/core/ui/slidecontroller.py | 18 +++++--- openlp/core/utils/actions.py | 50 +++++++++++++-------- openlp/plugins/alerts/alertsplugin.py | 6 ++- openlp/plugins/bibles/bibleplugin.py | 10 +++-- openlp/plugins/songs/songsplugin.py | 14 +++--- openlp/plugins/songusage/songusageplugin.py | 14 +++--- 10 files changed, 102 insertions(+), 70 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 35ea23497..20aca9c6a 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -259,7 +259,8 @@ def base_action(parent, name, category=None): action = QtGui.QAction(parent) action.setObjectName(name) if category is not None: - ActionList.add_action(action, category) + action_list = ActionList.get_instance() + action_list.add_action(action, category) return action def checkable_action(parent, name, checked=None, category=None): @@ -297,7 +298,8 @@ def shortcut_action(parent, name, shortcuts, function, icon=None, checked=None, action.setChecked(checked) action.setShortcuts(shortcuts) action.setShortcutContext(context) - ActionList.add_action(action, category) + action_list = ActionList.get_instance() + action_list.add_action(action, category) QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), function) return action @@ -335,7 +337,8 @@ def context_menu_action(base, icon, text, slot, shortcuts=None, category=None, if shortcuts is not None: action.setShortcuts(shortcuts) action.setShortcutContext(context) - ActionList.add_action(action) + action_list = ActionList.get_instance() + action_list.add_action(action) return action def context_menu(base, icon, text): diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 63c5aa777..2038e1972 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -39,7 +39,7 @@ from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \ ShortcutListForm, DisplayTagForm from openlp.core.utils import AppLocation, add_actions, LanguageManager, \ get_application_version -from openlp.core.utils.actions import ActionList +from openlp.core.utils.actions import ActionList, CategoryOrder log = logging.getLogger(__name__) @@ -162,7 +162,8 @@ class Ui_MainWindow(object): mainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.themeManagerDock) # Create the menu items - ActionList.add_category(UiStrings.File, 100) + action_list = ActionList.get_instance() + action_list.add_category(UiStrings.File, CategoryOrder.standardMenu) self.FileNewItem = shortcut_action(mainWindow, u'FileNewItem', [QtGui.QKeySequence(u'Ctrl+N')], self.ServiceManagerContents.onNewServiceClicked, @@ -185,17 +186,17 @@ class Ui_MainWindow(object): self.FileExitItem = shortcut_action(mainWindow, u'FileExitItem', [QtGui.QKeySequence(u'Alt+F4')], mainWindow.close, u':/system/system_exit.png', category=UiStrings.File) - ActionList.add_category(UiStrings.Import, 95) + action_list.add_category(UiStrings.Import, CategoryOrder.standardMenu) self.ImportThemeItem = base_action( mainWindow, u'ImportThemeItem', UiStrings.Import) self.ImportLanguageItem = base_action( mainWindow, u'ImportLanguageItem')#, UiStrings.Import) - ActionList.add_category(UiStrings.Export, 90) + action_list.add_category(UiStrings.Export, CategoryOrder.standardMenu) self.ExportThemeItem = base_action( mainWindow, u'ExportThemeItem', UiStrings.Export) self.ExportLanguageItem = base_action( mainWindow, u'ExportLanguageItem')#, UiStrings.Export) - ActionList.add_category(UiStrings.View, 85) + action_list.add_category(UiStrings.View, CategoryOrder.standardMenu) self.ViewMediaManagerItem = shortcut_action(mainWindow, u'ViewMediaManagerItem', [QtGui.QKeySequence(u'F8')], self.toggleMediaManager, u':/system/system_mediamanager.png', @@ -215,7 +216,7 @@ class Ui_MainWindow(object): self.ViewLivePanel = shortcut_action(mainWindow, u'ViewLivePanel', [QtGui.QKeySequence(u'F12')], self.setLivePanelVisibility, checked=liveVisible, category=UiStrings.View) - ActionList.add_category(UiStrings.ViewMode, 80) + action_list.add_category(UiStrings.ViewMode, CategoryOrder.standardMenu) self.ModeDefaultItem = checkable_action( mainWindow, u'ModeDefaultItem', category=UiStrings.ViewMode) self.ModeSetupItem = checkable_action( @@ -227,13 +228,13 @@ class Ui_MainWindow(object): self.ModeGroup.addAction(self.ModeSetupItem) self.ModeGroup.addAction(self.ModeLiveItem) self.ModeDefaultItem.setChecked(True) - ActionList.add_category(UiStrings.Tools, 75) + action_list.add_category(UiStrings.Tools, CategoryOrder.standardMenu) self.ToolsAddToolItem = icon_action(mainWindow, u'ToolsAddToolItem', u':/tools/tools_add.png', category=UiStrings.Tools) self.ToolsOpenDataFolder = icon_action(mainWindow, u'ToolsOpenDataFolder', u':/general/general_open.png', category=UiStrings.Tools) - ActionList.add_category(UiStrings.Settings, 70) + action_list.add_category(UiStrings.Settings, CategoryOrder.standardMenu) self.settingsPluginListItem = shortcut_action(mainWindow, u'settingsPluginListItem', [QtGui.QKeySequence(u'Alt+F7')], self.onPluginItemClicked, u':/system/settings_plugin_list.png', @@ -261,7 +262,7 @@ class Ui_MainWindow(object): self.SettingsConfigureItem = icon_action(mainWindow, u'SettingsConfigureItem', u':/system/system_settings.png', category=UiStrings.Settings) - ActionList.add_category(UiStrings.Help, 65) + action_list.add_category(UiStrings.Help, CategoryOrder.standardMenu) self.HelpDocumentationItem = icon_action(mainWindow, u'HelpDocumentationItem', u':/system/system_help_contents.png', category=None)#UiStrings.Help) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 528b642ce..864acc138 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -40,7 +40,7 @@ from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm from openlp.core.ui.printserviceform import PrintServiceForm from openlp.core.utils import AppLocation, delete_file, file_is_unicode, \ split_filename -from openlp.core.utils.actions import ActionList +from openlp.core.utils.actions import ActionList, CategoryOrder class ServiceManagerList(QtGui.QTreeWidget): """ @@ -167,8 +167,9 @@ class ServiceManager(QtGui.QWidget): 'Move item to the top of the service.'), self.onServiceTop, shortcuts=[QtCore.Qt.Key_Home]) self.serviceManagerList.moveTop.setObjectName(u'moveTop') - ActionList.add_category(UiStrings.Service, 45) - ActionList.add_action( + action_list = ActionList.get_instance() + action_list.add_category(UiStrings.Service, CategoryOrder.standardToolbar) + action_list.add_action( self.serviceManagerList.moveTop, UiStrings.Service) self.serviceManagerList.moveUp = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move &up'), @@ -177,7 +178,7 @@ class ServiceManager(QtGui.QWidget): 'Move item up one position in the service.'), self.onServiceUp, shortcuts=[QtCore.Qt.Key_PageUp]) self.serviceManagerList.moveUp.setObjectName(u'moveUp') - ActionList.add_action(self.serviceManagerList.moveUp, UiStrings.Service) + action_list.add_action(self.serviceManagerList.moveUp, UiStrings.Service) self.serviceManagerList.moveDown = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move &down'), u':/services/service_down.png', @@ -185,7 +186,7 @@ class ServiceManager(QtGui.QWidget): 'Move item down one position in the service.'), self.onServiceDown, shortcuts=[QtCore.Qt.Key_PageDown]) self.serviceManagerList.moveDown.setObjectName(u'moveDown') - ActionList.add_action( + action_list.add_action( self.serviceManagerList.moveDown, UiStrings.Service) self.serviceManagerList.moveBottom = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move to &bottom'), @@ -194,7 +195,7 @@ class ServiceManager(QtGui.QWidget): 'Move item to the end of the service.'), self.onServiceEnd, shortcuts=[QtCore.Qt.Key_End]) self.serviceManagerList.moveBottom.setObjectName(u'moveBottom') - ActionList.add_action( + action_list.add_action( self.serviceManagerList.moveBottom, UiStrings.Service) self.serviceManagerList.down = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move &down'), @@ -203,7 +204,7 @@ class ServiceManager(QtGui.QWidget): 'Moves the selection down the window.'), self.onMoveSelectionDown, shortcuts=[QtCore.Qt.Key_Down]) self.serviceManagerList.down.setObjectName(u'down') - ActionList.add_action(self.serviceManagerList.down) + action_list.add_action(self.serviceManagerList.down) self.serviceManagerList.down.setVisible(False) self.serviceManagerList.up = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move up'), @@ -212,7 +213,7 @@ class ServiceManager(QtGui.QWidget): 'Moves the selection up the window.'), self.onMoveSelectionUp, shortcuts=[QtCore.Qt.Key_Up]) self.serviceManagerList.up.setObjectName(u'up') - ActionList.add_action(self.serviceManagerList.up) + action_list.add_action(self.serviceManagerList.up) self.serviceManagerList.up.setVisible(False) self.orderToolbar.addSeparator() self.serviceManagerList.delete = self.orderToolbar.addToolbarButton( @@ -229,7 +230,7 @@ class ServiceManager(QtGui.QWidget): 'Expand all the service items.'), self.onExpandAll, shortcuts=[QtCore.Qt.Key_Plus]) self.serviceManagerList.expand.setObjectName(u'expand') - ActionList.add_action(self.serviceManagerList.expand, UiStrings.Service) + action_list.add_action(self.serviceManagerList.expand, UiStrings.Service) self.serviceManagerList.collapse = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', '&Collapse all'), u':/services/service_collapse_all.png', @@ -237,7 +238,7 @@ class ServiceManager(QtGui.QWidget): 'Collapse all the service items.'), self.onCollapseAll, shortcuts=[QtCore.Qt.Key_Minus]) self.serviceManagerList.collapse.setObjectName(u'collapse') - ActionList.add_action( + action_list.add_action( self.serviceManagerList.collapse, UiStrings.Service) self.orderToolbar.addSeparator() self.serviceManagerList.makeLive = self.orderToolbar.addToolbarButton( @@ -247,7 +248,7 @@ class ServiceManager(QtGui.QWidget): 'Send the selected item to Live.'), self.makeLive, shortcuts=[QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return]) self.serviceManagerList.makeLive.setObjectName(u'orderToolbar') - ActionList.add_action( + action_list.add_action( self.serviceManagerList.makeLive, UiStrings.Service) self.layout.addWidget(self.orderToolbar) # Connect up our signals and slots diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 336e160b1..ddf84fdd6 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -46,6 +46,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): QtGui.QDialog.__init__(self, parent) self.setupUi(self) self.changedActions = {} + self.action_list = ActionList.get_instance() QtCore.QObject.connect(self.primaryPushButton, QtCore.SIGNAL(u'toggled(bool)'), self.onPrimaryPushButtonClicked) QtCore.QObject.connect(self.alternatePushButton, @@ -96,7 +97,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): # The action we are attempting to change. changing_action = self._currentItemAction() shortcut_valid = True - for category in ActionList.categories: + for category in self.action_list.categories: for action in category.actions: shortcuts = self._actionShortcuts(action) if key_sequence not in shortcuts: @@ -149,7 +150,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): Reload the ``treeWidget`` list to add new and remove old actions. """ self.treeWidget.clear() - for category in ActionList.categories: + for category in self.action_list.categories: # Check if the category is for internal use only. if category.name is None: continue @@ -303,7 +304,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): return self._adjustButton(self.primaryPushButton, False, text=u'') self._adjustButton(self.alternatePushButton, False, text=u'') - for category in ActionList.categories: + for category in self.action_list.categories: for action in category.actions: self.changedActions[action] = action.defaultShortcuts self.refreshShortcutList() @@ -349,7 +350,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): """ settings = QtCore.QSettings() settings.beginGroup(u'shortcuts') - for category in ActionList.categories: + for category in self.action_list.categories: # Check if the category is for internal use only. if category.name is None: continue diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 204dada75..4f5ed1ea7 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -34,7 +34,7 @@ from openlp.core.lib import OpenLPToolbar, Receiver, resize_image, \ ItemCapabilities, translate from openlp.core.lib.ui import UiStrings, shortcut_action from openlp.core.ui import HideMode, MainDisplay -from openlp.core.utils.actions import ActionList +from openlp.core.utils.actions import ActionList, CategoryOrder log = logging.getLogger(__name__) @@ -367,16 +367,20 @@ class SlideController(QtGui.QWidget): def setPreviewHotkeys(self, parent=None): self.previousItem.setObjectName(u'previousItemPreview') self.nextItem.setObjectName(u'nextItemPreview') - ActionList.add_category(UiStrings.PreviewToolbar, 55) - ActionList.add_action(self.previousItem, UiStrings.PreviewToolbar) - ActionList.add_action(self.nextItem, UiStrings.PreviewToolbar) + action_list = ActionList.get_instance() + action_list.add_category( + UiStrings.PreviewToolbar, CategoryOrder.standardToolbar) + action_list.add_action(self.previousItem, UiStrings.PreviewToolbar) + action_list.add_action(self.nextItem, UiStrings.PreviewToolbar) def setLiveHotkeys(self, parent=None): self.previousItem.setObjectName(u'previousItemLive') self.nextItem.setObjectName(u'nextItemLive') - ActionList.add_category(UiStrings.LiveToolbar, 60) - ActionList.add_action(self.previousItem, UiStrings.LiveToolbar) - ActionList.add_action(self.nextItem, UiStrings.LiveToolbar) + action_list = ActionList.get_instance() + action_list.add_category( + UiStrings.LiveToolbar, CategoryOrder.standardToolbar) + action_list.add_action(self.previousItem, UiStrings.LiveToolbar) + action_list.add_action(self.nextItem, UiStrings.LiveToolbar) self.previousService = shortcut_action(parent, u'previousService', [QtCore.Qt.Key_Left], self.servicePrevious, UiStrings.LiveToolbar) self.previousService.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 214a42249..76b72c55c 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -183,10 +183,18 @@ class ActionList(object): has a weight by which it is sorted when iterating through the list of actions or categories. """ - categories = CategoryList() + instance = None + + def __init__(self): + self.categories = CategoryList() @staticmethod - def add_action(action, category=None, weight=None): + def get_instance(): + if ActionList.instance is None: + ActionList.instance = ActionList() + return ActionList.instance + + def add_action(self, action, category=None, weight=None): """ Add an action to the list of actions. @@ -206,13 +214,13 @@ class ActionList(object): """ if category is not None: category = unicode(category) - if category not in ActionList.categories: - ActionList.categories.append(category) + if category not in self.categories: + self.categories.append(category) action.defaultShortcuts = action.shortcuts() if weight is None: - ActionList.categories[category].actions.append(action) + self.categories[category].actions.append(action) else: - ActionList.categories[category].actions.add(action, weight) + self.categories[category].actions.add(action, weight) if category is None: # Stop here, as this action is not configurable. return @@ -225,8 +233,7 @@ class ActionList(object): [QtGui.QKeySequence(shortcut) for shortcut in shortcuts]) settings.endGroup() - @staticmethod - def remove_action(action, category=None): + def remove_action(self, action, category=None): """ This removes an action from its category. Empty categories are automatically removed. @@ -240,15 +247,14 @@ class ActionList(object): """ if category is not None: category = unicode(category) - if category not in ActionList.categories: + if category not in self.categories: return - ActionList.categories[category].actions.remove(action) + self.categories[category].actions.remove(action) # Remove empty categories. - if len(ActionList.categories[category].actions) == 0: - ActionList.categories.remove(category) + if len(self.categories[category].actions) == 0: + self.categories.remove(category) - @staticmethod - def add_category(name, weight): + def add_category(self, name, weight): """ Add an empty category to the list of categories. This is ony convenient for categories with a given weight. @@ -259,12 +265,20 @@ class ActionList(object): ``weight`` The category's weight (int). """ - if name in ActionList.categories: + if name in self.categories: # Only change the weight and resort the categories again. - for category in ActionList.categories: + for category in self.categories: if category.name == name: category.weight = weight - ActionList.categories.categories.sort( + self.categories.categories.sort( key=lambda cat: cat.weight, reverse=True) return - ActionList.categories.add(name, weight) + self.categories.add(name, weight) + + +class CategoryOrder(object): + """ + An enumeration class for category weights. + """ + standardMenu = 100 + standardToolbar = 90 diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index d564a9754..7d6eac6e2 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -75,7 +75,8 @@ class AlertsPlugin(Plugin): log.info(u'Alerts Initialising') Plugin.initialise(self) self.toolsAlertItem.setVisible(True) - ActionList.add_action(self.toolsAlertItem, UiStrings.Tools) + action_list = ActionList.get_instance() + action_list.add_action(self.toolsAlertItem, UiStrings.Tools) self.liveController.alertTab = self.settings_tab def finalise(self): @@ -86,7 +87,8 @@ class AlertsPlugin(Plugin): self.manager.finalise() Plugin.finalise(self) self.toolsAlertItem.setVisible(False) - ActionList.remove_action(self.toolsAlertItem, u'Tools') + action_list = ActionList.get_instance() + action_list.remove_action(self.toolsAlertItem, u'Tools') def toggleAlertsState(self): self.alertsActive = not self.alertsActive diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 60be388ba..aea732688 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -52,9 +52,10 @@ class BiblePlugin(Plugin): self.manager = BibleManager(self) Plugin.initialise(self) self.importBibleItem.setVisible(True) - ActionList.add_action(self.importBibleItem, UiStrings.Import) + action_list = ActionList.get_instance() + action_list.add_action(self.importBibleItem, UiStrings.Import) # Do not add the action to the list yet. - #ActionList.add_action(self.exportBibleItem, UiStrings.Export) + #action_list.add_action(self.exportBibleItem, UiStrings.Export) # Set to invisible until we can export bibles self.exportBibleItem.setVisible(False) @@ -65,9 +66,10 @@ class BiblePlugin(Plugin): log.info(u'Plugin Finalise') self.manager.finalise() Plugin.finalise(self) - ActionList.remove_action(self.importBibleItem, UiStrings.Import) + action_list = ActionList.get_instance() + action_list.remove_action(self.importBibleItem, UiStrings.Import) self.importBibleItem.setVisible(False) - #ActionList.remove_action(self.exportBibleItem, UiStrings.Export) + #action_list.remove_action(self.exportBibleItem, UiStrings.Export) self.exportBibleItem.setVisible(False) def addImportMenuItem(self, import_menu): diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index a3230ff7f..af50f3f94 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -66,9 +66,10 @@ class SongsPlugin(Plugin): log.info(u'Songs Initialising') Plugin.initialise(self) self.toolsReindexItem.setVisible(True) - ActionList.add_action(self.SongImportItem, UiStrings.Import) - ActionList.add_action(self.SongExportItem, UiStrings.Export) - ActionList.add_action(self.toolsReindexItem, UiStrings.Tools) + action_list = ActionList.get_instance() + action_list.add_action(self.SongImportItem, UiStrings.Import) + action_list.add_action(self.SongExportItem, UiStrings.Export) + action_list.add_action(self.toolsReindexItem, UiStrings.Tools) self.mediaItem.displayResultsSong( self.manager.get_all_objects(Song, order_by_ref=Song.search_title)) @@ -258,7 +259,8 @@ class SongsPlugin(Plugin): log.info(u'Songs Finalising') self.manager.finalise() self.toolsReindexItem.setVisible(False) - ActionList.remove_action(self.SongImportItem, UiStrings.Import) - ActionList.remove_action(self.SongExportItem, UiStrings.Export) - ActionList.remove_action(self.toolsReindexItem, UiStrings.Tools) + action_list = ActionList.get_instance() + action_list.remove_action(self.SongImportItem, UiStrings.Import) + action_list.remove_action(self.SongExportItem, UiStrings.Export) + action_list.remove_action(self.toolsReindexItem, UiStrings.Tools) Plugin.finalise(self) diff --git a/openlp/plugins/songusage/songusageplugin.py b/openlp/plugins/songusage/songusageplugin.py index b0a545ad8..5f21451b6 100644 --- a/openlp/plugins/songusage/songusageplugin.py +++ b/openlp/plugins/songusage/songusageplugin.py @@ -110,11 +110,12 @@ class SongUsagePlugin(Plugin): self.settingsSection + u'/active', QtCore.QVariant(False)).toBool() self.SongUsageStatus.setChecked(self.SongUsageActive) - ActionList.add_action(self.SongUsageDelete, + action_list = ActionList.get_instance() + action_list.add_action(self.SongUsageDelete, translate('SongUsagePlugin', 'Song Usage')) - ActionList.add_action(self.SongUsageReport, + action_list.add_action(self.SongUsageReport, translate('SongUsagePlugin', 'Song Usage')) - ActionList.add_action(self.SongUsageStatus, + action_list.add_action(self.SongUsageStatus, translate('SongUsagePlugin', 'Song Usage')) if self.manager is None: self.manager = Manager(u'songusage', init_schema) @@ -131,11 +132,12 @@ class SongUsagePlugin(Plugin): self.manager.finalise() Plugin.finalise(self) self.SongUsageMenu.menuAction().setVisible(False) - ActionList.remove_action(self.SongUsageDelete, + action_list = ActionList.get_instance() + action_list.remove_action(self.SongUsageDelete, translate('SongUsagePlugin', 'Song Usage')) - ActionList.remove_action(self.SongUsageReport, + action_list.remove_action(self.SongUsageReport, translate('SongUsagePlugin', 'Song Usage')) - ActionList.remove_action(self.SongUsageStatus, + action_list.remove_action(self.SongUsageStatus, translate('SongUsagePlugin', 'Song Usage')) #stop any events being processed self.SongUsageActive = False From a54a3eb79255727b3027973eec3b2151244fda41 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 9 Apr 2011 20:14:51 +0200 Subject: [PATCH 38/98] make first column wider; added description label back; removed Qt = Qt.QCore; fixed litte bug which left the shortcut text on the buttons --- openlp/core/ui/shortcutlistdialog.py | 13 +++++++++---- openlp/core/ui/shortcutlistform.py | 15 +++++++++------ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index a5050ab25..32fe68b95 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -33,11 +33,16 @@ class Ui_ShortcutListDialog(object): shortcutListDialog.setObjectName(u'shortcutListDialog') shortcutListDialog.resize(500, 438) self.shortcutListLayout = QtGui.QVBoxLayout(shortcutListDialog) - self.shortcutListLayout.setObjectName(u'shortcutListLayout') + self.shortcutListLayout.setObjectName(u'shortcutLitLayout') + self.descriptionLabel = QtGui.QLabel(shortcutListDialog) + self.descriptionLabel.setObjectName(u'descriptionLabel') + self.descriptionLabel.setWordWrap(True) + self.shortcutListLayout.addWidget(self.descriptionLabel) self.treeWidget = QtGui.QTreeWidget(shortcutListDialog) self.treeWidget.setObjectName(u'treeWidget') self.treeWidget.setAlternatingRowColors(True) self.treeWidget.setColumnCount(3) + self.treeWidget.setColumnWidth(0, 250) self.shortcutListLayout.addWidget(self.treeWidget) self.detailsLayout = QtGui.QGridLayout() self.detailsLayout.setObjectName(u'detailsLayout') @@ -102,9 +107,9 @@ class Ui_ShortcutListDialog(object): def retranslateUi(self, shortcutListDialog): shortcutListDialog.setWindowTitle( translate('OpenLP.ShortcutListDialog', 'Customize Shortcuts')) - #self.descriptionLabel.setText(translate('OpenLP.ShortcutListDialog', - #'Select an action and click the button below to start capturing ' - #'a new shortcut.')) + self.descriptionLabel.setText(translate('OpenLP.ShortcutListDialog', + 'Select an action and click one of the buttons below to start ' + 'capturing a new primary or alternate shortcut, respectively.')) self.treeWidget.setHeaderLabels([ translate('OpenLP.ShortcutListDialog', 'Action'), translate('OpenLP.ShortcutListDialog', 'Shortcut'), diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index ddf84fdd6..26bfddadd 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -78,20 +78,21 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): self.close() def keyReleaseEvent(self, event): - Qt = QtCore.Qt if not self.primaryPushButton.isChecked() and \ not self.alternatePushButton.isChecked(): return key = event.key() - if key == Qt.Key_Shift or key == Qt.Key_Control or \ - key == Qt.Key_Meta or key == Qt.Key_Alt: + if key == QtCore.Qt.Key_Shift or key == QtCore.Qt.Key_Control or \ + key == QtCore.Qt.Key_Meta or key == QtCore.Qt.Key_Alt: return key_string = QtGui.QKeySequence(key).toString() - if event.modifiers() & Qt.ControlModifier == Qt.ControlModifier: + if event.modifiers() & QtCore.Qt.ControlModifier == \ + QtCore.Qt.ControlModifier: key_string = u'Ctrl+' + key_string - if event.modifiers() & Qt.AltModifier == Qt.AltModifier: + if event.modifiers() & QtCore.Qt.AltModifier == QtCore.Qt.AltModifier: key_string = u'Alt+' + key_string - if event.modifiers() & Qt.ShiftModifier == Qt.ShiftModifier: + if event.modifiers() & QtCore.Qt.ShiftModifier == \ + QtCore.Qt.ShiftModifier: key_string = u'Shift+' + key_string key_sequence = QtGui.QKeySequence(key_string) # The action we are attempting to change. @@ -377,6 +378,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): new_shortcuts.append(shortcuts[1]) self.changedActions[action] = new_shortcuts self.refreshShortcutList() + self.onCurrentItemChanged(self.treeWidget.currentItem()) def onClearAlternateButtonClicked(self, toggled): """ @@ -394,6 +396,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): new_shortcuts.append(action.defaultShortcuts[1]) self.changedActions[action] = new_shortcuts self.refreshShortcutList() + self.onCurrentItemChanged(self.treeWidget.currentItem()) def _actionShortcuts(self, action): """ From 29ef34b569d9f9c1561201f8a1bb7d8d559fd3ef Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 10 Apr 2011 07:18:31 +0100 Subject: [PATCH 39/98] Add saving of selected Bible names in Quick and Advanced. Clean up combo box selection code (General) Fixes: https://launchpad.net/bugs/755687 --- openlp/core/lib/ui.py | 17 ++++++++++ openlp/core/ui/servicemanager.py | 11 ++----- openlp/core/ui/themestab.py | 9 ++---- openlp/plugins/bibles/lib/biblestab.py | 9 ++---- openlp/plugins/bibles/lib/mediaitem.py | 32 +++++++++++++------ openlp/plugins/custom/forms/editcustomform.py | 8 ++--- openlp/plugins/songs/forms/editsongform.py | 17 ++-------- 7 files changed, 51 insertions(+), 52 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index c1a9f8b35..ffcc32414 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -315,3 +315,20 @@ def create_valign_combo(form, parent, layout): form.verticalComboBox.addItem(UiStrings.Bottom) verticalLabel.setBuddy(form.verticalComboBox) layout.addRow(verticalLabel, form.verticalComboBox) + +def find_in_combo_box(combo_box, value_to_find): + """ + Find a string in a combo box and set it as the selected item if present + + ``combo_box`` + The combo box to check for selected items + + ``value_to_find`` + The value to find + """ + index = combo_box.findText(value_to_find, + QtCore.Qt.MatchExactly) + if index == -1: + # Not Found. + index = 0 + combo_box.setCurrentIndex(index) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 9c1c63917..a07d7f970 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -35,7 +35,8 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import OpenLPToolbar, ServiceItem, context_menu_action, \ Receiver, build_icon, ItemCapabilities, SettingsManager, translate from openlp.core.lib.theme import ThemeLevel -from openlp.core.lib.ui import UiStrings, critical_error_message_box +from openlp.core.lib.ui import UiStrings, critical_error_message_box, \ + find_in_combo_box from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm from openlp.core.ui.printserviceform import PrintServiceForm from openlp.core.utils import AppLocation, delete_file, file_is_unicode, \ @@ -1261,13 +1262,7 @@ class ServiceManager(QtGui.QWidget): action = context_menu_action(self.serviceManagerList, None, theme, self.onThemeChangeAction) self.themeMenu.addAction(action) - index = self.themeComboBox.findText(self.service_theme, - QtCore.Qt.MatchExactly) - # Not Found - if index == -1: - index = 0 - self.service_theme = u'' - self.themeComboBox.setCurrentIndex(index) + find_in_combo_box(self.themeComboBox, self.service_theme) self.mainwindow.renderManager.set_service_theme(self.service_theme) self.regenerateServiceItems() diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index 1b76d2198..3307b94fe 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -28,7 +28,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import SettingsTab, Receiver, translate from openlp.core.lib.theme import ThemeLevel -from openlp.core.lib.ui import UiStrings +from openlp.core.lib.ui import UiStrings, find_in_combo_box class ThemesTab(SettingsTab): """ @@ -185,12 +185,7 @@ class ThemesTab(SettingsTab): self.DefaultComboBox.clear() for theme in theme_list: self.DefaultComboBox.addItem(theme) - id = self.DefaultComboBox.findText( - self.global_theme, QtCore.Qt.MatchExactly) - if id == -1: - id = 0 # Not Found - self.global_theme = u'' - self.DefaultComboBox.setCurrentIndex(id) + find_in_combo_box(self.DefaultComboBox, self.global_theme) self.parent.renderManager.set_global_theme( self.global_theme, self.theme_level) if self.global_theme is not u'': diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index 7a631ad09..d2e6ad8d9 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -30,6 +30,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Receiver, SettingsTab, translate from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle +from openlp.core.lib.ui import find_in_combo_box log = logging.getLogger(__name__) @@ -208,10 +209,4 @@ class BiblesTab(SettingsTab): self.BibleThemeComboBox.addItem(u'') for theme in theme_list: self.BibleThemeComboBox.addItem(theme) - index = self.BibleThemeComboBox.findText( - unicode(self.bible_theme), QtCore.Qt.MatchExactly) - if index == -1: - # Not Found. - index = 0 - self.bible_theme = u'' - self.BibleThemeComboBox.setCurrentIndex(index) + find_in_combo_box(self.BibleThemeComboBox, self.bible_theme) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index a9694fd0c..5c77a75d7 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -32,7 +32,7 @@ from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \ translate from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.ui import UiStrings, add_widget_completer, \ - media_item_combo_box, critical_error_message_box + media_item_combo_box, critical_error_message_box, find_in_combo_box from openlp.plugins.bibles.forms import BibleImportForm from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, \ VerseReferenceList, get_reference_match @@ -274,7 +274,7 @@ class BibleMediaItem(MediaManagerItem): log.debug(u'bible manager initialise') self.parent.manager.media = self self.loadBibles() - self.updateAutoCompleter() + self.updateAutoCompleter(False) self.configUpdated() log.debug(u'bible manager initialise complete') @@ -298,23 +298,25 @@ class BibleMediaItem(MediaManagerItem): bibles = self.parent.manager.get_bibles().keys() bibles.sort() # Load the bibles into the combo boxes. - first = True for bible in bibles: if bible: self.quickVersionComboBox.addItem(bible) self.quickSecondComboBox.addItem(bible) self.advancedVersionComboBox.addItem(bible) self.advancedSecondComboBox.addItem(bible) - if first: - first = False - self.initialiseBible(bible) + # set the default value + book = QtCore.QSettings().value( + self.settingsSection + u'/advanced bible', + QtCore.QVariant(u'')).toString() + find_in_combo_box(self.advancedVersionComboBox, book) + self.initialiseAdvancedBible(unicode(book)) def reloadBibles(self): log.debug(u'Reloading Bibles') self.parent.manager.reload_bibles() self.loadBibles() - def initialiseBible(self, bible): + def initialiseAdvancedBible(self, bible): """ This initialises the given bible, which means that its book names and their chapter numbers is added to the combo boxes on the @@ -324,7 +326,7 @@ class BibleMediaItem(MediaManagerItem): ``bible`` The bible to initialise (unicode). """ - log.debug(u'initialiseBible %s', bible) + log.debug(u'initialiseAdvancedBible %s', bible) book_data = self.parent.manager.get_books(bible) self.advancedBookComboBox.clear() first = True @@ -354,12 +356,20 @@ class BibleMediaItem(MediaManagerItem): self.adjustComboBox(1, verse_count, self.advancedFromVerse) self.adjustComboBox(1, verse_count, self.advancedToVerse) - def updateAutoCompleter(self): + def updateAutoCompleter(self, updateConfig=True): """ This updates the bible book completion list for the search field. The completion depends on the bible. It is only updated when we are doing a reference search, otherwise the auto completion list is removed. """ + if updateConfig: + QtCore.QSettings().setValue(self.settingsSection + u'/quick bible', + QtCore.QVariant(self.quickVersionComboBox.currentText())) + else: + book = QtCore.QSettings().value( + self.settingsSection + u'/quick bible', + QtCore.QVariant(u'')).toString() + find_in_combo_box(self.quickVersionComboBox, book) books = [] # We have to do a 'Reference Search'. if self.quickSearchEdit.currentSearchType() == BibleSearch.Reference: @@ -372,7 +382,9 @@ class BibleMediaItem(MediaManagerItem): add_widget_completer(books, self.quickSearchEdit) def onAdvancedVersionComboBox(self): - self.initialiseBible( + QtCore.QSettings().setValue(self.settingsSection + u'/advanced bible', + QtCore.QVariant(self.advancedVersionComboBox.currentText())) + self.initialiseAdvancedBible( unicode(self.advancedVersionComboBox.currentText())) def onAdvancedBookComboBox(self): diff --git a/openlp/plugins/custom/forms/editcustomform.py b/openlp/plugins/custom/forms/editcustomform.py index f81bd4c7d..3ebd5f984 100644 --- a/openlp/plugins/custom/forms/editcustomform.py +++ b/openlp/plugins/custom/forms/editcustomform.py @@ -29,7 +29,7 @@ import logging from PyQt4 import QtCore, QtGui from openlp.core.lib import Receiver, translate -from openlp.core.lib.ui import critical_error_message_box +from openlp.core.lib.ui import critical_error_message_box, find_in_combo_box from openlp.plugins.custom.lib import CustomXMLBuilder, CustomXMLParser from openlp.plugins.custom.lib.db import CustomSlide from editcustomdialog import Ui_CustomEditDialog @@ -98,11 +98,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): for slide in slideList: self.slideListView.addItem(slide[1]) theme = self.customSlide.theme_name - id = self.themeComboBox.findText(theme, QtCore.Qt.MatchExactly) - # No theme match - if id == -1: - id = 0 - self.themeComboBox.setCurrentIndex(id) + find_in_combo_box(self.themeComboBox, theme) # If not preview hide the preview button. self.previewButton.setVisible(False) if preview: diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 2f21e57cd..8dfae1736 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -31,7 +31,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Receiver, translate from openlp.core.lib.ui import UiStrings, add_widget_completer, \ - critical_error_message_box + critical_error_message_box, find_in_combo_box from openlp.plugins.songs.forms import EditVerseForm from openlp.plugins.songs.lib import SongXML, VerseType, clean_song from openlp.plugins.songs.lib.db import Book, Song, Author, Topic @@ -208,20 +208,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.alternativeEdit.setText(u'') if self.song.song_book_id != 0: book_name = self.manager.get_object(Book, self.song.song_book_id) - id = self.songBookComboBox.findText( - unicode(book_name.name), QtCore.Qt.MatchExactly) - if id == -1: - # Not Found - id = 0 - self.songBookComboBox.setCurrentIndex(id) + find_in_combo_box(self.songBookComboBox, unicode(book_name.name)) if self.song.theme_name: - id = self.themeComboBox.findText( - unicode(self.song.theme_name), QtCore.Qt.MatchExactly) - if id == -1: - # Not Found - id = 0 - self.song.theme_name = None - self.themeComboBox.setCurrentIndex(id) + find_in_combo_box(self.themeComboBox, unicode(self.song.theme_name)) if self.song.copyright: self.copyrightEdit.setText(self.song.copyright) else: From c55df2583a2c3fc558b0c5d52079589f59dbe956 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 10 Apr 2011 07:27:23 +0100 Subject: [PATCH 40/98] Remove trailing comma Fixes: https://launchpad.net/bugs/748960 --- openlp/plugins/bibles/lib/versereferencelist.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openlp/plugins/bibles/lib/versereferencelist.py b/openlp/plugins/bibles/lib/versereferencelist.py index bc28f2570..bab6d7e11 100644 --- a/openlp/plugins/bibles/lib/versereferencelist.py +++ b/openlp/plugins/bibles/lib/versereferencelist.py @@ -96,4 +96,7 @@ class VerseReferenceList(object): version[u'copyright']) if version[u'permission'].strip(): result = result + u', ' + version[u'permission'] + result = result.rstrip() + if result.endswith(u','): + return result[:len(result)-1] return result From b7a1554abda5e718744261006240f97210d11f38 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 10 Apr 2011 09:07:02 +0200 Subject: [PATCH 41/98] fixed objectName --- openlp/core/ui/shortcutlistdialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index 32fe68b95..288086cba 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -33,7 +33,7 @@ class Ui_ShortcutListDialog(object): shortcutListDialog.setObjectName(u'shortcutListDialog') shortcutListDialog.resize(500, 438) self.shortcutListLayout = QtGui.QVBoxLayout(shortcutListDialog) - self.shortcutListLayout.setObjectName(u'shortcutLitLayout') + self.shortcutListLayout.setObjectName(u'shortcutListLayout') self.descriptionLabel = QtGui.QLabel(shortcutListDialog) self.descriptionLabel.setObjectName(u'descriptionLabel') self.descriptionLabel.setWordWrap(True) From c8d2baff7ce25635474903c478e7be54687827fa Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 10 Apr 2011 08:32:15 +0100 Subject: [PATCH 42/98] Add bible verse layout to Quick tab. Not moved to Advanced tab as no real estate. Fixes: https://launchpad.net/bugs/745218 --- openlp/core/lib/ui.py | 4 + openlp/plugins/bibles/lib/biblestab.py | 161 ++++++++++++------------- openlp/plugins/bibles/lib/mediaitem.py | 25 ++++ 3 files changed, 109 insertions(+), 81 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index ffcc32414..9b03cce03 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -49,8 +49,10 @@ class UiStrings(object): Cancel = translate('OpenLP.Ui', 'Cancel') CCLINumberLabel = translate('OpenLP.Ui', 'CCLI number:') CreateService = translate('OpenLP.Ui', 'Create a new service.') + Continuous = translate('OpenLP.Ui', 'Continuous') Default = unicode(translate('OpenLP.Ui', 'Default')) Delete = translate('OpenLP.Ui', '&Delete') + DisplayStyle = translate('OpenLP.Ui', 'Display style:') Edit = translate('OpenLP.Ui', '&Edit') EmptyField = translate('OpenLP.Ui', 'Empty Field') Error = translate('OpenLP.Ui', 'Error') @@ -97,6 +99,8 @@ class UiStrings(object): Theme = translate('OpenLP.Ui', 'Theme', 'Singular') Themes = translate('OpenLP.Ui', 'Themes', 'Plural') Top = translate('OpenLP.Ui', 'Top') + VersePerSlide = translate('OpenLP.Ui', 'Verse Per Slide') + VersePerLine = translate('OpenLP.Ui', 'Verse Per Line') Version = translate('OpenLP.Ui', 'Version') def add_welcome_page(parent, image): diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index d2e6ad8d9..5133587b3 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -30,7 +30,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Receiver, SettingsTab, translate from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle -from openlp.core.lib.ui import find_in_combo_box +from openlp.core.lib.ui import UiStrings, find_in_combo_box log = logging.getLogger(__name__) @@ -49,116 +49,115 @@ class BiblesTab(SettingsTab): def setupUi(self): self.setObjectName(u'BiblesTab') SettingsTab.setupUi(self) - self.VerseDisplayGroupBox = QtGui.QGroupBox(self.leftColumn) - self.VerseDisplayGroupBox.setObjectName(u'VerseDisplayGroupBox') - self.VerseDisplayLayout = QtGui.QFormLayout(self.VerseDisplayGroupBox) - self.VerseDisplayLayout.setObjectName(u'VerseDisplayLayout') - self.NewChaptersCheckBox = QtGui.QCheckBox(self.VerseDisplayGroupBox) - self.NewChaptersCheckBox.setObjectName(u'NewChaptersCheckBox') - self.VerseDisplayLayout.addRow(self.NewChaptersCheckBox) - self.DisplayStyleLabel = QtGui.QLabel(self.VerseDisplayGroupBox) - self.DisplayStyleLabel.setObjectName(u'DisplayStyleLabel') - self.DisplayStyleComboBox = QtGui.QComboBox(self.VerseDisplayGroupBox) - self.DisplayStyleComboBox.addItems([u'', u'', u'', u'']) - self.DisplayStyleComboBox.setObjectName(u'DisplayStyleComboBox') - self.VerseDisplayLayout.addRow(self.DisplayStyleLabel, - self.DisplayStyleComboBox) - self.LayoutStyleLabel = QtGui.QLabel(self.VerseDisplayGroupBox) - self.LayoutStyleLabel.setObjectName(u'LayoutStyleLabel') - self.LayoutStyleComboBox = QtGui.QComboBox(self.VerseDisplayGroupBox) - self.LayoutStyleComboBox.setObjectName(u'LayoutStyleComboBox') - self.LayoutStyleComboBox.addItems([u'', u'', u'']) - self.VerseDisplayLayout.addRow(self.LayoutStyleLabel, - self.LayoutStyleComboBox) - self.BibleSecondCheckBox = QtGui.QCheckBox(self.VerseDisplayGroupBox) - self.BibleSecondCheckBox.setObjectName(u'BibleSecondCheckBox') - self.VerseDisplayLayout.addRow(self.BibleSecondCheckBox) - self.BibleThemeLabel = QtGui.QLabel(self.VerseDisplayGroupBox) - self.BibleThemeLabel.setObjectName(u'BibleThemeLabel') - self.BibleThemeComboBox = QtGui.QComboBox(self.VerseDisplayGroupBox) - self.BibleThemeComboBox.setSizeAdjustPolicy( + self.verseDisplayGroupBox = QtGui.QGroupBox(self.leftColumn) + self.verseDisplayGroupBox.setObjectName(u'verseDisplayGroupBox') + self.verseDisplayLayout = QtGui.QFormLayout(self.verseDisplayGroupBox) + self.verseDisplayLayout.setObjectName(u'verseDisplayLayout') + self.newChaptersCheckBox = QtGui.QCheckBox(self.verseDisplayGroupBox) + self.newChaptersCheckBox.setObjectName(u'newChaptersCheckBox') + self.verseDisplayLayout.addRow(self.newChaptersCheckBox) + self.displayStyleLabel = QtGui.QLabel(self.verseDisplayGroupBox) + self.displayStyleLabel.setObjectName(u'displayStyleLabel') + self.displayStyleComboBox = QtGui.QComboBox(self.verseDisplayGroupBox) + self.displayStyleComboBox.addItems([u'', u'', u'', u'']) + self.displayStyleComboBox.setObjectName(u'displayStyleComboBox') + self.verseDisplayLayout.addRow(self.displayStyleLabel, + self.displayStyleComboBox) + self.layoutStyleLabel = QtGui.QLabel(self.verseDisplayGroupBox) + self.layoutStyleLabel.setObjectName(u'layoutStyleLabel') + self.layoutStyleComboBox = QtGui.QComboBox(self.verseDisplayGroupBox) + self.layoutStyleComboBox.setObjectName(u'layoutStyleComboBox') + self.layoutStyleComboBox.addItems([u'', u'', u'']) + self.verseDisplayLayout.addRow(self.layoutStyleLabel, + self.layoutStyleComboBox) + self.bibleSecondCheckBox = QtGui.QCheckBox(self.verseDisplayGroupBox) + self.bibleSecondCheckBox.setObjectName(u'bibleSecondCheckBox') + self.verseDisplayLayout.addRow(self.bibleSecondCheckBox) + self.bibleThemeLabel = QtGui.QLabel(self.verseDisplayGroupBox) + self.bibleThemeLabel.setObjectName(u'BibleThemeLabel') + self.bibleThemeComboBox = QtGui.QComboBox(self.verseDisplayGroupBox) + self.bibleThemeComboBox.setSizeAdjustPolicy( QtGui.QComboBox.AdjustToMinimumContentsLength) - self.BibleThemeComboBox.setSizePolicy( + self.bibleThemeComboBox.setSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) - self.BibleThemeComboBox.addItem(u'') - self.BibleThemeComboBox.setObjectName(u'BibleThemeComboBox') - self.VerseDisplayLayout.addRow(self.BibleThemeLabel, - self.BibleThemeComboBox) - self.ChangeNoteLabel = QtGui.QLabel(self.VerseDisplayGroupBox) - self.ChangeNoteLabel.setWordWrap(True) - self.ChangeNoteLabel.setObjectName(u'ChangeNoteLabel') - self.VerseDisplayLayout.addRow(self.ChangeNoteLabel) - self.leftLayout.addWidget(self.VerseDisplayGroupBox) + self.bibleThemeComboBox.addItem(u'') + self.bibleThemeComboBox.setObjectName(u'BibleThemeComboBox') + self.verseDisplayLayout.addRow(self.bibleThemeLabel, + self.bibleThemeComboBox) + self.changeNoteLabel = QtGui.QLabel(self.verseDisplayGroupBox) + self.changeNoteLabel.setWordWrap(True) + self.changeNoteLabel.setObjectName(u'changeNoteLabel') + self.verseDisplayLayout.addRow(self.changeNoteLabel) + self.leftLayout.addWidget(self.verseDisplayGroupBox) self.leftLayout.addStretch() self.rightColumn.setSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) self.rightLayout.addStretch() # Signals and slots QtCore.QObject.connect( - self.NewChaptersCheckBox, QtCore.SIGNAL(u'stateChanged(int)'), - self.onNewChaptersCheckBoxChanged) + self.newChaptersCheckBox, QtCore.SIGNAL(u'stateChanged(int)'), + self.onnewChaptersCheckBoxChanged) QtCore.QObject.connect( - self.DisplayStyleComboBox, QtCore.SIGNAL(u'activated(int)'), - self.onDisplayStyleComboBoxChanged) + self.displayStyleComboBox, QtCore.SIGNAL(u'activated(int)'), + self.ondisplayStyleComboBoxChanged) QtCore.QObject.connect( - self.BibleThemeComboBox, QtCore.SIGNAL(u'activated(int)'), + self.bibleThemeComboBox, QtCore.SIGNAL(u'activated(int)'), self.onBibleThemeComboBoxChanged) QtCore.QObject.connect( - self.LayoutStyleComboBox, QtCore.SIGNAL(u'activated(int)'), - self.onLayoutStyleComboBoxChanged) + self.layoutStyleComboBox, QtCore.SIGNAL(u'activated(int)'), + self.onlayoutStyleComboBoxChanged) QtCore.QObject.connect( - self.BibleSecondCheckBox, QtCore.SIGNAL(u'stateChanged(int)'), - self.onBibleSecondCheckBox) + self.bibleSecondCheckBox, QtCore.SIGNAL(u'stateChanged(int)'), + self.onbibleSecondCheckBox) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'theme_update_list'), self.updateThemeList) def retranslateUi(self): - self.VerseDisplayGroupBox.setTitle( + self.verseDisplayGroupBox.setTitle( translate('BiblesPlugin.BiblesTab', 'Verse Display')) - self.NewChaptersCheckBox.setText( + self.newChaptersCheckBox.setText( translate('BiblesPlugin.BiblesTab', 'Only show new chapter numbers')) - self.LayoutStyleLabel.setText( + self.layoutStyleLabel.setText( translate('BiblesPlugin.BiblesTab', 'Layout style:')) - self.DisplayStyleLabel.setText( - translate('BiblesPlugin.BiblesTab', 'Display style:')) - self.BibleThemeLabel.setText( + self.displayStyleLabel.setText(UiStrings.DisplayStyle) + self.bibleThemeLabel.setText( translate('BiblesPlugin.BiblesTab', 'Bible theme:')) - self.LayoutStyleComboBox.setItemText(LayoutStyle.VersePerSlide, - translate('BiblesPlugin.BiblesTab', 'Verse Per Slide')) - self.LayoutStyleComboBox.setItemText(LayoutStyle.VersePerLine, - translate('BiblesPlugin.BiblesTab', 'Verse Per Line')) - self.LayoutStyleComboBox.setItemText(LayoutStyle.Continuous, - translate('BiblesPlugin.BiblesTab', 'Continuous')) - self.DisplayStyleComboBox.setItemText(DisplayStyle.NoBrackets, + self.layoutStyleComboBox.setItemText(LayoutStyle.VersePerSlide, + UiStrings.VersePerSlide) + self.layoutStyleComboBox.setItemText(LayoutStyle.VersePerLine, + UiStrings.VersePerLine) + self.layoutStyleComboBox.setItemText(LayoutStyle.Continuous, + UiStrings.Continuous) + self.displayStyleComboBox.setItemText(DisplayStyle.NoBrackets, translate('BiblesPlugin.BiblesTab', 'No Brackets')) - self.DisplayStyleComboBox.setItemText(DisplayStyle.Round, + self.displayStyleComboBox.setItemText(DisplayStyle.Round, translate('BiblesPlugin.BiblesTab', '( And )')) - self.DisplayStyleComboBox.setItemText(DisplayStyle.Curly, + self.displayStyleComboBox.setItemText(DisplayStyle.Curly, translate('BiblesPlugin.BiblesTab', '{ And }')) - self.DisplayStyleComboBox.setItemText(DisplayStyle.Square, + self.displayStyleComboBox.setItemText(DisplayStyle.Square, translate('BiblesPlugin.BiblesTab', '[ And ]')) - self.ChangeNoteLabel.setText(translate('BiblesPlugin.BiblesTab', + self.changeNoteLabel.setText(translate('BiblesPlugin.BiblesTab', 'Note:\nChanges do not affect verses already in the service.')) - self.BibleSecondCheckBox.setText( + self.bibleSecondCheckBox.setText( translate('BiblesPlugin.BiblesTab', 'Display second Bible verses')) def onBibleThemeComboBoxChanged(self): - self.bible_theme = self.BibleThemeComboBox.currentText() + self.bible_theme = self.bibleThemeComboBox.currentText() - def onDisplayStyleComboBoxChanged(self): - self.display_style = self.DisplayStyleComboBox.currentIndex() + def ondisplayStyleComboBoxChanged(self): + self.display_style = self.displayStyleComboBox.currentIndex() - def onLayoutStyleComboBoxChanged(self): - self.layout_style = self.LayoutStyleComboBox.currentIndex() + def onlayoutStyleComboBoxChanged(self): + self.layout_style = self.layoutStyleComboBox.currentIndex() - def onNewChaptersCheckBoxChanged(self, check_state): + def onnewChaptersCheckBoxChanged(self, check_state): self.show_new_chapters = False # We have a set value convert to True/False. if check_state == QtCore.Qt.Checked: self.show_new_chapters = True - def onBibleSecondCheckBox(self, check_state): + def onbibleSecondCheckBox(self, check_state): self.second_bibles = False # We have a set value convert to True/False. if check_state == QtCore.Qt.Checked: @@ -177,10 +176,10 @@ class BiblesTab(SettingsTab): settings.value(u'bible theme', QtCore.QVariant(u'')).toString()) self.second_bibles = settings.value( u'second bibles', QtCore.QVariant(True)).toBool() - self.NewChaptersCheckBox.setChecked(self.show_new_chapters) - self.DisplayStyleComboBox.setCurrentIndex(self.display_style) - self.LayoutStyleComboBox.setCurrentIndex(self.layout_style) - self.BibleSecondCheckBox.setChecked(self.second_bibles) + self.newChaptersCheckBox.setChecked(self.show_new_chapters) + self.displayStyleComboBox.setCurrentIndex(self.display_style) + self.layoutStyleComboBox.setCurrentIndex(self.layout_style) + self.bibleSecondCheckBox.setChecked(self.second_bibles) settings.endGroup() def save(self): @@ -205,8 +204,8 @@ class BiblesTab(SettingsTab): [u'Bible Theme', u'Song Theme'] """ - self.BibleThemeComboBox.clear() - self.BibleThemeComboBox.addItem(u'') + self.bibleThemeComboBox.clear() + self.bibleThemeComboBox.addItem(u'') for theme in theme_list: - self.BibleThemeComboBox.addItem(theme) - find_in_combo_box(self.BibleThemeComboBox, self.bible_theme) + self.bibleThemeComboBox.addItem(theme) + find_in_combo_box(self.bibleThemeComboBox, self.bible_theme) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 5c77a75d7..84275f3fc 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -106,6 +106,12 @@ class BibleMediaItem(MediaManagerItem): translate('BiblesPlugin.MediaItem', 'Text Search')) ]) self.quickLayout.addRow(self.quickSearchLabel, self.quickSearchEdit) + self.quickLayoutLabel = QtGui.QLabel(self.quickTab) + self.quickLayoutLabel.setObjectName(u'quickClearLabel') + self.quickLayoutComboBox = media_item_combo_box(self.quickTab, + u'quickLayoutComboBox') + self.quickLayoutComboBox.addItems([u'', u'', u'']) + self.quickLayout.addRow(self.quickLayoutLabel, self.quickLayoutComboBox) self.quickClearLabel = QtGui.QLabel(self.quickTab) self.quickClearLabel.setObjectName(u'quickClearLabel') self.quickClearComboBox = media_item_combo_box(self.quickTab, @@ -210,6 +216,9 @@ class BibleMediaItem(MediaManagerItem): QtCore.SIGNAL(u'searchTypeChanged(int)'), self.updateAutoCompleter) QtCore.QObject.connect(self.quickVersionComboBox, QtCore.SIGNAL(u'activated(int)'), self.updateAutoCompleter) + QtCore.QObject.connect( + self.quickLayoutComboBox, QtCore.SIGNAL(u'activated(int)'), + self.onlayoutStyleComboBoxChanged) # Buttons QtCore.QObject.connect(self.advancedSearchButton, QtCore.SIGNAL(u'pressed()'), self.onAdvancedSearchButton) @@ -234,6 +243,7 @@ class BibleMediaItem(MediaManagerItem): self.advancedSecondComboBox.setVisible(False) self.quickSecondLabel.setVisible(False) self.quickSecondComboBox.setVisible(False) + self.quickLayoutComboBox.setCurrentIndex(self.settings.layout_style) def retranslateUi(self): log.debug(u'retranslateUi') @@ -269,6 +279,13 @@ class BibleMediaItem(MediaManagerItem): translate('BiblesPlugin.MediaItem', 'Clear')) self.advancedClearComboBox.addItem( translate('BiblesPlugin.MediaItem', 'Keep')) + self.quickLayoutLabel.setText(UiStrings.DisplayStyle) + self.quickLayoutComboBox.setItemText(LayoutStyle.VersePerSlide, + UiStrings.VersePerSlide) + self.quickLayoutComboBox.setItemText(LayoutStyle.VersePerLine, + UiStrings.VersePerLine) + self.quickLayoutComboBox.setItemText(LayoutStyle.Continuous, + UiStrings.Continuous) def initialise(self): log.debug(u'bible manager initialise') @@ -816,3 +833,11 @@ class BibleMediaItem(MediaManagerItem): if self.settings.display_style == DisplayStyle.Square: return u'{su}[%s]{/su}' % verse_text return u'{su}%s{/su}' % verse_text + + def onlayoutStyleComboBoxChanged(self): + self.settings.layout_style = self.quickLayoutComboBox.currentIndex() + self.settings.layoutStyleComboBox.setCurrentIndex( + self.settings.layout_style) + QtCore.QSettings().setValue( + self.settingsSection + u'/verse layout style', + QtCore.QVariant(self.settings.layout_style)) From 9dbd54bf8f6396c1121ca13b0dec77f07809862c Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 10 Apr 2011 20:44:27 +0200 Subject: [PATCH 43/98] more fixes --- openlp/core/ui/servicemanager.py | 4 ++-- openlp/plugins/songs/lib/mediaitem.py | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index adafe5fd6..5e14ec637 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -691,9 +691,9 @@ class ServiceManager(QtGui.QWidget): Called by the SlideController to request a preview item be made live and allows the next preview to be updated if relevent. """ - id, row = message.split(u':') + uuid, row = message.split(u':') for sitem in self.serviceItems: - if sitem[u'service_item']._uuid == id: + if sitem[u'service_item']._uuid == uuid: item = self.serviceManagerList.topLevelItem(sitem[u'order'] - 1) self.serviceManagerList.setCurrentItem(item) self.makeLive(int(row)) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index f4803d653..e2882ed29 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -283,19 +283,20 @@ class SongMediaItem(MediaManagerItem): self.remoteTriggered = None self.remoteSong = -1 - def onRemoteEdit(self, songid): + def onRemoteEdit(self, message): """ Called by ServiceManager or SlideController by event passing the Song Id in the payload along with an indicator to say which type of display is required. """ - log.debug(u'onRemoteEdit %s' % songid) - fields = songid.split(u':') - valid = self.parent.manager.get_object(Song, fields[1]) + log.debug(u'onRemoteEdit %s' % message) + remote_type, song_id = message.split(u':') + song_id = int(song_id) + valid = self.parent.manager.get_object(Song, song_id) if valid: - self.remoteSong = fields[1] - self.remoteTriggered = fields[0] - self.edit_song_form.loadSong(fields[1], (fields[0] == u'P')) + self.remoteSong = song_id + self.remoteTriggered = remote_type + self.edit_song_form.loadSong(song_id, (remote_type == u'P')) self.edit_song_form.exec_() def onEditClick(self): From 1882c5366d4b7456acb966016282cacf0f3a2bb0 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 10 Apr 2011 19:54:26 +0100 Subject: [PATCH 44/98] Review fixes --- openlp/core/lib/ui.py | 4 ++-- openlp/core/ui/servicemanager.py | 6 +++--- openlp/core/ui/themestab.py | 6 +++--- openlp/plugins/bibles/lib/biblestab.py | 20 +++++++++---------- openlp/plugins/bibles/lib/mediaitem.py | 8 ++++---- openlp/plugins/custom/forms/editcustomform.py | 6 +++--- openlp/plugins/songs/forms/editsongform.py | 8 ++++---- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 9b03cce03..4f6951d13 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -320,7 +320,7 @@ def create_valign_combo(form, parent, layout): verticalLabel.setBuddy(form.verticalComboBox) layout.addRow(verticalLabel, form.verticalComboBox) -def find_in_combo_box(combo_box, value_to_find): +def find_and_set_in_combo_box(combo_box, value_to_find): """ Find a string in a combo box and set it as the selected item if present @@ -335,4 +335,4 @@ def find_in_combo_box(combo_box, value_to_find): if index == -1: # Not Found. index = 0 - combo_box.setCurrentIndex(index) + combo_box.setCurrentIndex(index) \ No newline at end of file diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index a07d7f970..4c4837676 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -36,7 +36,7 @@ from openlp.core.lib import OpenLPToolbar, ServiceItem, context_menu_action, \ Receiver, build_icon, ItemCapabilities, SettingsManager, translate from openlp.core.lib.theme import ThemeLevel from openlp.core.lib.ui import UiStrings, critical_error_message_box, \ - find_in_combo_box + find_and_set_in_combo_box from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm from openlp.core.ui.printserviceform import PrintServiceForm from openlp.core.utils import AppLocation, delete_file, file_is_unicode, \ @@ -1262,7 +1262,7 @@ class ServiceManager(QtGui.QWidget): action = context_menu_action(self.serviceManagerList, None, theme, self.onThemeChangeAction) self.themeMenu.addAction(action) - find_in_combo_box(self.themeComboBox, self.service_theme) + find_and_set_in_combo_box(self.themeComboBox, self.service_theme) self.mainwindow.renderManager.set_service_theme(self.service_theme) self.regenerateServiceItems() @@ -1301,4 +1301,4 @@ class ServiceManager(QtGui.QWidget): Print a Service Order Sheet. """ settingDialog = PrintServiceForm(self.mainwindow, self) - settingDialog.exec_() + settingDialog.exec_() \ No newline at end of file diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index 3307b94fe..1415c6810 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -28,7 +28,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import SettingsTab, Receiver, translate from openlp.core.lib.theme import ThemeLevel -from openlp.core.lib.ui import UiStrings, find_in_combo_box +from openlp.core.lib.ui import UiStrings, find_and_set_in_combo_box class ThemesTab(SettingsTab): """ @@ -185,7 +185,7 @@ class ThemesTab(SettingsTab): self.DefaultComboBox.clear() for theme in theme_list: self.DefaultComboBox.addItem(theme) - find_in_combo_box(self.DefaultComboBox, self.global_theme) + find_and_set_in_combo_box(self.DefaultComboBox, self.global_theme) self.parent.renderManager.set_global_theme( self.global_theme, self.theme_level) if self.global_theme is not u'': @@ -201,4 +201,4 @@ class ThemesTab(SettingsTab): if not preview.isNull(): preview = preview.scaled(300, 255, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) - self.DefaultListView.setPixmap(preview) + self.DefaultListView.setPixmap(preview) \ No newline at end of file diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index 5133587b3..914377e35 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -30,7 +30,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Receiver, SettingsTab, translate from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle -from openlp.core.lib.ui import UiStrings, find_in_combo_box +from openlp.core.lib.ui import UiStrings, find_and_set_in_combo_box log = logging.getLogger(__name__) @@ -96,19 +96,19 @@ class BiblesTab(SettingsTab): # Signals and slots QtCore.QObject.connect( self.newChaptersCheckBox, QtCore.SIGNAL(u'stateChanged(int)'), - self.onnewChaptersCheckBoxChanged) + self.onNewChaptersCheckBoxChanged) QtCore.QObject.connect( self.displayStyleComboBox, QtCore.SIGNAL(u'activated(int)'), - self.ondisplayStyleComboBoxChanged) + self.onDisplayStyleComboBoxChanged) QtCore.QObject.connect( self.bibleThemeComboBox, QtCore.SIGNAL(u'activated(int)'), self.onBibleThemeComboBoxChanged) QtCore.QObject.connect( self.layoutStyleComboBox, QtCore.SIGNAL(u'activated(int)'), - self.onlayoutStyleComboBoxChanged) + self.onLayoutStyleComboBoxChanged) QtCore.QObject.connect( self.bibleSecondCheckBox, QtCore.SIGNAL(u'stateChanged(int)'), - self.onbibleSecondCheckBox) + self.onBibleSecondCheckBox) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'theme_update_list'), self.updateThemeList) @@ -145,19 +145,19 @@ class BiblesTab(SettingsTab): def onBibleThemeComboBoxChanged(self): self.bible_theme = self.bibleThemeComboBox.currentText() - def ondisplayStyleComboBoxChanged(self): + def onDisplayStyleComboBoxChanged(self): self.display_style = self.displayStyleComboBox.currentIndex() - def onlayoutStyleComboBoxChanged(self): + def onLayoutStyleComboBoxChanged(self): self.layout_style = self.layoutStyleComboBox.currentIndex() - def onnewChaptersCheckBoxChanged(self, check_state): + def onNewChaptersCheckBoxChanged(self, check_state): self.show_new_chapters = False # We have a set value convert to True/False. if check_state == QtCore.Qt.Checked: self.show_new_chapters = True - def onbibleSecondCheckBox(self, check_state): + def onBibleSecondCheckBox(self, check_state): self.second_bibles = False # We have a set value convert to True/False. if check_state == QtCore.Qt.Checked: @@ -208,4 +208,4 @@ class BiblesTab(SettingsTab): self.bibleThemeComboBox.addItem(u'') for theme in theme_list: self.bibleThemeComboBox.addItem(theme) - find_in_combo_box(self.bibleThemeComboBox, self.bible_theme) + find_and_set_in_combo_box(self.bibleThemeComboBox, self.bible_theme) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 84275f3fc..73ed0c9bd 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -32,7 +32,7 @@ from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \ translate from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.ui import UiStrings, add_widget_completer, \ - media_item_combo_box, critical_error_message_box, find_in_combo_box + media_item_combo_box, critical_error_message_box, find_and_set_in_combo_box from openlp.plugins.bibles.forms import BibleImportForm from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, \ VerseReferenceList, get_reference_match @@ -325,7 +325,7 @@ class BibleMediaItem(MediaManagerItem): book = QtCore.QSettings().value( self.settingsSection + u'/advanced bible', QtCore.QVariant(u'')).toString() - find_in_combo_box(self.advancedVersionComboBox, book) + find_and_set_in_combo_box(self.advancedVersionComboBox, book) self.initialiseAdvancedBible(unicode(book)) def reloadBibles(self): @@ -386,7 +386,7 @@ class BibleMediaItem(MediaManagerItem): book = QtCore.QSettings().value( self.settingsSection + u'/quick bible', QtCore.QVariant(u'')).toString() - find_in_combo_box(self.quickVersionComboBox, book) + find_and_set_in_combo_box(self.quickVersionComboBox, book) books = [] # We have to do a 'Reference Search'. if self.quickSearchEdit.currentSearchType() == BibleSearch.Reference: @@ -840,4 +840,4 @@ class BibleMediaItem(MediaManagerItem): self.settings.layout_style) QtCore.QSettings().setValue( self.settingsSection + u'/verse layout style', - QtCore.QVariant(self.settings.layout_style)) + QtCore.QVariant(self.settings.layout_style)) \ No newline at end of file diff --git a/openlp/plugins/custom/forms/editcustomform.py b/openlp/plugins/custom/forms/editcustomform.py index 3ebd5f984..9312b5ddd 100644 --- a/openlp/plugins/custom/forms/editcustomform.py +++ b/openlp/plugins/custom/forms/editcustomform.py @@ -29,7 +29,7 @@ import logging from PyQt4 import QtCore, QtGui from openlp.core.lib import Receiver, translate -from openlp.core.lib.ui import critical_error_message_box, find_in_combo_box +from openlp.core.lib.ui import critical_error_message_box, find_and_set_in_combo_box from openlp.plugins.custom.lib import CustomXMLBuilder, CustomXMLParser from openlp.plugins.custom.lib.db import CustomSlide from editcustomdialog import Ui_CustomEditDialog @@ -98,7 +98,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): for slide in slideList: self.slideListView.addItem(slide[1]) theme = self.customSlide.theme_name - find_in_combo_box(self.themeComboBox, theme) + find_and_set_in_combo_box(self.themeComboBox, theme) # If not preview hide the preview button. self.previewButton.setVisible(False) if preview: @@ -261,4 +261,4 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog): message=translate('CustomPlugin.EditCustomForm', 'You need to add at least one slide')) return False - return True + return True \ No newline at end of file diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 8dfae1736..85294d92b 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -31,7 +31,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.lib import Receiver, translate from openlp.core.lib.ui import UiStrings, add_widget_completer, \ - critical_error_message_box, find_in_combo_box + critical_error_message_box, find_and_set_in_combo_box from openlp.plugins.songs.forms import EditVerseForm from openlp.plugins.songs.lib import SongXML, VerseType, clean_song from openlp.plugins.songs.lib.db import Book, Song, Author, Topic @@ -208,9 +208,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.alternativeEdit.setText(u'') if self.song.song_book_id != 0: book_name = self.manager.get_object(Book, self.song.song_book_id) - find_in_combo_box(self.songBookComboBox, unicode(book_name.name)) + find_and_set_in_combo_box(self.songBookComboBox, unicode(book_name.name)) if self.song.theme_name: - find_in_combo_box(self.themeComboBox, unicode(self.song.theme_name)) + find_and_set_in_combo_box(self.themeComboBox, unicode(self.song.theme_name)) if self.song.copyright: self.copyrightEdit.setText(self.song.copyright) else: @@ -779,4 +779,4 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.song.verse_order) except: log.exception(u'Problem processing song Lyrics \n%s', - sxml.dump_xml()) + sxml.dump_xml()) \ No newline at end of file From 20dc7846ce0c9f74415ff53430fbc6f3b07e16db Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 10 Apr 2011 19:59:29 +0100 Subject: [PATCH 45/98] Take new count after loading files works better Fixes: https://launchpad.net/bugs/756545 --- openlp/core/ui/thememanager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index d033daeb3..81da6e021 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -445,6 +445,7 @@ class ThemeManager(QtGui.QWidget): files = SettingsManager.get_files(self.settingsSection, u'.png') if firstTime: self.firstTime() + files = SettingsManager.get_files(self.settingsSection, u'.png') # No themes have been found so create one if len(files) == 0: theme = ThemeXML() From 1ce94d1c2080bc608cde241c856305022595c4d3 Mon Sep 17 00:00:00 2001 From: Jonathan Corwin Date: Sun, 10 Apr 2011 23:10:23 +0100 Subject: [PATCH 46/98] Fix theme image appearing between media images. Try and do same for ppt, but not yet. Broken the blank button... (it'll get worse before it gets better!) --- openlp/core/lib/htmlbuilder.py | 20 +++++++--- openlp/core/ui/maindisplay.py | 8 +++- openlp/core/ui/slidecontroller.py | 63 +++++++++++++++++++++---------- 3 files changed, 64 insertions(+), 27 deletions(-) diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py index c4361a421..c874c9ff2 100644 --- a/openlp/core/lib/htmlbuilder.py +++ b/openlp/core/lib/htmlbuilder.py @@ -307,7 +307,7 @@ sup { - + + + From 20fbe4d28d99ad0d291e460a68c32ae1bc94bd36 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Tue, 12 Apr 2011 20:32:17 +0100 Subject: [PATCH 58/98] Settings Cleanup part2 --- openlp/core/lib/plugin.py | 23 ++++--------- openlp/core/lib/pluginmanager.py | 2 -- openlp/core/ui/advancedtab.py | 3 +- openlp/core/ui/generaltab.py | 3 +- openlp/core/ui/mediadockmanager.py | 2 +- openlp/core/ui/settingsdialog.py | 11 ++++-- openlp/core/ui/settingsform.py | 47 +++++++++++++++----------- openlp/core/ui/themestab.py | 3 +- openlp/plugins/alerts/alertsplugin.py | 5 +-- openlp/plugins/remotes/remoteplugin.py | 7 ++-- 10 files changed, 54 insertions(+), 52 deletions(-) diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index 99247c4b9..ef89e6424 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -141,6 +141,7 @@ class Plugin(QtCore.QObject): ``settings_tab_class`` The class name of the plugin's settings tab. """ + log.debug(u'Plugin %s initialised' % name) QtCore.QObject.__init__(self) self.name = name self.textStrings = {} @@ -250,7 +251,8 @@ class Plugin(QtCore.QObject): """ if self.settings_tab_class: return self.settings_tab_class(self.name, - self.getString(StringContent.VisibleName)[u'title'], self.icon_path) + self.getString(StringContent.VisibleName)[u'title'], + self.icon_path) return None def addToMenu(self, menubar): @@ -287,32 +289,19 @@ class Plugin(QtCore.QObject): """ if self.mediaItem: self.mediaItem.initialise() - self.insertToolboxItem() + self.mediadock.insert_dock(self.mediaItem, self.icon, self.weight) + if self.settings_tab: + self.settingsForm.insertTab(self.settings_tab, self.weight) def finalise(self): """ Called by the plugin Manager to cleanup things. """ - self.removeToolboxItem() - - def removeToolboxItem(self): - """ - Called by the plugin to remove toolbar - """ if self.mediaItem: self.mediadock.remove_dock(self.mediaItem) if self.settings_tab: self.settingsForm.removeTab(self.settings_tab) - def insertToolboxItem(self): - """ - Called by plugin to replace toolbar - """ - if self.mediaItem: - self.mediadock.insert_dock(self.mediaItem, self.icon, self.weight) - if self.settings_tab: - self.settingsForm.insertTab(self.settings_tab, self.weight) - def usesTheme(self, theme): """ Called to find out if a plugin is currently using a theme. diff --git a/openlp/core/lib/pluginmanager.py b/openlp/core/lib/pluginmanager.py index ef8287ba7..3b73750fe 100644 --- a/openlp/core/lib/pluginmanager.py +++ b/openlp/core/lib/pluginmanager.py @@ -206,8 +206,6 @@ class PluginManager(object): if plugin.isActive(): plugin.initialise() log.info(u'Initialisation Complete for %s ' % plugin.name) - if not plugin.isActive(): - plugin.removeToolboxItem() log.info(u'Initialise Plugins - Finished') def finalise_plugins(self): diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index cf4c5b9d5..6041382b8 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -41,7 +41,8 @@ class AdvancedTab(SettingsTab): """ Initialise the settings tab """ - SettingsTab.__init__(self, u'Advanced') + generalTranslated = translate('AdvancedTab', 'Advanced') + SettingsTab.__init__(self, u'Advanced', generalTranslated) self.default_image = u':/graphics/openlp-splash-screen.png' self.default_color = u'#ffffff' self.icon_path = u':/icon/openlp-logo-16x16.png' diff --git a/openlp/core/ui/generaltab.py b/openlp/core/ui/generaltab.py index 279cd8c26..d566244b8 100644 --- a/openlp/core/ui/generaltab.py +++ b/openlp/core/ui/generaltab.py @@ -45,7 +45,8 @@ class GeneralTab(SettingsTab): # Set to True to allow PostSetup to work on application start up self.overrideChanged = True self.icon_path = u':/icon/openlp-logo-16x16.png' - SettingsTab.__init__(self, u'General') + generalTranslated = translate('GeneralTab', 'General') + SettingsTab.__init__(self, u'General', generalTranslated) def preLoad(self): """ diff --git a/openlp/core/ui/mediadockmanager.py b/openlp/core/ui/mediadockmanager.py index ef4bc62e0..ca4f4d442 100644 --- a/openlp/core/ui/mediadockmanager.py +++ b/openlp/core/ui/mediadockmanager.py @@ -84,5 +84,5 @@ class MediaDockManager(object): if self.media_dock.widget(dock_index): if self.media_dock.widget(dock_index).settingsSection == \ media_item.plugin.name.lower(): - self.media_dock.widget(dock_index).hide() + self.media_dock.widget(dock_index).setVisible(False) self.media_dock.removeItem(dock_index) diff --git a/openlp/core/ui/settingsdialog.py b/openlp/core/ui/settingsdialog.py index 9359ff54c..e66defac3 100644 --- a/openlp/core/ui/settingsdialog.py +++ b/openlp/core/ui/settingsdialog.py @@ -38,15 +38,20 @@ class Ui_SettingsDialog(object): self.settingsLayout = QtGui.QGridLayout(settingsDialog) self.settingsLayout.setObjectName(u'settingsLayout') self.settingListWidget = QtGui.QListWidget(settingsDialog) + self.settingListWidget.setIconSize(QtCore.QSize(32, 32)) self.settingListWidget.setObjectName(u'settingListWidget') self.settingsLayout.addWidget(self.settingListWidget, 0, 0, 1, 1) - self.settingsTabWidget = QtGui.QTabWidget(settingsDialog) - self.settingsTabWidget.setObjectName(u'settingsTabWidget') - self.settingsLayout.addWidget(self.settingsTabWidget, 0, 1, 1, 1) + self.stackedLayout = QtGui.QStackedLayout() + self.stackedLayout.setObjectName(u'stackedLayout') + self.settingsLayout.addLayout(self.stackedLayout, 0, 1, 1, 1) self.buttonBox = create_accept_reject_button_box(settingsDialog, True) self.settingsLayout.addWidget(self.buttonBox, 1, 1, 1, 1) self.retranslateUi(settingsDialog) + self.stackedLayout.setCurrentIndex(0) QtCore.QMetaObject.connectSlotsByName(settingsDialog) + QtCore.QObject.connect(self.settingListWidget, + QtCore.SIGNAL(u'currentRowChanged(int)'), + self.stackedLayout.setCurrentIndex) def retranslateUi(self, settingsDialog): settingsDialog.setWindowTitle(translate('OpenLP.SettingsForm', diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index c145224be..6de2a14d8 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -58,8 +58,8 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): def exec_(self): # load all the settings - for tabIndex in range(0, self.settingsTabWidget.count()): - self.settingsTabWidget.widget(tabIndex).load() + for tabIndex in range(0, self.stackedLayout.count()): + self.stackedLayout.widget(tabIndex).load() return QtGui.QDialog.exec_(self) @@ -69,32 +69,39 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): """ log.debug(u'Inserting %s tab' % tab.tabTitle) # 14 : There are 3 tables currently and locations starts at -10 - self.settingsTabWidget.insertTab( - location + 14, tab, tab.tabTitleVisible) - print tab.tabTitleVisible - item_name = QtGui.QListWidgetItem(tab.tabTitleVisible) - icon = build_icon(tab.icon_path) - pixmap = icon.pixmap(QtCore.QSize(88, 50)) - item_name.setIcon(icon) - self.settingListWidget.insertItem(14 + location, item_name) + match = False + for tabIndex in range(0, self.stackedLayout.count()): + if self.stackedLayout.widget(tabIndex): + if self.stackedLayout.widget(tabIndex).tabTitleVisible == \ + tab.tabTitleVisible: + print tab.tabTitleVisible + self.stackedLayout.widget(tabIndex).setHidden(False) + match = True + break + if not match: + self.stackedLayout.addWidget(tab) + item_name = QtGui.QListWidgetItem(tab.tabTitleVisible) + icon = build_icon(tab.icon_path) + item_name.setIcon(icon) + self.settingListWidget.insertItem(14 + location, item_name) def removeTab(self, tab): """ Remove a tab from the form """ log.debug(u'remove %s tab' % tab.tabTitleVisible) - for tabIndex in range(0, self.settingsTabWidget.count()): - if self.settingsTabWidget.widget(tabIndex): - if self.settingsTabWidget.widget(tabIndex).tabTitleVisible == \ + for tabIndex in range(0, self.stackedLayout.count()): + if self.stackedLayout.widget(tabIndex): + if self.stackedLayout.widget(tabIndex).tabTitleVisible == \ tab.tabTitleVisible: - self.settingsTabWidget.removeTab(tabIndex) + self.settingListWidget.item(tabIndex).setHidden(True) def accept(self): """ Process the form saving the settings """ - for tabIndex in range(0, self.settingsTabWidget.count()): - self.settingsTabWidget.widget(tabIndex).save() + for tabIndex in range(0, self.stackedLayout.count()): + self.stackedLayout.widget(tabIndex).save() # Must go after all settings are save Receiver.send_message(u'config_updated') return QtGui.QDialog.accept(self) @@ -103,13 +110,13 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): """ Process the form saving the settings """ - for tabIndex in range(0, self.settingsTabWidget.count()): - self.settingsTabWidget.widget(tabIndex).cancel() + for tabIndex in range(0, self.stackedLayout.count()): + self.stackedLayout.widget(tabIndex).cancel() return QtGui.QDialog.reject(self) def postSetUp(self): """ Run any post-setup code for the tabs on the form """ - for tabIndex in range(0, self.settingsTabWidget.count()): - self.settingsTabWidget.widget(tabIndex).postSetUp() + for tabIndex in range(0, self.stackedLayout.count()): + self.stackedLayout.widget(tabIndex).postSetUp() diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index 5d07d0cf7..b5fcce1ec 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -36,7 +36,8 @@ class ThemesTab(SettingsTab): """ def __init__(self, parent): self.parent = parent - SettingsTab.__init__(self, u'Themes') + generalTranslated = translate('ThemeTab', 'Themes') + SettingsTab.__init__(self, u'Themes', generalTranslated) self.icon_path = u':/themes/theme_new.png' def setupUi(self): diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index db0ba3b7e..df7823ed5 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -41,9 +41,10 @@ class AlertsPlugin(Plugin): def __init__(self, plugin_helpers): Plugin.__init__(self, u'Alerts', plugin_helpers, - settingsTabClass=AlertsTab) + settings_tab_class=AlertsTab) self.weight = -3 - self.icon = build_icon(u':/plugins/plugin_alerts.png') + self.icon_path = u':/plugins/plugin_alerts.png' + self.icon = build_icon(self.icon_path) self.alertsmanager = AlertsManager(self) self.manager = Manager(u'alerts', init_schema) self.alertForm = AlertForm(self) diff --git a/openlp/plugins/remotes/remoteplugin.py b/openlp/plugins/remotes/remoteplugin.py index 41346dbab..fea9e5170 100644 --- a/openlp/plugins/remotes/remoteplugin.py +++ b/openlp/plugins/remotes/remoteplugin.py @@ -38,9 +38,9 @@ class RemotesPlugin(Plugin): """ remotes constructor """ - Plugin.__init__(self, u'Remotes', plugin_helpers, - settingsTabClass=RemoteTab) - self.icon = build_icon(u':/plugins/plugin_remote.png') + Plugin.__init__(self, u'Remotes', plugin_helpers) + self.icon_path = u':/plugins/plugin_remote.png' + self.icon = build_icon(self.icon_path) self.weight = -1 self.server = None @@ -50,7 +50,6 @@ class RemotesPlugin(Plugin): """ log.debug(u'initialise') Plugin.initialise(self) - self.insertToolboxItem() self.server = HttpServer(self) def finalise(self): From c6833003851d7e70681a87429cec43e830556c5c Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 12 Apr 2011 23:01:27 +0200 Subject: [PATCH 59/98] fixed weight --- openlp/core/utils/actions.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/openlp/core/utils/actions.py b/openlp/core/utils/actions.py index 76b72c55c..0c4eee655 100644 --- a/openlp/core/utils/actions.py +++ b/openlp/core/utils/actions.py @@ -154,10 +154,13 @@ class CategoryList(object): return False def append(self, name, actions=None): + weight = 0 + if len(self.categories) > 0: + weight = self.categories[-1].weight + 1 if actions: - self.add(name, actions=actions) + self.add(name, weight, actions) else: - self.add(name) + self.add(name, weight) def add(self, name, weight=0, actions=None): category = ActionCategory(name, weight) @@ -168,7 +171,7 @@ class CategoryList(object): else: category.actions.append(action) self.categories.append(category) - self.categories.sort(key=lambda cat: cat.weight, reverse=True) + self.categories.sort(key=lambda cat: cat.weight) def remove(self, name): for category in self.categories: @@ -270,8 +273,7 @@ class ActionList(object): for category in self.categories: if category.name == name: category.weight = weight - self.categories.categories.sort( - key=lambda cat: cat.weight, reverse=True) + self.categories.categories.sort(key=lambda cat: cat.weight) return self.categories.add(name, weight) @@ -280,5 +282,5 @@ class CategoryOrder(object): """ An enumeration class for category weights. """ - standardMenu = 100 - standardToolbar = 90 + standardMenu = -20 + standardToolbar = -10 From 28dec27ed2847b524d23fff4dbe57ce01a4e79b9 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Tue, 12 Apr 2011 23:12:07 +0200 Subject: [PATCH 60/98] fixed bug #758206 Fixes: https://launchpad.net/bugs/758206 --- openlp/core/lib/mediamanageritem.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 13277876d..7671064df 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -274,8 +274,8 @@ class MediaManagerItem(QtGui.QWidget): context_menu_action( self.listView, u':/general/general_preview.png', self.plugin.getString(StringContent.Preview)[u'title'], - self.onPreviewClick, [QtCore.Qt.Key_Enter], - context=QtCore.Qt.WidgetShortcut)) + self.onPreviewClick, [QtCore.Qt.Key_Enter, + QtCore.Qt.Key_Return], context=QtCore.Qt.WidgetShortcut)) self.listView.addAction( context_menu_action( self.listView, u':/general/general_live.png', From 6474c99a85cffb7b13692561a335fd35600eebe1 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 13 Apr 2011 05:55:00 +0100 Subject: [PATCH 61/98] Settings Layout Ok ish --- openlp/core/ui/settingsdialog.py | 6 ++++-- openlp/core/ui/settingsform.py | 3 ++- resources/forms/settings.ui | 37 ++++++++++++++++++++++++++------ 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/openlp/core/ui/settingsdialog.py b/openlp/core/ui/settingsdialog.py index e66defac3..45a68c321 100644 --- a/openlp/core/ui/settingsdialog.py +++ b/openlp/core/ui/settingsdialog.py @@ -37,8 +37,11 @@ class Ui_SettingsDialog(object): build_icon(u':/system/system_settings.png')) self.settingsLayout = QtGui.QGridLayout(settingsDialog) self.settingsLayout.setObjectName(u'settingsLayout') + self.settingsLayout.setMargin(0) self.settingListWidget = QtGui.QListWidget(settingsDialog) - self.settingListWidget.setIconSize(QtCore.QSize(32, 32)) + self.settingListWidget.setMinimumSize(QtCore.QSize(150, 0)) + self.settingListWidget.setHorizontalScrollBarPolicy( + QtCore.Qt.ScrollBarAlwaysOff) self.settingListWidget.setObjectName(u'settingListWidget') self.settingsLayout.addWidget(self.settingListWidget, 0, 0, 1, 1) self.stackedLayout = QtGui.QStackedLayout() @@ -47,7 +50,6 @@ class Ui_SettingsDialog(object): self.buttonBox = create_accept_reject_button_box(settingsDialog, True) self.settingsLayout.addWidget(self.buttonBox, 1, 1, 1, 1) self.retranslateUi(settingsDialog) - self.stackedLayout.setCurrentIndex(0) QtCore.QMetaObject.connectSlotsByName(settingsDialog) QtCore.QObject.connect(self.settingListWidget, QtCore.SIGNAL(u'currentRowChanged(int)'), diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index 6de2a14d8..1e71bc985 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -74,7 +74,6 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): if self.stackedLayout.widget(tabIndex): if self.stackedLayout.widget(tabIndex).tabTitleVisible == \ tab.tabTitleVisible: - print tab.tabTitleVisible self.stackedLayout.widget(tabIndex).setHidden(False) match = True break @@ -84,6 +83,8 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): icon = build_icon(tab.icon_path) item_name.setIcon(icon) self.settingListWidget.insertItem(14 + location, item_name) + self.stackedLayout.addWidget(tab) + self.stackedLayout.setCurrentIndex(0) def removeTab(self, tab): """ diff --git a/resources/forms/settings.ui b/resources/forms/settings.ui index 0c378e125..06e55a00d 100644 --- a/resources/forms/settings.ui +++ b/resources/forms/settings.ui @@ -17,26 +17,49 @@ :/icon/openlp.org-icon-32.bmp:/icon/openlp.org-icon-32.bmp - + 10 30 - 681 + 691 441 - - - - + + + 500 + 0 + + + + + + + 0 + 0 + 211 + 409 + + + + + 200 + 0 + + + + Qt::ScrollBarAlwaysOff + + + - + From 5f228a69d101b83d404989b86fb8e8d9e6985ce0 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 13 Apr 2011 06:12:31 +0100 Subject: [PATCH 62/98] Minor fixes and Bible Label corrections --- openlp/core/lib/ui.py | 3 ++- openlp/plugins/bibles/lib/biblestab.py | 3 +-- openlp/plugins/bibles/lib/mediaitem.py | 7 ++----- openlp/plugins/remotes/remoteplugin.py | 3 ++- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 4f6951d13..fa0bc8afa 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -62,6 +62,7 @@ class UiStrings(object): Hours = translate('OpenLP.Ui', 'h', 'The abbreviated unit for hours') Image = translate('OpenLP.Ui', 'Image') Import = translate('OpenLP.Ui', 'Import') + LayoutStyle = translate('OpenLP.Ui', 'Layout style:') LengthTime = unicode(translate('OpenLP.Ui', 'Length %s')) Live = translate('OpenLP.Ui', 'Live') LiveBGError = translate('OpenLP.Ui', 'Live Background Error') @@ -335,4 +336,4 @@ def find_and_set_in_combo_box(combo_box, value_to_find): if index == -1: # Not Found. index = 0 - combo_box.setCurrentIndex(index) \ No newline at end of file + combo_box.setCurrentIndex(index) diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index c2e05363e..1d87d20cb 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -118,8 +118,7 @@ class BiblesTab(SettingsTab): self.newChaptersCheckBox.setText( translate('BiblesPlugin.BiblesTab', 'Only show new chapter numbers')) - self.layoutStyleLabel.setText( - translate('BiblesPlugin.BiblesTab', 'Layout style:')) + self.layoutStyleLabel.setText(UiStrings.LayoutStyle) self.displayStyleLabel.setText(UiStrings.DisplayStyle) self.bibleThemeLabel.setText( translate('BiblesPlugin.BiblesTab', 'Bible theme:')) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 73ed0c9bd..89ca69e33 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -279,7 +279,7 @@ class BibleMediaItem(MediaManagerItem): translate('BiblesPlugin.MediaItem', 'Clear')) self.advancedClearComboBox.addItem( translate('BiblesPlugin.MediaItem', 'Keep')) - self.quickLayoutLabel.setText(UiStrings.DisplayStyle) + self.quickLayoutLabel.setText(UiStrings.LayoutStyle) self.quickLayoutComboBox.setItemText(LayoutStyle.VersePerSlide, UiStrings.VersePerSlide) self.quickLayoutComboBox.setItemText(LayoutStyle.VersePerLine, @@ -835,9 +835,6 @@ class BibleMediaItem(MediaManagerItem): return u'{su}%s{/su}' % verse_text def onlayoutStyleComboBoxChanged(self): - self.settings.layout_style = self.quickLayoutComboBox.currentIndex() - self.settings.layoutStyleComboBox.setCurrentIndex( - self.settings.layout_style) QtCore.QSettings().setValue( self.settingsSection + u'/verse layout style', - QtCore.QVariant(self.settings.layout_style)) \ No newline at end of file + QtCore.QVariant(self.settings.layout_style)) diff --git a/openlp/plugins/remotes/remoteplugin.py b/openlp/plugins/remotes/remoteplugin.py index fea9e5170..d3b50e36b 100644 --- a/openlp/plugins/remotes/remoteplugin.py +++ b/openlp/plugins/remotes/remoteplugin.py @@ -38,7 +38,8 @@ class RemotesPlugin(Plugin): """ remotes constructor """ - Plugin.__init__(self, u'Remotes', plugin_helpers) + Plugin.__init__(self, u'Remotes', plugin_helpers, + settings_tab_class=RemoteTab) self.icon_path = u':/plugins/plugin_remote.png' self.icon = build_icon(self.icon_path) self.weight = -1 From d207cf2243f8a2f9a2c354b6b4cdfa775d3532b5 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 13 Apr 2011 11:19:16 +0200 Subject: [PATCH 63/98] clean ups --- openlp.pyw | 2 +- openlp/core/lib/htmlbuilder.py | 5 +- openlp/core/ui/printserviceform.py | 109 ++++++++++++++--------------- 3 files changed, 57 insertions(+), 59 deletions(-) diff --git a/openlp.pyw b/openlp.pyw index 57dbcb698..5c3b8ca77 100755 --- a/openlp.pyw +++ b/openlp.pyw @@ -116,7 +116,7 @@ class OpenLP(QtGui.QApplication): self.processEvents() # start the main app window self.mainWindow = MainWindow(screens, self.clipboard(), - self.arguments()) + self.arguments()) self.mainWindow.show() if show_splash: # now kill the splashscreen diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py index c4361a421..a80fedd06 100644 --- a/openlp/core/lib/htmlbuilder.py +++ b/openlp/core/lib/htmlbuilder.py @@ -452,7 +452,7 @@ def build_lyrics_css(item, webkitvers): .lyricsshadow { %s } - """ + """ theme = item.themedata lyricstable = u'' lyrics = u'' @@ -460,8 +460,7 @@ def build_lyrics_css(item, webkitvers): outline = u'' shadow = u'' if theme and item.main: - lyricstable = u'left: %spx; top: %spx;' % \ - (item.main.x(), item.main.y()) + lyricstable = u'left: %spx; top: %spx;' % (item.main.x(), item.main.y()) lyrics = build_lyrics_format_css(theme, item.main.width(), item.main.height()) # For performance reasons we want to show as few DIV's as possible, diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index 407b8e2b4..25a631ef3 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -29,7 +29,7 @@ import os from PyQt4 import QtCore, QtGui from lxml import html -from openlp.core.lib import translate +from openlp.core.lib import translate, get_text_file_string from openlp.core.lib.ui import UiStrings from openlp.core.ui.printservicedialog import Ui_PrintServiceDialog, ZoomSize from openlp.core.utils import AppLocation @@ -155,8 +155,6 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): """ html_data = html.fromstring( u'%s' % unicode(self.titleLineEdit.text())) - style = html.Element(u'style') - style.set(u'type', u'text/css') css_path = os.path.join( AppLocation.get_data_path(), u'servicePrint.css') if not os.path.isfile(css_path): @@ -164,96 +162,97 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): css_file = open(css_path, u'w') css_file.write(DEFAULT_CSS) css_file.close() - css_file = open(css_path, u'r') - style.text = u' '.join(css_file.readlines()) - css_file.close() - html_data.head.append(style) - html_data.append(html.Element(u'body')) - service_title = html.Element(u'span') + custom_css = get_text_file_string(css_path) + style = self._addChildToParent(u'style', custom_css, html_data.head) + style.set(u'type', u'text/css') + self._addChildToParent(u'body', parent=html_data) + service_title = self._addChildToParent( + u'span', unicode(self.titleLineEdit.text()), html_data.body) service_title.set(u'class', u'serviceTitle') - service_title.text = unicode(self.titleLineEdit.text()) - html_data.body.append(service_title) for index, item in enumerate(self.serviceManager.serviceItems): item = item[u'service_item'] - div = html.Element(u'div') + div = self._addChildToParent(u'div', parent=html_data.body) # Add the title of the service item. - item_title = html.Element(u'h2') + item_title = self._addChildToParent(u'h2', parent=div) item_title.set(u'class', u'itemTitle') - icon = html.Element(u'img') + icon = self._addChildToParent(u'img', parent=item_title) icon.set(u'src', item.icon) - item_title.append(icon) - item_title.append(html.fromstring( - u' %s' % item.get_display_title())) - div.append(item_title) + self._fromstring( + u' %s' % item.get_display_title(), item_title) if self.slideTextCheckBox.isChecked(): # Add the text of the service item. if item.is_text(): verse_def = None for slide in item.get_frames(): if not verse_def or verse_def != slide[u'verseTag']: - p = html.Element(u'p') + p = self._addChildToParent(u'p', parent=div) p.set(u'class', u'itemText') - div.append(p) else: - p.append(html.Element(u'br')) - p.append(html.fromstring( - u'%s' % slide[u'html'])) + self._addChildToParent(u'br', parent=p) + self._fromstring(u'%s' % slide[u'html'], p) verse_def = slide[u'verseTag'] # Break the page before the div element. if index != 0 and self.pageBreakAfterText.isChecked(): div.set(u'style', u'page-break-before:always') # Add the image names of the service item. elif item.is_image(): - ol = html.Element(u'ol') + ol = self._addChildToParent(u'ol', parent=div) for slide in range(len(item.get_frames())): - li = html.Element(u'li') - li.text = item.get_frame_title(slide) - ol.append(li) - div.append(ol) + self._addChildToParent(u'li', item.get_frame_title(slide), ol) # add footer if item.foot_text: - p = html.fromstring(item.foot_text) + p = self._fromstring(item.foot_text, div) p.set(u'class', u'itemFooter') - div.append(p) # Add service items' notes. if self.notesCheckBox.isChecked(): if item.notes: - p = html.Element(u'p') - title = html.Element(u'span') + p = self._addChildToParent(u'p', parent=div) + title = self._addChildToParent(u'span', unicode( + translate('OpenLP.ServiceManager', 'Notes:')), p) title.set(u'class', u'itemNotesTitle') - title.text = unicode( - translate('OpenLP.ServiceManager', 'Notes:')) - p.append(title) - text = html.fromstring(u' %s' % - item.notes.replace(u'\n', u'
')) + text = self._fromstring(u' %s' % + item.notes.replace(u'\n', u'
'), p) text.set(u'class', u'itemNotesText') - p.append(text) - div.append(p) # Add play length of media files. if item.is_media() and self.metaDataCheckBox.isChecked(): tme = item.media_length if item.end_time > 0: tme = item.end_time - item.start_time - p = html.fromstring(u'

%s

' % - translate('OpenLP.ServiceManager', 'Playing time:')) - p.append(html.fromstring(u'%s' % - unicode(datetime.timedelta(seconds=tme)))) - div.append(p) - html_data.body.append(div) + title = self._fromstring(u'

%s

' % + translate('OpenLP.ServiceManager', 'Playing time:'), div) + self._fromstring(u'%s' % + unicode(datetime.timedelta(seconds=tme)), title) # Add the custom service notes: - if self.footerTextEdit.toPlainText(): - title = html.Element(u'span') - title.set(u'class', u'customNotesTitle') - title.text = unicode( - translate('OpenLP.ServiceManager', u'Custom Service Notes:')) - div.append(title) - text = html.Element(u'span') - text.set(u'class', u'customNotesText') - text.text = u' %s' % self.footerTextEdit.toPlainText() - div.append(text) + if self.footerTextEdit.toPlainText(): + footer_title = self._addChildToParent(u'span', translate( + 'OpenLP.ServiceManager', u'Custom Service Notes:'), div) + footer_title.set(u'class', u'customNotesTitle') + footer_text = self._addChildToParent(u'span', + u' %s' % self.footerTextEdit.toPlainText(), div) + footer_text.set(u'class', u'customNotesText') self.document.setHtml(html.tostring(html_data)) self.previewWidget.updatePreview() + def _addChildToParent(self, tag, text=None, parent=None): + """ + Creates a html element. If ``text`` is given, the element's text will + set and if a ``parent`` is given, the element is appended. + """ + element = html.Element(tag) + if text is not None: + element.text = text + if parent is not None: + parent.append(element) + return element + + def _fromstring(self, string, parent): + """ + This is used to create a child html element from a string. + """ + element = html.fromstring(string) + parent.append(element) + return element + def paintRequested(self, printer): """ Paint the preview of the *self.document*. From 7d2719288a601256315ca0b607bdf2e8739ee684 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 13 Apr 2011 11:42:18 +0200 Subject: [PATCH 64/98] extended helper methods --- openlp/core/ui/printserviceform.py | 68 ++++++++++++++++++------------ 1 file changed, 42 insertions(+), 26 deletions(-) diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index 25a631ef3..5de12d78e 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -163,20 +163,19 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): css_file.write(DEFAULT_CSS) css_file.close() custom_css = get_text_file_string(css_path) - style = self._addChildToParent(u'style', custom_css, html_data.head) - style.set(u'type', u'text/css') + self._addChildToParent( + u'style', custom_css, html_data.head, u'type', u'text/css') self._addChildToParent(u'body', parent=html_data) - service_title = self._addChildToParent( - u'span', unicode(self.titleLineEdit.text()), html_data.body) - service_title.set(u'class', u'serviceTitle') + self._addChildToParent(u'span', unicode(self.titleLineEdit.text()), + html_data.body, u'class', u'serviceTitle') for index, item in enumerate(self.serviceManager.serviceItems): item = item[u'service_item'] div = self._addChildToParent(u'div', parent=html_data.body) # Add the title of the service item. - item_title = self._addChildToParent(u'h2', parent=div) - item_title.set(u'class', u'itemTitle') - icon = self._addChildToParent(u'img', parent=item_title) - icon.set(u'src', item.icon) + item_title = self._addChildToParent( + u'h2', parent=div, attribute=u'class', value=u'itemTitle') + self._addChildToParent( + u'img', parent=item_title, attribute=u'src', value=item.icon) self._fromstring( u' %s' % item.get_display_title(), item_title) if self.slideTextCheckBox.isChecked(): @@ -185,8 +184,8 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): verse_def = None for slide in item.get_frames(): if not verse_def or verse_def != slide[u'verseTag']: - p = self._addChildToParent(u'p', parent=div) - p.set(u'class', u'itemText') + p = self._addChildToParent(u'p', parent=div, + attribute=u'class', value=u'itemText') else: self._addChildToParent(u'br', parent=p) self._fromstring(u'%s' % slide[u'html'], p) @@ -201,18 +200,17 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): self._addChildToParent(u'li', item.get_frame_title(slide), ol) # add footer if item.foot_text: - p = self._fromstring(item.foot_text, div) - p.set(u'class', u'itemFooter') + self._fromstring( + item.foot_text, div, u'class', u'itemFooter') # Add service items' notes. if self.notesCheckBox.isChecked(): if item.notes: p = self._addChildToParent(u'p', parent=div) - title = self._addChildToParent(u'span', unicode( - translate('OpenLP.ServiceManager', 'Notes:')), p) - title.set(u'class', u'itemNotesTitle') - text = self._fromstring(u' %s' % - item.notes.replace(u'\n', u'
'), p) - text.set(u'class', u'itemNotesText') + self._addChildToParent(u'span', unicode( + translate('OpenLP.ServiceManager', 'Notes:')), p, + u'class', u'itemNotesTitle') + self._fromstring(u' %s' % item.notes.replace( + u'\n', u'
'), p, u'class', u'itemNotesText') # Add play length of media files. if item.is_media() and self.metaDataCheckBox.isChecked(): tme = item.media_length @@ -224,25 +222,43 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): unicode(datetime.timedelta(seconds=tme)), title) # Add the custom service notes: if self.footerTextEdit.toPlainText(): - footer_title = self._addChildToParent(u'span', translate( - 'OpenLP.ServiceManager', u'Custom Service Notes:'), div) - footer_title.set(u'class', u'customNotesTitle') - footer_text = self._addChildToParent(u'span', - u' %s' % self.footerTextEdit.toPlainText(), div) - footer_text.set(u'class', u'customNotesText') + self._addChildToParent(u'span', translate('OpenLP.ServiceManager', + u'Custom Service Notes:'), div, u'class', u'customNotesTitle') + self._addChildToParent( + u'span', u' %s' % self.footerTextEdit.toPlainText(), div, + u'class', u'customNotesText') self.document.setHtml(html.tostring(html_data)) self.previewWidget.updatePreview() - def _addChildToParent(self, tag, text=None, parent=None): + def _addChildToParent(self, tag, text=None, parent=None, attribute=None, + value=None): """ Creates a html element. If ``text`` is given, the element's text will set and if a ``parent`` is given, the element is appended. + + ``tag`` + The html tag, e. g. ``u'span'``. Defaults to ``None``. + + ``text`` + The text for the tag. Defaults to ``None``. + + ``parent`` + The parent element. Defaults to ``None``. + + ``attribute`` + An optional attribute, for instance ``u'class``. + + ``value`` + The value for the given ``attribute``. It does not have and meaning, + if the attribute is left to its default. """ element = html.Element(tag) if text is not None: element.text = text if parent is not None: parent.append(element) + if attribute is not None: + element.set(attribute, value if value is not None else u'') return element def _fromstring(self, string, parent): From 7d5ee8adf636cb3fc06a4fbd15facd63ec64a9cf Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 13 Apr 2011 11:53:57 +0200 Subject: [PATCH 65/98] spelling --- openlp/core/ui/printserviceform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index 5de12d78e..c41084b2f 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -249,7 +249,7 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): An optional attribute, for instance ``u'class``. ``value`` - The value for the given ``attribute``. It does not have and meaning, + The value for the given ``attribute``. It does not have a meaning, if the attribute is left to its default. """ element = html.Element(tag) From 8451d52f27c0042e67dba88356a5d076f4601b9a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 13 Apr 2011 12:22:26 +0200 Subject: [PATCH 66/98] fixed _fromstring; changed setting name in config --- openlp/core/ui/printserviceform.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index c41084b2f..5348396d7 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -104,7 +104,7 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): self.slideTextCheckBox.setChecked(settings.value( u'print slide text', QtCore.QVariant(False)).toBool()) self.pageBreakAfterText.setChecked(settings.value( - u'enable page break', QtCore.QVariant(False)).toBool()) + u'add page break', QtCore.QVariant(False)).toBool()) if not self.slideTextCheckBox.isChecked(): self.pageBreakAfterText.setDisabled(True) self.metaDataCheckBox.setChecked(settings.value( @@ -261,11 +261,13 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): element.set(attribute, value if value is not None else u'') return element - def _fromstring(self, string, parent): + def _fromstring(self, string, parent, attribute=None, value=None): """ This is used to create a child html element from a string. """ element = html.fromstring(string) + if attribute is not None: + element.set(attribute, value if value is not None else u'') parent.append(element) return element @@ -371,7 +373,7 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): settings.beginGroup(u'advanced') settings.setValue(u'print slide text', QtCore.QVariant(self.slideTextCheckBox.isChecked())) - settings.setValue(u'enable page break', + settings.setValue(u'add page break', QtCore.QVariant(self.pageBreakAfterText.isChecked())) settings.setValue(u'print file meta data', QtCore.QVariant(self.metaDataCheckBox.isChecked())) From 2dc2304a2c8b01a62ffc99f67a047f0322b9f1f8 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 13 Apr 2011 15:31:23 +0200 Subject: [PATCH 67/98] fixed advanced bible book initialisation; append a space to completion suggestions --- openlp/plugins/bibles/lib/mediaitem.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index b0d4c8bca..fbafcd725 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -328,6 +328,8 @@ class BibleMediaItem(MediaManagerItem): if bible in bibles: find_and_set_in_combo_box(self.advancedVersionComboBox, bible) self.initialiseAdvancedBible(unicode(bible)) + elif len(bibles): + self.initialiseAdvancedBible(bibles[0]) def reloadBibles(self): log.debug(u'Reloading Bibles') @@ -395,7 +397,7 @@ class BibleMediaItem(MediaManagerItem): bible = unicode(self.quickVersionComboBox.currentText()) if bible: book_data = bibles[bible].get_books() - books = [book.name for book in book_data] + books = [book.name + u' ' for book in book_data] books.sort() add_widget_completer(books, self.quickSearchEdit) From 6082785b3eb2304f434395608545dc2d8ba41bdb Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 13 Apr 2011 16:28:31 +0200 Subject: [PATCH 68/98] use map; fixed changing quickBible --- openlp/plugins/bibles/lib/mediaitem.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index fbafcd725..3f1c2b612 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -291,7 +291,11 @@ class BibleMediaItem(MediaManagerItem): log.debug(u'bible manager initialise') self.parent.manager.media = self self.loadBibles() - self.updateAutoCompleter(False) + bible = QtCore.QSettings().value( + self.settingsSection + u'/quick bible', QtCore.QVariant( + self.quickVersionComboBox.currentText())).toString() + find_and_set_in_combo_box(self.quickVersionComboBox, bible) + self.updateAutoCompleter() self.configUpdated() log.debug(u'bible manager initialise complete') @@ -376,20 +380,14 @@ class BibleMediaItem(MediaManagerItem): self.adjustComboBox(1, verse_count, self.advancedFromVerse) self.adjustComboBox(1, verse_count, self.advancedToVerse) - def updateAutoCompleter(self, updateConfig=True): + def updateAutoCompleter(self): """ This updates the bible book completion list for the search field. The completion depends on the bible. It is only updated when we are doing a reference search, otherwise the auto completion list is removed. """ - if updateConfig: - QtCore.QSettings().setValue(self.settingsSection + u'/quick bible', - QtCore.QVariant(self.quickVersionComboBox.currentText())) - else: - book = QtCore.QSettings().value( - self.settingsSection + u'/quick bible', - QtCore.QVariant(u'')).toString() - find_and_set_in_combo_box(self.quickVersionComboBox, book) + QtCore.QSettings().setValue(self.settingsSection + u'/quick bible', + QtCore.QVariant(self.quickVersionComboBox.currentText())) books = [] # We have to do a 'Reference Search'. if self.quickSearchEdit.currentSearchType() == BibleSearch.Reference: @@ -397,7 +395,7 @@ class BibleMediaItem(MediaManagerItem): bible = unicode(self.quickVersionComboBox.currentText()) if bible: book_data = bibles[bible].get_books() - books = [book.name + u' ' for book in book_data] + books = map(lambda x: x.name + u' ', book_data) books.sort() add_widget_completer(books, self.quickSearchEdit) From 01c286e50ef4ad6d30ea9b6f9f54b2da652e688f Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 13 Apr 2011 20:01:28 +0200 Subject: [PATCH 69/98] fixed crash --- openlp/core/ui/printserviceform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index 5348396d7..01b937d61 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -254,7 +254,7 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): """ element = html.Element(tag) if text is not None: - element.text = text + element.text = unicode(text) if parent is not None: parent.append(element) if attribute is not None: From a22bb2e0bc4ebbc9f21ddb990d9e4079b5b93f6f Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 13 Apr 2011 19:14:38 +0100 Subject: [PATCH 70/98] Active/inactive fixes --- openlp/core/lib/pluginmanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/lib/pluginmanager.py b/openlp/core/lib/pluginmanager.py index 3b73750fe..6854cb53c 100644 --- a/openlp/core/lib/pluginmanager.py +++ b/openlp/core/lib/pluginmanager.py @@ -150,7 +150,7 @@ class PluginManager(object): if plugin.status is not PluginStatus.Disabled: plugin.settings_tab = plugin.getSettingsTab() visible_title = plugin.getString(StringContent.VisibleName) - if plugin.settings_tab: + if plugin.settings_tab and plugin.isActive(): log.debug(u'Inserting settings tab item from %s' % visible_title[u'title']) settingsform.insertTab(plugin.settings_tab, plugin.weight) From 606287bda10665592a1971b64e62474094587f98 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 13 Apr 2011 20:18:25 +0200 Subject: [PATCH 71/98] reverted change --- openlp/plugins/bibles/lib/mediaitem.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 3f1c2b612..118bdfc66 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -395,7 +395,7 @@ class BibleMediaItem(MediaManagerItem): bible = unicode(self.quickVersionComboBox.currentText()) if bible: book_data = bibles[bible].get_books() - books = map(lambda x: x.name + u' ', book_data) + books = [book.name + u' ' for book in book_data] books.sort() add_widget_completer(books, self.quickSearchEdit) From 79ed96648faa71539e85297edfbade42de4d66c4 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 13 Apr 2011 19:40:09 +0100 Subject: [PATCH 72/98] More cleanups --- openlp/core/ui/settingsdialog.py | 12 ++++++------ openlp/core/ui/settingsform.py | 6 ++---- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/openlp/core/ui/settingsdialog.py b/openlp/core/ui/settingsdialog.py index 45a68c321..3f5f12bd2 100644 --- a/openlp/core/ui/settingsdialog.py +++ b/openlp/core/ui/settingsdialog.py @@ -35,20 +35,20 @@ class Ui_SettingsDialog(object): settingsDialog.resize(800, 500) settingsDialog.setWindowIcon( build_icon(u':/system/system_settings.png')) - self.settingsLayout = QtGui.QGridLayout(settingsDialog) - self.settingsLayout.setObjectName(u'settingsLayout') - self.settingsLayout.setMargin(0) + self.dialogLayout = QtGui.QGridLayout(settingsDialog) + self.dialogLayout.setObjectName(u'dialogLayout') + self.dialogLayout.setMargin(0) self.settingListWidget = QtGui.QListWidget(settingsDialog) self.settingListWidget.setMinimumSize(QtCore.QSize(150, 0)) self.settingListWidget.setHorizontalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOff) self.settingListWidget.setObjectName(u'settingListWidget') - self.settingsLayout.addWidget(self.settingListWidget, 0, 0, 1, 1) + self.dialogLayout.addWidget(self.settingListWidget, 0, 0, 1, 1) self.stackedLayout = QtGui.QStackedLayout() self.stackedLayout.setObjectName(u'stackedLayout') - self.settingsLayout.addLayout(self.stackedLayout, 0, 1, 1, 1) + self.dialogLayout.addLayout(self.stackedLayout, 0, 1, 1, 1) self.buttonBox = create_accept_reject_button_box(settingsDialog, True) - self.settingsLayout.addWidget(self.buttonBox, 1, 1, 1, 1) + self.dialogLayout.addWidget(self.buttonBox, 1, 1, 1, 1) self.retranslateUi(settingsDialog) QtCore.QMetaObject.connectSlotsByName(settingsDialog) QtCore.QObject.connect(self.settingListWidget, diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index 1e71bc985..3415f3ec8 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -60,9 +60,9 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): # load all the settings for tabIndex in range(0, self.stackedLayout.count()): self.stackedLayout.widget(tabIndex).load() + self.settingListWidget.setCurrentRow(0) return QtGui.QDialog.exec_(self) - def insertTab(self, tab, location): """ Add a tab to the form at a specific location @@ -78,13 +78,11 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): match = True break if not match: - self.stackedLayout.addWidget(tab) + pos = self.stackedLayout.addWidget(tab) item_name = QtGui.QListWidgetItem(tab.tabTitleVisible) icon = build_icon(tab.icon_path) item_name.setIcon(icon) self.settingListWidget.insertItem(14 + location, item_name) - self.stackedLayout.addWidget(tab) - self.stackedLayout.setCurrentIndex(0) def removeTab(self, tab): """ From 9e88d0bff4c3a3a761ea974acc5e1a2e9aa2b0da Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Wed, 13 Apr 2011 20:53:05 +0200 Subject: [PATCH 73/98] clean ups --- openlp/plugins/bibles/lib/db.py | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index 85ab98de1..ec63dc02f 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -177,10 +177,7 @@ class BibleDB(QtCore.QObject, Manager): Returns the version name of the Bible. """ version_name = self.get_object(BibleMeta, u'Version') - if version_name: - self.name = version_name.value - else: - self.name = None + self.name = version_name.value if version_name else None return self.name def clean_filename(self, old_filename): @@ -256,10 +253,10 @@ class BibleDB(QtCore.QObject, Manager): # Text list has book and chapter as first two elements of the array. for verse_number, verse_text in textlist.iteritems(): verse = Verse.populate( - book_id = book_id, - chapter = chapter, - verse = verse_number, - text = verse_text + book_id=book_id, + chapter=chapter, + verse=verse_number, + text=verse_text ) self.session.add(verse) self.session.commit() @@ -383,15 +380,13 @@ class BibleDB(QtCore.QObject, Manager): log.debug(u'BibleDB.verse_search("%s")', text) verses = self.session.query(Verse) if text.find(u',') > -1: - or_clause = [] - keywords = [u'%%%s%%' % keyword.strip() - for keyword in text.split(u',')] - for keyword in keywords: - or_clause.append(Verse.text.like(keyword)) + keywords = \ + [u'%%%s%%' % keyword.strip() for keyword in text.split(u',')] + or_clause = [Verse.text.like(keyword) for keyword in keywords] verses = verses.filter(or_(*or_clause)) else: - keywords = [u'%%%s%%' % keyword.strip() - for keyword in text.split(u' ')] + keywords = \ + [u'%%%s%%' % keyword.strip() for keyword in text.split(u' ')] for keyword in keywords: verses = verses.filter(Verse.text.like(keyword)) verses = verses.all() From 6454b88a83aea140d4c19bad077768d4b93443e2 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Wed, 13 Apr 2011 20:12:47 +0100 Subject: [PATCH 74/98] Fix Tab parentage --- openlp/core/lib/plugin.py | 4 ++-- openlp/core/lib/pluginmanager.py | 2 +- openlp/core/lib/settingstab.py | 4 ++-- openlp/core/ui/advancedtab.py | 4 ++-- openlp/core/ui/generaltab.py | 4 ++-- openlp/core/ui/settingsdialog.py | 1 + openlp/core/ui/settingsform.py | 6 +++--- openlp/core/ui/themestab.py | 14 +++++++------- openlp/plugins/alerts/lib/alertstab.py | 4 ++-- openlp/plugins/bibles/lib/biblestab.py | 4 ++-- openlp/plugins/custom/lib/customtab.py | 4 ++-- openlp/plugins/media/lib/mediatab.py | 4 ++-- .../plugins/presentations/lib/presentationtab.py | 4 ++-- openlp/plugins/presentations/presentationplugin.py | 4 ++-- openlp/plugins/remotes/lib/remotetab.py | 4 ++-- openlp/plugins/songs/lib/songstab.py | 4 ++-- 16 files changed, 36 insertions(+), 35 deletions(-) diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index ef89e6424..e4359f6b0 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -244,13 +244,13 @@ class Plugin(QtCore.QObject): """ pass - def getSettingsTab(self): + def getSettingsTab(self, parent): """ Create a tab for the settings window to display the configurable options for this plugin to the user. """ if self.settings_tab_class: - return self.settings_tab_class(self.name, + return self.settings_tab_class(parent, self.name, self.getString(StringContent.VisibleName)[u'title'], self.icon_path) return None diff --git a/openlp/core/lib/pluginmanager.py b/openlp/core/lib/pluginmanager.py index 6854cb53c..6f1222276 100644 --- a/openlp/core/lib/pluginmanager.py +++ b/openlp/core/lib/pluginmanager.py @@ -148,7 +148,7 @@ class PluginManager(object): """ for plugin in self.plugins: if plugin.status is not PluginStatus.Disabled: - plugin.settings_tab = plugin.getSettingsTab() + plugin.settings_tab = plugin.getSettingsTab(settingsform) visible_title = plugin.getString(StringContent.VisibleName) if plugin.settings_tab and plugin.isActive(): log.debug(u'Inserting settings tab item from %s' % diff --git a/openlp/core/lib/settingstab.py b/openlp/core/lib/settingstab.py index 32d7dc912..e1396d984 100644 --- a/openlp/core/lib/settingstab.py +++ b/openlp/core/lib/settingstab.py @@ -31,7 +31,7 @@ class SettingsTab(QtGui.QWidget): SettingsTab is a helper widget for plugins to define Tabs for the settings dialog. """ - def __init__(self, title, visible_title=None, icon_path=None): + def __init__(self, parent, title, visible_title=None, icon_path=None): """ Constructor to create the Settings tab item. @@ -41,7 +41,7 @@ class SettingsTab(QtGui.QWidget): ``visible_title`` The title of the tab, which is usually displayed on the tab. """ - QtGui.QWidget.__init__(self) + QtGui.QWidget.__init__(self, parent) self.tabTitle = title self.tabTitleVisible = visible_title self.settingsSection = self.tabTitle.lower() diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index 6041382b8..3d9321fe1 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -37,12 +37,12 @@ class AdvancedTab(SettingsTab): The :class:`AdvancedTab` manages the advanced settings tab including the UI and the loading and saving of the displayed settings. """ - def __init__(self): + def __init__(self, parent): """ Initialise the settings tab """ generalTranslated = translate('AdvancedTab', 'Advanced') - SettingsTab.__init__(self, u'Advanced', generalTranslated) + SettingsTab.__init__(self, parent ,u'Advanced', generalTranslated) self.default_image = u':/graphics/openlp-splash-screen.png' self.default_color = u'#ffffff' self.icon_path = u':/icon/openlp-logo-16x16.png' diff --git a/openlp/core/ui/generaltab.py b/openlp/core/ui/generaltab.py index d566244b8..cfde7810c 100644 --- a/openlp/core/ui/generaltab.py +++ b/openlp/core/ui/generaltab.py @@ -36,7 +36,7 @@ class GeneralTab(SettingsTab): """ GeneralTab is the general settings tab in the settings dialog. """ - def __init__(self, screens): + def __init__(self, parent, screens): """ Initialise the general settings tab """ @@ -46,7 +46,7 @@ class GeneralTab(SettingsTab): self.overrideChanged = True self.icon_path = u':/icon/openlp-logo-16x16.png' generalTranslated = translate('GeneralTab', 'General') - SettingsTab.__init__(self, u'General', generalTranslated) + SettingsTab.__init__(self, parent, u'General', generalTranslated) def preLoad(self): """ diff --git a/openlp/core/ui/settingsdialog.py b/openlp/core/ui/settingsdialog.py index 3f5f12bd2..485cb9c5f 100644 --- a/openlp/core/ui/settingsdialog.py +++ b/openlp/core/ui/settingsdialog.py @@ -39,6 +39,7 @@ class Ui_SettingsDialog(object): self.dialogLayout.setObjectName(u'dialogLayout') self.dialogLayout.setMargin(0) self.settingListWidget = QtGui.QListWidget(settingsDialog) + self.settingListWidget.setUniformItemSizes(True) self.settingListWidget.setMinimumSize(QtCore.QSize(150, 0)) self.settingListWidget.setHorizontalScrollBarPolicy( QtCore.Qt.ScrollBarAlwaysOff) diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index 3415f3ec8..72e1b7fa4 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -47,13 +47,13 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): QtGui.QDialog.__init__(self, parent) self.setupUi(self) # General tab - generalTab = GeneralTab(screens) + generalTab = GeneralTab(self, screens) self.insertTab(generalTab, 1) # Themes tab - themesTab = ThemesTab(mainWindow) + themesTab = ThemesTab(self, mainWindow) self.insertTab(themesTab, 2) # Advanced tab - advancedTab = AdvancedTab() + advancedTab = AdvancedTab(self, ) self.insertTab(advancedTab, 3) def exec_(self): diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index b5fcce1ec..372ee0cc8 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -34,10 +34,10 @@ class ThemesTab(SettingsTab): """ ThemesTab is the theme settings tab in the settings dialog. """ - def __init__(self, parent): - self.parent = parent + def __init__(self, parent, mainwindow): + self.mainwindow = mainwindow generalTranslated = translate('ThemeTab', 'Themes') - SettingsTab.__init__(self, u'Themes', generalTranslated) + SettingsTab.__init__(self, parent, u'Themes', generalTranslated) self.icon_path = u':/themes/theme_new.png' def setupUi(self): @@ -149,7 +149,7 @@ class ThemesTab(SettingsTab): settings.setValue(u'global theme', QtCore.QVariant(self.global_theme)) settings.endGroup() - self.parent.renderManager.set_global_theme( + self.mainwindow.renderManager.set_global_theme( self.global_theme, self.theme_level) Receiver.send_message(u'theme_update_global', self.global_theme) @@ -167,7 +167,7 @@ class ThemesTab(SettingsTab): def onDefaultComboBoxChanged(self, value): self.global_theme = unicode(self.DefaultComboBox.currentText()) - self.parent.renderManager.set_global_theme( + self.mainwindow.renderManager.set_global_theme( self.global_theme, self.theme_level) self.__previewGlobalTheme() @@ -188,7 +188,7 @@ class ThemesTab(SettingsTab): for theme in theme_list: self.DefaultComboBox.addItem(theme) find_and_set_in_combo_box(self.DefaultComboBox, self.global_theme) - self.parent.renderManager.set_global_theme( + self.mainwindow.renderManager.set_global_theme( self.global_theme, self.theme_level) if self.global_theme is not u'': self.__previewGlobalTheme() @@ -197,7 +197,7 @@ class ThemesTab(SettingsTab): """ Utility method to update the global theme preview image. """ - image = self.parent.themeManagerContents.getPreviewImage( + image = self.mainwindow.themeManagerContents.getPreviewImage( self.global_theme) preview = QtGui.QPixmap(unicode(image)) if not preview.isNull(): diff --git a/openlp/plugins/alerts/lib/alertstab.py b/openlp/plugins/alerts/lib/alertstab.py index 2e97d27d3..3b6c50a10 100644 --- a/openlp/plugins/alerts/lib/alertstab.py +++ b/openlp/plugins/alerts/lib/alertstab.py @@ -33,8 +33,8 @@ class AlertsTab(SettingsTab): """ AlertsTab is the alerts settings tab in the settings dialog. """ - def __init__(self, name, visible_title, icon_path): - SettingsTab.__init__(self, name, visible_title, icon_path) + def __init__(self, parent, name, visible_title, icon_path): + SettingsTab.__init__(self, parent, name, visible_title, icon_path) def setupUi(self): self.setObjectName(u'AlertsTab') diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index 1d87d20cb..b013c402a 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -40,11 +40,11 @@ class BiblesTab(SettingsTab): """ log.info(u'Bible Tab loaded') - def __init__(self, title, visible_title, icon_path): + def __init__(self, parent, title, visible_title, icon_path): self.paragraph_style = True self.show_new_chapters = False self.display_style = 0 - SettingsTab.__init__(self, title, visible_title, icon_path) + SettingsTab.__init__(self, parent, title, visible_title, icon_path) def setupUi(self): self.setObjectName(u'BiblesTab') diff --git a/openlp/plugins/custom/lib/customtab.py b/openlp/plugins/custom/lib/customtab.py index 006295410..9de294418 100644 --- a/openlp/plugins/custom/lib/customtab.py +++ b/openlp/plugins/custom/lib/customtab.py @@ -32,8 +32,8 @@ class CustomTab(SettingsTab): """ CustomTab is the Custom settings tab in the settings dialog. """ - def __init__(self, title, visible_title, icon_path): - SettingsTab.__init__(self, title, visible_title, icon_path) + def __init__(self, parent, title, visible_title, icon_path): + SettingsTab.__init__(self, parent, title, visible_title, icon_path) def setupUi(self): self.setObjectName(u'CustomTab') diff --git a/openlp/plugins/media/lib/mediatab.py b/openlp/plugins/media/lib/mediatab.py index 72085158c..f54aa02fa 100644 --- a/openlp/plugins/media/lib/mediatab.py +++ b/openlp/plugins/media/lib/mediatab.py @@ -32,8 +32,8 @@ class MediaTab(SettingsTab): """ MediaTab is the Media settings tab in the settings dialog. """ - def __init__(self, title, visible_title, icon_path): - SettingsTab.__init__(self, title, visible_title, icon_path) + def __init__(self, parent, title, visible_title, icon_path): + SettingsTab.__init__(self, parent, title, visible_title, icon_path) def setupUi(self): self.setObjectName(u'MediaTab') diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py index c7a143006..8e3a98031 100644 --- a/openlp/plugins/presentations/lib/presentationtab.py +++ b/openlp/plugins/presentations/lib/presentationtab.py @@ -33,12 +33,12 @@ class PresentationTab(SettingsTab): """ PresentationsTab is the Presentations settings tab in the settings dialog. """ - def __init__(self, title, visible_title, controllers, icon_path): + def __init__(self, parent, title, visible_title, controllers, icon_path): """ Constructor """ self.controllers = controllers - SettingsTab.__init__(self, title, visible_title, icon_path) + SettingsTab.__init__(self, parent, title, visible_title, icon_path) def setupUi(self): """ diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index 1da9fe511..f217e6023 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -56,12 +56,12 @@ class PresentationPlugin(Plugin): self.icon_path = u':/plugins/plugin_presentations.png' self.icon = build_icon(self.icon_path) - def getSettingsTab(self): + def getSettingsTab(self, parent): """ Create the settings Tab """ visible_name = self.getString(StringContent.VisibleName) - return PresentationTab(self.name, visible_name[u'title'], + return PresentationTab(parent, self.name, visible_name[u'title'], self.controllers, self.icon_path) def initialise(self): diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index 97aedc9cd..297437d93 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -32,8 +32,8 @@ class RemoteTab(SettingsTab): """ RemoteTab is the Remotes settings tab in the settings dialog. """ - def __init__(self, title, visible_title, icon_path): - SettingsTab.__init__(self, title, visible_title, icon_path) + def __init__(self, parent, title, visible_title, icon_path): + SettingsTab.__init__(self, parent, title, visible_title, icon_path) def setupUi(self): self.setObjectName(u'RemoteTab') diff --git a/openlp/plugins/songs/lib/songstab.py b/openlp/plugins/songs/lib/songstab.py index 092c6af29..e39c22be7 100644 --- a/openlp/plugins/songs/lib/songstab.py +++ b/openlp/plugins/songs/lib/songstab.py @@ -32,8 +32,8 @@ class SongsTab(SettingsTab): """ SongsTab is the Songs settings tab in the settings dialog. """ - def __init__(self, title, visible_title, icon_path): - SettingsTab.__init__(self, title, visible_title, icon_path) + def __init__(self, parent, title, visible_title, icon_path): + SettingsTab.__init__(self, parent, title, visible_title, icon_path) def setupUi(self): self.setObjectName(u'SongsTab') From 5f93ff657ef7f5e84e2ae5b067259db316268ade Mon Sep 17 00:00:00 2001 From: Jonathan Corwin Date: Wed, 13 Apr 2011 23:10:41 +0100 Subject: [PATCH 75/98] PPT's are getting there. Going from blank ppt to song broken now --- openlp/core/ui/slidecontroller.py | 50 +++++++++++-------- .../presentations/lib/messagelistener.py | 13 ++--- 2 files changed, 36 insertions(+), 27 deletions(-) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 0261b5782..00202d8cc 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -460,7 +460,7 @@ class SlideController(QtGui.QWidget): request = unicode(self.sender().text()) slideno = self.slideList[request] self.__updatePreviewSelection(slideno) - self.onSlideSelected() + self.slideSelected() def receiveSpinDelay(self, value): """ @@ -556,7 +556,7 @@ class SlideController(QtGui.QWidget): # If service item is the same as the current on only change slide if item.__eq__(self.serviceItem): self.__checkUpdateSelectedSlide(slideno) - self.onSlideSelected() + self.slideSelected() return self._processItem(item, slideno) @@ -641,7 +641,7 @@ class SlideController(QtGui.QWidget): self.display.buildHtml(self.serviceItem) if serviceItem.is_media(): self.onMediaStart(serviceItem) - self.onSlideSelected(True) + self.slideSelected(True) self.previewListWidget.setFocus() if old_item: # Close the old item after the new one is opened @@ -699,7 +699,7 @@ class SlideController(QtGui.QWidget): self.updatePreview() else: self.previewListWidget.selectRow(0) - self.onSlideSelected() + self.slideSelected() def onSlideSelectedIndex(self, message): """ @@ -714,7 +714,7 @@ class SlideController(QtGui.QWidget): self.updatePreview() else: self.__checkUpdateSelectedSlide(index) - self.onSlideSelected() + self.slideSelected() def mainDisplaySetBackground(self): """ @@ -755,15 +755,13 @@ class SlideController(QtGui.QWidget): self.themeScreen.setChecked(False) self.desktopScreen.setChecked(False) if checked: - Receiver.send_message(u'maindisplay_hide', HideMode.Blank) QtCore.QSettings().setValue( self.parent.generalSettingsSection + u'/screen blank', QtCore.QVariant(u'blanked')) else: - Receiver.send_message(u'maindisplay_show') QtCore.QSettings().remove( self.parent.generalSettingsSection + u'/screen blank') - self.blankPlugin(checked) + self.blankPlugin() self.updatePreview() def onThemeDisplay(self, checked): @@ -776,15 +774,13 @@ class SlideController(QtGui.QWidget): self.themeScreen.setChecked(checked) self.desktopScreen.setChecked(False) if checked: - Receiver.send_message(u'maindisplay_hide', HideMode.Theme) QtCore.QSettings().setValue( self.parent.generalSettingsSection + u'/screen blank', QtCore.QVariant(u'themed')) else: - Receiver.send_message(u'maindisplay_show') QtCore.QSettings().remove( self.parent.generalSettingsSection + u'/screen blank') - self.blankPlugin(checked) + self.blankPlugin() self.updatePreview() def onHideDisplay(self, checked): @@ -797,28 +793,31 @@ class SlideController(QtGui.QWidget): self.themeScreen.setChecked(False) self.desktopScreen.setChecked(checked) if checked: - Receiver.send_message(u'maindisplay_hide', HideMode.Screen) QtCore.QSettings().setValue( self.parent.generalSettingsSection + u'/screen blank', QtCore.QVariant(u'hidden')) else: - Receiver.send_message(u'maindisplay_show') QtCore.QSettings().remove( self.parent.generalSettingsSection + u'/screen blank') self.hidePlugin(checked) self.updatePreview() - def blankPlugin(self, blank): + def blankPlugin(self): """ - Blank the display screen within a plugin if required. + Blank/Hide the display screen within a plugin if required. """ - log.debug(u'blankPlugin %s ', blank) + hide_mode = self.hideMode() + log.debug(u'blankPlugin %s ', hide_mode) if self.serviceItem is not None: - if blank: + if hide_mode: + if not self.serviceItem.is_command(): + Receiver.send_message(u'maindisplay_hide', hide_mode) Receiver.send_message(u'%s_blank' % self.serviceItem.name.lower(), - [self.serviceItem, self.isLive]) + [self.serviceItem, self.isLive, hide_mode]) else: + if not self.serviceItem.is_command(): + Receiver.send_message(u'maindisplay_show') Receiver.send_message(u'%s_unblank' % self.serviceItem.name.lower(), [self.serviceItem, self.isLive]) @@ -830,15 +829,24 @@ class SlideController(QtGui.QWidget): log.debug(u'hidePlugin %s ', hide) if self.serviceItem is not None: if hide: + Receiver.send_message(u'maindisplay_hide', HideMode.Screen) Receiver.send_message(u'%s_hide' % self.serviceItem.name.lower(), [self.serviceItem, self.isLive]) else: + if not self.serviceItem.is_command(): + Receiver.send_message(u'maindisplay_show') Receiver.send_message(u'%s_unblank' % self.serviceItem.name.lower(), [self.serviceItem, self.isLive]) def onSlideSelected(self, start=False): + """ + Slide selected in controller + """ + self.slideSelected() + + def slideSelected(self, start=False): """ Generate the preview when you click on a slide. if this is the Live Controller also display on the screen @@ -927,7 +935,7 @@ class SlideController(QtGui.QWidget): Receiver.send_message('servicemanager_next_item') return self.__checkUpdateSelectedSlide(row) - self.onSlideSelected() + self.slideSelected() def onSlideSelectedPreviousNoloop(self): self.onSlideSelectedPrevious(False) @@ -950,7 +958,7 @@ class SlideController(QtGui.QWidget): else: row = 0 self.__checkUpdateSelectedSlide(row) - self.onSlideSelected() + self.slideSelected() def __checkUpdateSelectedSlide(self, row): if row + 1 < self.previewListWidget.rowCount(): @@ -971,7 +979,7 @@ class SlideController(QtGui.QWidget): else: self.previewListWidget.selectRow( self.previewListWidget.rowCount() - 1) - self.onSlideSelected() + self.slideSelected() def onStartLoop(self): """ diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index 4f9268b3e..b76fd7d4c 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -70,11 +70,9 @@ class Controller(object): Receiver.send_message(u'maindisplay_hide', HideMode.Screen) self.stop() elif hide_mode == HideMode.Theme: - Receiver.send_message(u'maindisplay_hide', HideMode.Theme) - self.blank() + self.blank(hide_mode) elif hide_mode == HideMode.Blank: - Receiver.send_message(u'maindisplay_hide', HideMode.Blank) - self.blank() + self.blank(hide_mode) else: self.doc.start_presentation() Receiver.send_message(u'maindisplay_hide', HideMode.Screen) @@ -182,7 +180,7 @@ class Controller(object): #self.doc.slidenumber = 0 #self.timer.stop() - def blank(self): + def blank(self, hide_mode): """ Instruct the controller to blank the presentation """ @@ -193,6 +191,8 @@ class Controller(object): return if not self.doc.is_active(): return + if hide_mode == HideMode.Theme: + Receiver.send_message(u'maindisplay_hide', HideMode.Theme) self.doc.blank_screen() def stop(self): @@ -363,8 +363,9 @@ class MessageListener(object): React to the message to blank the display """ is_live = message[1] + hide_mode = message[2] if is_live: - self.live_handler.blank() + self.live_handler.blank(hide_mode) def unblank(self, message): """ From f2f49acd399e0780b3cbd85e2af0fd36148a6e62 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 14 Apr 2011 15:53:02 +0200 Subject: [PATCH 76/98] added ability to change language in a spelledit --- openlp/core/lib/__init__.py | 2 +- openlp/core/lib/spelltextedit.py | 32 +++++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 491f3e652..89eeb6ad4 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -274,8 +274,8 @@ def check_directory_exists(dir): from listwidgetwithdnd import ListWidgetWithDnD from displaytags import DisplayTags -from spelltextedit import SpellTextEdit from eventreceiver import Receiver +from spelltextedit import SpellTextEdit from imagemanager import ImageManager from settingsmanager import SettingsManager from plugin import PluginStatus, StringContent, Plugin diff --git a/openlp/core/lib/spelltextedit.py b/openlp/core/lib/spelltextedit.py index 95befad09..a99539775 100644 --- a/openlp/core/lib/spelltextedit.py +++ b/openlp/core/lib/spelltextedit.py @@ -36,7 +36,9 @@ except ImportError: # http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check from PyQt4 import QtCore, QtGui + from openlp.core.lib import translate, DisplayTags +from openlp.core.lib.ui import checkable_action log = logging.getLogger(__name__) @@ -80,6 +82,19 @@ class SpellTextEdit(QtGui.QPlainTextEdit): if not cursor.hasSelection(): cursor.select(QtGui.QTextCursor.WordUnderCursor) self.setTextCursor(cursor) + # Add menu with available languages. + if ENCHANT_AVAILABLE: + lang_menu = QtGui.QMenu( + translate('OpenLP.SpellTextEdit', 'Language:')) + for lang in enchant.list_languages(): + action = checkable_action( + lang_menu, lang, lang == self.dictionary.tag) + action.setText(lang) + lang_menu.addAction(action) + popupMenu.insertSeparator(popupMenu.actions()[0]) + popupMenu.insertMenu(popupMenu.actions()[0], lang_menu) + QtCore.QObject.connect(lang_menu, + QtCore.SIGNAL(u'triggered(QAction*)'), self.setLanguage) # Check if the selected word is misspelled and offer spelling # suggestions if it is. if ENCHANT_AVAILABLE and self.textCursor().hasSelection(): @@ -93,19 +108,30 @@ class SpellTextEdit(QtGui.QPlainTextEdit): spell_menu.addAction(action) # Only add the spelling suggests to the menu if there are # suggestions. - if len(spell_menu.actions()) != 0: - popupMenu.insertSeparator(popupMenu.actions()[0]) + if len(spell_menu.actions()): popupMenu.insertMenu(popupMenu.actions()[0], spell_menu) tagMenu = QtGui.QMenu(translate('OpenLP.SpellTextEdit', 'Formatting Tags')) for html in DisplayTags.get_html_tags(): - action = SpellAction( html[u'desc'], tagMenu) + action = SpellAction(html[u'desc'], tagMenu) action.correct.connect(self.htmlTag) tagMenu.addAction(action) popupMenu.insertSeparator(popupMenu.actions()[0]) popupMenu.insertMenu(popupMenu.actions()[0], tagMenu) popupMenu.exec_(event.globalPos()) + def setLanguage(self, action): + """ + Changes the language for this spelltextedit. + + ``action`` + The action. + """ + self.dictionary = enchant.Dict(action.text()) + self.highlighter.spellingDictionary = self.dictionary + self.highlighter.highlightBlock(self.toPlainText()) + self.highlighter.rehighlight() + def correctWord(self, word): """ Replaces the selected text with word. From 27e9edad517fa75d09c9bafa7b42b296bdc3d1ea Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 14 Apr 2011 16:51:05 +0200 Subject: [PATCH 77/98] fixed wrong parentage --- openlp/core/ui/printserviceform.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index 01b937d61..0de20d8a9 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -222,6 +222,7 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): unicode(datetime.timedelta(seconds=tme)), title) # Add the custom service notes: if self.footerTextEdit.toPlainText(): + div = self._addChildToParent(u'div', parent=html_data.body) self._addChildToParent(u'span', translate('OpenLP.ServiceManager', u'Custom Service Notes:'), div, u'class', u'customNotesTitle') self._addChildToParent( From 83e06b6eb1e08e83b0f7b96c44d5e214575b71ef Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 14 Apr 2011 20:05:00 +0200 Subject: [PATCH 78/98] don't allow to change the up/down shortcuts in the slidecontrollers --- openlp/core/lib/ui.py | 3 +-- openlp/core/ui/slidecontroller.py | 10 ++++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 3365b32a0..d54135937 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -87,7 +87,6 @@ class UiStrings(object): OpenService = translate('OpenLP.Ui', 'Open Service') Preview = translate('OpenLP.Ui', 'Preview') PreviewPanel = translate('OpenLP.Ui', 'Preview Panel') - PreviewToolbar = translate('OpenLP.Ui', 'Preview Toolbar') PrintServiceOrder = translate('OpenLP.Ui', 'Print Service Order') ReplaceBG = translate('OpenLP.Ui', 'Replace Background') ReplaceLiveBG = translate('OpenLP.Ui', 'Replace Live Background') @@ -427,4 +426,4 @@ def find_and_set_in_combo_box(combo_box, value_to_find): if index == -1: # Not Found. index = 0 - combo_box.setCurrentIndex(index) \ No newline at end of file + combo_box.setCurrentIndex(index) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 4f5ed1ea7..7a26bea19 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -368,10 +368,8 @@ class SlideController(QtGui.QWidget): self.previousItem.setObjectName(u'previousItemPreview') self.nextItem.setObjectName(u'nextItemPreview') action_list = ActionList.get_instance() - action_list.add_category( - UiStrings.PreviewToolbar, CategoryOrder.standardToolbar) - action_list.add_action(self.previousItem, UiStrings.PreviewToolbar) - action_list.add_action(self.nextItem, UiStrings.PreviewToolbar) + action_list.add_action(self.previousItem) + action_list.add_action(self.nextItem) def setLiveHotkeys(self, parent=None): self.previousItem.setObjectName(u'previousItemLive') @@ -379,8 +377,8 @@ class SlideController(QtGui.QWidget): action_list = ActionList.get_instance() action_list.add_category( UiStrings.LiveToolbar, CategoryOrder.standardToolbar) - action_list.add_action(self.previousItem, UiStrings.LiveToolbar) - action_list.add_action(self.nextItem, UiStrings.LiveToolbar) + action_list.add_action(self.previousItem) + action_list.add_action(self.nextItem) self.previousService = shortcut_action(parent, u'previousService', [QtCore.Qt.Key_Left], self.servicePrevious, UiStrings.LiveToolbar) self.previousService.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) From 206cd90639eac949f23198347f482c1e3b718b2d Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Thu, 14 Apr 2011 19:30:53 +0100 Subject: [PATCH 79/98] Finished --- openlp/core/lib/plugin.py | 4 -- openlp/core/lib/pluginmanager.py | 13 ++---- openlp/core/ui/settingsform.py | 55 +++++++++++--------------- openlp/plugins/bibles/lib/mediaitem.py | 3 ++ 4 files changed, 28 insertions(+), 47 deletions(-) diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index e4359f6b0..a6cc5df63 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -290,8 +290,6 @@ class Plugin(QtCore.QObject): if self.mediaItem: self.mediaItem.initialise() self.mediadock.insert_dock(self.mediaItem, self.icon, self.weight) - if self.settings_tab: - self.settingsForm.insertTab(self.settings_tab, self.weight) def finalise(self): """ @@ -299,8 +297,6 @@ class Plugin(QtCore.QObject): """ if self.mediaItem: self.mediadock.remove_dock(self.mediaItem) - if self.settings_tab: - self.settingsForm.removeTab(self.settings_tab) def usesTheme(self, theme): """ diff --git a/openlp/core/lib/pluginmanager.py b/openlp/core/lib/pluginmanager.py index 6f1222276..0fddc75c4 100644 --- a/openlp/core/lib/pluginmanager.py +++ b/openlp/core/lib/pluginmanager.py @@ -137,7 +137,7 @@ class PluginManager(object): if plugin.status is not PluginStatus.Disabled: plugin.mediaItem = plugin.getMediaManagerItem() - def hook_settings_tabs(self, settingsform=None): + def hook_settings_tabs(self, settings_form=None): """ Loop through all the plugins. If a plugin has a valid settings tab item, add it to the settings tab. @@ -148,15 +148,8 @@ class PluginManager(object): """ for plugin in self.plugins: if plugin.status is not PluginStatus.Disabled: - plugin.settings_tab = plugin.getSettingsTab(settingsform) - visible_title = plugin.getString(StringContent.VisibleName) - if plugin.settings_tab and plugin.isActive(): - log.debug(u'Inserting settings tab item from %s' % - visible_title[u'title']) - settingsform.insertTab(plugin.settings_tab, plugin.weight) - else: - log.debug( - u'No tab settings in %s' % visible_title[u'title']) + plugin.settings_tab = plugin.getSettingsTab(settings_form) + settings_form.plugins = self.plugins def hook_import_menu(self, import_menu): """ diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index 72e1b7fa4..6d1680b53 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -30,7 +30,7 @@ import logging from PyQt4 import QtGui, QtCore -from openlp.core.lib import Receiver, build_icon +from openlp.core.lib import Receiver, build_icon, PluginStatus from openlp.core.ui import AdvancedTab, GeneralTab, ThemesTab from settingsdialog import Ui_SettingsDialog @@ -47,53 +47,42 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): QtGui.QDialog.__init__(self, parent) self.setupUi(self) # General tab - generalTab = GeneralTab(self, screens) - self.insertTab(generalTab, 1) + self.generalTab = GeneralTab(self, screens) # Themes tab - themesTab = ThemesTab(self, mainWindow) - self.insertTab(themesTab, 2) + self.themesTab = ThemesTab(self, mainWindow) # Advanced tab - advancedTab = AdvancedTab(self, ) - self.insertTab(advancedTab, 3) + self.advancedTab = AdvancedTab(self) def exec_(self): # load all the settings - for tabIndex in range(0, self.stackedLayout.count()): - self.stackedLayout.widget(tabIndex).load() + self.settingListWidget.clear() + for tabIndex in range(0, self.stackedLayout.count() + 1): + # take at 0 and the rest shuffell up. + self.stackedLayout.takeAt(0) + self.insertTab(self.generalTab, 0, PluginStatus.Active) + self.insertTab(self.themesTab, 1, PluginStatus.Active) + self.insertTab(self.advancedTab, 2, PluginStatus.Active) + count = 3 + for plugin in self.plugins: + if plugin.settings_tab: + self.insertTab(plugin.settings_tab, count, plugin.status) + count += 1 self.settingListWidget.setCurrentRow(0) return QtGui.QDialog.exec_(self) - def insertTab(self, tab, location): + def insertTab(self, tab, location, is_active): """ Add a tab to the form at a specific location """ log.debug(u'Inserting %s tab' % tab.tabTitle) - # 14 : There are 3 tables currently and locations starts at -10 - match = False - for tabIndex in range(0, self.stackedLayout.count()): - if self.stackedLayout.widget(tabIndex): - if self.stackedLayout.widget(tabIndex).tabTitleVisible == \ - tab.tabTitleVisible: - self.stackedLayout.widget(tabIndex).setHidden(False) - match = True - break - if not match: - pos = self.stackedLayout.addWidget(tab) + pos = self.stackedLayout.addWidget(tab) + if is_active: item_name = QtGui.QListWidgetItem(tab.tabTitleVisible) icon = build_icon(tab.icon_path) item_name.setIcon(icon) - self.settingListWidget.insertItem(14 + location, item_name) - - def removeTab(self, tab): - """ - Remove a tab from the form - """ - log.debug(u'remove %s tab' % tab.tabTitleVisible) - for tabIndex in range(0, self.stackedLayout.count()): - if self.stackedLayout.widget(tabIndex): - if self.stackedLayout.widget(tabIndex).tabTitleVisible == \ - tab.tabTitleVisible: - self.settingListWidget.item(tabIndex).setHidden(True) + self.settingListWidget.insertItem(location, item_name) + else: + self.stackedLayout.takeAt(location) def accept(self): """ diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index b3ffff87c..736d3d82e 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -836,6 +836,9 @@ class BibleMediaItem(MediaManagerItem): return u'{su}%s{/su}' % verse_text def onlayoutStyleComboBoxChanged(self): + self.settings.layout_style = self.quickLayoutComboBox.currentIndex() + self.settings.layoutStyleComboBox.setCurrentIndex( + self.settings.layout_style) QtCore.QSettings().setValue( self.settingsSection + u'/verse layout style', QtCore.QVariant(self.settings.layout_style)) From 4dd70d454ff6c097f0afd002cfcd465c08bfcabe Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Thu, 14 Apr 2011 19:55:41 +0100 Subject: [PATCH 80/98] Fix Postsetup --- openlp/core/ui/settingsform.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index 6d1680b53..949d907b4 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -106,5 +106,9 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): """ Run any post-setup code for the tabs on the form """ - for tabIndex in range(0, self.stackedLayout.count()): - self.stackedLayout.widget(tabIndex).postSetUp() + self.generalTab.postSetUp() + self.themesTab.postSetUp() + self.advancedTab.postSetUp() + for plugin in self.plugins: + if plugin.settings_tab: + plugin.settings_tab.postSetUp() From 77559e021071bef7bae19337ac2edbc7d58d8813 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Thu, 14 Apr 2011 23:32:21 +0200 Subject: [PATCH 81/98] started to implement #746243 Fixes: https://launchpad.net/bugs/746243 --- openlp/core/lib/searchedit.py | 14 ++++++++++++++ openlp/plugins/songs/lib/mediaitem.py | 9 +++++++++ 2 files changed, 23 insertions(+) diff --git a/openlp/core/lib/searchedit.py b/openlp/core/lib/searchedit.py index d32961ef9..00e0f3ed3 100644 --- a/openlp/core/lib/searchedit.py +++ b/openlp/core/lib/searchedit.py @@ -110,6 +110,20 @@ class SearchEdit(QtGui.QLineEdit): """ return self._currentSearchType + def setCurrentSearchType(self, identifier): + """ + Set a new current search type. + + ``identifier`` + The search type identifier (int). + """ + menu = self.menuButton.menu() + for action in menu.actions(): + if identifier == action.data().toInt()[0]: + self.menuButton.setDefaultAction(action) + self._currentSearchType = identifier + return True + def setSearchTypes(self, items): """ A list of tuples to be used in the search type menu. The first item in diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index e2882ed29..2ce2478cb 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -156,8 +156,17 @@ class SongMediaItem(MediaManagerItem): (SongSearch.Themes, u':/slides/slide_theme.png', UiStrings.Themes) ]) self.configUpdated() + # FIXME: Saved search type need to be considered when loading the list. + self.searchTextEdit.setCurrentSearchType(QtCore.QSettings().value( + u'%s/last search type' % self.settingsSection, + QtCore.QVariant(SongSearch.Entire)).toInt()[0]) def onSearchTextButtonClick(self): + # Save the current search type to the config. so it can be restored. + QtCore.QSettings().setValue(u'%s/last search type' % + self.settingsSection, + QtCore.QVariant(self.searchTextEdit.currentSearchType())) + # Reload the list considering the new search type. search_keywords = unicode(self.searchTextEdit.displayText()) search_results = [] search_type = self.searchTextEdit.currentSearchType() From 4286f6389dda9888a90b61857e34ea7765c43d81 Mon Sep 17 00:00:00 2001 From: Jonathan Corwin Date: Thu, 14 Apr 2011 22:34:01 +0100 Subject: [PATCH 82/98] Switching from ppt to songs now fine --- openlp/core/lib/htmlbuilder.py | 8 ++++---- openlp/core/ui/slidecontroller.py | 10 ++++++---- openlp/plugins/presentations/lib/messagelistener.py | 5 ----- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py index 2ba235265..1f2d2498d 100644 --- a/openlp/core/lib/htmlbuilder.py +++ b/openlp/core/lib/htmlbuilder.py @@ -343,11 +343,11 @@ def build_html(item, screen, alert, islive, background, image=None): webkitvers = webkit_version() # Image generated and poked in if background: - bgimage = u'src="data:image/png;base64,%s"' % background + bgimage_src = u'src="data:image/png;base64,%s"' % background elif item.bg_image_bytes: - bgimage = u'src="data:image/png;base64,%s"' % item.bg_image_bytes + bgimage_src = u'src="data:image/png;base64,%s"' % item.bg_image_bytes else: - bgimage = u'style="display:none;"' + bgimage_src = u'style="display:none;"' if image: image_src = u'src="data:image/png;base64,%s"' % image else: @@ -359,7 +359,7 @@ def build_html(item, screen, alert, islive, background, image=None): build_lyrics_css(item, webkitvers), u'true' if theme and theme.display_slide_transition and islive \ else u'false', - bgimage, image_src, + bgimage_src, image_src, build_lyrics_html(item, webkitvers)) return html diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index b940b1e73..7110ddf02 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -365,7 +365,7 @@ class SlideController(QtGui.QWidget): QtCore.SIGNAL(u'config_screen_changed'), self.screenSizeChanged) def setPreviewHotkeys(self, parent=None): - self.previousItem.setObjectName(u'previousItemPreview') + self.previousItem.setObjectName(u'previousItemPreview') self.nextItem.setObjectName(u'nextItemPreview') action_list = ActionList.get_instance() action_list.add_category( @@ -374,7 +374,7 @@ class SlideController(QtGui.QWidget): action_list.add_action(self.nextItem, UiStrings.PreviewToolbar) def setLiveHotkeys(self, parent=None): - self.previousItem.setObjectName(u'previousItemLive') + self.previousItem.setObjectName(u'previousItemLive') self.nextItem.setObjectName(u'nextItemLive') action_list = ActionList.get_instance() action_list.add_category( @@ -575,6 +575,7 @@ class SlideController(QtGui.QWidget): log.debug(u'processManagerItem live = %s' % self.isLive) self.onStopLoop() old_item = self.serviceItem + self.serviceItem = serviceItem if old_item and self.isLive and old_item.is_capable( ItemCapabilities.ProvidesOwnDisplay): self._resetBlank() @@ -582,7 +583,6 @@ class SlideController(QtGui.QWidget): [serviceItem, self.isLive, self.hideMode(), slideno]) self.slideList = {} width = self.parent.controlSplitter.sizes()[self.split] - self.serviceItem = serviceItem self.previewListWidget.clear() self.previewListWidget.setRowCount(0) self.previewListWidget.setColumnWidth(0, width) @@ -642,7 +642,7 @@ class SlideController(QtGui.QWidget): self.__updatePreviewSelection(slideno) self.enableToolBar(serviceItem) # Pass to display for viewing. - # Postpone image build, we need to do this later to avoid theme + # Postpone image build, we need to do this later to avoid the theme # flashing on the screen if not self.serviceItem.is_image(): self.display.buildHtml(self.serviceItem) @@ -1136,6 +1136,8 @@ class SlideController(QtGui.QWidget): self.onThemeDisplay(True) elif hide_mode == HideMode.Screen: self.onHideDisplay(True) + else: + self.hidePlugin(False) def hideMode(self): """ diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index b76fd7d4c..94cd2bfa4 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -173,12 +173,8 @@ class Controller(object): Based on the handler passed at startup triggers slide show to shut down """ log.debug(u'Live = %s, shutdown' % self.is_live) - if self.is_live: - Receiver.send_message(u'maindisplay_show') self.doc.close_presentation() self.doc = None - #self.doc.slidenumber = 0 - #self.timer.stop() def blank(self, hide_mode): """ @@ -345,7 +341,6 @@ class MessageListener(object): """ is_live = message[1] if is_live: - Receiver.send_message(u'maindisplay_show') self.live_handler.shutdown() else: self.preview_handler.shutdown() From 14a2e8e4c2c332984bafe52a2a63ccfd52cfe914 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Fri, 15 Apr 2011 14:55:56 +0200 Subject: [PATCH 83/98] finished work on restoring current search methods --- openlp/core/lib/searchedit.py | 1 + openlp/plugins/bibles/lib/mediaitem.py | 21 ++++++++++++++------- openlp/plugins/songs/lib/mediaitem.py | 5 ++--- openlp/plugins/songs/songsplugin.py | 2 -- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/openlp/core/lib/searchedit.py b/openlp/core/lib/searchedit.py index 00e0f3ed3..94152ef2f 100644 --- a/openlp/core/lib/searchedit.py +++ b/openlp/core/lib/searchedit.py @@ -122,6 +122,7 @@ class SearchEdit(QtGui.QLineEdit): if identifier == action.data().toInt()[0]: self.menuButton.setDefaultAction(action) self._currentSearchType = identifier + self.emit(QtCore.SIGNAL(u'searchTypeChanged(int)'), identifier) return True def setSearchTypes(self, items): diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 118bdfc66..241854985 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -99,12 +99,6 @@ class BibleMediaItem(MediaManagerItem): self.quickSearchEdit = SearchEdit(self.quickTab) self.quickSearchEdit.setObjectName(u'quickSearchEdit') self.quickSearchLabel.setBuddy(self.quickSearchEdit) - self.quickSearchEdit.setSearchTypes([ - (BibleSearch.Reference, u':/bibles/bibles_search_reference.png', - translate('BiblesPlugin.MediaItem', 'Scripture Reference')), - (BibleSearch.Text, u':/bibles/bibles_search_text.png', - translate('BiblesPlugin.MediaItem', 'Text Search')) - ]) self.quickLayout.addRow(self.quickSearchLabel, self.quickSearchEdit) self.quickLayoutLabel = QtGui.QLabel(self.quickTab) self.quickLayoutLabel.setObjectName(u'quickClearLabel') @@ -295,7 +289,15 @@ class BibleMediaItem(MediaManagerItem): self.settingsSection + u'/quick bible', QtCore.QVariant( self.quickVersionComboBox.currentText())).toString() find_and_set_in_combo_box(self.quickVersionComboBox, bible) - self.updateAutoCompleter() + self.quickSearchEdit.setSearchTypes([ + (BibleSearch.Reference, u':/bibles/bibles_search_reference.png', + translate('BiblesPlugin.MediaItem', 'Scripture Reference')), + (BibleSearch.Text, u':/bibles/bibles_search_text.png', + translate('BiblesPlugin.MediaItem', 'Text Search')) + ]) + self.quickSearchEdit.setCurrentSearchType(QtCore.QSettings().value( + u'%s/last search type' % self.settingsSection, + QtCore.QVariant(BibleSearch.Reference)).toInt()[0]) self.configUpdated() log.debug(u'bible manager initialise complete') @@ -386,6 +388,11 @@ class BibleMediaItem(MediaManagerItem): completion depends on the bible. It is only updated when we are doing a reference search, otherwise the auto completion list is removed. """ + # Save the current search type to the configuration. + QtCore.QSettings().setValue(u'%s/last search type' % + self.settingsSection, + QtCore.QVariant(self.quickSearchEdit.currentSearchType())) + # Save the current bible to the configuration. QtCore.QSettings().setValue(self.settingsSection + u'/quick bible', QtCore.QVariant(self.quickVersionComboBox.currentText())) books = [] diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 2ce2478cb..5890fc76d 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -155,14 +155,13 @@ class SongMediaItem(MediaManagerItem): SongStrings.Authors), (SongSearch.Themes, u':/slides/slide_theme.png', UiStrings.Themes) ]) - self.configUpdated() - # FIXME: Saved search type need to be considered when loading the list. self.searchTextEdit.setCurrentSearchType(QtCore.QSettings().value( u'%s/last search type' % self.settingsSection, QtCore.QVariant(SongSearch.Entire)).toInt()[0]) + self.configUpdated() def onSearchTextButtonClick(self): - # Save the current search type to the config. so it can be restored. + # Save the current search type to the configuration. QtCore.QSettings().setValue(u'%s/last search type' % self.settingsSection, QtCore.QVariant(self.searchTextEdit.currentSearchType())) diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index af50f3f94..4fc098949 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -70,8 +70,6 @@ class SongsPlugin(Plugin): action_list.add_action(self.SongImportItem, UiStrings.Import) action_list.add_action(self.SongExportItem, UiStrings.Export) action_list.add_action(self.toolsReindexItem, UiStrings.Tools) - self.mediaItem.displayResultsSong( - self.manager.get_all_objects(Song, order_by_ref=Song.search_title)) def addImportMenuItem(self, import_menu): """ From 269c4fa044ef9093793027f0371aed005ab06336 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Fri, 15 Apr 2011 18:17:51 +0100 Subject: [PATCH 84/98] Fixups --- openlp/core/lib/plugin.py | 28 ++++++++++++++-------------- openlp/core/ui/advancedtab.py | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index a6cc5df63..2ec1045bb 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -115,7 +115,7 @@ class Plugin(QtCore.QObject): """ log.info(u'loaded') - def __init__(self, name, pluginHelpers=None, mediaItemClass=None, + def __init__(self, name, plugin_helpers=None, media_item_class=None, settings_tab_class=None, version=None): """ This is the constructor for the plugin object. This provides an easy @@ -132,10 +132,10 @@ class Plugin(QtCore.QObject): ``version`` Defaults to *None*. The version of the plugin. - ``pluginHelpers`` + ``plugin_helpers`` Defaults to *None*. A list of helper objects. - ``mediaItemClass`` + ``media_item_class`` The class name of the plugin's media item. ``settings_tab_class`` @@ -153,20 +153,20 @@ class Plugin(QtCore.QObject): self.version = get_application_version()[u'version'] self.settingsSection = self.name.lower() self.icon = None - self.mediaItemClass = mediaItemClass + self.media_item_class = media_item_class self.settings_tab_class = settings_tab_class self.weight = 0 self.status = PluginStatus.Inactive # Set up logging self.log = logging.getLogger(self.name) - self.previewController = pluginHelpers[u'preview'] - self.liveController = pluginHelpers[u'live'] - self.renderManager = pluginHelpers[u'render'] - self.serviceManager = pluginHelpers[u'service'] - self.settingsForm = pluginHelpers[u'settings form'] - self.mediadock = pluginHelpers[u'toolbox'] - self.pluginManager = pluginHelpers[u'pluginmanager'] - self.formparent = pluginHelpers[u'formparent'] + self.previewController = plugin_helpers[u'preview'] + self.liveController = plugin_helpers[u'live'] + self.renderManager = plugin_helpers[u'render'] + self.serviceManager = plugin_helpers[u'service'] + self.settingsForm = plugin_helpers[u'settings form'] + self.mediadock = plugin_helpers[u'toolbox'] + self.pluginManager = plugin_helpers[u'pluginmanager'] + self.formparent = plugin_helpers[u'formparent'] QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'%s_add_service_item' % self.name), self.processAddServiceEvent) @@ -213,8 +213,8 @@ class Plugin(QtCore.QObject): Construct a MediaManagerItem object with all the buttons and things you need, and return it for integration into openlp.org. """ - if self.mediaItemClass: - return self.mediaItemClass(self, self, self.icon) + if self.media_item_class: + return self.media_item_class(self, self, self.icon) return None def addImportMenuItem(self, importMenu): diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index 3d9321fe1..199ebff60 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -45,7 +45,7 @@ class AdvancedTab(SettingsTab): SettingsTab.__init__(self, parent ,u'Advanced', generalTranslated) self.default_image = u':/graphics/openlp-splash-screen.png' self.default_color = u'#ffffff' - self.icon_path = u':/icon/openlp-logo-16x16.png' + self.icon_path = u':/system/system_settings.png' def setupUi(self): """ From 7dfce92bc2865a4b2a3e6c3ad5a6a1763b9b6147 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Fri, 15 Apr 2011 23:18:49 +0200 Subject: [PATCH 85/98] Fixed up the Bible import documentation so that we don't have the same two screenshots all the way down the page. --- documentation/manual/source/bibles.rst | 105 ++++++++++--------------- 1 file changed, 40 insertions(+), 65 deletions(-) diff --git a/documentation/manual/source/bibles.rst b/documentation/manual/source/bibles.rst index 9fbae9b81..005ab72db 100644 --- a/documentation/manual/source/bibles.rst +++ b/documentation/manual/source/bibles.rst @@ -21,20 +21,35 @@ You will see the Bible Importer window, click :guilabel:`Next`. .. image:: pics/bibleimport01.png -After clicking :guilabel:`Next` you can select from the various types of -software that OpenLP will convert Bibles from. +After clicking :guilabel:`Next` you can select from the various types of +software that OpenLP will convert Bibles from. Click on the file folder icon to +choose the file(s) of the Bible database you want to import. See the sections +below for more information on the different formats that OpenLP will import. +Click :guilabel:`Next` to continue. .. image:: pics/bibleimport02.png -Click on the file folder icon to choose the file of the Bible database you -want to import. See the following sections for information on the different -formats that OpenLP will import. +After selecting your file(s), you'll be asked to fill in the details of the +Bible you are importing. Remember to check what information you need to display +for your Bible's translation, as some of them have strict rules around the +copyright notice. Click :guilabel:`Next` to continue. -Importing from OpenLP Version 1 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. image:: pics/bibleimportdetails1.png -Converting from OpenLP Version 1 is a simple process. First you will need to -locate your Version 1 Bibles. +After filling in the copyright details, OpenLP will start to import your Bible. +It may take some time to import your Bible so please be patient. + +.. image:: pics/bibleimportfinished1.png + +When the import has finished click :guilabel:`Finish` and you should be +ready to use your Bible in OpenLP. + +Importing from openlp.org 1.x +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Importing Bibles from openlp.org 1.x is a simple process. First you will need to +locate your version 1.x Bibles. Version 1.x Bibles have the `.bible` file +extension. Windows XP:: @@ -44,22 +59,15 @@ Windows Vista / Windows 7:: C:\ProgramData\openlp.org\Data\Bibles\ -OpenLP Version 1 Bibles have the `.bible` file extension. After selecting -all of the OpenLP Version 1 Bibles you want to convert, click :guilabel:`Next` - -.. image:: pics/bibleimportdetails1.png - -Enter your Bible name and copyright details. Click :guilabel:`Next`. It may -take some time to convert your Bibles so please be patient. - -.. image:: pics/bibleimportfinished1.png - -When the import has finished click :guilabel:`Finish` and you should be -ready to use your OpenLP Version 1 Bibles. +After selecting all of the openlp.org 1.x Bibles you want to convert, click +:guilabel:`Next` to continue the import process. Importing OSIS Bibles ^^^^^^^^^^^^^^^^^^^^^ +Importing OSIS files is very simple. Select OSIS as your import source, select +your OSIS Bible file and continue the import process. + **About OSIS Formatted Bibles** The OSIS XML standard was designed to provide a common format for distribution @@ -69,24 +77,11 @@ of electronic Bibles. More information can be found out at the `Bible Technologi If you have any software installed that is part of the `Sword Project `_ it can be easily converted. -Importing OSIS files is very simple. Select your OSIS Bible file and click -:guilabel:`Next` - -.. image:: pics/bibleimportdetails1.png - -Enter you Bible name and copyright details. Click :guilabel:`Next`. It may take -some time to convert your Bibles so please be patient. - -.. image:: pics/bibleimportfinished1.png - -Click :guilabel:`Finish` and you should be ready to use your OpenLP Version -1 Bibles. - You can use the commands below convert Bibles from that software to OSIS format. The following commands are used in all platforms and the commands are case -sensitive across all platforms. To convert a Bible using Command Prompt in -Windows or a Terminal in Linux or MAC you would type:: +sensitive across all platforms. To convert a Bible using the command prompt in +Windows or a terminal in Linux or Mac OS X you would type:: mod2osis biblename > biblename.osis @@ -114,17 +109,9 @@ You may also import downloaded bibles from OpenSong. The process is the same, except you will need to extract the bible from a zip file. This is usually done by right clicking on the downloaded file and select `Extract` or `Extract Here`. -After selecting the OpenSong Bibles you want to convert, click :guilabel:`Next` - -.. image:: pics/bibleimportdetails1.png - -Enter your Bible name and copyright details. Click :guilabel:`Next`. It may -take some time to convert your Bibles so please be patient. - -.. image:: pics/bibleimportfinished1.png - -When the import has finished then click :guilabel:`Finish` and you should now be -ready to use your OpenSong Bibles. +After selecting the OpenSong Bibles you want to import, follow the rest of the +import process. When the import has finished you should be ready to use your +OpenSong Bibles. Importing Web Download Bibles ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -139,8 +126,6 @@ as another option and does require an internet connection. To use the web download feature select web download from the import wizard. -.. image:: pics/bibleimport01.png - You can select from several options of location to download from and also what Bible translation you need. You will probably want to choose the location from where you get the best performance or has the translation you need. @@ -154,30 +139,20 @@ not be needed. .. image:: pics/webbibleproxy1.png After selecting your download location and the Bible you wish to use, click -:guilabel:`Next` When your import is completed click :guilabel:`Finish` - -.. image:: pics/biblewebcomplete.png - -You should now be ready to use the web bible. +:guilabel:`Next` to continue the import process. When your import is completed +you should now be ready to use the web bible. Importing CSV formatted Bibles ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If you have a Bible in .csv format OpenLP can import it. CSV Bibles will -consist of two files a `books` file and a `verse` file. - -Select CSV from the list of Bible types to import. - -.. image:: pics/bibleimport02.png +consist of two files a `books` file and a `verse` file. Select CSV from the list +of Bible types to import. You are now ready to select your .csv files. You will need to select both your books and verse file location. .. image:: pics/csvimport1.png -After you have selected the file locations you can click :guilabel:`Next` - -.. image:: pics/bibleimportfinished1.png - -Click :guilabel:`Finish` and you should now be ready to use your imported CSV -Bible \ No newline at end of file +After you have selected the file locations you can continue with the import +process. Once it is complete you should be ready to use your imported CSV Bible. From 8090adf1bc4167ede05e137f2c68efd763b05936 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Fri, 15 Apr 2011 23:43:59 +0200 Subject: [PATCH 86/98] Made UiStrings a Singleton, and updated all usage of it. --- openlp.pyw | 4 +- openlp/core/lib/mediamanageritem.py | 12 +- openlp/core/lib/plugin.py | 18 +- openlp/core/lib/serviceitem.py | 6 +- openlp/core/lib/theme.py | 4 +- openlp/core/lib/ui.py | 176 ++++++++++-------- openlp/core/ui/aboutdialog.py | 6 +- openlp/core/ui/advancedtab.py | 6 +- openlp/core/ui/displaytagdialog.py | 6 +- openlp/core/ui/exceptionform.py | 4 +- openlp/core/ui/generaltab.py | 4 +- openlp/core/ui/mainwindow.py | 86 ++++----- openlp/core/ui/plugindialog.py | 6 +- openlp/core/ui/printservicedialog.py | 4 +- openlp/core/ui/printserviceform.py | 6 +- openlp/core/ui/servicemanager.py | 30 +-- openlp/core/ui/slidecontroller.py | 22 +-- openlp/core/ui/starttimedialog.py | 14 +- openlp/core/ui/starttimeform.py | 8 +- openlp/core/ui/themeform.py | 6 +- openlp/core/ui/thememanager.py | 6 +- openlp/core/ui/themestab.py | 4 +- openlp/core/ui/themewizard.py | 16 +- openlp/core/ui/wizard.py | 4 +- openlp/plugins/alerts/alertsplugin.py | 4 +- openlp/plugins/alerts/lib/alertstab.py | 10 +- openlp/plugins/bibles/bibleplugin.py | 10 +- .../plugins/bibles/forms/bibleimportform.py | 24 +-- openlp/plugins/bibles/lib/biblestab.py | 12 +- openlp/plugins/bibles/lib/mediaitem.py | 20 +- .../plugins/custom/forms/editcustomdialog.py | 6 +- openlp/plugins/custom/lib/mediaitem.py | 6 +- openlp/plugins/images/lib/mediaitem.py | 14 +- openlp/plugins/media/lib/mediaitem.py | 14 +- openlp/plugins/presentations/lib/mediaitem.py | 4 +- .../presentations/lib/presentationtab.py | 4 +- openlp/plugins/songs/forms/editsongdialog.py | 12 +- openlp/plugins/songs/forms/editsongform.py | 6 +- openlp/plugins/songs/forms/songexportform.py | 6 +- openlp/plugins/songs/forms/songimportform.py | 52 +++--- .../songs/forms/songmaintenancedialog.py | 20 +- .../songs/forms/songmaintenanceform.py | 4 +- openlp/plugins/songs/lib/mediaitem.py | 12 +- openlp/plugins/songs/songsplugin.py | 16 +- 44 files changed, 366 insertions(+), 348 deletions(-) diff --git a/openlp.pyw b/openlp.pyw index 5c3b8ca77..425d3c874 100755 --- a/openlp.pyw +++ b/openlp.pyw @@ -140,7 +140,7 @@ class OpenLP(QtGui.QApplication): self.sharedMemory = QtCore.QSharedMemory('OpenLP') if self.sharedMemory.attach(): status = QtGui.QMessageBox.critical(None, - UiStrings.Error, UiStrings.OpenLPStart, + UiStrings().Error, UiStrings().OpenLPStart, QtGui.QMessageBox.StandardButtons( QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)) if status == QtGui.QMessageBox.No: @@ -250,4 +250,4 @@ if __name__ == u'__main__': """ Instantiate and run the application. """ - main() + main() \ No newline at end of file diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 7671064df..e7d7bc4ec 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -435,7 +435,7 @@ class MediaManagerItem(QtGui.QWidget): item to the preview slide controller. """ if not self.listView.selectedIndexes() and not self.remoteTriggered: - QtGui.QMessageBox.information(self, UiStrings.NISp, + QtGui.QMessageBox.information(self, UiStrings().NISp, translate('OpenLP.MediaManagerItem', 'You must select one or more items to preview.')) else: @@ -453,7 +453,7 @@ class MediaManagerItem(QtGui.QWidget): item to the live slide controller. """ if not self.listView.selectedIndexes(): - QtGui.QMessageBox.information(self, UiStrings.NISp, + QtGui.QMessageBox.information(self, UiStrings().NISp, translate('OpenLP.MediaManagerItem', 'You must select one or more items to send live.')) else: @@ -468,7 +468,7 @@ class MediaManagerItem(QtGui.QWidget): Add a selected item to the current service """ if not self.listView.selectedIndexes() and not self.remoteTriggered: - QtGui.QMessageBox.information(self, UiStrings.NISp, + QtGui.QMessageBox.information(self, UiStrings().NISp, translate('OpenLP.MediaManagerItem', 'You must select one or more items.')) else: @@ -494,14 +494,14 @@ class MediaManagerItem(QtGui.QWidget): Add a selected item to an existing item in the current service. """ if not self.listView.selectedIndexes() and not self.remoteTriggered: - QtGui.QMessageBox.information(self, UiStrings.NISp, + QtGui.QMessageBox.information(self, UiStrings().NISp, translate('OpenLP.MediaManagerItem', 'You must select one or more items.')) else: log.debug(u'%s Add requested', self.plugin.name) serviceItem = self.parent.serviceManager.getServiceItem() if not serviceItem: - QtGui.QMessageBox.information(self, UiStrings.NISs, + QtGui.QMessageBox.information(self, UiStrings().NISs, translate('OpenLP.MediaManagerItem', 'You must select an existing service item to add to.')) elif self.plugin.name.lower() == serviceItem.name.lower(): @@ -554,4 +554,4 @@ class MediaManagerItem(QtGui.QWidget): item_id = remoteItem else: item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0] - return item_id + return item_id \ No newline at end of file diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index 2ec1045bb..08be86541 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -330,28 +330,28 @@ class Plugin(QtCore.QObject): """ ## Load Action ## self.__setNameTextString(StringContent.Load, - UiStrings.Load, tooltips[u'load']) + UiStrings().Load, tooltips[u'load']) ## Import Action ## self.__setNameTextString(StringContent.Import, - UiStrings.Import, tooltips[u'import']) + UiStrings().Import, tooltips[u'import']) ## New Action ## self.__setNameTextString(StringContent.New, - UiStrings.Add, tooltips[u'new']) + UiStrings().Add, tooltips[u'new']) ## Edit Action ## self.__setNameTextString(StringContent.Edit, - UiStrings.Edit, tooltips[u'edit']) + UiStrings().Edit, tooltips[u'edit']) ## Delete Action ## self.__setNameTextString(StringContent.Delete, - UiStrings.Delete, tooltips[u'delete']) + UiStrings().Delete, tooltips[u'delete']) ## Preview Action ## self.__setNameTextString(StringContent.Preview, - UiStrings.Preview, tooltips[u'preview']) + UiStrings().Preview, tooltips[u'preview']) ## Send Live Action ## self.__setNameTextString(StringContent.Live, - UiStrings.Live, tooltips[u'live']) + UiStrings().Live, tooltips[u'live']) ## Add to Service Action ## self.__setNameTextString(StringContent.Service, - UiStrings.Service, tooltips[u'service']) + UiStrings().Service, tooltips[u'service']) def __setNameTextString(self, name, title, tooltip): """ @@ -359,4 +359,4 @@ class Plugin(QtCore.QObject): use of the singular name of the plugin object so must only be called after this has been set. """ - self.textStrings[name] = {u'title': title, u'tooltip': tooltip} + self.textStrings[name] = {u'title': title, u'tooltip': tooltip} \ No newline at end of file diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 48a277633..5de25f6aa 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -441,10 +441,10 @@ class ServiceItem(object): start = None end = None if self.start_time != 0: - start = UiStrings.StartTimeCode % \ + start = UiStrings().StartTimeCode % \ unicode(datetime.timedelta(seconds=self.start_time)) if self.media_length != 0: - end = UiStrings.LengthTime % \ + end = UiStrings().LengthTime % \ unicode(datetime.timedelta(seconds=self.media_length)) if not start and not end: return None @@ -453,4 +453,4 @@ class ServiceItem(object): elif not start and end: return end else: - return u'%s : %s' % (start, end) + return u'%s : %s' % (start, end) \ No newline at end of file diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index 134df7652..698f0d644 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -192,7 +192,7 @@ class VerticalType(object): Bottom = 2 Names = [u'top', u'middle', u'bottom'] - TranslatedNames = [UiStrings.Top, UiStrings.Middle, UiStrings.Bottom] + TranslatedNames = [UiStrings().Top, UiStrings().Middle, UiStrings().Bottom] BOOLEAN_LIST = [u'bold', u'italics', u'override', u'outline', u'shadow', @@ -637,4 +637,4 @@ class ThemeXML(object): self.font_footer_shadow_size) self.add_display(self.display_horizontal_align, self.display_vertical_align, - self.display_slide_transition) + self.display_slide_transition) \ No newline at end of file diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index bd47ee627..013231b8c 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -39,78 +39,96 @@ class UiStrings(object): """ Provide standard strings for objects to use. """ - # These strings should need a good reason to be retranslated elsewhere. - # Should some/more/less of these have an & attached? - About = translate('OpenLP.Ui', 'About') - Add = translate('OpenLP.Ui', '&Add') - Advanced = translate('OpenLP.Ui', 'Advanced') - AllFiles = translate('OpenLP.Ui', 'All Files') - Bottom = translate('OpenLP.Ui', 'Bottom') - Browse = translate('OpenLP.Ui', 'Browse...') - Cancel = translate('OpenLP.Ui', 'Cancel') - CCLINumberLabel = translate('OpenLP.Ui', 'CCLI number:') - CreateService = translate('OpenLP.Ui', 'Create a new service.') - Continuous = translate('OpenLP.Ui', 'Continuous') - Default = unicode(translate('OpenLP.Ui', 'Default')) - Delete = translate('OpenLP.Ui', '&Delete') - DisplayStyle = translate('OpenLP.Ui', 'Display style:') - Edit = translate('OpenLP.Ui', '&Edit') - EmptyField = translate('OpenLP.Ui', 'Empty Field') - Error = translate('OpenLP.Ui', 'Error') - Export = translate('OpenLP.Ui', 'Export') - File = translate('OpenLP.Ui', 'File') - FontSizePtUnit = translate('OpenLP.Ui', 'pt', - 'Abbreviated font pointsize unit') - Help = translate('OpenLP.Ui', 'Help') - Hours = translate('OpenLP.Ui', 'h', 'The abbreviated unit for hours') - Image = translate('OpenLP.Ui', 'Image') - Import = translate('OpenLP.Ui', 'Import') - LayoutStyle = translate('OpenLP.Ui', 'Layout style:') - LengthTime = unicode(translate('OpenLP.Ui', 'Length %s')) - Live = translate('OpenLP.Ui', 'Live') - LiveBGError = translate('OpenLP.Ui', 'Live Background Error') - LivePanel = translate('OpenLP.Ui', 'Live Panel') - LiveToolbar = translate('OpenLP.Ui', 'Live Toolbar') - Load = translate('OpenLP.Ui', 'Load') - Minutes = translate('OpenLP.Ui', 'm', 'The abbreviated unit for minutes') - Middle = translate('OpenLP.Ui', 'Middle') - New = translate('OpenLP.Ui', 'New') - NewService = translate('OpenLP.Ui', 'New Service') - NewTheme = translate('OpenLP.Ui', 'New Theme') - NFSs = translate('OpenLP.Ui', 'No File Selected', 'Singular') - NFSp = translate('OpenLP.Ui', 'No Files Selected', 'Plural') - NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular') - NISp = translate('OpenLP.Ui', 'No Items Selected', 'Plural') - OLPV1 = translate('OpenLP.Ui', 'openlp.org 1.x') - OLPV2 = translate('OpenLP.Ui', 'OpenLP 2.0') - OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. Do you ' - 'wish to continue?') - OpenService = translate('OpenLP.Ui', 'Open Service') - Preview = translate('OpenLP.Ui', 'Preview') - PreviewPanel = translate('OpenLP.Ui', 'Preview Panel') - PrintServiceOrder = translate('OpenLP.Ui', 'Print Service Order') - ReplaceBG = translate('OpenLP.Ui', 'Replace Background') - ReplaceLiveBG = translate('OpenLP.Ui', 'Replace Live Background') - ResetBG = translate('OpenLP.Ui', 'Reset Background') - ResetLiveBG = translate('OpenLP.Ui', 'Reset Live Background') - Seconds = translate('OpenLP.Ui', 's', 'The abbreviated unit for seconds') - SaveAndPreview = translate('OpenLP.Ui', 'Save && Preview') - Search = translate('OpenLP.Ui', 'Search') - SelectDelete = translate('OpenLP.Ui', 'You must select an item to delete.') - SelectEdit = translate('OpenLP.Ui', 'You must select an item to edit.') - Settings = translate('OpenLP.Ui', 'Settings') - SaveService = translate('OpenLP.Ui', 'Save Service') - Service = translate('OpenLP.Ui', 'Service') - StartTimeCode = unicode(translate('OpenLP.Ui', 'Start %s')) - Theme = translate('OpenLP.Ui', 'Theme', 'Singular') - Themes = translate('OpenLP.Ui', 'Themes', 'Plural') - Tools = translate('OpenLP.Ui', 'Tools') - Top = translate('OpenLP.Ui', 'Top') - VersePerSlide = translate('OpenLP.Ui', 'Verse Per Slide') - VersePerLine = translate('OpenLP.Ui', 'Verse Per Line') - Version = translate('OpenLP.Ui', 'Version') - View = translate('OpenLP.Ui', 'View') - ViewMode = translate('OpenLP.Ui', 'View Model') + __instance__ = None + + def __new__(cls): + """ + Override the default object creation method to return a single instance. + """ + if not cls.__instance__: + cls.__instance__ = object.__new__(cls) + return cls.__instance__ + + def __init__(self): + """ + These strings should need a good reason to be retranslated elsewhere. + Should some/more/less of these have an & attached? + """ + self.About = translate('OpenLP.Ui', 'About') + self.Add = translate('OpenLP.Ui', '&Add') + self.Advanced = translate('OpenLP.Ui', 'Advanced') + self.AllFiles = translate('OpenLP.Ui', 'All Files') + self.Bottom = translate('OpenLP.Ui', 'Bottom') + self.Browse = translate('OpenLP.Ui', 'Browse...') + self.Cancel = translate('OpenLP.Ui', 'Cancel') + self.CCLINumberLabel = translate('OpenLP.Ui', 'CCLI number:') + self.CreateService = translate('OpenLP.Ui', 'Create a new service.') + self.Continuous = translate('OpenLP.Ui', 'Continuous') + self.Default = unicode(translate('OpenLP.Ui', 'Default')) + self.Delete = translate('OpenLP.Ui', '&Delete') + self.DisplayStyle = translate('OpenLP.Ui', 'Display style:') + self.Edit = translate('OpenLP.Ui', '&Edit') + self.EmptyField = translate('OpenLP.Ui', 'Empty Field') + self.Error = translate('OpenLP.Ui', 'Error') + self.Export = translate('OpenLP.Ui', 'Export') + self.File = translate('OpenLP.Ui', 'File') + self.FontSizePtUnit = translate('OpenLP.Ui', 'pt', + 'Abbreviated font pointsize unit') + self.Help = translate('OpenLP.Ui', 'Help') + self.Hours = translate('OpenLP.Ui', 'h', + 'The abbreviated unit for hours') + self.Image = translate('OpenLP.Ui', 'Image') + self.Import = translate('OpenLP.Ui', 'Import') + self.LayoutStyle = translate('OpenLP.Ui', 'Layout style:') + self.LengthTime = unicode(translate('OpenLP.Ui', 'Length %s')) + self.Live = translate('OpenLP.Ui', 'Live') + self.LiveBGError = translate('OpenLP.Ui', 'Live Background Error') + self.LivePanel = translate('OpenLP.Ui', 'Live Panel') + self.LiveToolbar = translate('OpenLP.Ui', 'Live Toolbar') + self.Load = translate('OpenLP.Ui', 'Load') + self.Minutes = translate('OpenLP.Ui', 'm', + 'The abbreviated unit for minutes') + self.Middle = translate('OpenLP.Ui', 'Middle') + self.New = translate('OpenLP.Ui', 'New') + self.NewService = translate('OpenLP.Ui', 'New Service') + self.NewTheme = translate('OpenLP.Ui', 'New Theme') + self.NFSs = translate('OpenLP.Ui', 'No File Selected', 'Singular') + self.NFSp = translate('OpenLP.Ui', 'No Files Selected', 'Plural') + self.NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular') + self.NISp = translate('OpenLP.Ui', 'No Items Selected', 'Plural') + self.OLPV1 = translate('OpenLP.Ui', 'openlp.org 1.x') + self.OLPV2 = translate('OpenLP.Ui', 'OpenLP 2.0') + self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. ' + 'Do you wish to continue?') + self.OpenService = translate('OpenLP.Ui', 'Open Service') + self.Preview = translate('OpenLP.Ui', 'Preview') + self.PreviewPanel = translate('OpenLP.Ui', 'Preview Panel') + self.PrintServiceOrder = translate('OpenLP.Ui', 'Print Service Order') + self.ReplaceBG = translate('OpenLP.Ui', 'Replace Background') + self.ReplaceLiveBG = translate('OpenLP.Ui', 'Replace Live Background') + self.ResetBG = translate('OpenLP.Ui', 'Reset Background') + self.ResetLiveBG = translate('OpenLP.Ui', 'Reset Live Background') + self.Seconds = translate('OpenLP.Ui', 's', + 'The abbreviated unit for seconds') + self.SaveAndPreview = translate('OpenLP.Ui', 'Save && Preview') + self.Search = translate('OpenLP.Ui', 'Search') + self.SelectDelete = translate('OpenLP.Ui', 'You must select an item ' + 'to delete.') + self.SelectEdit = translate('OpenLP.Ui', 'You must select an item to ' + 'edit.') + self.Settings = translate('OpenLP.Ui', 'Settings') + self.SaveService = translate('OpenLP.Ui', 'Save Service') + self.Service = translate('OpenLP.Ui', 'Service') + self.StartTimeCode = unicode(translate('OpenLP.Ui', 'Start %s')) + self.Theme = translate('OpenLP.Ui', 'Theme', 'Singular') + self.Themes = translate('OpenLP.Ui', 'Themes', 'Plural') + self.Tools = translate('OpenLP.Ui', 'Tools') + self.Top = translate('OpenLP.Ui', 'Top') + self.VersePerSlide = translate('OpenLP.Ui', 'Verse Per Slide') + self.VersePerLine = translate('OpenLP.Ui', 'Verse Per Line') + self.Version = translate('OpenLP.Ui', 'Version') + self.View = translate('OpenLP.Ui', 'View') + self.ViewMode = translate('OpenLP.Ui', 'View Model') def add_welcome_page(parent, image): """ @@ -184,11 +202,11 @@ def critical_error_message_box(title=None, message=None, parent=None, Should this message box question the user. """ if question: - return QtGui.QMessageBox.critical(parent, UiStrings.Error, message, + return QtGui.QMessageBox.critical(parent, UiStrings().Error, message, QtGui.QMessageBox.StandardButtons( QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)) data = {u'message': message} - data[u'title'] = title if title else UiStrings.Error + data[u'title'] = title if title else UiStrings().Error return Receiver.send_message(u'openlp_error_message', data) def media_item_combo_box(parent, name): @@ -218,7 +236,7 @@ def create_delete_push_button(parent, icon=None): delete_button.setObjectName(u'deleteButton') delete_icon = icon if icon else u':/general/general_delete.png' delete_button.setIcon(build_icon(delete_icon)) - delete_button.setText(UiStrings.Delete) + delete_button.setText(UiStrings().Delete) delete_button.setToolTip( translate('OpenLP.Ui', 'Delete the selected item.')) QtCore.QObject.connect(delete_button, @@ -406,9 +424,9 @@ def create_valign_combo(form, parent, layout): verticalLabel.setText(translate('OpenLP.Ui', '&Vertical Align:')) form.verticalComboBox = QtGui.QComboBox(parent) form.verticalComboBox.setObjectName(u'VerticalComboBox') - form.verticalComboBox.addItem(UiStrings.Top) - form.verticalComboBox.addItem(UiStrings.Middle) - form.verticalComboBox.addItem(UiStrings.Bottom) + form.verticalComboBox.addItem(UiStrings().Top) + form.verticalComboBox.addItem(UiStrings().Middle) + form.verticalComboBox.addItem(UiStrings().Bottom) verticalLabel.setBuddy(form.verticalComboBox) layout.addRow(verticalLabel, form.verticalComboBox) @@ -427,4 +445,4 @@ def find_and_set_in_combo_box(combo_box, value_to_find): if index == -1: # Not Found. index = 0 - combo_box.setCurrentIndex(index) + combo_box.setCurrentIndex(index) \ No newline at end of file diff --git a/openlp/core/ui/aboutdialog.py b/openlp/core/ui/aboutdialog.py index 719cb02c2..d4ea463ea 100644 --- a/openlp/core/ui/aboutdialog.py +++ b/openlp/core/ui/aboutdialog.py @@ -87,7 +87,7 @@ class Ui_AboutDialog(object): QtCore.QMetaObject.connectSlotsByName(aboutDialog) def retranslateUi(self, aboutDialog): - aboutDialog.setWindowTitle(u'%s OpenLP' % UiStrings.About) + aboutDialog.setWindowTitle(u'%s OpenLP' % UiStrings().About) self.aboutTextEdit.setPlainText(translate('OpenLP.AboutForm', 'OpenLP - Open Source Lyrics ' 'Projection\n' @@ -105,7 +105,7 @@ class Ui_AboutDialog(object): 'consider contributing by using the button below.' )) self.aboutNotebook.setTabText( - self.aboutNotebook.indexOf(self.aboutTab), UiStrings.About) + self.aboutNotebook.indexOf(self.aboutTab), UiStrings().About) lead = u'Raoul "superfly" Snyman' developers = [u'Tim "TRB143" Bentley', u'Jonathan "gushie" Corwin', u'Michael "cocooncrash" Gorven', @@ -615,4 +615,4 @@ class Ui_AboutDialog(object): self.aboutNotebook.indexOf(self.licenseTab), translate('OpenLP.AboutForm', 'License')) self.contributeButton.setText(translate('OpenLP.AboutForm', - 'Contribute')) + 'Contribute')) \ No newline at end of file diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index 199ebff60..94bcb0801 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -127,7 +127,7 @@ class AdvancedTab(SettingsTab): """ Setup the interface translation strings. """ - self.tabTitleVisible = UiStrings.Advanced + self.tabTitleVisible = UiStrings().Advanced self.uiGroupBox.setTitle(translate('OpenLP.AdvancedTab', 'UI Settings')) self.recentLabel.setText( translate('OpenLP.AdvancedTab', @@ -226,10 +226,10 @@ class AdvancedTab(SettingsTab): def onDefaultBrowseButtonPressed(self): file_filters = u'%s;;%s (*.*) (*)' % (get_images_filter(), - UiStrings.AllFiles) + UiStrings().AllFiles) filename = QtGui.QFileDialog.getOpenFileName(self, translate('OpenLP.AdvancedTab', 'Open File'), '', file_filters) if filename: self.defaultFileEdit.setText(filename) - self.defaultFileEdit.setFocus() + self.defaultFileEdit.setFocus() \ No newline at end of file diff --git a/openlp/core/ui/displaytagdialog.py b/openlp/core/ui/displaytagdialog.py index 848818e88..8e71a60ff 100644 --- a/openlp/core/ui/displaytagdialog.py +++ b/openlp/core/ui/displaytagdialog.py @@ -136,10 +136,10 @@ class Ui_DisplayTagDialog(object): translate('OpenLP.DisplayTagDialog', 'Start tag')) self.endTagLabel.setText( translate('OpenLP.DisplayTagDialog', 'End tag')) - self.deletePushButton.setText(UiStrings.Delete) + self.deletePushButton.setText(UiStrings().Delete) self.defaultPushButton.setText( translate('OpenLP.DisplayTagDialog', 'Default')) - self.newPushButton.setText(UiStrings.New) + self.newPushButton.setText(UiStrings().New) self.tagTableWidget.horizontalHeaderItem(0).setText( translate('OpenLP.DisplayTagDialog', 'Description')) self.tagTableWidget.horizontalHeaderItem(1).setText( @@ -151,4 +151,4 @@ class Ui_DisplayTagDialog(object): self.tagTableWidget.setColumnWidth(0, 120) self.tagTableWidget.setColumnWidth(1, 40) self.tagTableWidget.setColumnWidth(2, 240) - self.tagTableWidget.setColumnWidth(3, 240) + self.tagTableWidget.setColumnWidth(3, 240) \ No newline at end of file diff --git a/openlp/core/ui/exceptionform.py b/openlp/core/ui/exceptionform.py index 0ae9497c9..622d60f79 100644 --- a/openlp/core/ui/exceptionform.py +++ b/openlp/core/ui/exceptionform.py @@ -178,11 +178,11 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog): self,translate('ImagePlugin.ExceptionDialog', 'Select Attachment'), SettingsManager.get_last_dir(u'exceptions'), - u'%s (*.*) (*)' % UiStrings.AllFiles) + u'%s (*.*) (*)' % UiStrings().AllFiles) log.info(u'New files(s) %s', unicode(files)) if files: self.fileAttachment = unicode(files) def __buttonState(self, state): self.saveReportButton.setEnabled(state) - self.sendReportButton.setEnabled(state) + self.sendReportButton.setEnabled(state) \ No newline at end of file diff --git a/openlp/core/ui/generaltab.py b/openlp/core/ui/generaltab.py index cfde7810c..7dac2fe9d 100644 --- a/openlp/core/ui/generaltab.py +++ b/openlp/core/ui/generaltab.py @@ -238,7 +238,7 @@ class GeneralTab(SettingsTab): self.timeoutSpinBox.setSuffix(translate('OpenLP.GeneralTab', ' sec')) self.ccliGroupBox.setTitle( translate('OpenLP.GeneralTab', 'CCLI Details')) - self.numberLabel.setText(UiStrings.CCLINumberLabel) + self.numberLabel.setText(UiStrings().CCLINumberLabel) self.usernameLabel.setText( translate('OpenLP.GeneralTab', 'SongSelect username:')) self.passwordLabel.setText( @@ -394,4 +394,4 @@ class GeneralTab(SettingsTab): """ Called when the width, height, x position or y position has changed. """ - self.overrideChanged = True + self.overrideChanged = True \ No newline at end of file diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 2038e1972..d2fbaef9e 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -163,82 +163,82 @@ class Ui_MainWindow(object): self.themeManagerDock) # Create the menu items action_list = ActionList.get_instance() - action_list.add_category(UiStrings.File, CategoryOrder.standardMenu) + action_list.add_category(UiStrings().File, CategoryOrder.standardMenu) self.FileNewItem = shortcut_action(mainWindow, u'FileNewItem', [QtGui.QKeySequence(u'Ctrl+N')], self.ServiceManagerContents.onNewServiceClicked, - u':/general/general_new.png', category=UiStrings.File) + u':/general/general_new.png', category=UiStrings().File) self.FileOpenItem = shortcut_action(mainWindow, u'FileOpenItem', [QtGui.QKeySequence(u'Ctrl+O')], self.ServiceManagerContents.onLoadServiceClicked, - u':/general/general_open.png', category=UiStrings.File) + u':/general/general_open.png', category=UiStrings().File) self.FileSaveItem = shortcut_action(mainWindow, u'FileSaveItem', [QtGui.QKeySequence(u'Ctrl+S')], self.ServiceManagerContents.saveFile, - u':/general/general_save.png', category=UiStrings.File) + u':/general/general_save.png', category=UiStrings().File) self.FileSaveAsItem = shortcut_action(mainWindow, u'FileSaveAsItem', [QtGui.QKeySequence(u'Ctrl+Shift+S')], - self.ServiceManagerContents.saveFileAs, category=UiStrings.File) + self.ServiceManagerContents.saveFileAs, category=UiStrings().File) self.printServiceOrderItem = shortcut_action(mainWindow, u'printServiceItem', [QtGui.QKeySequence(u'Ctrl+P')], self.ServiceManagerContents.printServiceOrder, - category=UiStrings.File) + category=UiStrings().File) self.FileExitItem = shortcut_action(mainWindow, u'FileExitItem', [QtGui.QKeySequence(u'Alt+F4')], mainWindow.close, - u':/system/system_exit.png', category=UiStrings.File) - action_list.add_category(UiStrings.Import, CategoryOrder.standardMenu) + u':/system/system_exit.png', category=UiStrings().File) + action_list.add_category(UiStrings().Import, CategoryOrder.standardMenu) self.ImportThemeItem = base_action( - mainWindow, u'ImportThemeItem', UiStrings.Import) + mainWindow, u'ImportThemeItem', UiStrings().Import) self.ImportLanguageItem = base_action( - mainWindow, u'ImportLanguageItem')#, UiStrings.Import) - action_list.add_category(UiStrings.Export, CategoryOrder.standardMenu) + mainWindow, u'ImportLanguageItem')#, UiStrings().Import) + action_list.add_category(UiStrings().Export, CategoryOrder.standardMenu) self.ExportThemeItem = base_action( - mainWindow, u'ExportThemeItem', UiStrings.Export) + mainWindow, u'ExportThemeItem', UiStrings().Export) self.ExportLanguageItem = base_action( - mainWindow, u'ExportLanguageItem')#, UiStrings.Export) - action_list.add_category(UiStrings.View, CategoryOrder.standardMenu) + mainWindow, u'ExportLanguageItem')#, UiStrings().Export) + action_list.add_category(UiStrings().View, CategoryOrder.standardMenu) self.ViewMediaManagerItem = shortcut_action(mainWindow, u'ViewMediaManagerItem', [QtGui.QKeySequence(u'F8')], self.toggleMediaManager, u':/system/system_mediamanager.png', - self.mediaManagerDock.isVisible(), UiStrings.View) + self.mediaManagerDock.isVisible(), UiStrings().View) self.ViewThemeManagerItem = shortcut_action(mainWindow, u'ViewThemeManagerItem', [QtGui.QKeySequence(u'F10')], self.toggleThemeManager, u':/system/system_thememanager.png', - self.themeManagerDock.isVisible(), UiStrings.View) + self.themeManagerDock.isVisible(), UiStrings().View) self.ViewServiceManagerItem = shortcut_action(mainWindow, u'ViewServiceManagerItem', [QtGui.QKeySequence(u'F9')], self.toggleServiceManager, u':/system/system_servicemanager.png', - self.serviceManagerDock.isVisible(), UiStrings.View) + self.serviceManagerDock.isVisible(), UiStrings().View) self.ViewPreviewPanel = shortcut_action(mainWindow, u'ViewPreviewPanel', [QtGui.QKeySequence(u'F11')], self.setPreviewPanelVisibility, checked=previewVisible, - category=UiStrings.View) + category=UiStrings().View) self.ViewLivePanel = shortcut_action(mainWindow, u'ViewLivePanel', [QtGui.QKeySequence(u'F12')], self.setLivePanelVisibility, - checked=liveVisible, category=UiStrings.View) - action_list.add_category(UiStrings.ViewMode, CategoryOrder.standardMenu) + checked=liveVisible, category=UiStrings().View) + action_list.add_category(UiStrings().ViewMode, CategoryOrder.standardMenu) self.ModeDefaultItem = checkable_action( - mainWindow, u'ModeDefaultItem', category=UiStrings.ViewMode) + mainWindow, u'ModeDefaultItem', category=UiStrings().ViewMode) self.ModeSetupItem = checkable_action( - mainWindow, u'ModeLiveItem', category=UiStrings.ViewMode) + mainWindow, u'ModeLiveItem', category=UiStrings().ViewMode) self.ModeLiveItem = checkable_action( - mainWindow, u'ModeLiveItem', True, UiStrings.ViewMode) + mainWindow, u'ModeLiveItem', True, UiStrings().ViewMode) self.ModeGroup = QtGui.QActionGroup(mainWindow) self.ModeGroup.addAction(self.ModeDefaultItem) self.ModeGroup.addAction(self.ModeSetupItem) self.ModeGroup.addAction(self.ModeLiveItem) self.ModeDefaultItem.setChecked(True) - action_list.add_category(UiStrings.Tools, CategoryOrder.standardMenu) + action_list.add_category(UiStrings().Tools, CategoryOrder.standardMenu) self.ToolsAddToolItem = icon_action(mainWindow, u'ToolsAddToolItem', - u':/tools/tools_add.png', category=UiStrings.Tools) + u':/tools/tools_add.png', category=UiStrings().Tools) self.ToolsOpenDataFolder = icon_action(mainWindow, u'ToolsOpenDataFolder', u':/general/general_open.png', - category=UiStrings.Tools) - action_list.add_category(UiStrings.Settings, CategoryOrder.standardMenu) + category=UiStrings().Tools) + action_list.add_category(UiStrings().Settings, CategoryOrder.standardMenu) self.settingsPluginListItem = shortcut_action(mainWindow, u'settingsPluginListItem', [QtGui.QKeySequence(u'Alt+F7')], self.onPluginItemClicked, u':/system/settings_plugin_list.png', - category=UiStrings.Settings) + category=UiStrings().Settings) # i18n Language Items self.AutoLanguageItem = checkable_action(mainWindow, u'AutoLanguageItem', LanguageManager.auto_language) @@ -255,25 +255,25 @@ class Ui_MainWindow(object): self.SettingsShortcutsItem = icon_action(mainWindow, u'SettingsShortcutsItem', u':/system/system_configure_shortcuts.png', - category=UiStrings.Settings) + category=UiStrings().Settings) self.DisplayTagItem = icon_action(mainWindow, u'DisplayTagItem', u':/system/tag_editor.png', - category=UiStrings.Settings) + category=UiStrings().Settings) self.SettingsConfigureItem = icon_action(mainWindow, u'SettingsConfigureItem', u':/system/system_settings.png', - category=UiStrings.Settings) - action_list.add_category(UiStrings.Help, CategoryOrder.standardMenu) + category=UiStrings().Settings) + action_list.add_category(UiStrings().Help, CategoryOrder.standardMenu) self.HelpDocumentationItem = icon_action(mainWindow, u'HelpDocumentationItem', u':/system/system_help_contents.png', - category=None)#UiStrings.Help) + category=None)#UiStrings().Help) self.HelpDocumentationItem.setEnabled(False) self.HelpAboutItem = shortcut_action(mainWindow, u'HelpAboutItem', [QtGui.QKeySequence(u'Ctrl+F1')], self.onHelpAboutItemClicked, - u':/system/system_about.png', category=UiStrings.Help) + u':/system/system_about.png', category=UiStrings().Help) self.HelpOnlineHelpItem = base_action( - mainWindow, u'HelpOnlineHelpItem', category=UiStrings.Help) + mainWindow, u'HelpOnlineHelpItem', category=UiStrings().Help) self.helpWebSiteItem = base_action( - mainWindow, u'helpWebSiteItem', category=UiStrings.Help) + mainWindow, u'helpWebSiteItem', category=UiStrings().Help) add_actions(self.FileImportMenu, (self.ImportThemeItem, self.ImportLanguageItem)) add_actions(self.FileExportMenu, @@ -320,7 +320,7 @@ class Ui_MainWindow(object): """ Set up the translation system """ - mainWindow.mainTitle = UiStrings.OLPV2 + mainWindow.mainTitle = UiStrings().OLPV2 mainWindow.setWindowTitle(mainWindow.mainTitle) self.FileMenu.setTitle(translate('OpenLP.MainWindow', '&File')) self.FileImportMenu.setTitle(translate('OpenLP.MainWindow', '&Import')) @@ -339,14 +339,14 @@ class Ui_MainWindow(object): self.themeManagerDock.setWindowTitle( translate('OpenLP.MainWindow', 'Theme Manager')) self.FileNewItem.setText(translate('OpenLP.MainWindow', '&New')) - self.FileNewItem.setToolTip(UiStrings.NewService) - self.FileNewItem.setStatusTip(UiStrings.CreateService) + self.FileNewItem.setToolTip(UiStrings().NewService) + self.FileNewItem.setStatusTip(UiStrings().CreateService) self.FileOpenItem.setText(translate('OpenLP.MainWindow', '&Open')) - self.FileOpenItem.setToolTip(UiStrings.OpenService) + self.FileOpenItem.setToolTip(UiStrings().OpenService) self.FileOpenItem.setStatusTip( translate('OpenLP.MainWindow', 'Open an existing service.')) self.FileSaveItem.setText(translate('OpenLP.MainWindow', '&Save')) - self.FileSaveItem.setToolTip(UiStrings.SaveService) + self.FileSaveItem.setToolTip(UiStrings().SaveService) self.FileSaveItem.setStatusTip( translate('OpenLP.MainWindow', 'Save the current service to disk.')) self.FileSaveAsItem.setText( @@ -355,7 +355,7 @@ class Ui_MainWindow(object): translate('OpenLP.MainWindow', 'Save Service As')) self.FileSaveAsItem.setStatusTip(translate('OpenLP.MainWindow', 'Save the current service under a new name.')) - self.printServiceOrderItem.setText(UiStrings.PrintServiceOrder) + self.printServiceOrderItem.setText(UiStrings().PrintServiceOrder) self.printServiceOrderItem.setStatusTip(translate('OpenLP.MainWindow', 'Print the current Service Order.')) self.FileExitItem.setText( @@ -1011,4 +1011,4 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.recentFiles.insert(0, QtCore.QString(filename)) while self.recentFiles.count() > maxRecentFiles: # Don't care what API says takeLast works, removeLast doesn't! - self.recentFiles.takeLast() + self.recentFiles.takeLast() \ No newline at end of file diff --git a/openlp/core/ui/plugindialog.py b/openlp/core/ui/plugindialog.py index 0ead739a5..84fb845c6 100644 --- a/openlp/core/ui/plugindialog.py +++ b/openlp/core/ui/plugindialog.py @@ -78,11 +78,11 @@ class Ui_PluginViewDialog(object): translate('OpenLP.PluginForm', 'Plugin List')) self.pluginInfoGroupBox.setTitle( translate('OpenLP.PluginForm', 'Plugin Details')) - self.versionLabel.setText(u'%s:' % UiStrings.Version) - self.aboutLabel.setText(u'%s:' % UiStrings.About) + self.versionLabel.setText(u'%s:' % UiStrings().Version) + self.aboutLabel.setText(u'%s:' % UiStrings().About) self.statusLabel.setText( translate('OpenLP.PluginForm', 'Status:')) self.statusComboBox.setItemText(0, translate('OpenLP.PluginForm', 'Active')) self.statusComboBox.setItemText(1, - translate('OpenLP.PluginForm', 'Inactive')) + translate('OpenLP.PluginForm', 'Inactive')) \ No newline at end of file diff --git a/openlp/core/ui/printservicedialog.py b/openlp/core/ui/printservicedialog.py index 9593e9ec4..889bc4f7d 100644 --- a/openlp/core/ui/printservicedialog.py +++ b/openlp/core/ui/printservicedialog.py @@ -148,7 +148,7 @@ class Ui_PrintServiceDialog(object): QtCore.SIGNAL(u'toggled(bool)'), self.toggleOptions) def retranslateUi(self, printServiceDialog): - printServiceDialog.setWindowTitle(UiStrings.PrintServiceOrder) + printServiceDialog.setWindowTitle(UiStrings().PrintServiceOrder) self.slideTextCheckBox.setText(translate('OpenLP.PrintServiceForm', 'Include slide text if available')) self.pageBreakAfterText.setText(translate('OpenLP.PrintServiceForm', @@ -164,4 +164,4 @@ class Ui_PrintServiceDialog(object): self.zoomComboBox.addItem(ZoomSize.Sizes[ZoomSize.OneHundred]) self.zoomComboBox.addItem(ZoomSize.Sizes[ZoomSize.SeventyFive]) self.zoomComboBox.addItem(ZoomSize.Sizes[ZoomSize.Fifty]) - self.zoomComboBox.addItem(ZoomSize.Sizes[ZoomSize.TwentyFive]) + self.zoomComboBox.addItem(ZoomSize.Sizes[ZoomSize.TwentyFive]) \ No newline at end of file diff --git a/openlp/core/ui/printserviceform.py b/openlp/core/ui/printserviceform.py index 0de20d8a9..42b773198 100644 --- a/openlp/core/ui/printserviceform.py +++ b/openlp/core/ui/printserviceform.py @@ -354,9 +354,9 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): Called when html copy check box is selected. """ if value == QtCore.Qt.Checked: - self.copyTextButton.setText(UiStrings.CopyToHtml) + self.copyTextButton.setText(UiStrings().CopyToHtml) else: - self.copyTextButton.setText(UiStrings.CopyToText) + self.copyTextButton.setText(UiStrings().CopyToText) def onSlideTextCheckBoxChanged(self, state): """ @@ -380,4 +380,4 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog): QtCore.QVariant(self.metaDataCheckBox.isChecked())) settings.setValue(u'print notes', QtCore.QVariant(self.notesCheckBox.isChecked())) - settings.endGroup() + settings.endGroup() \ No newline at end of file diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index ba9ca718a..d682f5b52 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -111,18 +111,18 @@ class ServiceManager(QtGui.QWidget): # Create the top toolbar self.toolbar = OpenLPToolbar(self) self.toolbar.addToolbarButton( - UiStrings.NewService, u':/general/general_new.png', - UiStrings.CreateService, self.onNewServiceClicked) + UiStrings().NewService, u':/general/general_new.png', + UiStrings().CreateService, self.onNewServiceClicked) self.toolbar.addToolbarButton( - UiStrings.OpenService, u':/general/general_open.png', + UiStrings().OpenService, u':/general/general_open.png', translate('OpenLP.ServiceManager', 'Load an existing service'), self.onLoadServiceClicked) self.toolbar.addToolbarButton( - UiStrings.SaveService, u':/general/general_save.png', + UiStrings().SaveService, u':/general/general_save.png', translate('OpenLP.ServiceManager', 'Save this service'), self.saveFile) self.toolbar.addSeparator() - self.themeLabel = QtGui.QLabel(u'%s:' % UiStrings.Theme, self) + self.themeLabel = QtGui.QLabel(u'%s:' % UiStrings().Theme, self) self.themeLabel.setMargin(3) self.themeLabel.setObjectName(u'themeLabel') self.toolbar.addToolbarWidget(u'ThemeLabel', self.themeLabel) @@ -169,9 +169,9 @@ class ServiceManager(QtGui.QWidget): self.onServiceTop, shortcuts=[QtCore.Qt.Key_Home]) self.serviceManagerList.moveTop.setObjectName(u'moveTop') action_list = ActionList.get_instance() - action_list.add_category(UiStrings.Service, CategoryOrder.standardToolbar) + action_list.add_category(UiStrings().Service, CategoryOrder.standardToolbar) action_list.add_action( - self.serviceManagerList.moveTop, UiStrings.Service) + self.serviceManagerList.moveTop, UiStrings().Service) self.serviceManagerList.moveUp = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move &up'), u':/services/service_up.png', @@ -179,7 +179,7 @@ class ServiceManager(QtGui.QWidget): 'Move item up one position in the service.'), self.onServiceUp, shortcuts=[QtCore.Qt.Key_PageUp]) self.serviceManagerList.moveUp.setObjectName(u'moveUp') - action_list.add_action(self.serviceManagerList.moveUp, UiStrings.Service) + action_list.add_action(self.serviceManagerList.moveUp, UiStrings().Service) self.serviceManagerList.moveDown = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move &down'), u':/services/service_down.png', @@ -188,7 +188,7 @@ class ServiceManager(QtGui.QWidget): self.onServiceDown, shortcuts=[QtCore.Qt.Key_PageDown]) self.serviceManagerList.moveDown.setObjectName(u'moveDown') action_list.add_action( - self.serviceManagerList.moveDown, UiStrings.Service) + self.serviceManagerList.moveDown, UiStrings().Service) self.serviceManagerList.moveBottom = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move to &bottom'), u':/services/service_bottom.png', @@ -197,7 +197,7 @@ class ServiceManager(QtGui.QWidget): self.onServiceEnd, shortcuts=[QtCore.Qt.Key_End]) self.serviceManagerList.moveBottom.setObjectName(u'moveBottom') action_list.add_action( - self.serviceManagerList.moveBottom, UiStrings.Service) + self.serviceManagerList.moveBottom, UiStrings().Service) self.serviceManagerList.down = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move &down'), None, @@ -231,7 +231,7 @@ class ServiceManager(QtGui.QWidget): 'Expand all the service items.'), self.onExpandAll, shortcuts=[QtCore.Qt.Key_Plus]) self.serviceManagerList.expand.setObjectName(u'expand') - action_list.add_action(self.serviceManagerList.expand, UiStrings.Service) + action_list.add_action(self.serviceManagerList.expand, UiStrings().Service) self.serviceManagerList.collapse = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', '&Collapse all'), u':/services/service_collapse_all.png', @@ -240,7 +240,7 @@ class ServiceManager(QtGui.QWidget): self.onCollapseAll, shortcuts=[QtCore.Qt.Key_Minus]) self.serviceManagerList.collapse.setObjectName(u'collapse') action_list.add_action( - self.serviceManagerList.collapse, UiStrings.Service) + self.serviceManagerList.collapse, UiStrings().Service) self.orderToolbar.addSeparator() self.serviceManagerList.makeLive = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Go Live'), @@ -250,7 +250,7 @@ class ServiceManager(QtGui.QWidget): shortcuts=[QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return]) self.serviceManagerList.makeLive.setObjectName(u'orderToolbar') action_list.add_action( - self.serviceManagerList.makeLive, UiStrings.Service) + self.serviceManagerList.makeLive, UiStrings().Service) self.layout.addWidget(self.orderToolbar) # Connect up our signals and slots QtCore.QObject.connect(self.themeComboBox, @@ -525,7 +525,7 @@ class ServiceManager(QtGui.QWidget): save the file. """ fileName = unicode(QtGui.QFileDialog.getSaveFileName(self.mainwindow, - UiStrings.SaveService, + UiStrings().SaveService, SettingsManager.get_last_dir( self.mainwindow.serviceSettingsSection), translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz)'))) @@ -1311,4 +1311,4 @@ class ServiceManager(QtGui.QWidget): Print a Service Order Sheet. """ settingDialog = PrintServiceForm(self.mainwindow, self) - settingDialog.exec_() + settingDialog.exec_() \ No newline at end of file diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 2ae39c5e7..5f2c63c23 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -88,11 +88,11 @@ class SlideController(QtGui.QWidget): # Type label for the top of the slide controller self.typeLabel = QtGui.QLabel(self.panel) if self.isLive: - self.typeLabel.setText(UiStrings.Live) + self.typeLabel.setText(UiStrings().Live) self.split = 1 self.typePrefix = u'live' else: - self.typeLabel.setText(UiStrings.Preview) + self.typeLabel.setText(UiStrings().Preview) self.split = 0 self.typePrefix = u'preview' self.typeLabel.setStyleSheet(u'font-weight: bold; font-size: 12pt;') @@ -161,18 +161,18 @@ class SlideController(QtGui.QWidget): translate('OpenLP.SlideController', 'Hide'), self.toolbar)) self.blankScreen = shortcut_action(self.hideMenu, u'blankScreen', [QtCore.Qt.Key_Period], self.onBlankDisplay, - u':/slides/slide_blank.png', False, UiStrings.LiveToolbar) + u':/slides/slide_blank.png', False, UiStrings().LiveToolbar) self.blankScreen.setText( translate('OpenLP.SlideController', 'Blank Screen')) self.themeScreen = shortcut_action(self.hideMenu, u'themeScreen', [QtGui.QKeySequence(u'T')], self.onThemeDisplay, - u':/slides/slide_theme.png', False, UiStrings.LiveToolbar) + u':/slides/slide_theme.png', False, UiStrings().LiveToolbar) self.themeScreen.setText( translate('OpenLP.SlideController', 'Blank to Theme')) self.desktopScreen = shortcut_action(self.hideMenu, u'desktopScreen', [QtGui.QKeySequence(u'D')], self.onHideDisplay, u':/slides/slide_desktop.png', False, - UiStrings.LiveToolbar) + UiStrings().LiveToolbar) self.desktopScreen.setText( translate('OpenLP.SlideController', 'Show Desktop')) self.hideMenu.setDefaultAction(self.blankScreen) @@ -194,7 +194,7 @@ class SlideController(QtGui.QWidget): self.delaySpinBox.setMinimum(1) self.delaySpinBox.setMaximum(180) self.toolbar.addToolbarWidget(u'Image SpinBox', self.delaySpinBox) - self.delaySpinBox.setSuffix(UiStrings.Seconds) + self.delaySpinBox.setSuffix(UiStrings().Seconds) self.delaySpinBox.setToolTip(translate('OpenLP.SlideController', 'Delay between slides in seconds')) else: @@ -376,21 +376,21 @@ class SlideController(QtGui.QWidget): self.nextItem.setObjectName(u'nextItemLive') action_list = ActionList.get_instance() action_list.add_category( - UiStrings.LiveToolbar, CategoryOrder.standardToolbar) + UiStrings().LiveToolbar, CategoryOrder.standardToolbar) action_list.add_action(self.previousItem) action_list.add_action(self.nextItem) self.previousService = shortcut_action(parent, u'previousService', - [QtCore.Qt.Key_Left], self.servicePrevious, UiStrings.LiveToolbar) + [QtCore.Qt.Key_Left], self.servicePrevious, UiStrings().LiveToolbar) self.previousService.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) self.previousService.setText( translate('OpenLP.SlideController', 'Previous Service')) self.nextService = shortcut_action(parent, 'nextService', - [QtCore.Qt.Key_Right], self.serviceNext, UiStrings.LiveToolbar) + [QtCore.Qt.Key_Right], self.serviceNext, UiStrings().LiveToolbar) self.nextService.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) self.nextService.setText( translate('OpenLP.SlideController', 'Next Service')) self.escapeItem = shortcut_action(parent, 'escapeItem', - [QtCore.Qt.Key_Escape], self.liveEscape, UiStrings.LiveToolbar) + [QtCore.Qt.Key_Escape], self.liveEscape, UiStrings().LiveToolbar) self.escapeItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) self.escapeItem.setText( translate('OpenLP.SlideController', 'Escape Item')) @@ -1150,4 +1150,4 @@ class SlideController(QtGui.QWidget): elif self.desktopScreen.isChecked(): return HideMode.Screen else: - return None + return None \ No newline at end of file diff --git a/openlp/core/ui/starttimedialog.py b/openlp/core/ui/starttimedialog.py index be34b765e..2d1711231 100644 --- a/openlp/core/ui/starttimedialog.py +++ b/openlp/core/ui/starttimedialog.py @@ -107,15 +107,15 @@ class Ui_StartTimeDialog(object): def retranslateUi(self, StartTimeDialog): self.setWindowTitle(translate('OpenLP.StartTimeForm', 'Item Start and Finish Time')) - self.hourSpinBox.setSuffix(UiStrings.Hours) - self.minuteSpinBox.setSuffix(UiStrings.Minutes) - self.secondSpinBox.setSuffix(UiStrings.Seconds) - self.hourFinishSpinBox.setSuffix(UiStrings.Hours) - self.minuteFinishSpinBox.setSuffix(UiStrings.Minutes) - self.secondFinishSpinBox.setSuffix(UiStrings.Seconds) + self.hourSpinBox.setSuffix(UiStrings().Hours) + self.minuteSpinBox.setSuffix(UiStrings().Minutes) + self.secondSpinBox.setSuffix(UiStrings().Seconds) + self.hourFinishSpinBox.setSuffix(UiStrings().Hours) + self.minuteFinishSpinBox.setSuffix(UiStrings().Minutes) + self.secondFinishSpinBox.setSuffix(UiStrings().Seconds) self.hourLabel.setText(translate('OpenLP.StartTimeForm', 'Hours:')) self.minuteLabel.setText(translate('OpenLP.StartTimeForm', 'Minutes:')) self.secondLabel.setText(translate('OpenLP.StartTimeForm', 'Seconds:')) self.startLabel.setText(translate('OpenLP.StartTimeForm', 'Start')) self.finishLabel.setText(translate('OpenLP.StartTimeForm', 'Finish')) - self.lengthLabel.setText(translate('OpenLP.StartTimeForm', 'Length')) + self.lengthLabel.setText(translate('OpenLP.StartTimeForm', 'Length')) \ No newline at end of file diff --git a/openlp/core/ui/starttimeform.py b/openlp/core/ui/starttimeform.py index a30dd3b8e..b460bbbd0 100644 --- a/openlp/core/ui/starttimeform.py +++ b/openlp/core/ui/starttimeform.py @@ -53,11 +53,11 @@ class StartTimeForm(QtGui.QDialog, Ui_StartTimeDialog): self.hourFinishSpinBox.setValue(hours) self.minuteFinishSpinBox.setValue(minutes) self.secondFinishSpinBox.setValue(seconds) - self.hourFinishLabel.setText(u'%s%s' % (unicode(hour), UiStrings.Hours)) + self.hourFinishLabel.setText(u'%s%s' % (unicode(hour), UiStrings().Hours)) self.minuteFinishLabel.setText(u'%s%s' % - (unicode(minutes), UiStrings.Minutes)) + (unicode(minutes), UiStrings().Minutes)) self.secondFinishLabel.setText(u'%s%s' % - (unicode(seconds), UiStrings.Seconds)) + (unicode(seconds), UiStrings().Seconds)) return QtGui.QDialog.exec_(self) def accept(self): @@ -90,4 +90,4 @@ class StartTimeForm(QtGui.QDialog, Ui_StartTimeDialog): seconds -= 3600 * hours minutes = seconds / 60 seconds -= 60 * minutes - return hours, minutes, seconds + return hours, minutes, seconds \ No newline at end of file diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index 3a3b3bb61..ebba45e7c 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -290,7 +290,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard): 'Edit Theme - %s')) % self.theme.theme_name) self.next() else: - self.setWindowTitle(UiStrings.NewTheme) + self.setWindowTitle(UiStrings().NewTheme) return QtGui.QWizard.exec_(self) def initializePage(self, id): @@ -473,7 +473,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard): """ images_filter = get_images_filter() images_filter = u'%s;;%s (*.*) (*)' % ( - images_filter, UiStrings.AllFiles) + images_filter, UiStrings().AllFiles) filename = QtGui.QFileDialog.getOpenFileName(self, translate('OpenLP.ThemeForm', 'Select Image'), u'', images_filter) @@ -589,4 +589,4 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard): QtGui.QColor(field), self) if new_color.isValid(): field = new_color.name() - return field + return field \ No newline at end of file diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 81da6e021..d2a653a26 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -63,7 +63,7 @@ class ThemeManager(QtGui.QWidget): self.layout.setMargin(0) self.layout.setObjectName(u'layout') self.toolbar = OpenLPToolbar(self) - self.toolbar.addToolbarButton(UiStrings.NewTheme, + self.toolbar.addToolbarButton(UiStrings().NewTheme, u':/themes/theme_new.png', translate('OpenLP.ThemeManager', 'Create a new theme.'), self.onAddTheme) @@ -449,7 +449,7 @@ class ThemeManager(QtGui.QWidget): # No themes have been found so create one if len(files) == 0: theme = ThemeXML() - theme.theme_name = UiStrings.Default + theme.theme_name = UiStrings().Default self._writeTheme(theme, None, None) QtCore.QSettings().setValue( self.settingsSection + u'/global theme', @@ -803,4 +803,4 @@ class ThemeManager(QtGui.QWidget): vAlignCorrection = VerticalType.Bottom newtheme.display_horizontal_align = theme.HorizontalAlign newtheme.display_vertical_align = vAlignCorrection - return newtheme.extract_xml() + return newtheme.extract_xml() \ No newline at end of file diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index 372ee0cc8..fccef1244 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -102,7 +102,7 @@ class ThemesTab(SettingsTab): QtCore.SIGNAL(u'theme_update_list'), self.updateThemeList) def retranslateUi(self): - self.tabTitleVisible = UiStrings.Themes + self.tabTitleVisible = UiStrings().Themes self.GlobalGroupBox.setTitle( translate('OpenLP.ThemesTab', 'Global Theme')) self.LevelGroupBox.setTitle( @@ -203,4 +203,4 @@ class ThemesTab(SettingsTab): if not preview.isNull(): preview = preview.scaled(300, 255, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation) - self.DefaultListView.setPixmap(preview) + self.DefaultListView.setPixmap(preview) \ No newline at end of file diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py index 9e464aa7a..759b36101 100644 --- a/openlp/core/ui/themewizard.py +++ b/openlp/core/ui/themewizard.py @@ -424,7 +424,7 @@ class Ui_ThemeWizard(object): self.backgroundComboBox.setItemText(BackgroundType.Gradient, translate('OpenLP.ThemeWizard', 'Gradient')) self.backgroundComboBox.setItemText( - BackgroundType.Image, UiStrings.Image) + BackgroundType.Image, UiStrings().Image) self.colorLabel.setText(translate('OpenLP.ThemeWizard', 'Color:')) self.gradientStartLabel.setText( translate(u'OpenLP.ThemeWizard', 'Starting color:')) @@ -442,7 +442,7 @@ class Ui_ThemeWizard(object): translate('OpenLP.ThemeWizard', 'Top Left - Bottom Right')) self.gradientComboBox.setItemText(BackgroundGradientType.LeftBottom, translate('OpenLP.ThemeWizard', 'Bottom Left - Top Right')) - self.imageLabel.setText(u'%s:' % UiStrings.Image) + self.imageLabel.setText(u'%s:' % UiStrings().Image) self.mainAreaPage.setTitle( translate('OpenLP.ThemeWizard', 'Main Area Font Details')) self.mainAreaPage.setSubTitle( @@ -451,17 +451,17 @@ class Ui_ThemeWizard(object): self.mainFontLabel.setText(translate('OpenLP.ThemeWizard', 'Font:')) self.mainColorLabel.setText(translate('OpenLP.ThemeWizard', 'Color:')) self.mainSizeLabel.setText(translate('OpenLP.ThemeWizard', 'Size:')) - self.mainSizeSpinBox.setSuffix(UiStrings.FontSizePtUnit) + self.mainSizeSpinBox.setSuffix(UiStrings().FontSizePtUnit) self.lineSpacingLabel.setText( translate('OpenLP.ThemeWizard', 'Line Spacing:')) - self.lineSpacingSpinBox.setSuffix(UiStrings.FontSizePtUnit) + self.lineSpacingSpinBox.setSuffix(UiStrings().FontSizePtUnit) self.outlineCheckBox.setText( translate('OpenLP.ThemeWizard', '&Outline:')) self.outlineSizeLabel.setText(translate('OpenLP.ThemeWizard', 'Size:')) - self.outlineSizeSpinBox.setSuffix(UiStrings.FontSizePtUnit) + self.outlineSizeSpinBox.setSuffix(UiStrings().FontSizePtUnit) self.shadowCheckBox.setText(translate('OpenLP.ThemeWizard', '&Shadow:')) self.shadowSizeLabel.setText(translate('OpenLP.ThemeWizard', 'Size:')) - self.shadowSizeSpinBox.setSuffix(UiStrings.FontSizePtUnit) + self.shadowSizeSpinBox.setSuffix(UiStrings().FontSizePtUnit) self.mainBoldCheckBox.setText(translate('OpenLP.ThemeWizard', 'Bold')) self.mainItalicsCheckBox.setText( translate('OpenLP.ThemeWizard', 'Italic')) @@ -473,7 +473,7 @@ class Ui_ThemeWizard(object): self.footerFontLabel.setText(translate('OpenLP.ThemeWizard', 'Font:')) self.footerColorLabel.setText(translate('OpenLP.ThemeWizard', 'Color:')) self.footerSizeLabel.setText(translate('OpenLP.ThemeWizard', 'Size:')) - self.footerSizeSpinBox.setSuffix(UiStrings.FontSizePtUnit) + self.footerSizeSpinBox.setSuffix(UiStrings().FontSizePtUnit) self.alignmentPage.setTitle( translate('OpenLP.ThemeWizard', 'Text Formatting Details')) self.alignmentPage.setSubTitle( @@ -537,4 +537,4 @@ class Ui_ThemeWizard(object): labelWidth = max(self.backgroundLabel.minimumSizeHint().width(), self.horizontalLabel.minimumSizeHint().width()) self.spacer.changeSize(labelWidth, 0, - QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) \ No newline at end of file diff --git a/openlp/core/ui/wizard.py b/openlp/core/ui/wizard.py index cb3a75294..74ca0715a 100644 --- a/openlp/core/ui/wizard.py +++ b/openlp/core/ui/wizard.py @@ -212,11 +212,11 @@ class OpenLPWizard(QtGui.QWizard): """ if filters: filters += u';;' - filters += u'%s (*)' % UiStrings.AllFiles + filters += u'%s (*)' % UiStrings().AllFiles filename = QtGui.QFileDialog.getOpenFileName(self, title, os.path.dirname(SettingsManager.get_last_dir( self.plugin.settingsSection, 1)), filters) if filename: editbox.setText(filename) SettingsManager.set_last_dir(self.plugin.settingsSection, - filename, 1) + filename, 1) \ No newline at end of file diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index c5714c914..0cfa9c60a 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -77,7 +77,7 @@ class AlertsPlugin(Plugin): Plugin.initialise(self) self.toolsAlertItem.setVisible(True) action_list = ActionList.get_instance() - action_list.add_action(self.toolsAlertItem, UiStrings.Tools) + action_list.add_action(self.toolsAlertItem, UiStrings().Tools) self.liveController.alertTab = self.settings_tab def finalise(self): @@ -118,4 +118,4 @@ class AlertsPlugin(Plugin): ## Name for MediaDockManager, SettingsManager ## self.textStrings[StringContent.VisibleName] = { u'title': translate('AlertsPlugin', 'Alerts', 'container title') - } + } \ No newline at end of file diff --git a/openlp/plugins/alerts/lib/alertstab.py b/openlp/plugins/alerts/lib/alertstab.py index 3b6c50a10..2ec2be24b 100644 --- a/openlp/plugins/alerts/lib/alertstab.py +++ b/openlp/plugins/alerts/lib/alertstab.py @@ -109,12 +109,12 @@ class AlertsTab(SettingsTab): translate('AlertsPlugin.AlertsTab', 'Background color:')) self.FontSizeLabel.setText( translate('AlertsPlugin.AlertsTab', 'Font size:')) - self.FontSizeSpinBox.setSuffix(UiStrings.FontSizePtUnit) + self.FontSizeSpinBox.setSuffix(UiStrings().FontSizePtUnit) self.TimeoutLabel.setText( translate('AlertsPlugin.AlertsTab', 'Alert timeout:')) - self.TimeoutSpinBox.setSuffix(UiStrings.Seconds) - self.PreviewGroupBox.setTitle(UiStrings.Preview) - self.FontPreview.setText(UiStrings.OLPV2) + self.TimeoutSpinBox.setSuffix(UiStrings().Seconds) + self.PreviewGroupBox.setTitle(UiStrings().Preview) + self.FontPreview.setText(UiStrings().OLPV2) def onBackgroundColorButtonClicked(self): new_color = QtGui.QColorDialog.getColor( @@ -191,4 +191,4 @@ class AlertsTab(SettingsTab): font.setPointSize(self.font_size) self.FontPreview.setFont(font) self.FontPreview.setStyleSheet(u'background-color: %s; color: %s' % - (self.bg_color, self.font_color)) + (self.bg_color, self.font_color)) \ No newline at end of file diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index aea732688..5a631bf00 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -53,9 +53,9 @@ class BiblePlugin(Plugin): Plugin.initialise(self) self.importBibleItem.setVisible(True) action_list = ActionList.get_instance() - action_list.add_action(self.importBibleItem, UiStrings.Import) + action_list.add_action(self.importBibleItem, UiStrings().Import) # Do not add the action to the list yet. - #action_list.add_action(self.exportBibleItem, UiStrings.Export) + #action_list.add_action(self.exportBibleItem, UiStrings().Export) # Set to invisible until we can export bibles self.exportBibleItem.setVisible(False) @@ -67,9 +67,9 @@ class BiblePlugin(Plugin): self.manager.finalise() Plugin.finalise(self) action_list = ActionList.get_instance() - action_list.remove_action(self.importBibleItem, UiStrings.Import) + action_list.remove_action(self.importBibleItem, UiStrings().Import) self.importBibleItem.setVisible(False) - #action_list.remove_action(self.exportBibleItem, UiStrings.Export) + #action_list.remove_action(self.exportBibleItem, UiStrings().Export) self.exportBibleItem.setVisible(False) def addImportMenuItem(self, import_menu): @@ -146,4 +146,4 @@ class BiblePlugin(Plugin): u'service': translate('BiblesPlugin', 'Add the selected Bible to the service') } - self.setPluginUiTextStrings(tooltips) + self.setPluginUiTextStrings(tooltips) \ No newline at end of file diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index 35cd17c4b..439724b66 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -377,7 +377,7 @@ class BibleImportForm(OpenLPWizard): self.formatComboBox.setItemText(BibleFormat.OpenSong, WizardStrings.OS) self.formatComboBox.setItemText(BibleFormat.WebDownload, translate('BiblesPlugin.ImportWizardForm', 'Web Download')) - self.formatComboBox.setItemText(BibleFormat.OpenLP1, UiStrings.OLPV1) + self.formatComboBox.setItemText(BibleFormat.OpenLP1, UiStrings().OLPV1) self.openlp1FileLabel.setText( translate('BiblesPlugin.ImportWizardForm', 'Bible file:')) self.osisFileLabel.setText( @@ -451,13 +451,13 @@ class BibleImportForm(OpenLPWizard): elif self.currentPage() == self.selectPage: if self.field(u'source_format').toInt()[0] == BibleFormat.OSIS: if not self.field(u'osis_location').toString(): - critical_error_message_box(UiStrings.NFSs, + critical_error_message_box(UiStrings().NFSs, WizardStrings.YouSpecifyFile % WizardStrings.OSIS) self.osisFileEdit.setFocus() return False elif self.field(u'source_format').toInt()[0] == BibleFormat.CSV: if not self.field(u'csv_testamentsfile').toString(): - answer = critical_error_message_box(UiStrings.NFSs, + answer = critical_error_message_box(UiStrings().NFSs, translate('BiblesPlugin.ImportWizardForm', 'You have not specified a testaments file. Do you ' 'want to proceed with the import?'), question=True) @@ -465,14 +465,14 @@ class BibleImportForm(OpenLPWizard): self.csvTestamentsEdit.setFocus() return False if not self.field(u'csv_booksfile').toString(): - critical_error_message_box(UiStrings.NFSs, + critical_error_message_box(UiStrings().NFSs, translate('BiblesPlugin.ImportWizardForm', 'You need to specify a file with books of ' 'the Bible to use in the import.')) self.csvBooksEdit.setFocus() return False elif not self.field(u'csv_versefile').toString(): - critical_error_message_box(UiStrings.NFSs, + critical_error_message_box(UiStrings().NFSs, translate('BiblesPlugin.ImportWizardForm', 'You need to specify a file of Bible ' 'verses to import.')) @@ -481,14 +481,14 @@ class BibleImportForm(OpenLPWizard): elif self.field(u'source_format').toInt()[0] == \ BibleFormat.OpenSong: if not self.field(u'opensong_file').toString(): - critical_error_message_box(UiStrings.NFSs, + critical_error_message_box(UiStrings().NFSs, WizardStrings.YouSpecifyFile % WizardStrings.OS) self.openSongFileEdit.setFocus() return False elif self.field(u'source_format').toInt()[0] == BibleFormat.OpenLP1: if not self.field(u'openlp1_location').toString(): - critical_error_message_box(UiStrings.NFSs, - WizardStrings.YouSpecifyFile % UiStrings.OLPV1) + critical_error_message_box(UiStrings().NFSs, + WizardStrings.YouSpecifyFile % UiStrings().OLPV1) self.openlp1FileEdit.setFocus() return False return True @@ -497,13 +497,13 @@ class BibleImportForm(OpenLPWizard): license_copyright = \ unicode(self.field(u'license_copyright').toString()) if not license_version: - critical_error_message_box(UiStrings.EmptyField, + critical_error_message_box(UiStrings().EmptyField, translate('BiblesPlugin.ImportWizardForm', 'You need to specify a version name for your Bible.')) self.versionNameEdit.setFocus() return False elif not license_copyright: - critical_error_message_box(UiStrings.EmptyField, + critical_error_message_box(UiStrings().EmptyField, translate('BiblesPlugin.ImportWizardForm', 'You need to set a copyright for your Bible. ' 'Bibles in the Public Domain need to be marked as such.')) @@ -576,7 +576,7 @@ class BibleImportForm(OpenLPWizard): """ Show the file open dialog for the openlp.org 1.x file. """ - self.getFileName(WizardStrings.OpenTypeFile % UiStrings.OLPV1, + self.getFileName(WizardStrings.OpenTypeFile % UiStrings().OLPV1, self.openlp1FileEdit, u'%s (*.bible)' % translate('BiblesPlugin.ImportWizardForm', 'openlp.org 1.x Bible Files')) @@ -765,4 +765,4 @@ class BibleImportForm(OpenLPWizard): self.progressLabel.setText(translate( 'BiblesPlugin.ImportWizardForm', 'Your Bible import failed.')) del self.manager.db_cache[importer.name] - delete_database(self.plugin.settingsSection, importer.file) + delete_database(self.plugin.settingsSection, importer.file) \ No newline at end of file diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index b013c402a..33c2c1f9f 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -118,16 +118,16 @@ class BiblesTab(SettingsTab): self.newChaptersCheckBox.setText( translate('BiblesPlugin.BiblesTab', 'Only show new chapter numbers')) - self.layoutStyleLabel.setText(UiStrings.LayoutStyle) - self.displayStyleLabel.setText(UiStrings.DisplayStyle) + self.layoutStyleLabel.setText(UiStrings().LayoutStyle) + self.displayStyleLabel.setText(UiStrings().DisplayStyle) self.bibleThemeLabel.setText( translate('BiblesPlugin.BiblesTab', 'Bible theme:')) self.layoutStyleComboBox.setItemText(LayoutStyle.VersePerSlide, - UiStrings.VersePerSlide) + UiStrings().VersePerSlide) self.layoutStyleComboBox.setItemText(LayoutStyle.VersePerLine, - UiStrings.VersePerLine) + UiStrings().VersePerLine) self.layoutStyleComboBox.setItemText(LayoutStyle.Continuous, - UiStrings.Continuous) + UiStrings().Continuous) self.displayStyleComboBox.setItemText(DisplayStyle.NoBrackets, translate('BiblesPlugin.BiblesTab', 'No Brackets')) self.displayStyleComboBox.setItemText(DisplayStyle.Round, @@ -207,4 +207,4 @@ class BiblesTab(SettingsTab): self.bibleThemeComboBox.addItem(u'') for theme in theme_list: self.bibleThemeComboBox.addItem(theme) - find_and_set_in_combo_box(self.bibleThemeComboBox, self.bible_theme) + find_and_set_in_combo_box(self.bibleThemeComboBox, self.bible_theme) \ No newline at end of file diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index bb72b9ff2..e0e06b02f 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -193,7 +193,7 @@ class BibleMediaItem(MediaManagerItem): self.advancedSearchButtonLayout.addWidget(self.advancedSearchButton) self.advancedLayout.addLayout( self.advancedSearchButtonLayout, 7, 0, 1, 3) - self.searchTabWidget.addTab(self.advancedTab, UiStrings.Advanced) + self.searchTabWidget.addTab(self.advancedTab, UiStrings().Advanced) # Add the search tab widget to the page layout. self.pageLayout.addWidget(self.searchTabWidget) # Combo Boxes @@ -242,15 +242,15 @@ class BibleMediaItem(MediaManagerItem): def retranslateUi(self): log.debug(u'retranslateUi') - self.quickVersionLabel.setText(u'%s:' % UiStrings.Version) + self.quickVersionLabel.setText(u'%s:' % UiStrings().Version) self.quickSecondLabel.setText( translate('BiblesPlugin.MediaItem', 'Second:')) self.quickSearchLabel.setText( translate('BiblesPlugin.MediaItem', 'Find:')) - self.quickSearchButton.setText(UiStrings.Search) + self.quickSearchButton.setText(UiStrings().Search) self.quickClearLabel.setText( translate('BiblesPlugin.MediaItem', 'Results:')) - self.advancedVersionLabel.setText(u'%s:' % UiStrings.Version) + self.advancedVersionLabel.setText(u'%s:' % UiStrings().Version) self.advancedSecondLabel.setText( translate('BiblesPlugin.MediaItem', 'Second:')) self.advancedBookLabel.setText( @@ -265,7 +265,7 @@ class BibleMediaItem(MediaManagerItem): translate('BiblesPlugin.MediaItem', 'To:')) self.advancedClearLabel.setText( translate('BiblesPlugin.MediaItem', 'Results:')) - self.advancedSearchButton.setText(UiStrings.Search) + self.advancedSearchButton.setText(UiStrings().Search) self.quickClearComboBox.addItem( translate('BiblesPlugin.MediaItem', 'Clear')) self.quickClearComboBox.addItem( @@ -274,13 +274,13 @@ class BibleMediaItem(MediaManagerItem): translate('BiblesPlugin.MediaItem', 'Clear')) self.advancedClearComboBox.addItem( translate('BiblesPlugin.MediaItem', 'Keep')) - self.quickLayoutLabel.setText(UiStrings.LayoutStyle) + self.quickLayoutLabel.setText(UiStrings().LayoutStyle) self.quickLayoutComboBox.setItemText(LayoutStyle.VersePerSlide, - UiStrings.VersePerSlide) + UiStrings().VersePerSlide) self.quickLayoutComboBox.setItemText(LayoutStyle.VersePerLine, - UiStrings.VersePerLine) + UiStrings().VersePerLine) self.quickLayoutComboBox.setItemText(LayoutStyle.Continuous, - UiStrings.Continuous) + UiStrings().Continuous) def initialise(self): log.debug(u'bible manager initialise') @@ -849,4 +849,4 @@ class BibleMediaItem(MediaManagerItem): self.settings.layout_style) QtCore.QSettings().setValue( self.settingsSection + u'/verse layout style', - QtCore.QVariant(self.settings.layout_style)) + QtCore.QVariant(self.settings.layout_style)) \ No newline at end of file diff --git a/openlp/plugins/custom/forms/editcustomdialog.py b/openlp/plugins/custom/forms/editcustomdialog.py index 1ea0413ff..7a6c1f07b 100644 --- a/openlp/plugins/custom/forms/editcustomdialog.py +++ b/openlp/plugins/custom/forms/editcustomdialog.py @@ -107,11 +107,11 @@ class Ui_CustomEditDialog(object): translate('CustomPlugin.EditCustomForm', 'Edit Custom Slides')) self.titleLabel.setText( translate('CustomPlugin.EditCustomForm', '&Title:')) - self.addButton.setText(UiStrings.Add) + self.addButton.setText(UiStrings().Add) self.addButton.setToolTip( translate('CustomPlugin.EditCustomForm', 'Add a new slide at ' 'bottom.')) - self.editButton.setText(UiStrings.Edit) + self.editButton.setText(UiStrings().Edit) self.editButton.setToolTip( translate('CustomPlugin.EditCustomForm', 'Edit the selected ' 'slide.')) @@ -124,4 +124,4 @@ class Ui_CustomEditDialog(object): translate('CustomPlugin.EditCustomForm', 'The&me:')) self.creditLabel.setText( translate('CustomPlugin.EditCustomForm', '&Credits:')) - self.previewButton.setText(UiStrings.SaveAndPreview) + self.previewButton.setText(UiStrings().SaveAndPreview) \ No newline at end of file diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index b045c58a4..b508104c4 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -110,7 +110,7 @@ class CustomMediaItem(MediaManagerItem): """ Edit a custom item """ - if check_item_selected(self.listView, UiStrings.SelectEdit): + if check_item_selected(self.listView, UiStrings().SelectEdit): item = self.listView.currentItem() item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0] self.parent.edit_custom_form.loadCustom(item_id, False) @@ -121,7 +121,7 @@ class CustomMediaItem(MediaManagerItem): """ Remove a custom item from the list and database """ - if check_item_selected(self.listView, UiStrings.SelectDelete): + if check_item_selected(self.listView, UiStrings().SelectDelete): row_list = [item.row() for item in self.listView.selectedIndexes()] row_list.sort(reverse=True) id_list = [(item.data(QtCore.Qt.UserRole)).toInt()[0] @@ -160,4 +160,4 @@ class CustomMediaItem(MediaManagerItem): else: raw_footer.append(u'') service_item.raw_footer = raw_footer - return True + return True \ No newline at end of file diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index c29adc3b1..298e701e3 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -55,11 +55,11 @@ class ImageMediaItem(MediaManagerItem): 'Select Image(s)') file_formats = get_images_filter() self.onNewFileMasks = u'%s;;%s (*.*) (*)' % (file_formats, - UiStrings.AllFiles) - self.replaceAction.setText(UiStrings.ReplaceBG) - self.replaceAction.setToolTip(UiStrings.ReplaceLiveBG) - self.resetAction.setText(UiStrings.ResetBG) - self.resetAction.setToolTip(UiStrings.ResetLiveBG) + UiStrings().AllFiles) + self.replaceAction.setText(UiStrings().ReplaceBG) + self.replaceAction.setToolTip(UiStrings().ReplaceLiveBG) + self.resetAction.setText(UiStrings().ResetBG) + self.resetAction.setToolTip(UiStrings().ResetLiveBG) def requiredIcons(self): MediaManagerItem.requiredIcons(self) @@ -198,7 +198,7 @@ class ImageMediaItem(MediaManagerItem): self.parent.liveController.display.directImage(name, filename) self.resetAction.setVisible(True) else: - critical_error_message_box(UiStrings.LiveBGError, + critical_error_message_box(UiStrings().LiveBGError, unicode(translate('ImagePlugin.MediaItem', 'There was a problem replacing your background, ' - 'the image file "%s" no longer exists.')) % filename) + 'the image file "%s" no longer exists.')) % filename) \ No newline at end of file diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 7612ab7c9..559af11e2 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -60,11 +60,11 @@ class MediaMediaItem(MediaManagerItem): self.onNewFileMasks = unicode(translate('MediaPlugin.MediaItem', 'Videos (%s);;Audio (%s);;%s (*)')) % ( u' '.join(self.parent.video_extensions_list), - u' '.join(self.parent.audio_extensions_list), UiStrings.AllFiles) - self.replaceAction.setText(UiStrings.ReplaceBG) - self.replaceAction.setToolTip(UiStrings.ReplaceLiveBG) - self.resetAction.setText(UiStrings.ResetBG) - self.resetAction.setToolTip(UiStrings.ResetLiveBG) + u' '.join(self.parent.audio_extensions_list), UiStrings().AllFiles) + self.replaceAction.setText(UiStrings().ReplaceBG) + self.replaceAction.setToolTip(UiStrings().ReplaceLiveBG) + self.resetAction.setText(UiStrings().ResetBG) + self.resetAction.setToolTip(UiStrings().ResetLiveBG) def requiredIcons(self): MediaManagerItem.requiredIcons(self) @@ -111,7 +111,7 @@ class MediaMediaItem(MediaManagerItem): self.parent.liveController.display.video(filename, 0, True) self.resetAction.setVisible(True) else: - critical_error_message_box(UiStrings.LiveBGError, + critical_error_message_box(UiStrings().LiveBGError, unicode(translate('MediaPlugin.MediaItem', 'There was a problem replacing your background, ' 'the media file "%s" no longer exists.')) % filename) @@ -209,4 +209,4 @@ class MediaMediaItem(MediaManagerItem): img = QtGui.QPixmap(u':/media/media_video.png').toImage() item_name.setIcon(build_icon(img)) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file)) - self.listView.addItem(item_name) + self.listView.addItem(item_name) \ No newline at end of file diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 6009ff906..74ff3fea8 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -203,7 +203,7 @@ class PresentationMediaItem(MediaManagerItem): """ Remove a presentation item from the list """ - if check_item_selected(self.listView, UiStrings.SelectDelete): + if check_item_selected(self.listView, UiStrings().SelectDelete): items = self.listView.selectedIndexes() row_list = [item.row() for item in items] row_list.sort(reverse=True) @@ -296,4 +296,4 @@ class PresentationMediaItem(MediaManagerItem): if self.controllers[controller].enabled(): if filetype in self.controllers[controller].alsosupports: return controller - return None + return None \ No newline at end of file diff --git a/openlp/plugins/presentations/lib/presentationtab.py b/openlp/plugins/presentations/lib/presentationtab.py index 8e3a98031..bba2b469e 100644 --- a/openlp/plugins/presentations/lib/presentationtab.py +++ b/openlp/plugins/presentations/lib/presentationtab.py @@ -86,7 +86,7 @@ class PresentationTab(SettingsTab): checkbox.setText( unicode(translate('PresentationPlugin.PresentationTab', '%s (unavailable)')) % controller.name) - self.AdvancedGroupBox.setTitle(UiStrings.Advanced) + self.AdvancedGroupBox.setTitle(UiStrings().Advanced) self.OverrideAppCheckBox.setText( translate('PresentationPlugin.PresentationTab', 'Allow presentation application to be overriden')) @@ -131,4 +131,4 @@ class PresentationTab(SettingsTab): QtCore.QVariant(self.OverrideAppCheckBox.checkState())) changed = True if changed: - Receiver.send_message(u'mediaitem_presentation_rebuild') + Receiver.send_message(u'mediaitem_presentation_rebuild') \ No newline at end of file diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index a9bd132cd..749d5184d 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -260,11 +260,11 @@ class Ui_EditSongDialog(object): translate('SongsPlugin.EditSongForm', '&Lyrics:')) self.verseOrderLabel.setText( translate('SongsPlugin.EditSongForm', '&Verse order:')) - self.verseAddButton.setText(UiStrings.Add) - self.verseEditButton.setText(UiStrings.Edit) + self.verseAddButton.setText(UiStrings().Add) + self.verseEditButton.setText(UiStrings().Edit) self.verseEditAllButton.setText( translate('SongsPlugin.EditSongForm', 'Ed&it All')) - self.verseDeleteButton.setText(UiStrings.Delete) + self.verseDeleteButton.setText(UiStrings().Delete) self.songTabWidget.setTabText( self.songTabWidget.indexOf(self.lyricsTab), translate('SongsPlugin.EditSongForm', 'Title && Lyrics')) @@ -289,13 +289,13 @@ class Ui_EditSongDialog(object): self.songTabWidget.indexOf(self.authorsTab), translate('SongsPlugin.EditSongForm', 'Authors, Topics && Song Book')) - self.themeGroupBox.setTitle(UiStrings.Theme) + self.themeGroupBox.setTitle(UiStrings().Theme) self.themeAddButton.setText( translate('SongsPlugin.EditSongForm', 'New &Theme')) self.rightsGroupBox.setTitle( translate('SongsPlugin.EditSongForm', 'Copyright Information')) self.copyrightInsertButton.setText(SongStrings.CopyrightSymbol) - self.CCLILabel.setText(UiStrings.CCLINumberLabel) + self.CCLILabel.setText(UiStrings().CCLINumberLabel) self.commentsGroupBox.setTitle( translate('SongsPlugin.EditSongForm', 'Comments')) self.songTabWidget.setTabText( @@ -313,4 +313,4 @@ def editSongDialogComboBox(parent, name): comboBox.setEditable(True) comboBox.setInsertPolicy(QtGui.QComboBox.NoInsert) comboBox.setObjectName(name) - return comboBox + return comboBox \ No newline at end of file diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 85294d92b..0b9c9316f 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -96,7 +96,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): QtCore.SIGNAL(u'theme_update_list'), self.loadThemes) self.previewButton = QtGui.QPushButton() self.previewButton.setObjectName(u'previewButton') - self.previewButton.setText(UiStrings.SaveAndPreview) + self.previewButton.setText(UiStrings().SaveAndPreview) self.buttonBox.addButton( self.previewButton, QtGui.QDialogButtonBox.ActionRole) QtCore.QObject.connect(self.buttonBox, @@ -355,7 +355,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.__addAuthorToList(author) self.authorsComboBox.setCurrentIndex(0) else: - QtGui.QMessageBox.warning(self, UiStrings.NISs, + QtGui.QMessageBox.warning(self, UiStrings().NISs, translate('SongsPlugin.EditSongForm', 'You have not selected ' 'a valid author. Either select an author from the list, ' 'or type in a new author and click the "Add Author to ' @@ -414,7 +414,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.topicsListView.addItem(topic_item) self.topicsComboBox.setCurrentIndex(0) else: - QtGui.QMessageBox.warning(self, UiStrings.NISs, + QtGui.QMessageBox.warning(self, UiStrings().NISs, translate('SongsPlugin.EditSongForm', 'You have not selected ' 'a valid topic. Either select a topic from the list, or ' 'type in a new topic and click the "Add Topic to Song" ' diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index d71321b98..745ee3f67 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -175,7 +175,7 @@ class SongExportForm(OpenLPWizard): self.availableSongsPage.setSubTitle( translate('SongsPlugin.ExportWizardForm', 'Check the songs you want to export.')) - self.searchLabel.setText(u'%s:' % UiStrings.Search) + self.searchLabel.setText(u'%s:' % UiStrings().Search) self.uncheckButton.setText( translate('SongsPlugin.ExportWizardForm', 'Uncheck All')) self.checkButton.setText( @@ -207,7 +207,7 @@ class SongExportForm(OpenLPWizard): self.availableListWidget) if item.checkState() ] if not items: - critical_error_message_box(UiStrings.NISp, + critical_error_message_box(UiStrings().NISp, translate('SongsPlugin.ExportWizardForm', 'You need to add at least one Song to export.')) return False @@ -360,4 +360,4 @@ class SongExportForm(OpenLPWizard): SettingsManager.get_last_dir(self.plugin.settingsSection, 1), options=QtGui.QFileDialog.ShowDirsOnly)) SettingsManager.set_last_dir(self.plugin.settingsSection, path, 1) - self.directoryLineEdit.setText(path) + self.directoryLineEdit.setText(path) \ No newline at end of file diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 468d2f341..dde8826e0 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -235,8 +235,8 @@ class SongImportForm(OpenLPWizard): self.sourcePage.setTitle(WizardStrings.ImportSelect) self.sourcePage.setSubTitle(WizardStrings.ImportSelectLong) self.formatLabel.setText(WizardStrings.FormatLabel) - self.formatComboBox.setItemText(SongFormat.OpenLP2, UiStrings.OLPV2) - self.formatComboBox.setItemText(SongFormat.OpenLP1, UiStrings.OLPV1) + self.formatComboBox.setItemText(SongFormat.OpenLP2, UiStrings().OLPV2) + self.formatComboBox.setItemText(SongFormat.OpenLP1, UiStrings().OLPV1) self.formatComboBox.setItemText( SongFormat.OpenLyrics, WizardStrings.OL) self.formatComboBox.setItemText(SongFormat.OpenSong, WizardStrings.OS) @@ -261,10 +261,10 @@ class SongImportForm(OpenLPWizard): # self.formatComboBox.setItemText(SongFormat.CSV, WizardStrings.CSV) self.openLP2FilenameLabel.setText( translate('SongsPlugin.ImportWizardForm', 'Filename:')) - self.openLP2BrowseButton.setText(UiStrings.Browse) + self.openLP2BrowseButton.setText(UiStrings().Browse) self.openLP1FilenameLabel.setText( translate('SongsPlugin.ImportWizardForm', 'Filename:')) - self.openLP1BrowseButton.setText(UiStrings.Browse) + self.openLP1BrowseButton.setText(UiStrings().Browse) self.openLP1DisabledLabel.setText(WizardStrings.NoSqlite) self.openLyricsAddButton.setText( translate('SongsPlugin.ImportWizardForm', 'Add Files...')) @@ -305,10 +305,10 @@ class SongImportForm(OpenLPWizard): 'find OpenOffice.org on your computer.')) self.easiSlidesFilenameLabel.setText( translate('SongsPlugin.ImportWizardForm', 'Filename:')) - self.easiSlidesBrowseButton.setText(UiStrings.Browse) + self.easiSlidesBrowseButton.setText(UiStrings().Browse) self.ewFilenameLabel.setText( translate('SongsPlugin.ImportWizardForm', 'Filename:')) - self.ewBrowseButton.setText(UiStrings.Browse) + self.ewBrowseButton.setText(UiStrings().Browse) self.songBeamerAddButton.setText( translate('SongsPlugin.ImportWizardForm', 'Add Files...')) self.songBeamerRemoveButton.setText( @@ -323,7 +323,7 @@ class SongImportForm(OpenLPWizard): translate('SongsPlugin.ImportWizardForm', 'Remove File(s)')) # self.csvFilenameLabel.setText( # translate('SongsPlugin.ImportWizardForm', 'Filename:')) -# self.csvBrowseButton.setText(UiStrings.Browse) +# self.csvBrowseButton.setText(UiStrings().Browse) self.progressPage.setTitle(WizardStrings.Importing) self.progressPage.setSubTitle( translate('SongsPlugin.ImportWizardForm', @@ -346,49 +346,49 @@ class SongImportForm(OpenLPWizard): source_format = self.formatComboBox.currentIndex() if source_format == SongFormat.OpenLP2: if self.openLP2FilenameEdit.text().isEmpty(): - critical_error_message_box(UiStrings.NFSs, - WizardStrings.YouSpecifyFile % UiStrings.OLPV2) + critical_error_message_box(UiStrings().NFSs, + WizardStrings.YouSpecifyFile % UiStrings().OLPV2) self.openLP2BrowseButton.setFocus() return False elif source_format == SongFormat.OpenLP1: if self.openLP1FilenameEdit.text().isEmpty(): - critical_error_message_box(UiStrings.NFSs, - WizardStrings.YouSpecifyFile % UiStrings.OLPV1) + critical_error_message_box(UiStrings().NFSs, + WizardStrings.YouSpecifyFile % UiStrings().OLPV1) self.openLP1BrowseButton.setFocus() return False elif source_format == SongFormat.OpenLyrics: if self.openLyricsFileListWidget.count() == 0: - critical_error_message_box(UiStrings.NFSp, + critical_error_message_box(UiStrings().NFSp, WizardStrings.YouSpecifyFile % WizardStrings.OL) self.openLyricsAddButton.setFocus() return False elif source_format == SongFormat.OpenSong: if self.openSongFileListWidget.count() == 0: - critical_error_message_box(UiStrings.NFSp, + critical_error_message_box(UiStrings().NFSp, WizardStrings.YouSpecifyFile % WizardStrings.OS) self.openSongAddButton.setFocus() return False elif source_format == SongFormat.WordsOfWorship: if self.wordsOfWorshipFileListWidget.count() == 0: - critical_error_message_box(UiStrings.NFSp, + critical_error_message_box(UiStrings().NFSp, WizardStrings.YouSpecifyFile % WizardStrings.WoW) self.wordsOfWorshipAddButton.setFocus() return False elif source_format == SongFormat.CCLI: if self.ccliFileListWidget.count() == 0: - critical_error_message_box(UiStrings.NFSp, + critical_error_message_box(UiStrings().NFSp, WizardStrings.YouSpecifyFile % WizardStrings.CCLI) self.ccliAddButton.setFocus() return False elif source_format == SongFormat.SongsOfFellowship: if self.songsOfFellowshipFileListWidget.count() == 0: - critical_error_message_box(UiStrings.NFSp, + critical_error_message_box(UiStrings().NFSp, WizardStrings.YouSpecifyFile % WizardStrings.SoF) self.songsOfFellowshipAddButton.setFocus() return False elif source_format == SongFormat.Generic: if self.genericFileListWidget.count() == 0: - critical_error_message_box(UiStrings.NFSp, + critical_error_message_box(UiStrings().NFSp, translate('SongsPlugin.ImportWizardForm', 'You need to specify at least one document or ' 'presentation file to import from.')) @@ -396,31 +396,31 @@ class SongImportForm(OpenLPWizard): return False elif source_format == SongFormat.EasiSlides: if self.easiSlidesFilenameEdit.text().isEmpty(): - critical_error_message_box(UiStrings.NFSp, + critical_error_message_box(UiStrings().NFSp, WizardStrings.YouSpecifyFile % WizardStrings.ES) self.easiSlidesBrowseButton.setFocus() return False elif source_format == SongFormat.EasyWorship: if self.ewFilenameEdit.text().isEmpty(): - critical_error_message_box(UiStrings.NFSs, + critical_error_message_box(UiStrings().NFSs, WizardStrings.YouSpecifyFile % WizardStrings.EW) self.ewBrowseButton.setFocus() return False elif source_format == SongFormat.SongBeamer: if self.songBeamerFileListWidget.count() == 0: - critical_error_message_box(UiStrings.NFSp, + critical_error_message_box(UiStrings().NFSp, WizardStrings.YouSpecifyFile % WizardStrings.SB) self.songBeamerAddButton.setFocus() return False elif source_format == SongFormat.SongShowPlus: if self.songShowPlusFileListWidget.count() == 0: - critical_error_message_box(UiStrings.NFSp, + critical_error_message_box(UiStrings().NFSp, WizardStrings.YouSpecifyFile % WizardStrings.SSP) self.wordsOfWorshipAddButton.setFocus() return False elif source_format == SongFormat.FoilPresenter: if self.foilPresenterFileListWidget.count() == 0: - critical_error_message_box(UiStrings.NFSp, + critical_error_message_box(UiStrings().NFSp, WizardStrings.YouSpecifyFile % WizardStrings.FP) self.foilPresenterAddButton.setFocus() return False @@ -446,7 +446,7 @@ class SongImportForm(OpenLPWizard): """ if filters: filters += u';;' - filters += u'%s (*)' % UiStrings.AllFiles + filters += u'%s (*)' % UiStrings().AllFiles filenames = QtGui.QFileDialog.getOpenFileNames(self, title, SettingsManager.get_last_dir(self.plugin.settingsSection, 1), filters) @@ -476,7 +476,7 @@ class SongImportForm(OpenLPWizard): """ Get OpenLP v2 song database file """ - self.getFileName(WizardStrings.OpenTypeFile % UiStrings.OLPV2, + self.getFileName(WizardStrings.OpenTypeFile % UiStrings().OLPV2, self.openLP2FilenameEdit, u'%s (*.sqlite)' % (translate('SongsPlugin.ImportWizardForm', 'OpenLP 2.0 Databases')) @@ -486,7 +486,7 @@ class SongImportForm(OpenLPWizard): """ Get OpenLP v1 song database file """ - self.getFileName(WizardStrings.OpenTypeFile % UiStrings.OLPV1, + self.getFileName(WizardStrings.OpenTypeFile % UiStrings().OLPV1, self.openLP1FilenameEdit, u'%s (*.olp)' % translate('SongsPlugin.ImportWizardForm', 'openlp.org v1.x Databases') @@ -836,4 +836,4 @@ class SongImportForm(OpenLPWizard): setattr(self, prefix + u'DisabledLayout', disabledLayout) setattr(self, prefix + u'DisabledLabel', disabledLabel) setattr(self, prefix + u'ImportWidget', importWidget) - return importWidget + return importWidget \ No newline at end of file diff --git a/openlp/plugins/songs/forms/songmaintenancedialog.py b/openlp/plugins/songs/forms/songmaintenancedialog.py index 9d4b7da91..3d65783ac 100644 --- a/openlp/plugins/songs/forms/songmaintenancedialog.py +++ b/openlp/plugins/songs/forms/songmaintenancedialog.py @@ -149,17 +149,17 @@ class Ui_SongMaintenanceDialog(object): self.listItemAuthors.setText(SongStrings.Authors) self.listItemTopics.setText(SongStrings.Topics) self.listItemBooks.setText(SongStrings.SongBooks) - self.authorsAddButton.setText(UiStrings.Add) - self.authorsEditButton.setText(UiStrings.Edit) - self.authorsDeleteButton.setText(UiStrings.Delete) - self.topicsAddButton.setText(UiStrings.Add) - self.topicsEditButton.setText(UiStrings.Edit) - self.topicsDeleteButton.setText(UiStrings.Delete) - self.booksAddButton.setText(UiStrings.Add) - self.booksEditButton.setText(UiStrings.Edit) - self.booksDeleteButton.setText(UiStrings.Delete) + self.authorsAddButton.setText(UiStrings().Add) + self.authorsEditButton.setText(UiStrings().Edit) + self.authorsDeleteButton.setText(UiStrings().Delete) + self.topicsAddButton.setText(UiStrings().Add) + self.topicsEditButton.setText(UiStrings().Edit) + self.topicsDeleteButton.setText(UiStrings().Delete) + self.booksAddButton.setText(UiStrings().Add) + self.booksEditButton.setText(UiStrings().Edit) + self.booksDeleteButton.setText(UiStrings().Delete) typeListWidth = max(self.fontMetrics().width(SongStrings.Authors), self.fontMetrics().width(SongStrings.Topics), self.fontMetrics().width(SongStrings.SongBooks)) self.typeListWidget.setFixedWidth(typeListWidth + - self.typeListWidget.iconSize().width() + 32) + self.typeListWidget.iconSize().width() + 32) \ No newline at end of file diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 0a1effcf3..48e614eeb 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -115,7 +115,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): else: critical_error_message_box(dlg_title, err_text) else: - critical_error_message_box(dlg_title, UiStrings.NISs) + critical_error_message_box(dlg_title, UiStrings().NISs) def resetAuthors(self): """ @@ -503,4 +503,4 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): editButton.setEnabled(False) else: deleteButton.setEnabled(True) - editButton.setEnabled(True) + editButton.setEnabled(True) \ No newline at end of file diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 5890fc76d..8245adfbc 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -137,8 +137,8 @@ class SongMediaItem(MediaManagerItem): QtCore.QVariant(u'True')).toBool() def retranslateUi(self): - self.searchTextLabel.setText(u'%s:' % UiStrings.Search) - self.searchTextButton.setText(UiStrings.Search) + self.searchTextLabel.setText(u'%s:' % UiStrings().Search) + self.searchTextButton.setText(UiStrings().Search) self.maintenanceAction.setText(SongStrings.SongMaintenance) self.maintenanceAction.setToolTip(translate('SongsPlugin.MediaItem', 'Maintain the lists of authors, topics and books')) @@ -153,7 +153,7 @@ class SongMediaItem(MediaManagerItem): translate('SongsPlugin.MediaItem', 'Lyrics')), (SongSearch.Authors, u':/songs/song_search_author.png', SongStrings.Authors), - (SongSearch.Themes, u':/slides/slide_theme.png', UiStrings.Themes) + (SongSearch.Themes, u':/slides/slide_theme.png', UiStrings().Themes) ]) self.searchTextEdit.setCurrentSearchType(QtCore.QSettings().value( u'%s/last search type' % self.settingsSection, @@ -312,7 +312,7 @@ class SongMediaItem(MediaManagerItem): Edit a song """ log.debug(u'onEditClick') - if check_item_selected(self.listView, UiStrings.SelectEdit): + if check_item_selected(self.listView, UiStrings().SelectEdit): self.editItem = self.listView.currentItem() item_id = (self.editItem.data(QtCore.Qt.UserRole)).toInt()[0] self.edit_song_form.loadSong(item_id, False) @@ -323,7 +323,7 @@ class SongMediaItem(MediaManagerItem): """ Remove a song from the list and database """ - if check_item_selected(self.listView, UiStrings.SelectDelete): + if check_item_selected(self.listView, UiStrings().SelectDelete): items = self.listView.selectedIndexes() if QtGui.QMessageBox.question(self, translate('SongsPlugin.MediaItem', 'Delete Song(s)?'), @@ -472,4 +472,4 @@ class SongMediaItem(MediaManagerItem): Locale aware collation of song titles """ return locale.strcoll(unicode(song_1.title.lower()), - unicode(song_2.title.lower())) + unicode(song_2.title.lower())) \ No newline at end of file diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 4fc098949..0cace4977 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -67,9 +67,9 @@ class SongsPlugin(Plugin): Plugin.initialise(self) self.toolsReindexItem.setVisible(True) action_list = ActionList.get_instance() - action_list.add_action(self.SongImportItem, UiStrings.Import) - action_list.add_action(self.SongExportItem, UiStrings.Export) - action_list.add_action(self.toolsReindexItem, UiStrings.Tools) + action_list.add_action(self.SongImportItem, UiStrings().Import) + action_list.add_action(self.SongExportItem, UiStrings().Export) + action_list.add_action(self.toolsReindexItem, UiStrings().Tools) def addImportMenuItem(self, import_menu): """ @@ -139,7 +139,7 @@ class SongsPlugin(Plugin): if maxSongs == 0: return progressDialog = QtGui.QProgressDialog( - translate('SongsPlugin', 'Reindexing songs...'), UiStrings.Cancel, + translate('SongsPlugin', 'Reindexing songs...'), UiStrings().Cancel, 0, maxSongs, self.formparent) progressDialog.setWindowModality(QtCore.Qt.WindowModal) songs = self.manager.get_all_objects(Song) @@ -258,7 +258,7 @@ class SongsPlugin(Plugin): self.manager.finalise() self.toolsReindexItem.setVisible(False) action_list = ActionList.get_instance() - action_list.remove_action(self.SongImportItem, UiStrings.Import) - action_list.remove_action(self.SongExportItem, UiStrings.Export) - action_list.remove_action(self.toolsReindexItem, UiStrings.Tools) - Plugin.finalise(self) + action_list.remove_action(self.SongImportItem, UiStrings().Import) + action_list.remove_action(self.SongExportItem, UiStrings().Export) + action_list.remove_action(self.toolsReindexItem, UiStrings().Tools) + Plugin.finalise(self) \ No newline at end of file From 7fdd4bb1c1828e1c1cc70f0c7de2a9ed8bf720f8 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Fri, 15 Apr 2011 23:55:11 +0200 Subject: [PATCH 87/98] Line length cleanups. --- openlp/core/lib/ui.py | 5 +++-- openlp/core/ui/servicemanager.py | 11 +++++++---- openlp/core/ui/starttimeform.py | 5 +++-- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 013231b8c..a0686416c 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -175,7 +175,8 @@ def create_accept_reject_button_box(parent, okay=False): accept_button = QtGui.QDialogButtonBox.Save if okay: accept_button = QtGui.QDialogButtonBox.Ok - button_box.setStandardButtons(accept_button | QtGui.QDialogButtonBox.Cancel) + button_box.setStandardButtons( + accept_button | QtGui.QDialogButtonBox.Cancel) button_box.setObjectName(u'%sButtonBox' % parent) QtCore.QObject.connect(button_box, QtCore.SIGNAL(u'accepted()'), parent.accept) @@ -445,4 +446,4 @@ def find_and_set_in_combo_box(combo_box, value_to_find): if index == -1: # Not Found. index = 0 - combo_box.setCurrentIndex(index) \ No newline at end of file + combo_box.setCurrentIndex(index) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index d682f5b52..66cc61989 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -169,7 +169,8 @@ class ServiceManager(QtGui.QWidget): self.onServiceTop, shortcuts=[QtCore.Qt.Key_Home]) self.serviceManagerList.moveTop.setObjectName(u'moveTop') action_list = ActionList.get_instance() - action_list.add_category(UiStrings().Service, CategoryOrder.standardToolbar) + action_list.add_category( + UiStrings().Service, CategoryOrder.standardToolbar) action_list.add_action( self.serviceManagerList.moveTop, UiStrings().Service) self.serviceManagerList.moveUp = self.orderToolbar.addToolbarButton( @@ -179,7 +180,8 @@ class ServiceManager(QtGui.QWidget): 'Move item up one position in the service.'), self.onServiceUp, shortcuts=[QtCore.Qt.Key_PageUp]) self.serviceManagerList.moveUp.setObjectName(u'moveUp') - action_list.add_action(self.serviceManagerList.moveUp, UiStrings().Service) + action_list.add_action( + self.serviceManagerList.moveUp, UiStrings().Service) self.serviceManagerList.moveDown = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', 'Move &down'), u':/services/service_down.png', @@ -231,7 +233,8 @@ class ServiceManager(QtGui.QWidget): 'Expand all the service items.'), self.onExpandAll, shortcuts=[QtCore.Qt.Key_Plus]) self.serviceManagerList.expand.setObjectName(u'expand') - action_list.add_action(self.serviceManagerList.expand, UiStrings().Service) + action_list.add_action( + self.serviceManagerList.expand, UiStrings().Service) self.serviceManagerList.collapse = self.orderToolbar.addToolbarButton( translate('OpenLP.ServiceManager', '&Collapse all'), u':/services/service_collapse_all.png', @@ -1311,4 +1314,4 @@ class ServiceManager(QtGui.QWidget): Print a Service Order Sheet. """ settingDialog = PrintServiceForm(self.mainwindow, self) - settingDialog.exec_() \ No newline at end of file + settingDialog.exec_() diff --git a/openlp/core/ui/starttimeform.py b/openlp/core/ui/starttimeform.py index b460bbbd0..956b01a9d 100644 --- a/openlp/core/ui/starttimeform.py +++ b/openlp/core/ui/starttimeform.py @@ -53,7 +53,8 @@ class StartTimeForm(QtGui.QDialog, Ui_StartTimeDialog): self.hourFinishSpinBox.setValue(hours) self.minuteFinishSpinBox.setValue(minutes) self.secondFinishSpinBox.setValue(seconds) - self.hourFinishLabel.setText(u'%s%s' % (unicode(hour), UiStrings().Hours)) + self.hourFinishLabel.setText(u'%s%s' % (unicode(hour), + UiStrings().Hours)) self.minuteFinishLabel.setText(u'%s%s' % (unicode(minutes), UiStrings().Minutes)) self.secondFinishLabel.setText(u'%s%s' % @@ -90,4 +91,4 @@ class StartTimeForm(QtGui.QDialog, Ui_StartTimeDialog): seconds -= 3600 * hours minutes = seconds / 60 seconds -= 60 * minutes - return hours, minutes, seconds \ No newline at end of file + return hours, minutes, seconds From b46aa38ce93836662c75efae5e35294dc3c53aa9 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Sat, 16 Apr 2011 00:46:24 +0200 Subject: [PATCH 88/98] Add a bit of spacing around the edges of the settings form to make it feel a little less crowded. --- openlp/core/ui/settingsdialog.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/settingsdialog.py b/openlp/core/ui/settingsdialog.py index 485cb9c5f..50bcca4e2 100644 --- a/openlp/core/ui/settingsdialog.py +++ b/openlp/core/ui/settingsdialog.py @@ -37,7 +37,7 @@ class Ui_SettingsDialog(object): build_icon(u':/system/system_settings.png')) self.dialogLayout = QtGui.QGridLayout(settingsDialog) self.dialogLayout.setObjectName(u'dialogLayout') - self.dialogLayout.setMargin(0) + self.dialogLayout.setMargin(8) self.settingListWidget = QtGui.QListWidget(settingsDialog) self.settingListWidget.setUniformItemSizes(True) self.settingListWidget.setMinimumSize(QtCore.QSize(150, 0)) From bc3fca35fc70612d3992d08e65d37c7f5babc3a4 Mon Sep 17 00:00:00 2001 From: John Cegalis Date: Fri, 15 Apr 2011 20:50:55 -0400 Subject: [PATCH 89/98] added blank to --- documentation/manual/source/songs.rst | 29 +++++++++++++++------------ 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/documentation/manual/source/songs.rst b/documentation/manual/source/songs.rst index 23f054127..c718f812d 100644 --- a/documentation/manual/source/songs.rst +++ b/documentation/manual/source/songs.rst @@ -103,23 +103,25 @@ Creating or editing a song slide ================================ If you want to create a new song slide or, once you have a song imported, you -can edit and rearrange the Title & Lyrics, Author, Topics & Song Book, -assign a Theme, or edit *Copyright Info & Comments* as needed. This is done -through the `Song Editor`. To use the `Song Editor` to edit an existing song you -can click on a song in the `Media Manager` and then click the button to -:guilabel:`Edit the selected song` or right click a song from either the -`Media Manager` or from the `Service Manager` and click :guilabel:`Edit item`. -If you are adding a new song click :guilabel:`Add a new Song` in the -`Media Manager`. +want to edit and rearrange the Title & Lyrics, Author, Topics & Song Book, +assign a Theme, or edit Copyright Info & Comments, you will do this through the +`Song Editor`. + +**Edit:** To edit an existing song you can either click on a song in the +`Media Manager` and then click the button to :guilabel:`Edit the selected song` +or right click a song from either the `Media Manager` or additionally from the +`Service Manager` and click :guilabel:`Edit item`. If you are adding a new song +click :guilabel:`Add a new Song` in the `Media Manager`. .. image:: pics/song_edit_lyrics.png **Title:** This is where you would name your song or edit a song name. -**Alternate title:** You can add a name in this box that will bring up the song -in Titles search. **Example:** You could use an alternate title of "hymn" on all -your hymn song titles for grouping. When you search "hymn" it will show all the -hymns that have "hymn" for the Alternate title. +**Alternate title:**Alternate Title was for songs with two names +"Lord the Light" - "Shine Jesus Shine". You can also add a name in this box that +will bring up the song in Titles search. **Example:** You could use an alternate +title of "hymn" on all your hymn song titles for grouping. When you search "hymn" +it will show all the hymns that have "hymn" for the Alternate title. **Lyrics:** The *Lyrics* window shows all lyrics imported or added. On the left side of the lyrics you will see a capital letter followed by a number. A V1 @@ -135,7 +137,8 @@ displayed, with only a blank space in between each entry. The correct format wil look like this: V1 C1 V2 C1 V3 C1. If you forget to put a space in between the order, or if you do not have the corresponding verse number, OpenLP will politely tell you with a pop-up error message what is wrong so you can correct your -mistake and save it. +mistake and save it. Verse order is optional and if left blank the verses will +display in the order seen in *Lyrics*. .. image:: pics/song_edit_verse_error.png From de8e8ee422119212b1b44bc292efb58c0cf4ba8c Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sat, 16 Apr 2011 09:23:00 +0200 Subject: [PATCH 90/98] fixed missing space --- openlp/core/ui/servicemanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index ba9ca718a..b81c746d2 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -611,7 +611,7 @@ class ServiceManager(QtGui.QWidget): u'%s' % fileName) QtGui.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Corrupt File'), - translate('OpenLP.ServiceManager', 'This file is either' + translate('OpenLP.ServiceManager', 'This file is either ' 'corrupt or not an OpenLP 2.0 service file.')) return finally: From 336489d215fd581f5e7490c2184ea801fc93a30f Mon Sep 17 00:00:00 2001 From: John Cegalis Date: Sat, 16 Apr 2011 04:32:30 -0400 Subject: [PATCH 91/98] added blank to --- .../pics/song_edit_author_maintenance.png | Bin 0 -> 16943 bytes .../manual/source/pics/song_edit_authors.png | Bin 0 -> 37481 bytes .../manual/source/pics/song_edit_lyrics.png | Bin 0 -> 62750 bytes .../source/pics/song_edit_maintenance.png | Bin 0 -> 46167 bytes .../pics/song_edit_songbook_maintenance.png | Bin 0 -> 12072 bytes .../source/pics/song_edit_theme_copyright.png | Bin 0 -> 29992 bytes .../source/pics/song_edit_topic_maintenance.png | Bin 0 -> 9956 bytes .../source/pics/song_edit_verse_error.png | Bin 0 -> 14183 bytes .../manual/source/pics/song_edit_verse_type.png | Bin 0 -> 46119 bytes documentation/manual/source/songs.rst | 2 +- 10 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 documentation/manual/source/pics/song_edit_author_maintenance.png create mode 100644 documentation/manual/source/pics/song_edit_authors.png create mode 100644 documentation/manual/source/pics/song_edit_lyrics.png create mode 100644 documentation/manual/source/pics/song_edit_maintenance.png create mode 100644 documentation/manual/source/pics/song_edit_songbook_maintenance.png create mode 100644 documentation/manual/source/pics/song_edit_theme_copyright.png create mode 100644 documentation/manual/source/pics/song_edit_topic_maintenance.png create mode 100644 documentation/manual/source/pics/song_edit_verse_error.png create mode 100644 documentation/manual/source/pics/song_edit_verse_type.png diff --git a/documentation/manual/source/pics/song_edit_author_maintenance.png b/documentation/manual/source/pics/song_edit_author_maintenance.png new file mode 100644 index 0000000000000000000000000000000000000000..56b5550d2125f2abc768c40d7bd8d5d337f5e0f8 GIT binary patch literal 16943 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 U$~M01%fY%#4dDjmdTtN@0fq0r)Bpeg literal 0 HcmV?d00001 diff --git a/documentation/manual/source/pics/song_edit_lyrics.png b/documentation/manual/source/pics/song_edit_lyrics.png new file mode 100644 index 0000000000000000000000000000000000000000..10727376c7f8caee62e1b62017241cdbc3f09416 GIT binary patch literal 62750 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≈Dg1{%<21-q#C7^MS0d)*Vh1T;35X2xYRWRGPzh! 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 OLrIG(h*gLf`u`tbXK|$f literal 0 HcmV?d00001 diff --git a/documentation/manual/source/pics/song_edit_theme_copyright.png b/documentation/manual/source/pics/song_edit_theme_copyright.png new file mode 100644 index 0000000000000000000000000000000000000000..5234ccc1389595480af4e01362ff6ab5fae41ecd GIT binary patch literal 29992 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 literal 0 HcmV?d00001 diff --git a/documentation/manual/source/pics/song_edit_topic_maintenance.png b/documentation/manual/source/pics/song_edit_topic_maintenance.png new file mode 100644 index 0000000000000000000000000000000000000000..70446b81921adad28a71a053ff2e3d351b894c84 GIT binary patch literal 9956 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 literal 0 HcmV?d00001 diff --git a/documentation/manual/source/pics/song_edit_verse_error.png b/documentation/manual/source/pics/song_edit_verse_error.png new file mode 100644 index 0000000000000000000000000000000000000000..4fa10fec3f5d1b83a48c1ac39a75ce7dc4dc1e87 GIT binary patch literal 14183 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%0D>Y zo3T(d25t|5(T6If4|+ChHh(it&-Z~;v xRMf4MDLTDaJL)#PxX^eXC>2v>-rOsEpp&217uD66w>RY=WF!>Dt3-_h{|D~A+Zq4> literal 0 HcmV?d00001 diff --git a/documentation/manual/source/pics/song_edit_verse_type.png b/documentation/manual/source/pics/song_edit_verse_type.png new file mode 100644 index 0000000000000000000000000000000000000000..e4cdfa1a676d8e0aa97a54e8ed101595fd8d3412 GIT binary patch literal 46119 zcmZ^K19+remuWu--6p)jC;fPi4dL!PLg_3b>ma2nY{IOps5}HRF84%~R53Yv^Wb z+_3H{R8$f*PL!WAl&cbttg_KmDe+e!bRlNtsa7Gpxdk~(7^)DtR)hFel05%R-eb~g zGJ*tAELT03k0hd+@dSP9@PjuKP38#OQtKoW>0NsL_=`Ut#D9CM3hVE>DSScwy+GVh z^w~Ja5Kp8x4HBtb4qAyyr9SQ7neUMJsE7c<=UTIWU`P-v3MAI!gXnDaK8&LEWA=NR z{Fgc3S5~y)h7B5{dL&q-a^Gs?)}AV9+>eh{4KpPWK7>Hd7^J(@I z7NqgJd%0htsz!|*4Nn4p1SoZC;{pRg3gpp!k=pnRemzzfZ0k_r&8VcvmMWgFw`Kxr zl9|n{uy>LMbiafhP|Ca@As8ɗ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

n>9%$gc-^-0ACY+uS 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 mqhnM+ZTi2_!!*>Ke}03iM`v2>t Date: Sun, 17 Apr 2011 07:26:55 +0100 Subject: [PATCH 92/98] Allow alerts to be sent but not saved Fixes: https://launchpad.net/bugs/763066 --- openlp/plugins/alerts/forms/alertform.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/openlp/plugins/alerts/forms/alertform.py b/openlp/plugins/alerts/forms/alertform.py index b87ff3c5d..5f81076db 100644 --- a/openlp/plugins/alerts/forms/alertform.py +++ b/openlp/plugins/alerts/forms/alertform.py @@ -61,6 +61,12 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): QtCore.QObject.connect(self.alertListWidget, QtCore.SIGNAL(u'currentRowChanged(int)'), self.onCurrentRowChanged) + def exec_(self): + self.displayButton.setEnabled(False) + self.displayCloseButton.setEnabled(False) + self.alertTextEdit.setText(u'') + return QtGui.QDialog.exec_(self) + def loadList(self): """ Loads the list with alerts. @@ -125,6 +131,13 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): # Only enable the button, if we are editing an item. if self.item_id: self.saveButton.setEnabled(True) + if self.alertTextEdit.text(): + self.displayButton.setEnabled(True) + self.displayCloseButton.setEnabled(True) + else: + self.displayButton.setEnabled(False) + self.displayCloseButton.setEnabled(False) + def onDoubleClick(self): """ From 946d74372de8564e0e89b58d17791b0875691347 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 17 Apr 2011 13:00:44 +0200 Subject: [PATCH 93/98] --- openlp/core/lib/ui.py | 4 ++-- openlp/core/ui/mainwindow.py | 10 +++------- openlp/core/ui/servicemanager.py | 3 ++- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index a0686416c..b544efab9 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -323,7 +323,7 @@ def shortcut_action(parent, name, shortcuts, function, icon=None, checked=None, action.setShortcutContext(context) action_list = ActionList.get_instance() action_list.add_action(action, category) - QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), function) + QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered(bool)'), function) return action def context_menu_action(base, icon, text, slot, shortcuts=None, category=None, @@ -356,7 +356,7 @@ def context_menu_action(base, icon, text, slot, shortcuts=None, category=None, action = QtGui.QAction(text, base) if icon: action.setIcon(build_icon(icon)) - QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), slot) + QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered(bool)'), slot) if shortcuts is not None: action.setShortcuts(shortcuts) action.setShortcutContext(context) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index d2fbaef9e..32a28d474 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -896,7 +896,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): def toggleThemeManager(self): self.themeManagerDock.setVisible(not self.themeManagerDock.isVisible()) - def setPreviewPanelVisibility(self, visible=None): + def setPreviewPanelVisibility(self, visible): """ Sets the visibility of the preview panel including saving the setting and updating the menu. @@ -906,14 +906,12 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): True - Visible False - Hidden """ - if visible is None: - visible = self.ViewPreviewPanel.isVisible() self.previewController.panel.setVisible(visible) QtCore.QSettings().setValue(u'user interface/preview panel', QtCore.QVariant(visible)) self.ViewPreviewPanel.setChecked(visible) - def setLivePanelVisibility(self, visible=None): + def setLivePanelVisibility(self, visible): """ Sets the visibility of the live panel including saving the setting and updating the menu. @@ -923,8 +921,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): True - Visible False - Hidden """ - if visible is None: - visible = self.ViewLivePanel.isVisible() self.liveController.panel.setVisible(visible) QtCore.QSettings().setValue(u'user interface/live panel', QtCore.QVariant(visible)) @@ -1011,4 +1007,4 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.recentFiles.insert(0, QtCore.QString(filename)) while self.recentFiles.count() > maxRecentFiles: # Don't care what API says takeLast works, removeLast doesn't! - self.recentFiles.takeLast() \ No newline at end of file + self.recentFiles.takeLast() diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 9de82dd82..40bd5e9d4 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -347,7 +347,8 @@ class ServiceManager(QtGui.QWidget): has been modified. """ self._modified = modified - serviceFile = self.shortFileName() or u'Untitled Service' + serviceFile = self.shortFileName() or translate( + 'OpenLP.ServiceManager', 'Untitled Service') self.mainwindow.setServiceModified(modified, serviceFile) def isModified(self): From d3a0952d66a300378b4d6ab62dd8bcc0b1dfd310 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Sun, 17 Apr 2011 14:36:35 +0200 Subject: [PATCH 94/98] fixed a bug, where themes 'lost' their background image; fixed theme re name when the name has not been changed --- openlp/core/ui/thememanager.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 81da6e021..cc972e5c8 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -280,6 +280,8 @@ class ThemeManager(QtGui.QWidget): self.fileRenameForm.fileNameEdit.setText(oldThemeName) if self.fileRenameForm.exec_(): newThemeName = unicode(self.fileRenameForm.fileNameEdit.text()) + if oldThemeName == newThemeName: + return if self.checkIfThemeExists(newThemeName): oldThemeData = self.getThemeData(oldThemeName) self.cloneThemeData(oldThemeData, newThemeName) @@ -333,6 +335,7 @@ class ThemeManager(QtGui.QWidget): self.oldBackgroundImage = theme.background_filename self.themeForm.theme = theme self.themeForm.exec_(True) + self.oldBackgroundImage = None def onDeleteTheme(self): """ From b436c34742bfe06c08613127a3810ecca3fa9554 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sun, 17 Apr 2011 13:53:35 +0100 Subject: [PATCH 95/98] Remove blank line --- openlp/plugins/alerts/forms/alertform.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openlp/plugins/alerts/forms/alertform.py b/openlp/plugins/alerts/forms/alertform.py index 5f81076db..6f6311392 100644 --- a/openlp/plugins/alerts/forms/alertform.py +++ b/openlp/plugins/alerts/forms/alertform.py @@ -138,7 +138,6 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): self.displayButton.setEnabled(False) self.displayCloseButton.setEnabled(False) - def onDoubleClick(self): """ List item has been double clicked to display it From c7843012e961d262bf48e7e957cb2a3f7cb9da6f Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 18 Apr 2011 16:57:37 +0200 Subject: [PATCH 96/98] fixed bug #76330 Fixes: https://launchpad.net/bugs/7633099 --- openlp/core/ui/firsttimeform.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index dc1932015..dade26cf9 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -140,6 +140,8 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard): return FirstTimePage.Songs elif self.currentId() == FirstTimePage.Progress: return -1 + elif self.currentId() == FirstTimePage.NoInternet: + return FirstTimePage.Progress else: return self.currentId() + 1 @@ -147,11 +149,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard): """ Detects Page changes and updates as approprate. """ - if pageId == FirstTimePage.NoInternet: - self.finishButton.setVisible(True) - self.finishButton.setEnabled(True) - self.nextButton.setVisible(False) - elif pageId == FirstTimePage.Defaults: + if pageId == FirstTimePage.Defaults: self.themeComboBox.clear() for iter in xrange(self.themesListWidget.count()): item = self.themesListWidget.item(iter) From 3c8568443be04767f931e304506a4916f76ed688 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Mon, 18 Apr 2011 16:30:32 +0100 Subject: [PATCH 97/98] Delete the correct item Fixes: https://launchpad.net/bugs/764651 --- openlp/core/ui/settingsform.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index 949d907b4..9f3d5208f 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -82,7 +82,9 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): item_name.setIcon(icon) self.settingListWidget.insertItem(location, item_name) else: - self.stackedLayout.takeAt(location) + # add then remove tab to stop th UI displaying it even if + # it is not required. + self.stackedLayout.takeAt(pos) def accept(self): """ From 133145d5c226c4eff5ccb8905fe452c9f8010c29 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Mon, 18 Apr 2011 16:31:20 +0100 Subject: [PATCH 98/98] Fix comment --- openlp/core/ui/settingsform.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openlp/core/ui/settingsform.py b/openlp/core/ui/settingsform.py index 9f3d5208f..1967412f5 100644 --- a/openlp/core/ui/settingsform.py +++ b/openlp/core/ui/settingsform.py @@ -75,6 +75,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): Add a tab to the form at a specific location """ log.debug(u'Inserting %s tab' % tab.tabTitle) + # add the tab to get it to display in the correct part of the screen pos = self.stackedLayout.addWidget(tab) if is_active: item_name = QtGui.QListWidgetItem(tab.tabTitleVisible) @@ -82,7 +83,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog): item_name.setIcon(icon) self.settingListWidget.insertItem(location, item_name) else: - # add then remove tab to stop th UI displaying it even if + # then remove tab to stop the UI displaying it even if # it is not required. self.stackedLayout.takeAt(pos)