diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index 5a030e841..698216365 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -35,11 +35,11 @@ class HideMode(object): ``Blank`` This mode is used to hide all output, specifically by covering the display with a black screen. - + ``Theme`` This mode is used to hide all output, but covers the display with the current theme background, as opposed to black. - + ``Desktop`` This mode hides all output by minimising the display, leaving the user's desktop showing. diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 70b87966c..f2545766e 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -142,7 +142,8 @@ class Ui_MainWindow(object): build_icon(u':/system/system_servicemanager.png')) self.ServiceManagerDock.setMinimumWidth( self.settingsmanager.mainwindow_right) - self.ServiceManagerContents = ServiceManager(MainWindow) + self.ServiceManagerContents = ServiceManager(MainWindow, + self.ServiceManagerDock) self.ServiceManagerDock.setWidget(self.ServiceManagerContents) MainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.ServiceManagerDock) @@ -152,7 +153,8 @@ class Ui_MainWindow(object): build_icon(u':/system/system_thememanager.png')) self.ThemeManagerDock.setMinimumWidth( self.settingsmanager.mainwindow_right) - self.ThemeManagerContents = ThemeManager(MainWindow) + self.ThemeManagerContents = ThemeManager(MainWindow, + self.ThemeManagerDock) self.ThemeManagerContents.setObjectName(u'ThemeManagerContents') self.ThemeManagerDock.setWidget(self.ThemeManagerContents) MainWindow.addDockWidget(QtCore.Qt.RightDockWidgetArea, diff --git a/openlp/core/ui/pluginform.py b/openlp/core/ui/pluginform.py index 3162458bc..8e82f0003 100644 --- a/openlp/core/ui/pluginform.py +++ b/openlp/core/ui/pluginform.py @@ -61,6 +61,7 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog): self.programaticChange = True self._clearDetails() self.programaticChange = True + pluginListWidth = 0 for plugin in self.parent.pluginManager.plugins: item = QtGui.QListWidgetItem(self.pluginListWidget) # We do this just to make 100% sure the status is an integer as @@ -83,8 +84,11 @@ class PluginForm(QtGui.QDialog, Ui_PluginViewDialog): if plugin.icon: item.setIcon(plugin.icon) self.pluginListWidget.addItem(item) - self.pluginListWidget.setFixedWidth( - self.pluginListWidget.sizeHint().width()) + pluginListWidth = max(pluginListWidth, self.fontMetrics().width( + unicode(translate('OpenLP.PluginForm', '%s (Inactive)')) % + name_string[u'singular'])) + self.pluginListWidget.setFixedWidth(pluginListWidth + + self.pluginListWidget.iconSize().width() + 48) def _clearDetails(self): self.statusComboBox.setCurrentIndex(-1) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index bacef0177..acd775ef7 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -43,33 +43,33 @@ class ServiceManagerList(QtGui.QTreeWidget): """ Set up key bindings and mouse behaviour for the service list """ - def __init__(self, parent=None, name=None): + def __init__(self, mainwindow, parent=None, name=None): QtGui.QTreeWidget.__init__(self, parent) - self.parent = parent + self.mainwindow = mainwindow def keyPressEvent(self, event): if isinstance(event, QtGui.QKeyEvent): #here accept the event and do something if event.key() == QtCore.Qt.Key_Enter: - self.parent.makeLive() + self.mainwindow.makeLive() event.accept() elif event.key() == QtCore.Qt.Key_Home: - self.parent.onServiceTop() + self.mainwindow.onServiceTop() event.accept() elif event.key() == QtCore.Qt.Key_End: - self.parent.onServiceEnd() + self.mainwindow.onServiceEnd() event.accept() elif event.key() == QtCore.Qt.Key_PageUp: - self.parent.onServiceUp() + self.mainwindow.onServiceUp() event.accept() elif event.key() == QtCore.Qt.Key_PageDown: - self.parent.onServiceDown() + self.mainwindow.onServiceDown() event.accept() elif event.key() == QtCore.Qt.Key_Up: - self.parent.onMoveSelectionUp() + self.mainwindow.onMoveSelectionUp() event.accept() elif event.key() == QtCore.Qt.Key_Down: - self.parent.onMoveSelectionDown() + self.mainwindow.onMoveSelectionDown() event.accept() event.ignore() else: @@ -98,12 +98,12 @@ class ServiceManager(QtGui.QWidget): the resources used into one OSZ file for use on any OpenLP v2 installation. Also handles the UI tasks of moving things up and down etc. """ - def __init__(self, parent): + def __init__(self, mainwindow, parent=None): """ Sets up the service manager, toolbars, list view, et al. """ QtGui.QWidget.__init__(self, parent) - self.parent = parent + self.mainwindow = mainwindow self.serviceItems = [] self.serviceName = u'' self.suffixes = [] @@ -112,8 +112,8 @@ class ServiceManager(QtGui.QWidget): # is a new service and has not been saved self._modified = False self._fileName = u'' - self.serviceNoteForm = ServiceNoteForm(self.parent) - self.serviceItemEditForm = ServiceItemEditForm(self.parent) + self.serviceNoteForm = ServiceNoteForm(self.mainwindow) + self.serviceItemEditForm = ServiceItemEditForm(self.mainwindow) # start with the layout self.layout = QtGui.QVBoxLayout(self) self.layout.setSpacing(0) @@ -144,8 +144,10 @@ class ServiceManager(QtGui.QWidget): self.themeComboBox = QtGui.QComboBox(self.toolbar) self.themeComboBox.setToolTip(translate('OpenLP.ServiceManager', 'Select a theme for the service')) - self.themeComboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, - QtGui.QSizePolicy.Fixed) + self.themeComboBox.setSizeAdjustPolicy( + QtGui.QComboBox.AdjustToMinimumContentsLength) + self.themeComboBox.setSizePolicy( + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) self.themeComboBox.setObjectName(u'themeComboBox') self.toolbar.addToolbarWidget(u'ThemeWidget', self.themeComboBox) self.toolbar.setObjectName(u'toolbar') @@ -247,7 +249,7 @@ class ServiceManager(QtGui.QWidget): QtCore.SIGNAL(u'service_item_update'), self.serviceItemUpdate) # Last little bits of setting up self.service_theme = unicode(QtCore.QSettings().value( - self.parent.serviceSettingsSection + u'/service theme', + self.mainwindow.serviceSettingsSection + u'/service theme', QtCore.QVariant(u'')).toString()) self.servicePath = AppLocation.get_section_data_path(u'servicemanager') # build the drag and drop context menu @@ -294,7 +296,7 @@ class ServiceManager(QtGui.QWidget): """ self._modified = modified serviceFile = self.shortFileName() or u'Untitled Service' - self.parent.setServiceModified(modified, serviceFile) + self.mainwindow.setServiceModified(modified, serviceFile) def isModified(self): """ @@ -307,7 +309,8 @@ class ServiceManager(QtGui.QWidget): Setter for service file. """ self._fileName = unicode(fileName) - self.parent.setServiceModified(self.isModified(), self.shortFileName()) + self.mainwindow.setServiceModified(self.isModified(), + self.shortFileName()) QtCore.QSettings(). \ setValue(u'service/last file',QtCore.QVariant(fileName)) @@ -341,7 +344,7 @@ class ServiceManager(QtGui.QWidget): Create a new service. """ if self.isModified(): - result = QtGui.QMessageBox.question(self.parent, + result = QtGui.QMessageBox.question(self.mainwindow, translate('OpenLP.ServiceManager', 'Save Changes'), translate('OpenLP.ServiceManager', 'The current service has ' 'been modified, would you like to save it?'), @@ -356,7 +359,7 @@ class ServiceManager(QtGui.QWidget): def onLoadServiceClicked(self): if self.isModified(): - result = QtGui.QMessageBox.question(self.parent, + result = QtGui.QMessageBox.question(self.mainwindow, translate('OpenLP.ServiceManager', 'Save Changes'), translate('OpenLP.ServiceManager', 'The current service has ' 'been modified, would you like to save it?'), @@ -366,14 +369,14 @@ class ServiceManager(QtGui.QWidget): return False elif result == QtGui.QMessageBox.Save: self.saveFile() - fileName = unicode(QtGui.QFileDialog.getOpenFileName(self.parent, + fileName = unicode(QtGui.QFileDialog.getOpenFileName(self.mainwindow, translate('OpenLP.ServiceManager', 'Open File'), - SettingsManager.get_last_dir(self.parent.serviceSettingsSection), + SettingsManager.get_last_dir(self.mainwindow.serviceSettingsSection), translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz)'))) if not fileName: return False - SettingsManager.set_last_dir(self.parent.serviceSettingsSection, + SettingsManager.set_last_dir(self.mainwindow.serviceSettingsSection, split_filename(fileName)[0]) self.loadFile(fileName) @@ -407,7 +410,7 @@ class ServiceManager(QtGui.QWidget): else: fileName = self.fileName() log.debug(u'ServiceManager.saveFile - %s' % fileName) - SettingsManager.set_last_dir(self.parent.serviceSettingsSection, + SettingsManager.set_last_dir(self.mainwindow.serviceSettingsSection, split_filename(fileName)[0]) service = [] serviceFileName = fileName.replace(u'.osz', u'.osd') @@ -447,7 +450,7 @@ class ServiceManager(QtGui.QWidget): except (IOError, OSError): # if not present do not worry pass - self.parent.addRecentFile(fileName) + self.mainwindow.addRecentFile(fileName) self.setModified(False) return True @@ -456,9 +459,9 @@ class ServiceManager(QtGui.QWidget): Get a file name and then call :function:`ServiceManager.saveFile` to save the file. """ - fileName = unicode(QtGui.QFileDialog.getSaveFileName(self.parent, + fileName = unicode(QtGui.QFileDialog.getSaveFileName(self.mainwindow, translate('OpenLP.ServiceManager', 'Save Service'), - SettingsManager.get_last_dir(self.parent.serviceSettingsSection), + SettingsManager.get_last_dir(self.mainwindow.serviceSettingsSection), translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz)'))) if not fileName: @@ -508,7 +511,7 @@ class ServiceManager(QtGui.QWidget): self.newFile() for item in items: serviceItem = ServiceItem() - serviceItem.render_manager = self.parent.renderManager + serviceItem.render_manager = self.mainwindow.renderManager serviceItem.set_from_service(item, self.servicePath) self.validateItem(serviceItem) self.addServiceItem(serviceItem) @@ -535,7 +538,7 @@ class ServiceManager(QtGui.QWidget): if zip: zip.close() self.setFileName(fileName) - self.parent.addRecentFile(fileName) + self.mainwindow.addRecentFile(fileName) self.setModified(False) QtCore.QSettings(). \ setValue(u'service/last file',QtCore.QVariant(fileName)) @@ -886,9 +889,9 @@ class ServiceManager(QtGui.QWidget): """ log.debug(u'onThemeComboBoxSelected') self.service_theme = unicode(self.themeComboBox.currentText()) - self.parent.renderManager.set_service_theme(self.service_theme) + self.mainwindow.renderManager.set_service_theme(self.service_theme) QtCore.QSettings().setValue( - self.parent.serviceSettingsSection + u'/service theme', + self.mainwindow.serviceSettingsSection + u'/service theme', QtCore.QVariant(self.service_theme)) self.regenerateServiceItems() @@ -898,7 +901,7 @@ class ServiceManager(QtGui.QWidget): sure the theme combo box is in the correct state. """ log.debug(u'themeChange') - if self.parent.renderManager.theme_level == ThemeLevel.Global: + if self.mainwindow.renderManager.theme_level == ThemeLevel.Global: self.toolbar.actions[u'ThemeLabel'].setVisible(False) self.toolbar.actions[u'ThemeWidget'].setVisible(False) else: @@ -913,7 +916,7 @@ class ServiceManager(QtGui.QWidget): Receiver.send_message(u'cursor_busy') log.debug(u'regenerateServiceItems') # force reset of renderer as theme data has changed - self.parent.renderManager.themedata = None + self.mainwindow.renderManager.themedata = None if self.serviceItems: tempServiceItems = self.serviceItems self.serviceManagerList.clear() @@ -948,7 +951,7 @@ class ServiceManager(QtGui.QWidget): newItem.merge(item[u'service_item']) item[u'service_item'] = newItem self.repaintServiceList(itemcount + 1, 0) - self.parent.liveController.replaceServiceManagerItem(newItem) + self.mainwindow.liveController.replaceServiceManagerItem(newItem) self.setModified(True) def addServiceItem(self, item, rebuild=False, expand=None, replace=False): @@ -970,7 +973,7 @@ class ServiceManager(QtGui.QWidget): item.merge(self.serviceItems[sitem][u'service_item']) self.serviceItems[sitem][u'service_item'] = item self.repaintServiceList(sitem + 1, 0) - self.parent.liveController.replaceServiceManagerItem(item) + self.mainwindow.liveController.replaceServiceManagerItem(item) else: # nothing selected for dnd if self.dropPosition == 0: @@ -991,7 +994,7 @@ class ServiceManager(QtGui.QWidget): self.repaintServiceList(self.dropPosition, 0) # if rebuilding list make sure live is fixed. if rebuild: - self.parent.liveController.replaceServiceManagerItem(item) + self.mainwindow.liveController.replaceServiceManagerItem(item) self.dropPosition = 0 self.setModified(True) @@ -1001,7 +1004,7 @@ class ServiceManager(QtGui.QWidget): """ item, count = self.findServiceItem() if self.serviceItems[item][u'service_item'].is_valid: - self.parent.previewController.addServiceManagerItem( + self.mainwindow.previewController.addServiceManagerItem( self.serviceItems[item][u'service_item'], count) else: QtGui.QMessageBox.critical(self, @@ -1025,18 +1028,18 @@ class ServiceManager(QtGui.QWidget): """ item, count = self.findServiceItem() if self.serviceItems[item][u'service_item'].is_valid: - self.parent.liveController.addServiceManagerItem( + self.mainwindow.liveController.addServiceManagerItem( self.serviceItems[item][u'service_item'], count) if QtCore.QSettings().value( - self.parent.generalSettingsSection + u'/auto preview', + self.mainwindow.generalSettingsSection + u'/auto preview', QtCore.QVariant(False)).toBool(): item += 1 if self.serviceItems and item < len(self.serviceItems) and \ self.serviceItems[item][u'service_item'].is_capable( ItemCapabilities.AllowsPreview): - self.parent.previewController.addServiceManagerItem( + self.mainwindow.previewController.addServiceManagerItem( self.serviceItems[item][u'service_item'], 0) - self.parent.liveController.PreviewListWidget.setFocus() + self.mainwindow.liveController.PreviewListWidget.setFocus() else: QtGui.QMessageBox.critical(self, translate('OpenLP.ServiceManager', 'Missing Display Handler'), @@ -1156,7 +1159,7 @@ class ServiceManager(QtGui.QWidget): index = 0 self.service_theme = u'' self.themeComboBox.setCurrentIndex(index) - self.parent.renderManager.set_service_theme(self.service_theme) + self.mainwindow.renderManager.set_service_theme(self.service_theme) self.regenerateServiceItems() def onThemeChangeAction(self): diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py index 760e3b261..f8236a1eb 100644 --- a/openlp/core/ui/themeform.py +++ b/openlp/core/ui/themeform.py @@ -212,8 +212,8 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard): """ Updates the lines on a page on the wizard """ - self.mainLineCountLabel.setText(unicode(translate('OpenLP.ThemeForm', \ - '(%d lines per slide)' % int(lines)))) + self.mainLineCountLabel.setText(unicode(translate('OpenLP.ThemeForm', + '(%d lines per slide)')) % int(lines)) def resizeEvent(self, event=None): """ diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index e3512ef74..590f33ff7 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -45,13 +45,13 @@ class ThemeManager(QtGui.QWidget): """ Manages the orders of Theme. """ - def __init__(self, parent): + def __init__(self, mainwindow, parent=None): QtGui.QWidget.__init__(self, parent) - self.parent = parent + self.mainwindow = mainwindow self.settingsSection = u'themes' self.themeForm = ThemeForm(self) self.fileRenameForm = FileRenameForm(self) - self.serviceComboBox = self.parent.ServiceManagerContents.themeComboBox + self.serviceComboBox = self.mainwindow.ServiceManagerContents.themeComboBox # start with the layout self.layout = QtGui.QVBoxLayout(self) self.layout.setSpacing(0) @@ -252,7 +252,7 @@ class ThemeManager(QtGui.QWidget): oldThemeData = self.getThemeData(oldThemeName) self.deleteTheme(oldThemeName) self.cloneThemeData(oldThemeData, newThemeName) - for plugin in self.parent.pluginManager.plugins: + for plugin in self.mainwindow.pluginManager.plugins: if plugin.usesTheme(oldThemeName): plugin.renameTheme(oldThemeName, newThemeName) @@ -638,7 +638,7 @@ class ThemeManager(QtGui.QWidget): Flag to tell message lines per page need to be generated. """ log.debug(u'generateImage \n%s ', themeData) - return self.parent.renderManager.generate_preview(themeData, forcePage) + return self.mainwindow.renderManager.generate_preview(themeData, forcePage) def getPreviewImage(self, theme): """ @@ -699,7 +699,7 @@ class ThemeManager(QtGui.QWidget): return False # check for use in the system else where. if testPlugin: - for plugin in self.parent.pluginManager.plugins: + for plugin in self.mainwindow.pluginManager.plugins: if plugin.usesTheme(theme): Receiver.send_message(u'openlp_error_message', { u'title': translate('OpenLP.ThemeManager', @@ -766,4 +766,4 @@ class ThemeManager(QtGui.QWidget): vAlignCorrection = 2 newtheme.display_horizontal_align = theme.HorizontalAlign newtheme.display_vertical_align = vAlignCorrection - return newtheme.extract_xml() \ No newline at end of file + return newtheme.extract_xml() diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index 229650739..a440a564e 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -46,8 +46,8 @@ class ThemesTab(SettingsTab): self.DefaultComboBox = QtGui.QComboBox(self.GlobalGroupBox) self.DefaultComboBox.setSizeAdjustPolicy( QtGui.QComboBox.AdjustToMinimumContentsLength) - self.DefaultComboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, - QtGui.QSizePolicy.Fixed) + self.DefaultComboBox.setSizePolicy( + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) self.DefaultComboBox.setObjectName(u'DefaultComboBox') self.GlobalGroupBoxLayout.addWidget(self.DefaultComboBox) self.DefaultListView = QtGui.QLabel(self.GlobalGroupBox) diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py index f61a44584..50e8109c5 100644 --- a/openlp/core/ui/themewizard.py +++ b/openlp/core/ui/themewizard.py @@ -476,8 +476,6 @@ class Ui_ThemeWizard(object): self.mainColorLabel.setText(translate('OpenLP.ThemeWizard', 'Color:')) self.mainSizeLabel.setText(translate('OpenLP.ThemeWizard', 'Size:')) self.mainSizeSpinBox.setSuffix(translate('OpenLP.ThemeWizard', 'pt')) - self.mainLineCountLabel.setText( - translate('OpenLP.ThemeWizard', '(%d lines per slide)')) self.lineSpacingLabel.setText( translate('OpenLP.ThemeWizard', 'Line Spacing:')) self.lineSpacingSpinBox.setSuffix(translate('OpenLP.ThemeWizard', 'pt')) @@ -569,17 +567,17 @@ class Ui_ThemeWizard(object): self.themeNameLabel.setText( translate('OpenLP.ThemeWizard', 'Theme name:')) # Align all QFormLayouts towards each other. - width = max(self.backgroundLabel.minimumSizeHint().width(), - self.colorLabel.minimumSizeHint().width()) - width = max(width, self.gradientStartLabel.minimumSizeHint().width()) - width = max(width, self.gradientEndLabel.minimumSizeHint().width()) - width = max(width, self.gradientTypeLabel.minimumSizeHint().width()) - width = max(width, self.imageLabel.minimumSizeHint().width()) - self.backgroundTypeSpacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Fixed) - self.colorSpacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Fixed) - self.gradientSpacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Fixed) - self.imageSpacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Fixed) + labelWidth = max(self.backgroundLabel.minimumSizeHint().width(), + self.colorLabel.minimumSizeHint().width(), + self.gradientStartLabel.minimumSizeHint().width(), + self.gradientEndLabel.minimumSizeHint().width(), + self.gradientTypeLabel.minimumSizeHint().width(), + self.imageLabel.minimumSizeHint().width()) + self.backgroundTypeSpacer.changeSize(labelWidth, 0, + QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + self.colorSpacer.changeSize(labelWidth, 0, + QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + self.gradientSpacer.changeSize(labelWidth, 0, + QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + self.imageSpacer.changeSize(labelWidth, 0, + QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index 4a7e18cef..61be922d5 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -73,7 +73,7 @@ class AlertsPlugin(Plugin): self.toolsAlertItem.setStatusTip( translate('AlertsPlugin', 'Show an alert message.')) self.toolsAlertItem.setShortcut(u'F7') - self.serviceManager.parent.ToolsMenu.addAction(self.toolsAlertItem) + self.serviceManager.mainwindow.ToolsMenu.addAction(self.toolsAlertItem) QtCore.QObject.connect(self.toolsAlertItem, QtCore.SIGNAL(u'triggered()'), self.onAlertsTrigger) self.toolsAlertItem.setVisible(False) diff --git a/openlp/plugins/bibles/forms/bibleimportwizard.py b/openlp/plugins/bibles/forms/bibleimportwizard.py index a85e430a1..c6a6775a5 100644 --- a/openlp/plugins/bibles/forms/bibleimportwizard.py +++ b/openlp/plugins/bibles/forms/bibleimportwizard.py @@ -234,7 +234,7 @@ class Ui_BibleImportWizard(object): QtGui.QSizePolicy.Minimum) self.openlp1Layout.setItem(1, QtGui.QFormLayout.LabelRole, self.openlp1Spacer) - self.selectStack.addWidget(self.openlp1Widget) + self.selectStack.addWidget(self.openlp1Widget) self.selectPageLayout.addLayout(self.selectStack) bibleImportWizard.addPage(self.selectPage) # License Page @@ -373,19 +373,19 @@ class Ui_BibleImportWizard(object): 'you want to use this importer, you will need to install the ' '"python-sqlite" module.')) # Align all QFormLayouts towards each other. - width = max(self.formatLabel.minimumSizeHint().width(), - self.osisFileLabel.minimumSizeHint().width()) - width = max(width, self.csvBooksLabel.minimumSizeHint().width()) - width = max(width, self.csvVersesLabel.minimumSizeHint().width()) - width = max(width, self.openSongFileLabel.minimumSizeHint().width()) - width = max(width, self.openlp1FileLabel.minimumSizeHint().width()) - self.formatSpacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Fixed) - self.osisSpacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Fixed) - self.csvSpacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Fixed) - self.openSongSpacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Fixed) - self.openlp1Spacer.changeSize(width, 0, QtGui.QSizePolicy.Fixed, - QtGui.QSizePolicy.Fixed) + labelWidth = max(self.formatLabel.minimumSizeHint().width(), + self.osisFileLabel.minimumSizeHint().width(), + self.csvBooksLabel.minimumSizeHint().width(), + self.csvVersesLabel.minimumSizeHint().width(), + self.openSongFileLabel.minimumSizeHint().width(), + self.openlp1FileLabel.minimumSizeHint().width()) + self.formatSpacer.changeSize(labelWidth, 0, + QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + self.osisSpacer.changeSize(labelWidth, 0, + QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + self.csvSpacer.changeSize(labelWidth, 0, + QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + self.openSongSpacer.changeSize(labelWidth, 0, + QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) + self.openlp1Spacer.changeSize(labelWidth, 0, + QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) diff --git a/openlp/plugins/bibles/lib/biblestab.py b/openlp/plugins/bibles/lib/biblestab.py index 0a2effa5e..f6bd27324 100644 --- a/openlp/plugins/bibles/lib/biblestab.py +++ b/openlp/plugins/bibles/lib/biblestab.py @@ -76,8 +76,8 @@ class BiblesTab(SettingsTab): self.BibleThemeComboBox = QtGui.QComboBox(self.VerseDisplayGroupBox) self.BibleThemeComboBox.setSizeAdjustPolicy( QtGui.QComboBox.AdjustToMinimumContentsLength) - self.BibleThemeComboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, - QtGui.QSizePolicy.Fixed) + self.BibleThemeComboBox.setSizePolicy( + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) self.BibleThemeComboBox.addItem(u'') self.BibleThemeComboBox.setObjectName(u'BibleThemeComboBox') self.VerseDisplayLayout.addRow(self.BibleThemeLabel, @@ -88,8 +88,8 @@ class BiblesTab(SettingsTab): self.VerseDisplayLayout.addRow(self.ChangeNoteLabel) self.leftLayout.addWidget(self.VerseDisplayGroupBox) self.leftLayout.addStretch() - self.rightColumn.setSizePolicy(QtGui.QSizePolicy.Expanding, - QtGui.QSizePolicy.Preferred) + self.rightColumn.setSizePolicy( + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) self.rightLayout.addStretch() # Signals and slots QtCore.QObject.connect( diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index d9210a275..b7e3504a6 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -309,7 +309,7 @@ class BSExtract(object): finally: if not content: return None - verse_number = re.compile(r'v(\d{2})(\d{3})(\d{3}) verse') + verse_number = re.compile(r'v(\d{1,2})(\d{3})(\d{3}) verse') verses = {} for verse in content: Receiver.send_message(u'openlp_process_events') diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 661a9a679..253cc2c62 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -86,6 +86,8 @@ class BibleMediaItem(MediaManagerItem): self.quickVersionLabel = QtGui.QLabel(self.quickTab) self.quickVersionLabel.setObjectName(u'quickVersionLabel') self.quickVersionComboBox = QtGui.QComboBox(self.quickTab) + self.quickVersionComboBox.setSizeAdjustPolicy( + QtGui.QComboBox.AdjustToMinimumContentsLength) self.quickVersionComboBox.setSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) self.quickVersionComboBox.setObjectName(u'quickVersionComboBox') @@ -95,6 +97,8 @@ class BibleMediaItem(MediaManagerItem): self.quickSecondLabel = QtGui.QLabel(self.quickTab) self.quickSecondLabel.setObjectName(u'quickSecondLabel') self.quickSecondComboBox = QtGui.QComboBox(self.quickTab) + self.quickSecondComboBox.setSizeAdjustPolicy( + QtGui.QComboBox.AdjustToMinimumContentsLength) self.quickSecondComboBox.setSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) self.quickSecondComboBox.setObjectName(u'quickSecondComboBox') @@ -103,6 +107,8 @@ class BibleMediaItem(MediaManagerItem): self.quickSearchTypeLabel = QtGui.QLabel(self.quickTab) self.quickSearchTypeLabel.setObjectName(u'quickSearchTypeLabel') self.quickSearchComboBox = QtGui.QComboBox(self.quickTab) + self.quickSearchComboBox.setSizeAdjustPolicy( + QtGui.QComboBox.AdjustToMinimumContentsLength) self.quickSearchComboBox.setSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) self.quickSearchComboBox.setObjectName(u'quickSearchComboBox') @@ -118,6 +124,8 @@ class BibleMediaItem(MediaManagerItem): self.quickClearLabel = QtGui.QLabel(self.quickTab) self.quickClearLabel.setObjectName(u'quickClearLabel') self.quickClearComboBox = QtGui.QComboBox(self.quickTab) + self.quickClearComboBox.setSizeAdjustPolicy( + QtGui.QComboBox.AdjustToMinimumContentsLength) self.quickClearComboBox.setSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) self.quickClearComboBox.setObjectName(u'quickClearComboBox') @@ -129,9 +137,6 @@ class BibleMediaItem(MediaManagerItem): self.quickSearchButton.setObjectName(u'quickSearchButton') self.quickSearchButtonLayout.addWidget(self.quickSearchButton) self.quickLayout.addRow(self.quickSearchButtonLayout) - self.quickMessage = QtGui.QLabel(self.quickTab) - self.quickMessage.setObjectName(u'quickMessage') - self.quickLayout.addRow(self.quickMessage) self.searchTabWidget.addTab(self.quickTab, translate('BiblesPlugin.MediaItem', 'Quick')) # Add the Advanced Search tab. @@ -144,6 +149,8 @@ class BibleMediaItem(MediaManagerItem): self.advancedLayout.addWidget(self.advancedVersionLabel, 0, 0, QtCore.Qt.AlignRight) self.advancedVersionComboBox = QtGui.QComboBox(self.advancedTab) + self.advancedVersionComboBox.setSizeAdjustPolicy( + QtGui.QComboBox.AdjustToMinimumContentsLength) self.advancedVersionComboBox.setSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) self.advancedVersionComboBox.setObjectName(u'advancedVersionComboBox') @@ -154,6 +161,8 @@ class BibleMediaItem(MediaManagerItem): self.advancedLayout.addWidget(self.advancedSecondLabel, 1, 0, QtCore.Qt.AlignRight) self.advancedSecondComboBox = QtGui.QComboBox(self.advancedTab) + self.advancedSecondComboBox.setSizeAdjustPolicy( + QtGui.QComboBox.AdjustToMinimumContentsLength) self.advancedSecondComboBox.setSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) self.advancedSecondComboBox.setObjectName(u'advancedSecondComboBox') @@ -164,6 +173,8 @@ class BibleMediaItem(MediaManagerItem): self.advancedLayout.addWidget(self.advancedBookLabel, 2, 0, QtCore.Qt.AlignRight) self.advancedBookComboBox = QtGui.QComboBox(self.advancedTab) + self.advancedBookComboBox.setSizeAdjustPolicy( + QtGui.QComboBox.AdjustToMinimumContentsLength) self.advancedBookComboBox.setSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) self.advancedBookComboBox.setObjectName(u'advancedBookComboBox') @@ -201,6 +212,8 @@ class BibleMediaItem(MediaManagerItem): self.advancedLayout.addWidget(self.advancedClearLabel, 6, 0, QtCore.Qt.AlignRight) self.advancedClearComboBox = QtGui.QComboBox(self.quickTab) + self.advancedClearComboBox.setSizeAdjustPolicy( + QtGui.QComboBox.AdjustToMinimumContentsLength) self.advancedClearComboBox.setSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) self.advancedClearComboBox.setObjectName(u'advancedClearComboBox') @@ -215,9 +228,6 @@ class BibleMediaItem(MediaManagerItem): self.advancedSearchButtonLayout.addWidget(self.advancedSearchButton) self.advancedLayout.addLayout( self.advancedSearchButtonLayout, 7, 0, 1, 3) - self.advancedMessage = QtGui.QLabel(self.advancedTab) - self.advancedMessage.setObjectName(u'advancedMessage') - self.advancedLayout.addWidget(self.advancedMessage, 8, 0, 1, 3) self.searchTabWidget.addTab(self.advancedTab, translate('BiblesPlugin.MediaItem', 'Advanced')) # Add the search tab widget to the page layout. @@ -331,13 +341,6 @@ class BibleMediaItem(MediaManagerItem): self.configUpdated() log.debug(u'bible manager initialise complete') - def setQuickMessage(self, text): - self.quickMessage.setText(text) - self.advancedMessage.setText(text) - Receiver.send_message(u'openlp_process_events') - # Minor delay to get the events processed. - time.sleep(0.1) - def onListViewResize(self, width, height): listViewGeometry = self.listView.geometry() self.SearchProgress.setGeometry(listViewGeometry.x(), @@ -416,11 +419,13 @@ class BibleMediaItem(MediaManagerItem): verse_count = self.parent.manager.get_verse_count(bible, book, 1) if verse_count == 0: self.advancedSearchButton.setEnabled(False) - self.advancedMessage.setText( - translate('BiblesPlugin.MediaItem', 'Bible not fully loaded.')) + Receiver.send_message(u'openlp_error_message', { + u'title': translate('BiblePlugin.MediaItem', 'Error'), + u'message': translate('BiblePlugin.MediaItem', + 'Bible not fully loaded') + }) else: self.advancedSearchButton.setEnabled(True) - self.advancedMessage.setText(u'') self.adjustComboBox(1, self.chapter_count, self.advancedFromChapter) self.adjustComboBox(1, self.chapter_count, self.advancedToChapter) self.adjustComboBox(1, verse_count, self.advancedFromVerse) @@ -590,6 +595,7 @@ class BibleMediaItem(MediaManagerItem): second_bible, text) else: # We are doing a 'Text Search'. + Receiver.send_message(u'cursor_busy') bibles = self.parent.manager.get_bibles() self.search_results = self.parent.manager.verse_search(bible, second_bible, text) @@ -620,6 +626,7 @@ class BibleMediaItem(MediaManagerItem): elif self.search_results: self.displayResults(bible, second_bible) self.quickSearchButton.setEnabled(True) + Receiver.send_message(u'cursor_normal') def displayResults(self, bible, second_bible=u''): """ diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 3c27801c8..1e65a3358 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -116,8 +116,10 @@ class PresentationMediaItem(MediaManagerItem): self.displayTypeLabel = QtGui.QLabel(self.presentationWidget) self.displayTypeLabel.setObjectName(u'displayTypeLabel') self.displayTypeComboBox = QtGui.QComboBox(self.presentationWidget) - self.displayTypeComboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, - QtGui.QSizePolicy.Fixed) + self.displayTypeComboBox.setSizeAdjustPolicy( + QtGui.QComboBox.AdjustToMinimumContentsLength) + self.displayTypeComboBox.setSizePolicy( + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) self.displayTypeComboBox.setObjectName(u'displayTypeComboBox') self.displayTypeLabel.setBuddy(self.displayTypeComboBox) self.displayLayout.addRow(self.displayTypeLabel, diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py index e97d33762..d06e69164 100644 --- a/openlp/plugins/remotes/lib/remotetab.py +++ b/openlp/plugins/remotes/lib/remotetab.py @@ -46,8 +46,8 @@ class RemoteTab(SettingsTab): self.addressLabel = QtGui.QLabel(self.serverSettingsGroupBox) self.addressLabel.setObjectName(u'addressLabel') self.addressEdit = QtGui.QLineEdit(self.serverSettingsGroupBox) - self.addressEdit.setSizePolicy(QtGui.QSizePolicy.Preferred, - QtGui.QSizePolicy.Fixed) + self.addressEdit.setSizePolicy( + QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed) self.addressEdit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp( u'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'), self)) self.addressEdit.setObjectName(u'addressEdit') diff --git a/openlp/plugins/songs/forms/authorsdialog.py b/openlp/plugins/songs/forms/authorsdialog.py index 28083ae05..6f1c7f2a4 100644 --- a/openlp/plugins/songs/forms/authorsdialog.py +++ b/openlp/plugins/songs/forms/authorsdialog.py @@ -54,7 +54,7 @@ class Ui_AuthorsDialog(object): self.displayEdit.setObjectName(u'displayEdit') self.displayLabel.setBuddy(self.displayEdit) self.authorLayout.addRow(self.displayLabel, self.displayEdit) - self.dialogLayout.addLayout(self.authorLayout) + self.dialogLayout.addLayout(self.authorLayout) self.buttonBox = QtGui.QDialogButtonBox(authorsDialog) self.buttonBox.setStandardButtons( QtGui.QDialogButtonBox.Save | QtGui.QDialogButtonBox.Cancel) diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index c0ccd5d6a..675108af7 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -114,8 +114,8 @@ class Ui_EditSongDialog(object): self.authorsComboBox = QtGui.QComboBox(self.authorsGroupBox) self.authorsComboBox.setSizeAdjustPolicy( QtGui.QComboBox.AdjustToMinimumContentsLength) - self.authorsComboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, - QtGui.QSizePolicy.Fixed) + self.authorsComboBox.setSizePolicy( + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) self.authorsComboBox.setEditable(True) self.authorsComboBox.setInsertPolicy(QtGui.QComboBox.NoInsert) self.authorsComboBox.setObjectName(u'authorsComboBox') @@ -155,8 +155,8 @@ class Ui_EditSongDialog(object): self.topicsComboBox = QtGui.QComboBox(self.topicsGroupBox) self.topicsComboBox.setSizeAdjustPolicy( QtGui.QComboBox.AdjustToMinimumContentsLength) - self.topicsComboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, - QtGui.QSizePolicy.Fixed) + self.topicsComboBox.setSizePolicy( + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) self.topicsComboBox.setEditable(True) self.topicsComboBox.setInsertPolicy(QtGui.QComboBox.NoInsert) self.topicsComboBox.setObjectName(u'topicsComboBox') @@ -184,8 +184,10 @@ class Ui_EditSongDialog(object): self.songBookNameLabel = QtGui.QLabel(self.songBookGroupBox) self.songBookNameLabel.setObjectName(u'songBookNameLabel') self.songBookComboBox = QtGui.QComboBox(self.songBookGroupBox) - self.songBookComboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, - QtGui.QSizePolicy.Fixed) + self.songBookComboBox.setSizeAdjustPolicy( + QtGui.QComboBox.AdjustToMinimumContentsLength) + self.songBookComboBox.setSizePolicy( + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) self.songBookComboBox.setEditable(True) self.songBookComboBox.setInsertPolicy(QtGui.QComboBox.NoInsert) self.songBookComboBox.setObjectName(u'songBookComboBox') @@ -216,8 +218,8 @@ class Ui_EditSongDialog(object): self.themeComboBox = QtGui.QComboBox(self.themeGroupBox) self.themeComboBox.setSizeAdjustPolicy( QtGui.QComboBox.AdjustToMinimumContentsLength) - self.themeComboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, - QtGui.QSizePolicy.Fixed) + self.themeComboBox.setSizePolicy( + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) self.themeComboBox.setEditable(True) self.themeComboBox.setInsertPolicy(QtGui.QComboBox.NoInsert) self.themeComboBox.setObjectName(u'themeComboBox') diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 202cc43fe..86249f024 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.plugins.songs.forms import EditVerseForm -from openlp.plugins.songs.lib import SongXMLBuilder, SongXMLParser, VerseType +from openlp.plugins.songs.lib import SongXML, VerseType from openlp.plugins.songs.lib.db import Book, Song, Author, Topic from editsongdialog import Ui_EditSongDialog @@ -263,8 +263,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): if isinstance(self.song.lyrics, buffer): self.song.lyrics = unicode(self.song.lyrics) if self.song.lyrics.startswith(u' - + + + +The XML of `OpenLyrics `_ songs is of the format:: + + + + + Amazing Grace + + + + + + Amazing grace how sweet the sound + + + + """ import logging import re from lxml import etree, objectify + +from openlp.core.lib import translate from openlp.plugins.songs.lib import VerseType -from openlp.plugins.songs.lib.db import Author, Song +from openlp.plugins.songs.lib.db import Author, Book, Song, Topic log = logging.getLogger(__name__) -class SongXMLBuilder(object): +class SongXML(object): """ - This class builds the XML used to describe songs. + This class builds and parses the XML used to describe songs. """ - log.info(u'SongXMLBuilder Loaded') + log.info(u'SongXML Loaded') - def __init__(self, song_language=None): + def __init__(self): """ - Set up the song builder. - - ``song_language`` - The language used in this song + Set up the default variables. """ - lang = u'en' - if song_language: - lang = song_language self.song_xml = objectify.fromstring(u'') - self.lyrics = etree.SubElement(self.song_xml, u'lyrics', language=lang) + self.lyrics = etree.SubElement(self.song_xml, u'lyrics') def add_verse_to_lyrics(self, type, number, content): """ - Add a verse to the ```` tag. + Add a verse to the ** tag. ``type`` - A string denoting the type of verse. Possible values are "Chorus", - "Verse", "Bridge", and "Custom". + A string denoting the type of verse. Possible values are "V", + "C", "B", "P", "I", "E" and "O". ``number`` An integer denoting the number of the item, for example: verse 1. @@ -80,18 +98,11 @@ class SongXMLBuilder(object): ``content`` The actual text of the verse to be stored. """ - verse = etree.Element(u'verse', type = unicode(type), - label = unicode(number)) + verse = etree.Element(u'verse', type=unicode(type), + label=unicode(number)) verse.text = etree.CDATA(content) self.lyrics.append(verse) - def dump_xml(self): - """ - Debugging aid to dump XML so that we can see what we have. - """ - return etree.tostring(self.song_xml, encoding=u'UTF-8', - xml_declaration=True, pretty_print=True) - def extract_xml(self): """ Extract our newly created XML song. @@ -99,16 +110,10 @@ class SongXMLBuilder(object): return etree.tostring(self.song_xml, encoding=u'UTF-8', xml_declaration=True) - -class SongXMLParser(object): - """ - A class to read in and parse a song's XML. - """ - log.info(u'SongXMLParser Loaded') - - def __init__(self, xml): + def get_verses(self, xml): """ - Set up our song XML parser. + Iterates through the verses in the XML and returns a list of verses + and their attributes. ``xml`` The XML of the song to be parsed. @@ -120,12 +125,6 @@ class SongXMLParser(object): self.song_xml = objectify.fromstring(xml) except etree.XMLSyntaxError: log.exception(u'Invalid xml %s', xml) - - def get_verses(self): - """ - Iterates through the verses in the XML and returns a list of verses - and their attributes. - """ xml_iter = self.song_xml.getiterator() verse_list = [] for element in xml_iter: @@ -142,137 +141,109 @@ class SongXMLParser(object): return etree.dump(self.song_xml) -class LyricsXML(object): +class OpenLyrics(object): """ - This class represents the XML in the ``lyrics`` field of a song. - """ - def __init__(self, song=None): - if song: - if song.lyrics.startswith(u'* + OpenLP does not support the attribute *type* and *lang*. - def extract(self, text): - """ - If the ``lyrics`` field in the database is not XML, this method is - called and used to construct the verse structure similar to the output - of the ``parse`` function. + ** + This property is not supported. - ``text`` - The text to pull verses out of. - """ - text = text.replace('\r\n', '\n') - verses = text.split('\n\n') - self.languages = [{u'language': u'en', u'verses': []}] - counter = 0 - for verse in verses: - counter = counter + 1 - self.languages[0][u'verses'].append({ - u'type': u'verse', - u'label': unicode(counter), - u'text': verse - }) - return True + ** + The ** property is fully supported. But comments in lyrics + are not supported. - def add_verse(self, type, label, text): - """ - Add a verse to the list of verses. + ** + This property is fully supported. - ``type`` - The type of list, one of "verse", "chorus", "bridge", "pre-chorus", - "intro", "outtro". + ** + This property is not supported. - ``label`` - The number associated with this verse, like 1 or 2. + ** + This property is not supported. - ``text`` - The text of the verse. - """ - self.verses.append({ - u'type': type, - u'label': label, - u'text': text - }) + ** + This property is not supported. - def export(self): - """ - Build up the XML for the verse structure. - """ - lyrics_output = u'' - for language in self.languages: - verse_output = u'' - for verse in language[u'verses']: - verse_output = verse_output + \ - u'' % \ - (verse[u'type'], verse[u'label'], verse[u'text']) - lyrics_output = lyrics_output + \ - u'%s' % \ - (language[u'language'], verse_output) - song_output = u'' + \ - u'%s' % lyrics_output - return song_output + ** + The attribute *part* is not supported. + ** + This property is not supported. -class OpenLyricsParser(object): - """ - This class represents the converter for Song to/from OpenLyrics XML. + ** + As OpenLP does only support one songbook, we cannot consider more than + one songbook. + + ** + This property is not supported. + + ** + Topics, as they are called in OpenLP, are fully supported, whereby only + the topic text (e. g. Grace) is considered, but neither the *id* nor + *lang*. + + ** + This property is not supported. + + ** + This property is not supported. + + ** + The attribute *translit* and *lang* are not supported. + + ** + OpenLP supports this property. """ def __init__(self, manager): self.manager = manager - def song_to_xml(self, song): + def song_to_xml(self, song, pretty_print=False): """ - Convert the song to OpenLyrics Format + Convert the song to OpenLyrics Format. """ - song_xml_parser = SongXMLParser(song.lyrics) - verse_list = song_xml_parser.get_verses() + sxml = SongXML() + verse_list = sxml.get_verses(song.lyrics) song_xml = objectify.fromstring( u'') properties = etree.SubElement(song_xml, u'properties') titles = etree.SubElement(properties, u'titles') - self._add_text_to_element(u'title', titles, song.title) + self._add_text_to_element(u'title', titles, song.title.strip()) if song.alternate_title: - self._add_text_to_element(u'title', titles, song.alternate_title) - if song.theme_name: - themes = etree.SubElement(properties, u'themes') - self._add_text_to_element(u'theme', themes, song.theme_name) - self._add_text_to_element(u'copyright', properties, song.copyright) - self._add_text_to_element(u'verseOrder', properties, song.verse_order) + self._add_text_to_element( + u'title', titles, song.alternate_title.strip()) + if song.comments: + comments = etree.SubElement(properties, u'comments') + self._add_text_to_element(u'comment', comments, song.comments) + if song.copyright: + self._add_text_to_element(u'copyright', properties, song.copyright) + if song.verse_order: + self._add_text_to_element( + u'verseOrder', properties, song.verse_order) if song.ccli_number: self._add_text_to_element(u'ccliNo', properties, song.ccli_number) - authors = etree.SubElement(properties, u'authors') - for author in song.authors: - self._add_text_to_element(u'author', authors, author.display_name) + if song.authors: + authors = etree.SubElement(properties, u'authors') + for author in song.authors: + self._add_text_to_element( + u'author', authors, author.display_name) + book = self.manager.get_object_filtered( + Book, Book.id == song.song_book_id) + if book is not None: + book = book.name + songbooks = etree.SubElement(properties, u'songbooks') + element = self._add_text_to_element( + u'songbook', songbooks, None, book) + element.set(u'entry', song.song_number) + if song.topics: + themes = etree.SubElement(properties, u'themes') + for topic in song.topics: + self._add_text_to_element(u'theme', themes, topic.name) lyrics = etree.SubElement(song_xml, u'lyrics') for verse in verse_list: verse_tag = u'%s%s' % ( @@ -282,78 +253,36 @@ class OpenLyricsParser(object): element = self._add_text_to_element(u'lines', element) for line in unicode(verse[1]).split(u'\n'): self._add_text_to_element(u'line', element, line) - return self._extract_xml(song_xml) + return self._extract_xml(song_xml, pretty_print) def xml_to_song(self, xml): """ - Create a Song from OpenLyrics format xml + Create and save a song from OpenLyrics format xml to the database. Since + we also export XML from external sources (e. g. OpenLyrics import), we + cannot ensure, that it completely conforms to the OpenLyrics standard. + + ``xml`` + The XML to parse (unicode). """ - # No xml get out of here + # No xml get out of here. if not xml: return 0 song = Song() if xml[:5] == u'').sub(u'', xml) song_xml = objectify.fromstring(xml) properties = song_xml.properties - song.copyright = unicode(properties.copyright.text) - if song.copyright == u'None': - song.copyright = u'' - song.verse_order = unicode(properties.verseOrder.text) - if song.verse_order == u'None': - song.verse_order = u'' - song.topics = [] - song.book = None - theme_name = None - try: - song.ccli_number = unicode(properties.ccliNo.text) - except: - song.ccli_number = u'' - try: - theme_name = unicode(properties.themes.theme) - except: - pass - if theme_name: - song.theme_name = theme_name - else: - song.theme_name = u'' - # Process Titles - for title in properties.titles.title: - if not song.title: - song.title = unicode(title.text) - song.search_title = unicode(song.title) - song.alternate_title = u'' - else: - song.alternate_title = unicode(title.text) - song.search_title += u'@' + song.alternate_title - song.search_title = re.sub(r'[\'"`,;:(){}?]+', u'', - unicode(song.search_title)).lower() - # Process Lyrics - sxml = SongXMLBuilder() - search_text = u'' - for lyrics in song_xml.lyrics: - for verse in song_xml.lyrics.verse: - text = u'' - for line in verse.lines.line: - line = unicode(line) - if not text: - text = line - else: - text += u'\n' + line - type = VerseType.expand_string(verse.attrib[u'name'][0]) - sxml.add_verse_to_lyrics(type, verse.attrib[u'name'][1], text) - search_text = search_text + text - song.search_lyrics = search_text.lower() - song.lyrics = unicode(sxml.extract_xml(), u'utf-8') - song.comments = u'' - song.song_number = u'' - # Process Authors - try: - for author in properties.authors.author: - self._process_author(author.text, song) - except: - # No Author in XML so ignore - pass + self._process_copyright(properties, song) + self._process_cclinumber(properties, song) + self._process_titles(properties, song) + # The verse order is processed with the lyrics! + self._process_lyrics(properties, song_xml.lyrics, song) + self._process_comments(properties, song) + self._process_authors(properties, song) + self._process_songbooks(properties, song) + self._process_topics(properties, song) self.manager.save_object(song) return song.id @@ -367,33 +296,258 @@ class OpenLyricsParser(object): parent.append(element) return element + def _extract_xml(self, xml, pretty_print): + """ + Extract our newly created XML song. + """ + return etree.tostring(xml, encoding=u'UTF-8', + xml_declaration=True, pretty_print=pretty_print) + + def _get(self, element, attribute): + """ + This returns the element's attribute as unicode string. + + ``element`` + The element. + + ``attribute`` + The element's attribute (unicode). + """ + if element.get(attribute) is not None: + return unicode(element.get(attribute)) + return u'' + + def _text(self, element): + """ + This returns the text of an element as unicode string. + + ``element`` + The element. + """ + if element.text is not None: + return unicode(element.text) + return u'' + + def _process_authors(self, properties, song): + """ + Adds the authors specified in the XML to the song. + + ``properties`` + The property object (lxml.objectify.ObjectifiedElement). + + ``song`` + The song object. + """ + authors = [] + try: + for author in properties.authors.author: + display_name = self._text(author) + if display_name: + authors.append(display_name) + except AttributeError: + pass + if not authors: + # Add "Author unknown" (can be translated). + authors.append((unicode(translate('SongsPlugin.XML', + 'Author unknown')))) + for display_name in authors: + author = self.manager.get_object_filtered(Author, + Author.display_name == display_name) + if author is None: + # We need to create a new author, as the author does not exist. + author = Author.populate(display_name=display_name, + last_name=display_name.split(u' ')[-1], + first_name=u' '.join(display_name.split(u' ')[:-1])) + self.manager.save_object(author) + song.authors.append(author) + + def _process_cclinumber(self, properties, song): + """ + Adds the CCLI number to the song. + + ``properties`` + The property object (lxml.objectify.ObjectifiedElement). + + ``song`` + The song object. + """ + try: + song.ccli_number = self._text(properties.ccliNo) + except AttributeError: + song.ccli_number = u'' + + def _process_comments(self, properties, song): + """ + Joins the comments specified in the XML and add it to the song. + + ``properties`` + The property object (lxml.objectify.ObjectifiedElement). + + ``song`` + The song object. + """ + try: + comments_list = [] + for comment in properties.comments.comment: + commenttext = self._text(comment) + if commenttext: + comments_list.append(commenttext) + song.comments = u'\n'.join(comments_list) + except AttributeError: + song.comments = u'' + + def _process_copyright(self, properties, song): + """ + Adds the copyright to the song. + + ``properties`` + The property object (lxml.objectify.ObjectifiedElement). + + ``song`` + The song object. + """ + try: + song.copyright = self._text(properties.copyright) + except AttributeError: + song.copyright = u'' + + def _process_lyrics(self, properties, lyrics, song): + """ + Processes the verses and search_lyrics for the song. + + ``properties`` + The properties object (lxml.objectify.ObjectifiedElement). + + ``lyrics`` + The lyrics object (lxml.objectify.ObjectifiedElement). + + ``song`` + The song object. + """ + sxml = SongXML() + search_text = u'' + temp_verse_order = [] + for verse in lyrics.verse: + text = u'' + for lines in verse.lines: + if text: + text += u'\n' + text += u'\n'.join([unicode(line) for line in lines.line]) + verse_name = self._get(verse, u'name') + verse_type = unicode(VerseType.expand_string(verse_name[0]))[0] + verse_number = re.compile(u'[a-zA-Z]*').sub(u'', verse_name) + verse_part = re.compile(u'[0-9]*').sub(u'', verse_name[1:]) + # OpenLyrics allows e. g. "c", but we need "c1". + if not verse_number: + verse_number = u'1' + temp_verse_order.append((verse_type, verse_number, verse_part)) + sxml.add_verse_to_lyrics(verse_type, verse_number, text) + search_text = search_text + text + song.search_lyrics = search_text.lower() + song.lyrics = unicode(sxml.extract_xml(), u'utf-8') + # Process verse order + try: + song.verse_order = self._text(properties.verseOrder) + except AttributeError: + # We have to process the temp_verse_order, as the verseOrder + # property is not present. + previous_type = u'' + previous_number = u'' + previous_part = u'' + verse_order = [] + # Currently we do not support different "parts"! + for name in temp_verse_order: + if name[0] == previous_type: + if name[1] != previous_number: + verse_order.append(u''.join((name[0], name[1]))) + else: + verse_order.append(u''.join((name[0], name[1]))) + previous_type = name[0] + previous_number = name[1] + previous_part = name[2] + song.verse_order = u' '.join(verse_order) + + def _process_songbooks(self, properties, song): + """ + Adds the song book and song number specified in the XML to the song. + + ``properties`` + The property object (lxml.objectify.ObjectifiedElement). + + ``song`` + The song object. + """ + song.song_book_id = 0 + song.song_number = u'' + try: + for songbook in properties.songbooks.songbook: + bookname = self._get(songbook, u'name') + if bookname: + book = self.manager.get_object_filtered(Book, + Book.name == bookname) + if book is None: + # We need to create a book, because it does not exist. + book = Book.populate(name=bookname, publisher=u'') + self.manager.save_object(book) + song.song_book_id = book.id + try: + if self._get(songbook, u'entry'): + song.song_number = self._get(songbook, u'entry') + except AttributeError: + pass + # We does only support one song book, so take the first one. + break + except AttributeError: + pass + + def _process_titles(self, properties, song): + """ + Processes the titles specified in the song's XML. + + ``properties`` + The property object (lxml.objectify.ObjectifiedElement). + + ``song`` + The song object. + """ + for title in properties.titles.title: + if not song.title: + song.title = self._text(title) + song.search_title = unicode(song.title) + song.alternate_title = u'' + else: + song.alternate_title = self._text(title) + song.search_title += u'@' + song.alternate_title + song.search_title = re.sub(r'[\'"`,;:(){}?]+', u'', + unicode(song.search_title)).lower() + + def _process_topics(self, properties, song): + """ + Adds the topics to the song. + + ``properties`` + The property object (lxml.objectify.ObjectifiedElement). + + ``song`` + The song object. + """ + try: + for topictext in properties.themes.theme: + topictext = self._text(topictext) + if topictext: + topic = self.manager.get_object_filtered(Topic, + Topic.name == topictext) + if topic is None: + # We need to create a topic, because it does not exist. + topic = Topic.populate(name=topictext) + self.manager.save_object(topic) + song.topics.append(topic) + except AttributeError: + pass + def _dump_xml(self, xml): """ Debugging aid to dump XML so that we can see what we have. """ return etree.tostring(xml, encoding=u'UTF-8', xml_declaration=True, pretty_print=True) - - def _extract_xml(self, xml): - """ - Extract our newly created XML song. - """ - return etree.tostring(xml, encoding=u'UTF-8', - xml_declaration=True) - - def _process_author(self, name, song): - """ - Find or create an Author from display_name. - """ - name = unicode(name) - author = self.manager.get_object_filtered(Author, - Author.display_name == name) - if author: - # should only be one! so take the first - song.authors.append(author) - else: - # Need a new author - new_author = Author.populate(first_name=name.rsplit(u' ', 1)[0], - last_name=name.rsplit(u' ', 1)[1], display_name=name) - self.manager.save_object(new_author) - song.authors.append(new_author) \ No newline at end of file diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 545497acb..17e609fd4 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.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.plugins.songs.lib import SongMediaItem, SongsTab, SongXMLParser +from openlp.plugins.songs.lib import SongMediaItem, SongsTab, SongXML from openlp.plugins.songs.lib.db import init_schema, Song from openlp.plugins.songs.lib.importer import SongFormat @@ -153,7 +153,7 @@ class SongsPlugin(Plugin): song.search_title = self.whitespace.sub(u' ', song.title.lower() + \ u' ' + song.alternate_title.lower()) lyrics = u'' - verses = SongXMLParser(song.lyrics).get_verses() + verses = SongXML().get_verses(song.lyrics) for verse in verses: lyrics = lyrics + self.whitespace.sub(u' ', verse[1]) + u' ' song.search_lyrics = lyrics.lower()