diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index 5763b3470..0f62a6dcf 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -63,7 +63,6 @@ from splashscreen import SplashScreen from generaltab import GeneralTab from themestab import ThemesTab from advancedtab import AdvancedTab -from displaytagtab import DisplayTagTab from aboutform import AboutForm from pluginform import PluginForm from settingsform import SettingsForm diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index 39fec1f9d..5cb22cb8b 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -28,8 +28,9 @@ The :mod:`advancedtab` provides an advanced settings facility. """ from PyQt4 import QtCore, QtGui -from openlp.core.lib import SettingsTab, translate +from openlp.core.lib import SettingsTab, translate, build_icon from openlp.core.lib.ui import UiStrings +from openlp.core.utils import get_images_filter class AdvancedTab(SettingsTab): """ @@ -41,6 +42,8 @@ class AdvancedTab(SettingsTab): Initialise the settings tab """ SettingsTab.__init__(self, u'Advanced') + self.default_image = u':/graphics/openlp-splash-screen.png' + self.default_color = u'#ffffff' def setupUi(self): """ @@ -81,33 +84,38 @@ class AdvancedTab(SettingsTab): self.hideMouseCheckBox.setObjectName(u'hideMouseCheckBox') self.hideMouseLayout.addWidget(self.hideMouseCheckBox) self.leftLayout.addWidget(self.hideMouseGroupBox) -# self.sharedDirGroupBox = QtGui.QGroupBox(self.leftColumn) -# self.sharedDirGroupBox.setObjectName(u'sharedDirGroupBox') -# self.sharedDirLayout = QtGui.QFormLayout(self.sharedDirGroupBox) -# self.sharedCheckBox = QtGui.QCheckBox(self.sharedDirGroupBox) -# self.sharedCheckBox.setObjectName(u'sharedCheckBox') -# self.sharedDirLayout.addRow(self.sharedCheckBox) -# self.sharedLabel = QtGui.QLabel(self.sharedDirGroupBox) -# self.sharedLabel.setObjectName(u'sharedLabel') -# self.sharedSubLayout = QtGui.QHBoxLayout() -# self.sharedSubLayout.setObjectName(u'sharedSubLayout') -# self.sharedLineEdit = QtGui.QLineEdit(self.sharedDirGroupBox) -# self.sharedLineEdit.setObjectName(u'sharedLineEdit') -# self.sharedSubLayout.addWidget(self.sharedLineEdit) -# self.sharedPushButton = QtGui.QPushButton(self.sharedDirGroupBox) -# self.sharedPushButton.setObjectName(u'sharedPushButton') -# self.sharedSubLayout.addWidget(self.sharedPushButton) -# self.sharedDirLayout.addRow(self.sharedLabel, self.sharedSubLayout) -# self.leftLayout.addWidget(self.sharedDirGroupBox) self.leftLayout.addStretch() -# self.databaseGroupBox = QtGui.QGroupBox(self.rightColumn) -# self.databaseGroupBox.setObjectName(u'databaseGroupBox') -# self.databaseGroupBox.setEnabled(False) -# self.databaseLayout = QtGui.QVBoxLayout(self.databaseGroupBox) -# self.rightLayout.addWidget(self.databaseGroupBox) + self.defaultImageGroupBox = QtGui.QGroupBox(self.rightColumn) + self.defaultImageGroupBox.setObjectName(u'defaultImageGroupBox') + self.defaultImageLayout = QtGui.QFormLayout(self.defaultImageGroupBox) + self.defaultImageLayout.setObjectName(u'defaultImageLayout') + self.defaultColorLabel = QtGui.QLabel(self.defaultImageGroupBox) + self.defaultColorLabel.setObjectName(u'defaultColorLabel') + self.defaultColorButton = QtGui.QPushButton(self.defaultImageGroupBox) + self.defaultColorButton.setObjectName(u'defaultColorButton') + self.defaultImageLayout.addRow(self.defaultColorLabel, + self.defaultColorButton) + self.defaultFileLabel = QtGui.QLabel(self.defaultImageGroupBox) + self.defaultFileLabel.setObjectName(u'defaultFileLabel') + self.defaultFileEdit = QtGui.QLineEdit(self.defaultImageGroupBox) + self.defaultFileEdit.setObjectName(u'defaultFileEdit') + self.defaultBrowseButton = QtGui.QToolButton(self.defaultImageGroupBox) + self.defaultBrowseButton.setObjectName(u'defaultBrowseButton') + self.defaultBrowseButton.setIcon( + build_icon(u':/general/general_open.png')) + self.defaultFileLayout = QtGui.QHBoxLayout() + self.defaultFileLayout.setObjectName(u'defaultFileLayout') + self.defaultFileLayout.addWidget(self.defaultFileEdit) + self.defaultFileLayout.addWidget(self.defaultBrowseButton) + self.defaultImageLayout.addRow(self.defaultFileLabel, + self.defaultFileLayout) + self.rightLayout.addWidget(self.defaultImageGroupBox) self.rightLayout.addStretch() -# QtCore.QObject.connect(self.sharedCheckBox, -# QtCore.SIGNAL(u'stateChanged(int)'), self.onSharedCheckBoxChanged) + + QtCore.QObject.connect(self.defaultColorButton, + QtCore.SIGNAL(u'pressed()'), self.onDefaultColorButtonPressed) + QtCore.QObject.connect(self.defaultBrowseButton, + QtCore.SIGNAL(u'pressed()'), self.onDefaultBrowseButtonPressed) def retranslateUi(self): """ @@ -129,14 +137,13 @@ class AdvancedTab(SettingsTab): self.hideMouseGroupBox.setTitle(translate('OpenLP.AdvancedTab', 'Mouse Cursor')) self.hideMouseCheckBox.setText(translate('OpenLP.AdvancedTab', - 'Hide the mouse cursor when moved over the display window')) -# self.sharedDirGroupBox.setTitle( -# translate('AdvancedTab', 'Central Data Store')) -# self.sharedCheckBox.setText( -# translate('AdvancedTab', 'Enable a shared data location')) -# self.sharedLabel.setText(translate('AdvancedTab', 'Store location:')) -# self.sharedPushButton.setText(UiStrings.Browse) -# self.databaseGroupBox.setTitle(translate('AdvancedTab', 'Databases')) + 'Hide mouse cursor when over display window')) + self.defaultImageGroupBox.setTitle(translate('OpenLP.AdvancedTab', + 'Default Image')) + self.defaultColorLabel.setText(translate('OpenLP.AdvancedTab', + 'Background color:')) + self.defaultFileLabel.setText(translate('OpenLP.AdvancedTab', + 'Image file:')) def load(self): """ @@ -165,7 +172,14 @@ class AdvancedTab(SettingsTab): QtCore.QVariant(True)).toBool()) self.hideMouseCheckBox.setChecked( settings.value(u'hide mouse', QtCore.QVariant(False)).toBool()) + self.default_color = settings.value(u'default color', + QtCore.QVariant(u'#ffffff')).toString() + self.defaultFileEdit.setText(settings.value(u'default image', + QtCore.QVariant(u':/graphics/openlp-splash-screen.png'))\ + .toString()) settings.endGroup() + self.defaultColorButton.setStyleSheet( + u'background-color: %s' % self.default_color) def save(self): """ @@ -185,12 +199,24 @@ class AdvancedTab(SettingsTab): QtCore.QVariant(self.enableAutoCloseCheckBox.isChecked())) settings.setValue(u'hide mouse', QtCore.QVariant(self.hideMouseCheckBox.isChecked())) + settings.setValue(u'default color', self.default_color) + settings.setValue(u'default image', self.defaultFileEdit.text()) settings.endGroup() -# def onSharedCheckBoxChanged(self, checked): -# """ -# Enables the widgets to allow a shared data location -# """ -# self.sharedLabel.setEnabled(checked) -# self.sharedTextEdit.setEnabled(checked) -# self.sharedPushButton.setEnabled(checked) + def onDefaultColorButtonPressed(self): + new_color = QtGui.QColorDialog.getColor( + QtGui.QColor(self.default_color), self) + if new_color.isValid(): + self.default_color = new_color.name() + self.defaultColorButton.setStyleSheet( + u'background-color: %s' % self.default_color) + + def onDefaultBrowseButtonPressed(self): + file_filters = u'%s;;%s (*.*) (*)' % (get_images_filter(), + UiStrings.AllFiles) + filename = QtGui.QFileDialog.getOpenFileName(self, + translate('OpenLP.AdvancedTab', 'Open File'), '', + file_filters) + if filename: + self.defaultFileEdit.setText(filename) + self.defaultFileEdit.setFocus() diff --git a/openlp/core/ui/displaytagdialog.py b/openlp/core/ui/displaytagdialog.py index ab3363058..edb8567cf 100644 --- a/openlp/core/ui/displaytagdialog.py +++ b/openlp/core/ui/displaytagdialog.py @@ -123,30 +123,30 @@ class Ui_DisplayTagDialog(object): QtCore.QMetaObject.connectSlotsByName(displayTagDialog) def retranslateUi(self, displayTagDialog): - displayTagDialog.setWindowTitle(translate('OpenLP.displayTagForm', + displayTagDialog.setWindowTitle(translate('OpenLP.displayTagDialog', 'Configure Display Tags')) self.editGroupBox.setTitle( - translate('OpenLP.DisplayTagTab', 'Edit Selection')) + translate('OpenLP.DisplayTagDialog', 'Edit Selection')) self.updatePushButton.setText( - translate('OpenLP.DisplayTagTab', 'Update')) + translate('OpenLP.DisplayTagDialog', 'Update')) self.descriptionLabel.setText( - translate('OpenLP.DisplayTagTab', 'Description')) - self.tagLabel.setText(translate('OpenLP.DisplayTagTab', 'Tag')) + translate('OpenLP.DisplayTagDialog', 'Description')) + self.tagLabel.setText(translate('OpenLP.DisplayTagDialog', 'Tag')) self.startTagLabel.setText( - translate('OpenLP.DisplayTagTab', 'Start tag')) - self.endTagLabel.setText(translate('OpenLP.DisplayTagTab', 'End tag')) + translate('OpenLP.DisplayTagDialog', 'Start tag')) + self.endTagLabel.setText(translate('OpenLP.DisplayTagDialog', 'End tag')) self.deletePushButton.setText(UiStrings.Delete) self.defaultPushButton.setText( - translate('OpenLP.DisplayTagTab', 'Default')) + translate('OpenLP.DisplayTagDialog', 'Default')) self.newPushButton.setText(UiStrings.New) self.tagTableWidget.horizontalHeaderItem(0).setText( - translate('OpenLP.DisplayTagTab', 'Description')) + translate('OpenLP.DisplayTagDialog', 'Description')) self.tagTableWidget.horizontalHeaderItem(1).setText( - translate('OpenLP.DisplayTagTab', 'Tag id')) + translate('OpenLP.DisplayTagDialog', 'Tag id')) self.tagTableWidget.horizontalHeaderItem(2).setText( - translate('OpenLP.DisplayTagTab', 'Start Html')) + translate('OpenLP.DisplayTagDialog', 'Start Html')) self.tagTableWidget.horizontalHeaderItem(3).setText( - translate('OpenLP.DisplayTagTab', 'End Html')) + translate('OpenLP.DisplayTagDialog', 'End Html')) self.tagTableWidget.setColumnWidth(0, 120) self.tagTableWidget.setColumnWidth(1, 40) self.tagTableWidget.setColumnWidth(2, 240) diff --git a/openlp/core/ui/displaytagform.py b/openlp/core/ui/displaytagform.py index f7a8e7740..fa05c578a 100644 --- a/openlp/core/ui/displaytagform.py +++ b/openlp/core/ui/displaytagform.py @@ -47,6 +47,7 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog): """ QtGui.QDialog.__init__(self, parent) self.setupUi(self) + self.preLoad() QtCore.QObject.connect(self.tagTableWidget, QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onRowSelected) QtCore.QObject.connect(self.defaultPushButton, @@ -63,7 +64,18 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog): Load Display and set field state. """ # Create initial copy from master + self.preLoad() self._resetTable() + self.selected = -1 + return QtGui.QDialog.exec_(self) + + def preLoad(self): + """ + Load the Tags from store so can be used in the system or used to + update the display. If Cancel was selected this is needed to reset the + dsiplay to the correct version. + """ + # Initial Load of the Tags DisplayTags.reset_html_tags() user_expands = QtCore.QSettings().value(u'displayTags/html_tags', QtCore.QVariant(u'')).toString() @@ -74,37 +86,6 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog): # If we have some user ones added them as well for t in user_tags: DisplayTags.add_html_tag(t) - self.selected = -1 - self.load() - return QtGui.QDialog.exec_(self) - - def load(self): - """ - Load the form with data and set the initial state of the buttons - """ - self.newPushButton.setEnabled(True) - self.updatePushButton.setEnabled(False) - self.deletePushButton.setEnabled(False) - for linenumber, html in enumerate(DisplayTags.get_html_tags()): - self.tagTableWidget.setRowCount( - self.tagTableWidget.rowCount() + 1) - self.tagTableWidget.setItem(linenumber, 0, - QtGui.QTableWidgetItem(html[u'desc'])) - self.tagTableWidget.setItem(linenumber, 1, - QtGui.QTableWidgetItem(self._strip(html[u'start tag']))) - self.tagTableWidget.setItem(linenumber, 2, - QtGui.QTableWidgetItem(html[u'start html'])) - self.tagTableWidget.setItem(linenumber, 3, - QtGui.QTableWidgetItem(html[u'end html'])) - self.tagTableWidget.resizeRowsToContents() - self.descriptionLineEdit.setText(u'') - self.tagLineEdit.setText(u'') - self.startTagLineEdit.setText(u'') - self.endTagLineEdit.setText(u'') - self.descriptionLineEdit.setEnabled(False) - self.tagLineEdit.setEnabled(False) - self.startTagLineEdit.setEnabled(False) - self.endTagLineEdit.setEnabled(False) def accept(self): """ @@ -223,6 +204,29 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog): """ self.tagTableWidget.clearContents() self.tagTableWidget.setRowCount(0) + self.newPushButton.setEnabled(True) + self.updatePushButton.setEnabled(False) + self.deletePushButton.setEnabled(False) + for linenumber, html in enumerate(DisplayTags.get_html_tags()): + self.tagTableWidget.setRowCount( + self.tagTableWidget.rowCount() + 1) + self.tagTableWidget.setItem(linenumber, 0, + QtGui.QTableWidgetItem(html[u'desc'])) + self.tagTableWidget.setItem(linenumber, 1, + QtGui.QTableWidgetItem(self._strip(html[u'start tag']))) + self.tagTableWidget.setItem(linenumber, 2, + QtGui.QTableWidgetItem(html[u'start html'])) + self.tagTableWidget.setItem(linenumber, 3, + QtGui.QTableWidgetItem(html[u'end html'])) + self.tagTableWidget.resizeRowsToContents() + self.descriptionLineEdit.setText(u'') + self.tagLineEdit.setText(u'') + self.startTagLineEdit.setText(u'') + self.endTagLineEdit.setText(u'') + self.descriptionLineEdit.setEnabled(False) + self.tagLineEdit.setEnabled(False) + self.startTagLineEdit.setEnabled(False) + self.endTagLineEdit.setEnabled(False) def _strip(self, tag): """ diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 90042f80b..91d61f1d7 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -132,14 +132,22 @@ class MainDisplay(DisplayWidget): painter_image.begin(self.black) painter_image.fillRect(self.black.rect(), QtCore.Qt.black) # Build the initial frame. + image_file = QtCore.QSettings().value(u'advanced/default image', + QtCore.QVariant(u':/graphics/openlp-splash-screen.png'))\ + .toString() + background_color = QtGui.QColor(QtCore.QSettings().value( + u'advanced/default color', + QtCore.QVariant(u'#ffffff')).toString()) + if not background_color.isValid(): + background_color = QtCore.Qt.white + splash_image = QtGui.QImage(image_file) initialFrame = QtGui.QImage( self.screens.current[u'size'].width(), self.screens.current[u'size'].height(), QtGui.QImage.Format_ARGB32_Premultiplied) - splash_image = QtGui.QImage(u':/graphics/openlp-splash-screen.png') painter_image = QtGui.QPainter() painter_image.begin(initialFrame) - painter_image.fillRect(initialFrame.rect(), QtCore.Qt.white) + painter_image.fillRect(initialFrame.rect(), background_color) painter_image.drawImage( (self.screens.current[u'size'].width() - splash_image.width()) / 2, diff --git a/openlp/core/ui/wizard.py b/openlp/core/ui/wizard.py index 2960607e4..209f3f212 100644 --- a/openlp/core/ui/wizard.py +++ b/openlp/core/ui/wizard.py @@ -47,6 +47,7 @@ class WizardStrings(object): CSV = u'CSV' EW = u'EasyWorship' ES = u'EasiSlides' + FP = u'Foilpresenter' OL = u'OpenLyrics' OS = u'OpenSong' OSIS = u'OSIS' diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 9f3f98636..8bf201180 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -61,8 +61,8 @@ class MediaMediaItem(MediaManagerItem): self.onNewPrompt = translate('MediaPlugin.MediaItem', 'Select Media') self.onNewFileMasks = unicode(translate('MediaPlugin.MediaItem', 'Videos (%s);;Audio (%s);;%s (*)')) % ( - u' '.join(self.parent.video_list), - u' '.join(self.parent.audio_list), UiStrings.AllFiles) + 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) diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py index 12887a76c..74a8f3f2e 100644 --- a/openlp/plugins/media/mediaplugin.py +++ b/openlp/plugins/media/mediaplugin.py @@ -45,28 +45,26 @@ class MediaPlugin(Plugin): self.icon = build_icon(self.icon_path) # passed with drag and drop messages self.dnd_id = u'Media' - self.audio_list = [] - self.video_list = [] + self.audio_extensions_list = [] + self.video_extensions_list = [] mimetypes.init() for mimetype in Phonon.BackendCapabilities.availableMimeTypes(): mimetype = unicode(mimetype) if mimetype.startswith(u'audio/'): - self._addToList(self.audio_list, mimetype) + self._addToList(self.audio_extensions_list, mimetype) elif mimetype.startswith(u'video/'): - self._addToList(self.video_list, mimetype) - log.info(u'MediaPlugin handles audio extensions: %s', - u' '.join(self.audio_list)) - log.info(u'MediaPlugin handles video extensions: %s', - u' '.join(self.video_list)) + self._addToList(self.video_extensions_list, mimetype) def _addToList(self, list, mimetype): - # Is it a media type + # Add all extensions which mimetypes provides us for supported types. extensions = mimetypes.guess_all_extensions(unicode(mimetype)) for extension in extensions: ext = u'*%s' % extension if ext not in list: list.append(ext) self.serviceManager.supportedSuffixes(extension[1:]) + log.info(u'MediaPlugin: %s extensions: %s' % (mimetype, + u' '.join(extensions))) def about(self): about_text = translate('MediaPlugin', 'Media Plugin' diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py index 6ec9476a8..7ced66103 100644 --- a/openlp/plugins/remotes/lib/httpserver.py +++ b/openlp/plugins/remotes/lib/httpserver.py @@ -68,11 +68,13 @@ class HttpServer(object): """ log.debug(u'Start TCP server') port = QtCore.QSettings().value( - self.parent.settingsSection + u'/remote port', + self.parent.settingsSection + u'/port', QtCore.QVariant(4316)).toInt()[0] + address = QtCore.QSettings().value( + self.parent.settingsSection + u'/ip address', + QtCore.QVariant(u'0.0.0.0')).toString() self.server = QtNetwork.QTcpServer() - self.server.listen(QtNetwork.QHostAddress(QtNetwork.QHostAddress.Any), - port) + self.server.listen(QtNetwork.QHostAddress(address), port) QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'slidecontroller_live_changed'), self.slide_change) @@ -347,4 +349,4 @@ class HttpConnection(object): log.debug(u'close socket') self.socket.close() self.socket = None - self.parent.close_connection(self) \ No newline at end of file + self.parent.close_connection(self) diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index d7a575de4..16c0f9edb 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -146,6 +146,12 @@ class SongImportForm(OpenLPWizard): QtCore.QObject.connect(self.songShowPlusRemoveButton, QtCore.SIGNAL(u'clicked()'), self.onSongShowPlusRemoveButtonClicked) + QtCore.QObject.connect(self.foilPresenterAddButton, + QtCore.SIGNAL(u'clicked()'), + self.onFoilPresenterAddButtonClicked) + QtCore.QObject.connect(self.foilPresenterRemoveButton, + QtCore.SIGNAL(u'clicked()'), + self.onFoilPresenterRemoveButtonClicked) def addCustomPages(self): """ @@ -196,6 +202,8 @@ class SongImportForm(OpenLPWizard): self.addFileSelectItem(u'songBeamer') # Song Show Plus self.addFileSelectItem(u'songShowPlus') + # Foilpresenter + self.addFileSelectItem(u'foilPresenter') # Commented out for future use. # self.addFileSelectItem(u'csv', u'CSV', single_select=True) self.sourceLayout.addLayout(self.formatStack) @@ -238,6 +246,8 @@ class SongImportForm(OpenLPWizard): SongFormat.SongBeamer, WizardStrings.SB) self.formatComboBox.setItemText( SongFormat.SongShowPlus, WizardStrings.SSP) + self.formatComboBox.setItemText( + SongFormat.FoilPresenter, WizardStrings.FP) # self.formatComboBox.setItemText(SongFormat.CSV, WizardStrings.CSV) self.openLP2FilenameLabel.setText( translate('SongsPlugin.ImportWizardForm', 'Filename:')) @@ -297,6 +307,10 @@ class SongImportForm(OpenLPWizard): translate('SongsPlugin.ImportWizardForm', 'Add Files...')) self.songShowPlusRemoveButton.setText( translate('SongsPlugin.ImportWizardForm', 'Remove File(s)')) + self.foilPresenterAddButton.setText( + translate('SongsPlugin.ImportWizardForm', 'Add Files...')) + self.foilPresenterRemoveButton.setText( + translate('SongsPlugin.ImportWizardForm', 'Remove File(s)')) # self.csvFilenameLabel.setText( # translate('SongsPlugin.ImportWizardForm', 'Filename:')) # self.csvBrowseButton.setText(UiStrings.Browse) @@ -394,6 +408,12 @@ class SongImportForm(OpenLPWizard): 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, + WizardStrings.YouSpecifyFile % WizardStrings.FP) + self.foilPresenterAddButton.setFocus() + return False return True elif self.currentPage() == self.progressPage: return True @@ -574,7 +594,7 @@ class SongImportForm(OpenLPWizard): Remove selected SongBeamer files from the import list """ self.removeSelectedItems(self.songBeamerFileListWidget) - + def onSongShowPlusAddButtonClicked(self): """ Get SongShow Plus song database files @@ -591,6 +611,22 @@ class SongImportForm(OpenLPWizard): """ self.removeSelectedItems(self.songShowPlusFileListWidget) + def onFoilPresenterAddButtonClicked(self): + """ + Get FoilPresenter song database files + """ + self.getFiles(WizardStrings.OpenTypeFile % WizardStrings.FP, + self.foilPresenterFileListWidget, u'%s (*.foil)' + % translate('SongsPlugin.ImportWizardForm', + 'Foilpresenter Song Files') + ) + + def onFoilPresenterRemoveButtonClicked(self): + """ + Remove selected FoilPresenter files from the import list + """ + self.removeSelectedItems(self.foilPresenterFileListWidget) + def setDefaults(self): """ Set default form values for the song import wizard. @@ -611,6 +647,7 @@ class SongImportForm(OpenLPWizard): self.ewFilenameEdit.setText(u'') self.songBeamerFileListWidget.clear() self.songShowPlusFileListWidget.clear() + self.foilPresenterFileListWidget.clear() #self.csvFilenameEdit.setText(u'') def preWizard(self): @@ -691,6 +728,11 @@ class SongImportForm(OpenLPWizard): importer = self.plugin.importSongs(SongFormat.SongShowPlus, filenames=self.getListOfFiles(self.songShowPlusFileListWidget) ) + elif source_format == SongFormat.FoilPresenter: + # Import Foilpresenter songs + importer = self.plugin.importSongs(SongFormat.FoilPresenter, + filenames=self.getListOfFiles(self.foilPresenterFileListWidget) + ) if importer.do_import(): self.progressLabel.setText(WizardStrings.FinishedImport) else: diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index 95fae92a8..70a47a239 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -25,7 +25,10 @@ ############################################################################### from PyQt4 import QtGui + from openlp.core.lib import translate +from db import Author +from ui import SongStrings class VerseType(object): """ @@ -241,6 +244,23 @@ def retrieve_windows_encoding(recommendation=None): return None return filter(lambda item: item[1] == choice[0], encodings)[0][0] +def add_author_unknown(manager, song): + """ + Add the default author *Author Unknown* to the song. + + ``manager`` + The song's manager. + + ``song`` + The song object. + """ + name = SongStrings.AuthorUnknown + author = manager.get_object_filtered(Author, Author.display_name == name) + if author is None: + author = Author.populate( + display_name=name, last_name=u'', first_name=u'') + song.authors.append(author) + from xml import OpenLyrics, SongXML from songstab import SongsTab from mediaitem import SongMediaItem diff --git a/openlp/plugins/songs/lib/foilpresenterimport.py b/openlp/plugins/songs/lib/foilpresenterimport.py new file mode 100644 index 000000000..2eded332f --- /dev/null +++ b/openlp/plugins/songs/lib/foilpresenterimport.py @@ -0,0 +1,576 @@ + # -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2011 Raoul Snyman # +# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian # +# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, # +# Carsten Tinggaard, Frode Woldsund # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +The XML of `Foilpresenter `_ songs is of the format:: + + + + 2004.6.18.18.44.37.0767 + 2011.1.21.8.53.5 + + Above all + + 1 + + + 0 + Notiz + 1.0 + + 0,0,0 + 255,255,255 + + + Standard + 0 + + + + 0 + Verdana + 14 + 0 + 0 + 0 + 1 + Above all powers, above all kings, + above all nature an all created things; + above all wisdom and all the ways of man, + You were here before the world began. + 1 + + + + Herr du bist maechtig.foil + + + Arial + 7 + 3 + 0 + 1 + 0 + Text und Musik: Lenny LeBlanc/Paul Baloche + + + + Feiert Jesus 3 + 10 + + + + Worship + + +""" + +import logging +import re +import os + +from lxml import etree, objectify + +from openlp.core.lib import translate +from openlp.core.ui.wizard import WizardStrings +from openlp.plugins.songs.lib import VerseType +from openlp.plugins.songs.lib.songimport import SongImport +from openlp.plugins.songs.lib.db import Author, Book, Song, Topic +from openlp.plugins.songs.lib.xml import SongXML +from openlp.plugins.songs.lib.ui import SongStrings + +log = logging.getLogger(__name__) + +class FoilPresenterImport(SongImport): + """ + This provides the Foilpresenter import. + """ + def __init__(self, master_manager, **kwargs): + """ + Initialise the import. + """ + log.debug(u'initialise FoilPresenterImport') + SongImport.__init__(self, master_manager, **kwargs) + self.FoilPresenter = FoilPresenter(self.manager) + + def do_import(self): + """ + Imports the songs. + """ + self.import_wizard.progressBar.setMaximum(len(self.import_source)) + parser = etree.XMLParser(remove_blank_text=True) + for file_path in self.import_source: + if self.stop_import_flag: + return False + self.import_wizard.incrementProgressBar( + WizardStrings.ImportingType % os.path.basename(file_path)) + try: + parsed_file = etree.parse(file_path, parser) + xml = unicode(etree.tostring(parsed_file)) + if self.FoilPresenter.xml_to_song(xml) is None: + log.debug(u'File could not be imported: %s' % file_path) + except etree.XMLSyntaxError: + log.exception(u'XML syntax error in file %s' % file_path) + return True + +class FoilPresenter(object): + """ + This class represents the converter for Foilpresenter XML from a song. + + As Foilpresenter has a rich set of different features, we cannot support + them all. The following features are supported by the :class:`Foilpresenter` + + OpenPL does not support styletype and font attributes like "align, font, + textsize, bold, italic, underline" + + ** + This property is currently not supported. + + ** + As OpenLP does only support one title, the first titlestring becomes + title, all other titlestrings will be alternate titles + + *<sprache>* + This property is not supported. + + *<ccliid>* + The *<ccliid>* property is fully supported. + + *<tonart>* + This property is currently not supported. + + *<valign>* + This property is not supported. + + *<notiz>* + The *<notiz>* property is fully supported. + + *<versionsinfo>* + This property is not supported. + + *<farben>* + This property is not supported. + + *<reihenfolge>* = verseOrder + OpenLP supports this property. + + *<strophen>* + Only the attributes *key* and *text* are supported. + + *<verkn>* + This property is not supported. + + *<verkn>* + This property is not supported. + + *<copyright>* + Only the attribute *text* is supported. => Done + + *<buch>* = songbooks + As OpenLP does only support one songbook, we cannot consider more than + one songbook. + + *<kategorien>* + This property is not supported. + + The tag *<author>* is not support by foilpresenter, mostly the author is + named in the <copyright> tag. We try to extract the authors from the + <copyright> tag. + + """ + def __init__(self, manager): + self.manager = manager + + def xml_to_song(self, xml): + """ + Create and save a song from Foilpresenter format xml to the database. + + ``xml`` + The XML to parse (unicode). + """ + # No xml get out of here. + if not xml: + return None + song = Song() + if xml[:5] == u'<?xml': + xml = xml[38:] + # Because "text" seems to be an reserverd word, we have to recompile it. + xml = re.compile(u'<text>').sub(u'<text_>', xml) + xml = re.compile(u'</text>').sub(u'</text_>', xml) + song_xml = objectify.fromstring(xml) + foilpresenterfolie = song_xml + self._process_copyright(foilpresenterfolie, song) + self._process_cclinumber(foilpresenterfolie, song) + self._process_titles(foilpresenterfolie, song) + # The verse order is processed with the lyrics! + self._process_lyrics(foilpresenterfolie, song) + self._process_comments(foilpresenterfolie, song) + self._process_authors(foilpresenterfolie, song) + self._process_songbooks(foilpresenterfolie, song) + self._process_topics(foilpresenterfolie, song) + self.manager.save_object(song) + return song.id + + def _child(self, element): + """ + This returns the text of an element as unicode string. + + ``element`` + The element. + """ + if element is not None: + return unicode(element) + return u'' + + def _process_authors(self, foilpresenterfolie, song): + """ + Adds the authors specified in the XML to the song. + + ``foilpresenterfolie`` + The property object (lxml.objectify.ObjectifiedElement). + + ``song`` + The song object. + """ + authors = [] + try: + copyright = self._child(foilpresenterfolie.copyright.text_) + except AttributeError: + copyright = None + if copyright: + strings = [] + author_temp = [] + if copyright.find(u'Copyright') != -1: + temp = copyright.partition(u'Copyright') + copyright = temp[0] + elif copyright.find(u'copyright') != -1: + temp = copyright.partition(u'copyright') + copyright = temp[0] + elif copyright.find(u'©') != -1: + temp = copyright.partition(u'©') + copyright = temp[0] + elif copyright.find(u'(c)') != -1: + temp = copyright.partition(u'(c)') + copyright = temp[0] + elif copyright.find(u'(C)') != -1: + temp = copyright.partition(u'(C)') + copyright = temp[0] + elif copyright.find(u'c)') != -1: + temp = copyright.partition(u'c)') + copyright = temp[0] + elif copyright.find(u'C)') != -1: + temp = copyright.partition(u'C)') + copyright = temp[0] + elif copyright.find(u'C:') != -1: + temp = copyright.partition(u'C:') + copyright = temp[0] + elif copyright.find(u'C,)') != -1: + temp = copyright.partition(u'C,)') + copyright = temp[0] + copyright = re.compile(u'\\n').sub(u' ', copyright) + copyright = re.compile(u'\(.*\)').sub(u'', copyright) + if copyright.find(u'Rechte') != -1: + temp = copyright.partition(u'Rechte') + copyright = temp[0] + markers = [u'Text +u\.?n?d? +Melodie[a-zA-Z0-9\,\. ]*:', + u'Text +u\.?n?d? +Musik', u'T & M', u'Melodie und Satz', + u'Text[a-zA-Z0-9\,\. ]*:', u'Melodie', u'Musik', u'Satz', + u'Weise', u'[dD]eutsch', u'[dD]t[\.\:]', u'Englisch', + u'[oO]riginal', u'Bearbeitung', u'[R|r]efrain'] + for marker in markers: + copyright = re.compile(marker).sub(u'<marker>', copyright) + copyright = re.compile(u'(?<=<marker>) *:').sub(u'', copyright) + i = 0 + x = 0 + while i != 1: + if copyright.find(u'<marker>') != -1: + temp = copyright.partition(u'<marker>') + if (temp[0].strip() != u'') & (x > 0): + strings.append(temp[0]) + copyright = temp[2] + x += 1 + elif x > 0: + strings.append(copyright) + i = 1 + else: + i = 1 + for author in strings: + temp = re.split(u',(?=\D{2})|(?<=\D),|\/(?=\D{3,})|(?<=\D);', + author) + for tempx in temp: + author_temp.append(tempx) + for author in author_temp: + regex = u'^[\/,;\-\s]+|[\/,;\-\s]+$|'\ + '\s*[0-9]{4}\s*[\-\/]?\s*([0-9]{4})?[\/,;\-\s]*$' + author = re.compile(regex).sub(u'', author) + author = re.compile( + u'[0-9]{1,2}\.\s?J(ahr)?h\.|um\s*$|vor\s*$').sub(u'', + author) + author = re.compile(u'[N|n]ach.*$').sub(u'', author) + author = author.strip() + if re.search( + u'\w+\.?\s+\w{3,}\s+[a|u]nd\s|\w+\.?\s+\w{3,}\s+&\s', + author, re.U) != None: + temp = re.split(u'\s[a|u]nd\s|\s&\s', author) + for tempx in temp: + tempx = tempx.strip() + authors.append(tempx) + elif (len(author) > 2): + authors.append(author) + if not authors: + authors.append(SongStrings.AuthorUnknownUnT) + 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, foilpresenterfolie, song): + """ + Adds the CCLI number to the song. + + ``foilpresenterfolie`` + The property object (lxml.objectify.ObjectifiedElement). + + ``song`` + The song object. + """ + try: + song.ccli_number = self._child(foilpresenterfolie.ccliid) + except AttributeError: + song.ccli_number = u'' + + def _process_comments(self, foilpresenterfolie, song): + """ + Joins the comments specified in the XML and add it to the song. + + ``foilpresenterfolie`` + The property object (lxml.objectify.ObjectifiedElement). + + ``song`` + The song object. + """ + try: + song.comments = self._child(foilpresenterfolie.notiz) + except AttributeError: + song.comments = u'' + + def _process_copyright(self, foilpresenterfolie, song): + """ + Adds the copyright to the song. + + ``foilpresenterfolie`` + The property object (lxml.objectify.ObjectifiedElement). + + ``song`` + The song object. + """ + try: + song.copyright = self._child(foilpresenterfolie.copyright.text_) + except AttributeError: + song.copyright = u'' + + def _process_lyrics(self, foilpresenterfolie, song): + """ + Processes the verses and search_lyrics for the song. + + ``foilpresenterfolie`` + The foilpresenterfolie object (lxml.objectify.ObjectifiedElement). + + ``song`` + The song object. + """ + sxml = SongXML() + search_text = u'' + temp_verse_order = {} + temp_verse_order_backup = [] + temp_verse_sort = [] + temp_sortnr_backup = 1 + temp_sortnr_liste = [] + versenumber = {u'V': 1, u'C': 1, u'B': 1, u'E': 1, u'O': 1, u'I': 1, + u'P': 1} + for strophe in foilpresenterfolie.strophen.strophe: + text = self._child(strophe.text_) + verse_name = self._child(strophe.key) + children = strophe.getchildren() + sortnr = False + for child in children: + if child.tag == u'sortnr': + verse_sortnr = self._child(strophe.sortnr) + sortnr = True + # In older Version there is no sortnr, but we need one + if sortnr == False: + verse_sortnr = unicode(temp_sortnr_backup) + temp_sortnr_backup += 1 + # Foilpresenter allows e. g. "Ref" or "1", but we need "C1" or "V1". + temp_sortnr_liste.append(verse_sortnr) + temp_verse_name = re.compile(u'[0-9].*').sub(u'', verse_name) + temp_verse_name = temp_verse_name[:3].lower() + if temp_verse_name == u'ref': + verse_type = u'C' + elif temp_verse_name == u'r': + verse_type = u'C' + elif temp_verse_name == u'': + verse_type = u'V' + elif temp_verse_name == u'v': + verse_type = u'V' + elif temp_verse_name == u'bri': + verse_type = u'B' + elif temp_verse_name == u'cod': + verse_type = u'E' + elif temp_verse_name == u'sch': + verse_type = u'E' + elif temp_verse_name == u'pre': + verse_type = u'P' + elif temp_verse_name == u'int': + verse_type = u'I' + else: + verse_type = u'O' + verse_number = re.compile(u'[a-zA-Z.+-_ ]*').sub(u'', verse_name) + verse_part = re.compile(u'[0-9]*').sub(u'', verse_name[1:]) + # Foilpresenter allows e. g. "C", but we need "C1". + if not verse_number: + verse_number = unicode(versenumber[verse_type]) + versenumber[verse_type] += 1 + else: + # test if foilpresenter have the same versenumber two times with + # different parts raise the verse number + for value in temp_verse_order_backup: + if value == (u''.join((verse_type, verse_number))): + verse_number = unicode(int(verse_number) + 1) + verse_type_index = VerseType.from_tag(verse_type[0]) + verse_type = VerseType.Names[verse_type_index] + temp_verse_order[verse_sortnr] = (u''.join((verse_type[0], + verse_number))) + temp_verse_order_backup.append(u''.join((verse_type[0], + verse_number))) + 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 + verse_order = [] + verse_strophenr = [] + for strophennummer in foilpresenterfolie.reihenfolge.strophennummer: + verse_strophenr.append(strophennummer) + # Currently we do not support different "parts"! + if u'0' in temp_verse_order: + for vers in temp_verse_order_backup: + verse_order.append(vers) + else: + for number in verse_strophenr: + numberx = temp_sortnr_liste[int(number)] + verse_order.append(temp_verse_order[unicode(numberx)]) + song.verse_order = u' '.join(verse_order) + + def _process_songbooks(self, foilpresenterfolie, song): + """ + Adds the song book and song number specified in the XML to the song. + + ``foilpresenterfolie`` + The property object (lxml.objectify.ObjectifiedElement). + + ``song`` + The song object. + """ + song.song_book_id = 0 + song.song_number = u'' + try: + for bucheintrag in foilpresenterfolie.buch.bucheintrag: + bookname = self._child(bucheintrag.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._child(bucheintrag.nummer): + song.song_number = self._child(bucheintrag.nummer) + except AttributeError: + pass + # We only support one song book, so take the first one. + break + except AttributeError: + pass + + def _process_titles(self, foilpresenterfolie, song): + """ + Processes the titles specified in the song's XML. + + ``foilpresenterfolie`` + The property object (lxml.objectify.ObjectifiedElement). + + ``song`` + The song object. + """ + for titelstring in foilpresenterfolie.titel.titelstring: + if not song.title: + song.title = self._child(titelstring) + song.search_title = unicode(song.title) + song.alternate_title = u'' + else: + song.alternate_title = self._child(titelstring) + song.search_title += u'@' + song.alternate_title + song.search_title = re.sub(r'[\'"`,;:(){}?]+', u'', + unicode(song.search_title)).lower() + + def _process_topics(self, foilpresenterfolie, song): + """ + Adds the topics to the song. + + ``foilpresenterfolie`` + The property object (lxml.objectify.ObjectifiedElement). + + ``song`` + The song object. + """ + try: + for name in foilpresenterfolie.kategorien.name: + topictext = self._child(name) + 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) diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py index 230dcd8d0..f3a64a998 100644 --- a/openlp/plugins/songs/lib/importer.py +++ b/openlp/plugins/songs/lib/importer.py @@ -35,6 +35,7 @@ from cclifileimport import CCLIFileImport from ewimport import EasyWorshipSongImport from songbeamerimport import SongBeamerImport from songshowplusimport import SongShowPlusImport +from foilpresenterimport import FoilPresenterImport # Imports that might fail try: from olp1import import OpenLP1SongImport @@ -72,7 +73,8 @@ class SongFormat(object): EasyWorship = 9 SongBeamer = 10 SongShowPlus = 11 - #CSV = 12 + FoilPresenter = 12 + #CSV = 13 @staticmethod def get_class(format): @@ -106,6 +108,8 @@ class SongFormat(object): return SongBeamerImport elif format == SongFormat.SongShowPlus: return SongShowPlusImport + elif format == SongFormat.FoilPresenter: + return FoilPresenterImport return None @staticmethod @@ -125,7 +129,8 @@ class SongFormat(object): SongFormat.EasiSlides, SongFormat.EasyWorship, SongFormat.SongBeamer, - SongFormat.SongShowPlus + SongFormat.SongShowPlus, + SongFormat.FoilPresenter ] @staticmethod diff --git a/openlp/plugins/songs/lib/olpimport.py b/openlp/plugins/songs/lib/olpimport.py index b3f03b951..fba518772 100644 --- a/openlp/plugins/songs/lib/olpimport.py +++ b/openlp/plugins/songs/lib/olpimport.py @@ -36,6 +36,7 @@ from sqlalchemy.orm.exc import UnmappedClassError from openlp.core.lib import translate from openlp.core.lib.db import BaseModel +from openlp.plugins.songs.lib import add_author_unknown from openlp.plugins.songs.lib.db import Author, Book, Song, Topic #, MediaFile from songimport import SongImport @@ -47,30 +48,35 @@ class OldAuthor(BaseModel): """ pass + class OldBook(BaseModel): """ Book model """ pass + class OldMediaFile(BaseModel): """ MediaFile model """ pass + class OldSong(BaseModel): """ Song model """ pass + class OldTopic(BaseModel): """ Topic model """ pass + class OpenLPSongImport(SongImport): """ The :class:`OpenLPSongImport` class provides OpenLP with the ability to @@ -170,25 +176,18 @@ class OpenLPSongImport(SongImport): new_song.comments = song.comments new_song.theme_name = song.theme_name new_song.ccli_number = song.ccli_number - if song.authors: - for author in song.authors: - existing_author = self.manager.get_object_filtered( - Author, Author.display_name == author.display_name) - if existing_author: - new_song.authors.append(existing_author) - else: - new_song.authors.append(Author.populate( - first_name=author.first_name, - last_name=author.last_name, - display_name=author.display_name)) - else: - au = self.manager.get_object_filtered(Author, - Author.display_name == u'Author Unknown') - if au: - new_song.authors.append(au) + for author in song.authors: + existing_author = self.manager.get_object_filtered( + Author, Author.display_name == author.display_name) + if existing_author: + new_song.authors.append(existing_author) else: new_song.authors.append(Author.populate( - display_name=u'Author Unknown')) + first_name=author.first_name, + last_name=author.last_name, + display_name=author.display_name)) + if not new_song.authors: + add_author_unknown(self.manager, new_song) if song.book: existing_song_book = self.manager.get_object_filtered( Book, Book.name == song.book.name) diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index 40be1675e..36f2e3504 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -29,7 +29,7 @@ import re from PyQt4 import QtCore from openlp.core.lib import Receiver, translate -from openlp.plugins.songs.lib import VerseType +from openlp.plugins.songs.lib import add_author_unknown, VerseType from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile from openlp.plugins.songs.lib.ui import SongStrings from openlp.plugins.songs.lib.xml import SongXML @@ -270,8 +270,6 @@ class SongImport(QtCore.QObject): """ All fields have been set to this song. Write the song to disk. """ - if not self.authors: - self.authors.append(SongStrings.AuthorUnknownUnT) log.info(u'committing song %s to database', self.title) song = Song() song.title = self.title @@ -315,10 +313,13 @@ class SongImport(QtCore.QObject): author = self.manager.get_object_filtered(Author, Author.display_name == authortext) if not author: - author = Author.populate(display_name = authortext, + author = Author.populate(display_name=authortext, last_name=authortext.split(u' ')[-1], first_name=u' '.join(authortext.split(u' ')[:-1])) song.authors.append(author) + # No author, add the default author. + if not song.authors: + add_author_unknown(self.manager, song) for filename in self.media_files: media_file = self.manager.get_object_filtered(MediaFile, MediaFile.file_name == filename) diff --git a/openlp/plugins/songs/lib/ui.py b/openlp/plugins/songs/lib/ui.py index 65f473e63..6415c48c9 100644 --- a/openlp/plugins/songs/lib/ui.py +++ b/openlp/plugins/songs/lib/ui.py @@ -36,8 +36,7 @@ class SongStrings(object): # These strings should need a good reason to be retranslated elsewhere. Author = translate('OpenLP.Ui', 'Author', 'Singular') Authors = translate('OpenLP.Ui', 'Authors', 'Plural') - AuthorUnknown = translate('OpenLP.Ui', 'Author Unknown') # Used in the UI. - AuthorUnknownUnT = u'Author Unknown' # Used to populate the database. + AuthorUnknown = u'Author Unknown' # Used to populate the database. CopyrightSymbol = translate('OpenLP.Ui', '\xa9', 'Copyright symbol.') SongBook = translate('OpenLP.Ui', 'Song Book', 'Singular') SongBooks = translate('OpenLP.Ui', 'Song Books', 'Plural') diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index a552a42a1..8f5c35c0a 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -66,7 +66,7 @@ import re from lxml import etree, objectify -from openlp.plugins.songs.lib import VerseType +from openlp.plugins.songs.lib import add_author_unknown, VerseType from openlp.plugins.songs.lib.db import Author, Book, Song, Topic from openlp.plugins.songs.lib.ui import SongStrings @@ -374,8 +374,6 @@ class OpenLyrics(object): display_name = self._text(author) if display_name: authors.append(display_name) - if not authors: - authors.append(SongStrings.AuthorUnknownUnT) for display_name in authors: author = self.manager.get_object_filtered(Author, Author.display_name == display_name) @@ -384,8 +382,8 @@ class OpenLyrics(object): 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) + if not song.authors: + add_author_unknown(self.manager, song) def _process_cclinumber(self, properties, song): """ diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index bf33cd56e..609c1990d 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -32,7 +32,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 UiStrings -from openlp.plugins.songs.lib import SongMediaItem, SongsTab, SongXML +from openlp.plugins.songs.lib import add_author_unknown, SongMediaItem, \ + SongsTab, SongXML from openlp.plugins.songs.lib.db import init_schema, Song from openlp.plugins.songs.lib.importer import SongFormat @@ -145,6 +146,9 @@ class SongsPlugin(Plugin): counter = 0 for song in songs: counter += 1 + # The song does not have any author, add one. + if not song.authors: + add_author_unknown(self.manager, song) if song.title is None: song.title = u'' if song.alternate_title is None: