diff --git a/copyright.txt b/copyright.txt index c99a64287..6882a7282 100644 --- a/copyright.txt +++ b/copyright.txt @@ -4,8 +4,8 @@ ############################################################################### # OpenLP - Open Source Lyrics Projection # # --------------------------------------------------------------------------- # -# Copyright (c) 2008-2010 Raoul Snyman # -# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # +# 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 # diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index eb0e89775..d820c9a5b 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -28,7 +28,7 @@ The :mod:`ui` module provides the core user interface for OpenLP """ from PyQt4 import QtGui -from openlp.core.lib import translate, Receiver +from openlp.core.lib import translate class HideMode(object): """ diff --git a/openlp/core/ui/aboutdialog.py b/openlp/core/ui/aboutdialog.py index 597d3fb24..bb09ab91f 100644 --- a/openlp/core/ui/aboutdialog.py +++ b/openlp/core/ui/aboutdialog.py @@ -164,9 +164,10 @@ class Ui_AboutDialog(object): self.licenseTextEdit.setPlainText(translate('OpenLP.AboutForm', 'Copyright \xa9 2004-2011 Raoul Snyman\n' 'Portions copyright \xa9 2004-2011 ' - 'Tim Bentley, Jonathan Corwin, Michael Gorven, Scott Guerrieri, ' - 'Christian Richter, Maikel Stuivenberg, Martin Thompson, Jon ' - 'Tibble, Carsten Tinggaard\n' + 'Tim Bentley, Jonathan Corwin, Michael Gorven, Scott Guerrieri,\n' + 'Meinert Jordan, Andreas Preikschat, Christian Richter, Philip\n' + 'Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, Carstenn' + 'Tinggaard, Frode Woldsund\n' '\n' 'This program is free software; you can redistribute it and/or ' 'modify it under the terms of the GNU General Public License as ' diff --git a/openlp/plugins/songs/forms/__init__.py b/openlp/plugins/songs/forms/__init__.py index 160a014ac..e75f9fe04 100644 --- a/openlp/plugins/songs/forms/__init__.py +++ b/openlp/plugins/songs/forms/__init__.py @@ -57,4 +57,6 @@ from songbookform import SongBookForm from editverseform import EditVerseForm from editsongform import EditSongForm from songmaintenanceform import SongMaintenanceForm -from songimportform import SongImportForm \ No newline at end of file +from songimportform import SongImportForm +from songexportform import SongExportForm + diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py new file mode 100644 index 000000000..5d5b9a8b1 --- /dev/null +++ b/openlp/plugins/songs/forms/songexportform.py @@ -0,0 +1,372 @@ +# -*- 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 :mod:`songexportform` module provides the wizard for exporting songs to the +OpenLyrics format. +""" +import logging + +from PyQt4 import QtCore, QtGui + +from openlp.core.lib import build_icon, Receiver, SettingsManager, translate +from openlp.core.lib.ui import critical_error_message_box +from openlp.core.ui.wizard import OpenLPWizard +from openlp.plugins.songs.lib.db import Song +from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport + +log = logging.getLogger(__name__) + +class SongExportForm(OpenLPWizard): + """ + This is the Song Export Wizard, which allows easy exporting of Songs to the + OpenLyrics format. + """ + log.info(u'SongExportForm loaded') + + def __init__(self, parent, plugin): + """ + Instantiate the wizard, and run any extra setup we need to. + + ``parent`` + The QWidget-derived parent of the wizard. + + ``plugin`` + The songs plugin. + """ + self.plugin = plugin + OpenLPWizard.__init__(self, parent, plugin, u'songExportWizard', + u':/wizards/wizard_exportsong.bmp') + self.stop_export_flag = False + QtCore.QObject.connect(Receiver.get_receiver(), + QtCore.SIGNAL(u'openlp_stop_wizard'), self.stop_export) + + def stop_export(self): + """ + Sets the flag for the exporter to stop the export. + """ + log.debug(u'Stopping songs export') + self.stop_export_flag = True + + def setupUi(self, image): + """ + Set up the song wizard UI. + """ + OpenLPWizard.setupUi(self, image) + + def customInit(self): + """ + Song wizard specific initialisation. + """ + pass + + def customSignals(self): + """ + Song wizard specific signals. + """ + QtCore.QObject.connect(self.availableListWidget, + QtCore.SIGNAL(u'itemActivated(QListWidgetItem*)'), self.onItemPressed) + QtCore.QObject.connect(self.searchLineEdit, + QtCore.SIGNAL(u'textEdited(const QString&)'), + self.onSearchLineEditChanged) + QtCore.QObject.connect(self.uncheckButton, + QtCore.SIGNAL(u'clicked()'), self.onUncheckButtonClicked) + QtCore.QObject.connect(self.checkButton, + QtCore.SIGNAL(u'clicked()'), self.onCheckButtonClicked) + QtCore.QObject.connect(self.directoryButton, + QtCore.SIGNAL(u'clicked()'), self.onDirectoryButtonClicked) + + def addCustomPages(self): + """ + Add song wizard specific pages. + """ + # The page with all available songs. + self.availableSongsPage = QtGui.QWizardPage() + self.availableSongsPage.setObjectName(u'availableSongsPage') + self.availableSongsLayout = QtGui.QHBoxLayout(self.availableSongsPage) + self.availableSongsLayout.setObjectName(u'availableSongsLayout') + self.verticalLayout = QtGui.QVBoxLayout() + self.verticalLayout.setObjectName(u'verticalLayout') + self.availableListWidget = QtGui.QListWidget(self.availableSongsPage) + self.availableListWidget.setObjectName(u'availableListWidget') + self.verticalLayout.addWidget(self.availableListWidget) + self.horizontalLayout = QtGui.QHBoxLayout() + self.horizontalLayout.setObjectName(u'horizontalLayout') + self.searchLabel = QtGui.QLabel(self.availableSongsPage) + self.searchLabel.setObjectName(u'searchLabel') + self.horizontalLayout.addWidget(self.searchLabel) + self.searchLineEdit = QtGui.QLineEdit(self.availableSongsPage) + self.searchLineEdit.setObjectName(u'searchLineEdit') + self.horizontalLayout.addWidget(self.searchLineEdit) + spacerItem = QtGui.QSpacerItem(40, 20, + QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.horizontalLayout.addItem(spacerItem) + self.uncheckButton = QtGui.QPushButton(self.availableSongsPage) + self.uncheckButton.setObjectName(u'uncheckButton') + self.horizontalLayout.addWidget(self.uncheckButton) + self.checkButton = QtGui.QPushButton(self.availableSongsPage) + self.checkButton.setObjectName(u'selectButton') + self.horizontalLayout.addWidget(self.checkButton) + self.verticalLayout.addLayout(self.horizontalLayout) + self.availableSongsLayout.addLayout(self.verticalLayout) + self.addPage(self.availableSongsPage) + # The page with the selected songs. + self.exportSongPage = QtGui.QWizardPage() + self.exportSongPage.setObjectName(u'availableSongsPage') + self.exportSongLayout = QtGui.QHBoxLayout(self.exportSongPage) + self.exportSongLayout.setObjectName(u'exportSongLayout') + self.gridLayout = QtGui.QGridLayout() + self.gridLayout.setObjectName(u'gridLayout') + self.selectedListWidget = QtGui.QListWidget(self.exportSongPage) + self.selectedListWidget.setObjectName(u'selectedListWidget') + self.gridLayout.addWidget(self.selectedListWidget, 1, 0, 1, 1) + self.horizontalLayout = QtGui.QHBoxLayout() + self.horizontalLayout.setObjectName(u'horizontalLayout') + self.directoryLabel = QtGui.QLabel(self.exportSongPage) + self.directoryLabel.setObjectName(u'directoryLabel') + self.horizontalLayout.addWidget(self.directoryLabel) + self.directoryLineEdit = QtGui.QLineEdit(self.exportSongPage) + self.directoryLineEdit.setObjectName(u'directoryLineEdit') + self.horizontalLayout.addWidget(self.directoryLineEdit) + self.directoryButton = QtGui.QToolButton(self.exportSongPage) + self.directoryButton.setIcon(build_icon(u':/exports/export_load.png')) + self.directoryButton.setObjectName(u'directoryButton') + self.horizontalLayout.addWidget(self.directoryButton) + self.gridLayout.addLayout(self.horizontalLayout, 0, 0, 1, 1) + self.exportSongLayout.addLayout(self.gridLayout) + self.addPage(self.exportSongPage) + + def retranslateUi(self): + """ + Song wizard localisation. + """ + self.setWindowTitle( + translate('SongsPlugin.ExportWizardForm', 'Song Export Wizard')) + self.titleLabel.setText( + u'%s' % + translate('SongsPlugin.ExportWizardForm', + 'Welcome to the Song Export Wizard')) + self.informationLabel.setText( + translate('SongsPlugin.ExportWizardForm', 'This wizard will help to' + ' export your songs to the open and free OpenLyrics worship song ' + 'format.')) + self.availableSongsPage.setTitle( + translate('SongsPlugin.ExportWizardForm', 'Select Songs')) + self.availableSongsPage.setSubTitle( + translate('SongsPlugin.ExportWizardForm', + 'Check the songs, you want to export.')) + self.searchLabel.setText( + translate('SongsPlugin.ExportWizardForm', 'Search:')) + self.uncheckButton.setText( + translate('SongsPlugin.ExportWizardForm', 'Uncheck All')) + self.checkButton.setText( + translate('SongsPlugin.ExportWizardForm', 'Check All')) + self.exportSongPage.setTitle( + translate('SongsPlugin.ExportWizardForm', 'Select Directory')) + self.exportSongPage.setSubTitle( + translate('SongsPlugin.ExportWizardForm', + 'Select the directory you want the songs to be saved.')) + self.directoryLabel.setText( + translate('SongsPlugin.ExportWizardForm', 'Directory:')) + self.progressPage.setTitle( + translate('SongsPlugin.ExportWizardForm', 'Exporting')) + self.progressPage.setSubTitle( + translate('SongsPlugin.ExportWizardForm', + 'Please wait while your songs are exported.')) + self.progressLabel.setText( + translate('SongsPlugin.ExportWizardForm', 'Ready.')) + self.progressBar.setFormat( + translate('SongsPlugin.ExportWizardForm', '%p%')) + + def validateCurrentPage(self): + """ + Validate the current page before moving on to the next page. + """ + if self.currentPage() == self.welcomePage: + return True + elif self.currentPage() == self.availableSongsPage: + items = [ + item for item in self._findListWidgetItems( + self.availableListWidget) if item.checkState() + ] + if not items: + critical_error_message_box( + translate('SongsPlugin.ExportWizardForm', + 'No Song Selected'), + translate('SongsPlugin.ExportWizardForm', + 'You need to add at least one Song to export.')) + return False + self.selectedListWidget.clear() + # Add the songs to the list of selected songs. + for item in items: + song = QtGui.QListWidgetItem(item.text()) + song.setData(QtCore.Qt.UserRole, + QtCore.QVariant(item.data(QtCore.Qt.UserRole).toPyObject())) + song.setFlags(QtCore.Qt.ItemIsEnabled) + self.selectedListWidget.addItem(song) + return True + elif self.currentPage() == self.exportSongPage: + if not self.directoryLineEdit.text(): + critical_error_message_box( + translate('SongsPlugin.ExportWizardForm', + 'No Save Location specified'), + translate('SongsPlugin.ExportWizardForm', + 'You need to specified a directory to save the songs in.')) + return False + return True + elif self.currentPage() == self.progressPage: + self.availableListWidget.clear() + self.selectedListWidget.clear() + return True + + def registerFields(self): + """ + Register song export wizard fields. + """ + pass + + def setDefaults(self): + """ + Set default form values for the song export wizard. + """ + self.restart() + self.finishButton.setVisible(False) + self.cancelButton.setVisible(True) + self.availableListWidget.clear() + self.selectedListWidget.clear() + self.directoryLineEdit.clear() + # Load the list of songs. + Receiver.send_message(u'cursor_busy') + songs = self.plugin.manager.get_all_objects(Song) + for song in songs: + authors = u', '.join([author.display_name + for author in song.authors]) + title = u'%s (%s)' % (unicode(song.title), authors) + item = QtGui.QListWidgetItem(title) + item.setData(QtCore.Qt.UserRole, QtCore.QVariant(song)) + item.setFlags(QtCore.Qt.ItemIsSelectable| + QtCore.Qt.ItemIsUserCheckable | QtCore.Qt.ItemIsEnabled) + item.setCheckState(QtCore.Qt.Unchecked) + self.availableListWidget.addItem(item) + Receiver.send_message(u'cursor_normal') + + def preWizard(self): + """ + Perform pre export tasks. + """ + OpenLPWizard.preWizard(self) + self.progressLabel.setText( + translate('SongsPlugin.ExportWizardForm', 'Starting export...')) + Receiver.send_message(u'openlp_process_events') + + def performWizard(self): + """ + Perform the actual export. This creates an *openlyricsexport* instance + and calls the *do_export* method. + """ + songs = [ + song.data(QtCore.Qt.UserRole).toPyObject() + for song in self._findListWidgetItems(self.selectedListWidget) + ] + exporter = OpenLyricsExport( + self, songs, unicode(self.directoryLineEdit.text())) + if exporter.do_export(): + self.progressLabel.setText( + translate('SongsPlugin.SongExportForm', 'Finished export.')) + else: + self.progressLabel.setText( + translate('SongsPlugin.SongExportForm', + 'Your song export failed.')) + + def _findListWidgetItems(self, listWidget, text=u''): + """ + Returns a list of *QListWidgetItem*s of the ``listWidget``. Note, that + hidden items are included. + + ``listWidget`` + The widget to get all items from. (QListWidget) + + ``text`` + The text to search for. (unicode string) + """ + return [item for item in listWidget.findItems( + QtCore.QString(unicode(text)), QtCore.Qt.MatchContains) + ] + + def onItemPressed(self, item): + """ + Called, when an item in the *availableListWidget* has been pressed. Thes + item is check if it was not checked, whereas it is unchecked when it was + checked. + + ``item`` + The *QListWidgetItem* which was pressed. + """ + item.setCheckState( + QtCore.Qt.Unchecked if item.checkState() else QtCore.Qt.Checked) + + def onSearchLineEditChanged(self, text): + """ + The *searchLineEdit*'s text has been changed. Update the list of + available songs. Note that any song, which does not match the ``text`` + will be hidden, but not unchecked! + + ``text`` + The text of the *searchLineEdit*. (QString) + """ + search_result = [ + song for song in self._findListWidgetItems( + self.availableListWidget, unicode(text)) + ] + for item in self._findListWidgetItems(self.availableListWidget): + item.setHidden(False if item in search_result else True) + + def onUncheckButtonClicked(self): + """ + The *uncheckButton* has been clicked. Set all songs unchecked. + """ + for row in range(self.availableListWidget.count()): + item = self.availableListWidget.item(row) + item.setCheckState(QtCore.Qt.Unchecked) + + def onCheckButtonClicked(self): + """ + The *checkButton* has been clicked. Set all songs checked. + """ + for row in range(self.availableListWidget.count()): + item = self.availableListWidget.item(row) + item.setCheckState(QtCore.Qt.Checked) + + def onDirectoryButtonClicked(self): + """ + Called when the *directoryButton* was clicked. Opens a dialog and writes + the path to *directoryLineEdit*. + """ + path = unicode(QtGui.QFileDialog.getExistingDirectory(self, + translate('SongsPlugin.ExportWizardForm', 'Selecte to Folder'), + SettingsManager.get_last_dir(self.plugin.settingsSection, 1), + options=QtGui.QFileDialog.ShowDirsOnly)) + SettingsManager.set_last_dir(self.plugin.settingsSection, path, 1) + self.directoryLineEdit.setText(path) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 312d0d6e7..aaa26b1c0 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -35,7 +35,7 @@ from openlp.core.lib import MediaManagerItem, BaseListWithDnD, Receiver, \ ItemCapabilities, translate, check_item_selected, PluginStatus from openlp.core.lib.ui import UiStrings from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \ - SongImportForm + SongImportForm, SongExportForm from openlp.plugins.songs.lib import OpenLyrics, SongXML from openlp.plugins.songs.lib.db import Author, Song from openlp.core.lib.searchedit import SearchEdit @@ -265,6 +265,11 @@ class SongMediaItem(MediaManagerItem): if self.import_wizard.exec_() == QtGui.QDialog.Accepted: Receiver.send_message(u'songs_load_list') + def onExportClick(self): + if not hasattr(self, u'export_wizard'): + self.export_wizard = SongExportForm(self, self.parent) + self.export_wizard.exec_() + def onNewClick(self): log.debug(u'onNewClick') self.edit_song_form.newSong() diff --git a/openlp/plugins/songs/lib/openlyricsexport.py b/openlp/plugins/songs/lib/openlyricsexport.py new file mode 100755 index 000000000..ffb1a2d6f --- /dev/null +++ b/openlp/plugins/songs/lib/openlyricsexport.py @@ -0,0 +1,74 @@ +# -*- 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 :mod:`openlyricsexport` module provides the functionality for exporting +songs from the database to the OpenLyrics format. +""" +import logging +import os + +from lxml import etree + +from openlp.core.lib import Receiver, translate +from openlp.plugins.songs.lib import OpenLyrics + +log = logging.getLogger(__name__) + +class OpenLyricsExport(object): + """ + This provides the Openlyrics export. + """ + def __init__(self, parent, songs, save_path): + """ + Initialise the export. + """ + log.debug(u'initialise OpenLyricsExport') + self.parent = parent + self.manager = parent.plugin.manager + self.songs = songs + self.save_path = save_path + if not os.path.exists(self.save_path): + os.mkdir(self.save_path) + + def do_export(self): + """ + Export the songs. + """ + log.debug(u'started OpenLyricsExport') + openLyrics = OpenLyrics(self.manager) + self.parent.progressBar.setMaximum(len(self.songs)) + for song in self.songs: + Receiver.send_message(u'openlp_process_events') + if self.parent.stop_export_flag: + return False + self.parent.incrementProgressBar(unicode(translate( + 'SongsPlugin.OpenLyricsExport', 'Exporting "%s"...')) % + song.title) + xml = openLyrics.song_to_xml(song) + tree = etree.ElementTree(etree.fromstring(xml)) + tree.write(os.path.join(self.save_path, song.title + u'.xml'), + encoding=u'utf-8', xml_declaration=True, pretty_print=True) + return True diff --git a/openlp/plugins/songs/lib/opensongimport.py b/openlp/plugins/songs/lib/opensongimport.py index e7557f952..8c6d283e3 100644 --- a/openlp/plugins/songs/lib/opensongimport.py +++ b/openlp/plugins/songs/lib/opensongimport.py @@ -36,6 +36,7 @@ from openlp.plugins.songs.lib.songimport import SongImport log = logging.getLogger(__name__) +#TODO: Use lxml for parsing and make sure we use methods of "SongImport" . class OpenSongImport(SongImport): """ Import songs exported from OpenSong @@ -146,7 +147,7 @@ class OpenSongImport(SongImport): log.info(u'Zip importing %s', parts[-1]) self.import_wizard.incrementProgressBar( unicode(translate('SongsPlugin.ImportWizardForm', - 'Importing %s...')) % parts[-1]) + 'Importing %s...')) % parts[-1]) songfile = z.open(song) self.do_import_file(songfile) if self.commit: @@ -276,7 +277,7 @@ class OpenSongImport(SongImport): for num in versenums: versetag = u'%s%s' % (our_verse_type, num) lines = u'\n'.join(verses[versetype][num]) - self.verses.append([versetag, lines]) + self.add_verse(lines, versetag) # Keep track of what we have for error checking later versetags[versetag] = 1 # now figure out the presentation order @@ -292,6 +293,8 @@ class OpenSongImport(SongImport): else: log.warn(u'No verse order available for %s, skipping.', self.title) + # TODO: make sure that the default order list will be overwritten, if + # the songs provides its own order list. for tag in order: if tag[0].isdigit(): # Assume it's a verse if it has no prefix diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 4bf11887a..73ac8120c 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -60,6 +60,7 @@ The XML of an `OpenLyrics `_ song looks like this:: """ +import datetime import logging import re @@ -89,8 +90,10 @@ class SongXML(object): Add a verse to the ```` tag. ``type`` - A string denoting the type of verse. Possible values are "V", - "C", "B", "P", "I", "E" and "O". + A string denoting the type of verse. Possible values are *Verse*, + *Chorus*, *Bridge*, *Pre-Chorus*, *Intro*, *Ending* and *Other*. + Any other type is **not** allowed, this also includes translated + types. ``number`` An integer denoting the number of the item, for example: verse 1. @@ -126,8 +129,8 @@ class SongXML(object): The returned list has the following format:: - [[{'lang': 'en', 'type': 'V', 'label': '1'}, u"The English verse."], - [{'lang': 'en', 'type': 'C', 'label': '1'}, u"The English chorus."]] + [[{'lang': 'en', 'type': 'Verse', 'label': '1'}, u"English verse"], + [{'lang': 'en', 'type': 'Chorus', 'label': '1'}, u"English chorus"]] """ self.song_xml = None if xml[:5] == u'`` - The attribute *translit* is not supported. + The attribute *translit* is not supported. Note, the attribute *lang* is + considered, but there is not further functionality implemented yet. ```` OpenLP supports this property. """ + IMPLEMENTED_VERSION = u'0.7' def __init__(self, manager): self.manager = manager @@ -222,8 +227,14 @@ class OpenLyrics(object): """ sxml = SongXML() verse_list = sxml.get_verses(song.lyrics) - song_xml = objectify.fromstring( - u'') + song_xml = objectify.fromstring(u'') + # Append the necessary meta data to the song. + song_xml.set(u'xmlns', u'http://openlyrics.info/namespace/2009/song') + song_xml.set(u'version', OpenLyrics.IMPLEMENTED_VERSION) + song_xml.set(u'createdIn', u'OpenLP 1.9.4') # Use variable + song_xml.set(u'modifiedIn', u'OpenLP 1.9.4') # Use variable + song_xml.set(u'modifiedDate', + datetime.datetime.now().strftime(u'%Y-%m-%dT%H:%M:%S')) properties = etree.SubElement(song_xml, u'properties') titles = etree.SubElement(properties, u'titles') self._add_text_to_element(u'title', titles, song.title.strip()) @@ -237,7 +248,7 @@ class OpenLyrics(object): 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) + u'verseOrder', properties, song.verse_order.lower()) if song.ccli_number: self._add_text_to_element(u'ccliNo', properties, song.ccli_number) if song.authors: @@ -263,6 +274,8 @@ class OpenLyrics(object): verse[0][u'type'][0].lower(), verse[0][u'label']) element = \ self._add_text_to_element(u'verse', lyrics, None, verse_tag) + if verse[0].has_key(u'lang'): + element.set(u'lang', verse[0][u'lang']) 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) @@ -450,7 +463,7 @@ class OpenLyrics(object): 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.to_string(verse_name[0]))[0] + verse_type = unicode(VerseType.to_string(verse_name[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". @@ -478,9 +491,9 @@ class OpenLyrics(object): 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]))) + verse_order.append(u''.join((name[0][0], name[1]))) else: - verse_order.append(u''.join((name[0], name[1]))) + verse_order.append(u''.join((name[0][0], name[1]))) previous_type = name[0] previous_number = name[1] previous_part = name[2] diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index 74991fe37..1c1d319cf 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -107,8 +107,17 @@ class SongsPlugin(Plugin): The actual **Export** menu item, so that your actions can use it as their parent. """ - # No menu items for now. - pass + # Main song import menu item - will eventually be the only one + self.SongExportItem = QtGui.QAction(export_menu) + self.SongExportItem.setObjectName(u'SongExportItem') + self.SongExportItem.setText(translate( + 'SongsPlugin', '&Song')) + self.SongExportItem.setToolTip(translate('SongsPlugin', + 'Exports songs using the export wizard.')) + export_menu.addAction(self.SongExportItem) + # Signals and slots + QtCore.QObject.connect(self.SongExportItem, + QtCore.SIGNAL(u'triggered()'), self.onSongExportItemClicked) def addToolsMenuItem(self, tools_menu): """ @@ -173,6 +182,10 @@ class SongsPlugin(Plugin): if self.mediaItem: self.mediaItem.onImportClick() + def onSongExportItemClicked(self): + if self.mediaItem: + self.mediaItem.onExportClick() + def about(self): about_text = translate('SongsPlugin', 'Songs Plugin' '
The songs plugin provides the ability to display and ' diff --git a/resources/forms/songexport.ui b/resources/forms/songexport.ui deleted file mode 100644 index 9830db3ef..000000000 --- a/resources/forms/songexport.ui +++ /dev/null @@ -1,241 +0,0 @@ - - SongExportDialog - - - - 0 - 0 - 641 - 607 - - - - Dialog - - - - 8 - - - 8 - - - - - - 8 - - - 0 - - - - - Available Songs - - - - - - - - - Select All - - - - - - - - - - - 0 - 0 - - - - - 30 - 16777215 - - - - - 8 - - - QLayout::SetMinimumSize - - - 0 - - - - - Qt::Vertical - - - QSizePolicy::MinimumExpanding - - - - 20 - 132 - - - - - - - - Select Songs - - - - :/exports/export_move_to_list.png:/exports/export_move_to_list.png - - - - 20 - 20 - - - - - - - - Deselect Songs - - - - :/exports/export_remove.png:/exports/export_remove.png - - - - 20 - 20 - - - - - - - - Qt::Vertical - - - QSizePolicy::MinimumExpanding - - - - 20 - 131 - - - - - - - - - - - Selected Songs - - - - - - - - - Select All - - - - - - - - - - - - - 0 - - - - OpenLyric Format - - - - - Text File - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - SongExportButtonBox - AvailableListWidget - AvailableAllToolButton - SelectToolButton - DeselectToolButton - SelectedListWidget - SelectedAllToolButton - ExportTabWidget - - - - - - - SongExportButtonBox - accepted() - SongExportDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - SongExportButtonBox - rejected() - SongExportDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/resources/images/export_move_to_list.png b/resources/images/export_move_to_list.png deleted file mode 100644 index 5c0005856..000000000 Binary files a/resources/images/export_move_to_list.png and /dev/null differ diff --git a/resources/images/export_remove.png b/resources/images/export_remove.png deleted file mode 100644 index ef8e685e2..000000000 Binary files a/resources/images/export_remove.png and /dev/null differ diff --git a/resources/images/export_selectall.png b/resources/images/export_selectall.png deleted file mode 100644 index 0f0d9f152..000000000 Binary files a/resources/images/export_selectall.png and /dev/null differ diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc index 2db924363..dd5abd861 100644 --- a/resources/images/openlp-2.qrc +++ b/resources/images/openlp-2.qrc @@ -80,12 +80,10 @@ import_load.png - export_selectall.png - export_remove.png export_load.png - export_move_to_list.png + wizard_exportsong.bmp wizard_importsong.bmp wizard_importbible.bmp wizard_createtheme.bmp diff --git a/resources/images/wizard_exportsong.bmp b/resources/images/wizard_exportsong.bmp new file mode 100644 index 000000000..948422dcc Binary files /dev/null and b/resources/images/wizard_exportsong.bmp differ