diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py index 082070575..7278099e6 100644 --- a/openlp/core/lib/plugin.py +++ b/openlp/core/lib/plugin.py @@ -130,7 +130,6 @@ class Plugin(QtCore.QObject): self.serviceManager = plugin_helpers[u'service'] self.settingsForm = plugin_helpers[u'settings form'] self.mediadock = plugin_helpers[u'toolbox'] - self.displayManager = plugin_helpers[u'displaymanager'] self.pluginManager = plugin_helpers[u'pluginmanager'] QtCore.QObject.connect(Receiver.get_receiver(), QtCore.SIGNAL(u'%s_add_service_item' % self.name), diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py index 5b8568aba..96c9c5616 100644 --- a/openlp/core/ui/__init__.py +++ b/openlp/core/ui/__init__.py @@ -42,7 +42,6 @@ from servicenoteform import ServiceNoteForm from serviceitemeditform import ServiceItemEditForm from screen import ScreenList from maindisplay import WebViewer -from maindisplay import DisplayManager from amendthemeform import AmendThemeForm from slidecontroller import SlideController from splashscreen import SplashScreen diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index ac01e5f29..85623937b 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -57,51 +57,52 @@ HTMLVIDEO = u""" """ -class DisplayManager(QtGui.QWidget): - pass - """ - Wrapper class to hold the display widgets. - I will provide API's in future to access the screens allow for - extra displays to be added. - RenderManager is poked in by MainWindow - """ - def __init__(self, parent, screens): - QtGui.QWidget.__init__(self) - self.parent = parent - self.screens = screens - self.audioPlayer = AudioPlayer(self) - # Live display - #self.mainDisplay = WebViewer(self, screens, True) - # Display for Preview and Theme previews - #self.previewDisplay = WebViewer(self, screens, False) -# QtCore.QObject.connect(Receiver.get_receiver(), -# QtCore.SIGNAL(u'maindisplay_hide'), self.hideDisplay) -# QtCore.QObject.connect(Receiver.get_receiver(), -# QtCore.SIGNAL(u'maindisplay_show'), self.showDisplay) -# QtCore.QObject.connect(Receiver.get_receiver(), -# QtCore.SIGNAL(u'videodisplay_start'), self.onStartVideo) -# QtCore.QObject.connect(Receiver.get_receiver(), -# QtCore.SIGNAL(u'videodisplay_stop'), self.onStopVideo) -# QtCore.QObject.connect(Receiver.get_receiver(), -# QtCore.SIGNAL(u'config_updated'), self.setup) +#class DisplayManager(QtGui.QWidget): +# pass +# """ +# Wrapper class to hold the display widgets. +# I will provide API's in future to access the screens allow for +# extra displays to be added. +# RenderManager is poked in by MainWindow +# """ +# def __init__(self, parent, screens): +# QtGui.QWidget.__init__(self) +# self.parent = parent +# self.screens = screens +# self.audioPlayer = AudioPlayer(self) +# # Live display +# #self.mainDisplay = WebViewer(self, screens, True) +# # Display for Preview and Theme previews +# #self.previewDisplay = WebViewer(self, screens, False) +## QtCore.QObject.connect(Receiver.get_receiver(), +## QtCore.SIGNAL(u'maindisplay_hide'), self.hideDisplay) +## QtCore.QObject.connect(Receiver.get_receiver(), +## QtCore.SIGNAL(u'maindisplay_show'), self.showDisplay) +## QtCore.QObject.connect(Receiver.get_receiver(), +## QtCore.SIGNAL(u'videodisplay_start'), self.onStartVideo) +## QtCore.QObject.connect(Receiver.get_receiver(), +## QtCore.SIGNAL(u'videodisplay_stop'), self.onStopVideo) +## QtCore.QObject.connect(Receiver.get_receiver(), +## QtCore.SIGNAL(u'config_updated'), self.setup) +# +# def setup(self): +# log.debug(u'mainDisplay - setup') +# # let the render manager have the preview display. +# #self.parent.RenderManager.previewDisplay = self.previewDisplay +# #Build the initial frame. +# self.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(self.initialFrame) +# painter_image.fillRect(self.initialFrame.rect(), QtCore.Qt.white) +# painter_image.drawImage( +# (self.screens.current[u'size'].width() - splash_image.width()) / 2, +# (self.screens.current[u'size'].height() - splash_image.height()) / 2, +# splash_image) - def setup(self): - log.debug(u'mainDisplay - setup') - # let the render manager have the preview display. - #self.parent.RenderManager.previewDisplay = self.previewDisplay - #Build the initial frame. - self.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(self.initialFrame) - painter_image.fillRect(self.initialFrame.rect(), QtCore.Qt.white) - painter_image.drawImage( - (self.screens.current[u'size'].width() - splash_image.width()) / 2, - (self.screens.current[u'size'].height() - splash_image.height()) / 2, - splash_image) # self.mainDisplay.setup() # self.previewDisplay.setup() # if self.isLive @@ -619,6 +620,202 @@ class WebViewer(DisplayWidget): # Phonon.createPath(self.mediaObject, self) # Phonon.createPath(self.mediaObject, self.audioObject) # flags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Dialog +# +# self.videoDisplay = self.page.mainFrame() +# self.videoDisplay.setScrollBarPolicy(QtCore.Qt.Vertical, +# QtCore.Qt.ScrollBarAlwaysOff) +# self.videoDisplay.setScrollBarPolicy(QtCore.Qt.Horizontal, +# QtCore.Qt.ScrollBarAlwaysOff) +# self.proxy = QtGui.QGraphicsProxyWidget() +# self.proxy.setWidget(self.webView) +# self.proxy.setWindowFlags(QtCore.Qt.Window | +# QtCore.Qt.FramelessWindowHint) +# self.proxy.setZValue(1) +# self.scene.addItem(self.proxy) +# +# def setupImage(self): +# self.imageDisplay = QtGui.QGraphicsPixmapItem() +# self.imageDisplay.setZValue(2) +# self.scene.addItem(self.imageDisplay) +# +# def setupText(self): +# #self.displayText = QtGui.QGraphicsTextItem() +# self.displayText = QtGui.QGraphicsPixmapItem() +# #self.displayText.setPos(0,0) +# #self.displayText.setTextWidth(self.size().width()) +# self.displayText.setZValue(4) +# self.scene.addItem(self.displayText) +# +# def setupAlert(self): +# self.alertText = QtGui.QGraphicsTextItem() +# self.alertText.setTextWidth(self.size().width()) +# self.alertText.setZValue(8) +# self.scene.addItem(self.alertText) +# +# def setupBlank(self): +# self.displayBlank = QtGui.QGraphicsPixmapItem() +# self.displayBlank.setZValue(10) +# self.scene.addItem(self.displayBlank) +# +## def hideDisplayForVideo(self): +## """ +## Hides the main display if for the video to be played +## """ +## self.hideDisplay(HideMode.Screen) +# +# def hideDisplay(self, mode=HideMode.Screen): +# """ +# Hide the display by making all layers transparent +# Store the images so they can be replaced when required +# """ +# log.debug(u'hideDisplay mode = %d', mode) +# #self.displayText.setPixmap(self.transparent) +# if mode == HideMode.Screen: +# #self.display_image.setPixmap(self.transparent) +# self.setVisible(False) +# elif mode == HideMode.Blank: +# self.displayBlank.setPixmap( +# QtGui.QPixmap.fromImage(self.blankFrame)) +# else: +# if self.parent.renderManager.renderer.bg_frame: +# self.displayBlank.setPixmap(QtGui.QPixmap.fromImage( +# self.parent.renderManager.renderer.bg_frame)) +# else: +# self.displayBlank.setPixmap( +# QtGui.QPixmap.fromImage(self.blankFrame)) +# if mode != HideMode.Screen and self.isHidden(): +# self.setVisible(True) +# +# def showDisplay(self, message=u''): +# """ +# Show the stored layers so the screen reappears as it was +# originally. +# Make the stored images None to release memory. +# """ +# log.debug(u'showDisplay') +# self.displayBlank.setPixmap(self.transparent) +# if self.isHidden(): +# self.setVisible(True) +# #Trigger actions when display is active again +# Receiver.send_message(u'maindisplay_active') +# +# def addImageWithText(self, frame): +# log.debug(u'addImageWithText') +# frame = resize_image( +# frame, self.screen[u'size'].width(), self.screen[u'size'].height()) +# self.imageDisplay.setPixmap(QtGui.QPixmap.fromImage(frame)) +# self.videoDisplay.setHtml(u'') +# +# def addAlert(self, message, location): +# """ +# Places the Alert text on the display at the correct location +# ``message`` +# Text to be displayed +# ``location`` +# Where on the screen the text should be. From the AlertTab +# Combo box. +# """ +# log.debug(u'addAlertImage') +# if location == 0: +# self.alertText.setPos(0, 0) +# elif location == 1: +# self.alertText.setPos(0, self.size().height() / 2) +# else: +# self.alertText.setPos(0, self.size().height() - 76) +# self.alertText.setHtml(message) +# +# def displayImage(self, frame): +# """ +# Places the Image passed on the display screen +# ``frame`` +# The image to be displayed +# """ +# log.debug(u'adddisplayImage') +# if isinstance(frame, QtGui.QImage): +# self.imageDisplay.setPixmap(QtGui.QPixmap.fromImage(frame)) +# else: +# self.imageDisplay.setPixmap(frame) +# self.frameView(self.transparent) +# self.videoDisplay.setHtml(u'') +# +# def displayVideo(self, path): +# """ +# Places the Video passed on the display screen +# ``path`` +# The path to the image to be displayed +# """ +# log.debug(u'adddisplayVideo') +# self.displayImage(self.transparent) +# self.videoDisplay.setHtml(HTMLVIDEO % +# (path, self.screen[u'size'].width(), +# self.screen[u'size'].height())) +# +# def frameView(self, frame, transition=False): +# """ +# Called from a slide controller to display a frame +# if the alert is in progress the alert is added on top +# ``frame`` +# Image frame to be rendered +# ``transition`` +# Are transitions required. +# """ +# log.debug(u'frameView') +# if transition: +# if self.frame is not None: +# self.displayText.setPixmap( +# QtGui.QPixmap.fromImage(self.frame)) +# self.repaint() +# Receiver.send_message(u'openlp_process_events') +# time.sleep(0.1) +# self.frame = None +# if frame[u'trans'] is not None: +# self.displayText.setPixmap( +# QtGui.QPixmap.fromImage(frame[u'trans'])) +# self.repaint() +# Receiver.send_message(u'openlp_process_events') +# time.sleep(0.1) +# self.frame = frame[u'trans'] +# self.displayText.setPixmap( +# QtGui.QPixmap.fromImage(frame[u'main'])) +# else: +# if isinstance(frame, QtGui.QPixmap): +# self.displayText.setPixmap(frame) +# else: +# self.displayText.setPixmap(QtGui.QPixmap.fromImage(frame)) +# if not self.isVisible() and self.screens.display: +# self.setVisible(True) +# +#class VideoDisplay(Phonon.VideoWidget): +# """ +# This is the form that is used to display videos on the projector. +# """ +# log.info(u'VideoDisplay Loaded') +# +# def __init__(self, parent, screens, +# aspect=Phonon.VideoWidget.AspectRatioWidget): +# """ +# The constructor for the display form. +# +# ``parent`` +# The parent widget. +# +# ``screens`` +# The list of screens. +# """ +# log.debug(u'VideoDisplay Initialisation started') +# Phonon.VideoWidget.__init__(self) +# self.setWindowTitle(u'OpenLP Video Display') +# self.parent = parent +# self.screens = screens +# self.hidden = False +# self.message = None +# self.mediaActive = False +# self.mediaObject = Phonon.MediaObject() +# self.setAspectRatio(aspect) +# self.audioObject = Phonon.AudioOutput(Phonon.VideoCategory) +# Phonon.createPath(self.mediaObject, self) +# Phonon.createPath(self.mediaObject, self.audioObject) +# flags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Dialog ## # WindowsStaysOnBottomHint is not available in QT4.4 # try: # self.setAttribute(QtCore.Qt.WA_TranslucentBackground) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index a3ec3d4a6..2c55b743e 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -30,7 +30,7 @@ import re from PyQt4 import QtCore, QtGui from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \ - ThemeManager, SlideController, PluginForm, MediaDockManager, DisplayManager + ThemeManager, SlideController, PluginForm, MediaDockManager from openlp.core.lib import RenderManager, build_icon, OpenLPDockWidget, \ SettingsManager, PluginManager, Receiver, translate from openlp.core.utils import check_latest_version, AppLocation, add_actions, \ @@ -527,7 +527,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.songsSettingsSection = u'songs' self.serviceNotSaved = False self.settingsmanager = SettingsManager(screens) - self.displayManager = DisplayManager(self, screens) self.aboutForm = AboutForm(self, applicationVersion) self.settingsForm = SettingsForm(self.screens, self, self) self.recentFiles = QtCore.QStringList() @@ -622,7 +621,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): #ThemeManager needs to call RenderManager self.RenderManager = RenderManager( self.ThemeManagerContents, self.screens) - self.displayManager.renderManager = self.RenderManager #Define the media Dock Manager self.mediaDockManager = MediaDockManager(self.MediaToolBox) log.info(u'Load Plugins') @@ -633,7 +631,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): self.plugin_helpers[u'service'] = self.ServiceManagerContents self.plugin_helpers[u'settings form'] = self.settingsForm self.plugin_helpers[u'toolbox'] = self.mediaDockManager - self.plugin_helpers[u'displaymanager'] = self.displayManager self.plugin_helpers[u'pluginmanager'] = self.plugin_manager self.plugin_manager.find_plugins(pluginpath, self.plugin_helpers) # hook methods have to happen after find_plugins. Find plugins needs @@ -789,7 +786,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow): their locations """ self.RenderManager.update_display() - self.displayManager.setup() + #self.displayManager.setup() self.setFocus() self.activateWindow() diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index d321630f4..bf4dabb2d 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -211,6 +211,14 @@ class ThemeManager(QtGui.QWidget): 'You must select a theme to delete.')): item = self.ThemeListWidget.currentItem() theme = unicode(item.text()) + # confirm deletion + answer = QtGui.QMessageBox.question(self, + translate('ThemeManager', 'Delete Confirmation'), + translate('ThemeManager', 'Delete theme?'), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | + QtGui.QMessageBox.No), QtGui.QMessageBox.No) + if answer == QtGui.QMessageBox.No: + return # should be the same unless default if theme != unicode(item.data(QtCore.Qt.UserRole).toString()): QtGui.QMessageBox.critical(self, diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py index 3a7baf48b..5ea870da6 100644 --- a/openlp/plugins/bibles/bibleplugin.py +++ b/openlp/plugins/bibles/bibleplugin.py @@ -40,7 +40,7 @@ class BiblePlugin(Plugin): self.weight = -9 self.icon_path = u':/plugins/plugin_bibles.png' self.icon = build_icon(self.icon_path) - #Register the bible Manager + # Register the bible Manager. self.status = PluginStatus.Active self.manager = None @@ -62,7 +62,7 @@ class BiblePlugin(Plugin): return BiblesTab(self.name) def getMediaManagerItem(self): - # Create the BibleManagerItem object + # Create the BibleManagerItem object. return BibleMediaItem(self, self.icon, self.name) def addImportMenuItem(self, import_menu): @@ -71,7 +71,7 @@ class BiblePlugin(Plugin): import_menu.addAction(self.ImportBibleItem) self.ImportBibleItem.setText( translate('BiblePlugin', '&Bible')) - # Signals and slots + # signals and slots QtCore.QObject.connect(self.ImportBibleItem, QtCore.SIGNAL(u'triggered()'), self.onBibleImportClick) self.ImportBibleItem.setVisible(False) diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 8c0b03cb2..7879c4b8e 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -129,8 +129,7 @@ class BibleMediaItem(MediaManagerItem): self.QuickClearLabel.setObjectName(u'QuickSearchLabel') self.QuickLayout.addWidget(self.QuickClearLabel, 4, 0, 1, 1) self.ClearQuickSearchComboBox = QtGui.QComboBox(self.QuickTab) - self.ClearQuickSearchComboBox.setObjectName( - u'ClearQuickSearchComboBox') + self.ClearQuickSearchComboBox.setObjectName(u'ClearQuickSearchComboBox') self.QuickLayout.addWidget(self.ClearQuickSearchComboBox, 4, 1, 1, 2) self.QuickSearchButtonLayout = QtGui.QHBoxLayout() self.QuickSearchButtonLayout.setMargin(0) @@ -168,8 +167,7 @@ class BibleMediaItem(MediaManagerItem): self.AdvancedVersionComboBox.setObjectName(u'AdvancedVersionComboBox') self.AdvancedLayout.addWidget(self.AdvancedVersionComboBox, 0, 1, 1, 2) self.AdvancedSecondBibleLabel = QtGui.QLabel(self.AdvancedTab) - self.AdvancedSecondBibleLabel.setObjectName( - u'AdvancedSecondBibleLabel') + self.AdvancedSecondBibleLabel.setObjectName(u'AdvancedSecondBibleLabel') self.AdvancedLayout.addWidget(self.AdvancedSecondBibleLabel, 1, 0, 1, 1) self.AdvancedSecondBibleComboBox = QtGui.QComboBox(self.AdvancedTab) self.AdvancedSecondBibleComboBox.setSizeAdjustPolicy( @@ -223,8 +221,7 @@ class BibleMediaItem(MediaManagerItem): u'AdvancedSearchButtonLayout') self.AdvancedSearchButtonSpacer = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.AdvancedSearchButtonLayout.addItem( - self.AdvancedSearchButtonSpacer) + self.AdvancedSearchButtonLayout.addItem(self.AdvancedSearchButtonSpacer) self.AdvancedSearchButton = QtGui.QPushButton(self.AdvancedTab) self.AdvancedSearchButton.setObjectName(u'AdvancedSearchButton') self.AdvancedSearchButtonLayout.addWidget(self.AdvancedSearchButton) @@ -618,8 +615,7 @@ class BibleMediaItem(MediaManagerItem): else: self.AdvancedSearchButton.setEnabled(True) self.AdvancedMessage.setText(u'') - self.adjustComboBox(1, self.chapters_from, - self.AdvancedFromChapter) + self.adjustComboBox(1, self.chapters_from, self.AdvancedFromChapter) self.adjustComboBox(1, self.chapters_from, self.AdvancedToChapter) self.adjustComboBox(1, self.verses, self.AdvancedFromVerse) self.adjustComboBox(1, self.verses, self.AdvancedToVerse) diff --git a/openlp/plugins/songs/forms/authorsform.py b/openlp/plugins/songs/forms/authorsform.py index c219b12ac..fdc704294 100644 --- a/openlp/plugins/songs/forms/authorsform.py +++ b/openlp/plugins/songs/forms/authorsform.py @@ -97,7 +97,7 @@ class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog): if QtGui.QMessageBox.critical( self, translate('SongsPlugin.AuthorsForm', 'Error'), translate('SongsPlugin.AuthorsForm', - 'You haven\'t set a display name for the ' + 'You have not set a display name for the ' 'author, would you like me to combine the first and ' 'last names for you?'), QtGui.QMessageBox.StandardButtons( diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index 43684c1a9..922147628 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -430,7 +430,7 @@ class Ui_EditSongDialog(object): self.AuthorRemoveButton.setText( translate('SongsPlugin.EditSongForm', '&Remove')) self.MaintenanceButton.setText(translate('SongsPlugin.EditSongForm', - '&Manage Authors, Topics, Books')) + '&Manage Authors, Topics, Song Books')) self.TopicGroupBox.setTitle( translate('SongsPlugin.EditSongForm', 'Topic')) self.TopicAddButton.setText( @@ -441,7 +441,7 @@ class Ui_EditSongDialog(object): translate('SongsPlugin.EditSongForm', 'Song Book')) self.SongTabWidget.setTabText( self.SongTabWidget.indexOf(self.AuthorsTab), - translate('SongsPlugin.EditSongForm', 'Authors, Topics && Book')) + translate('SongsPlugin.EditSongForm', 'Authors, Topics && Song Book')) self.ThemeGroupBox.setTitle( translate('SongsPlugin.EditSongForm', 'Theme')) self.ThemeAddButton.setText( diff --git a/openlp/plugins/songs/forms/songbookdialog.py b/openlp/plugins/songs/forms/songbookdialog.py index 2ab223b34..0401ff75b 100644 --- a/openlp/plugins/songs/forms/songbookdialog.py +++ b/openlp/plugins/songs/forms/songbookdialog.py @@ -68,7 +68,7 @@ class Ui_SongBookDialog(object): def retranslateUi(self, SongBookDialog): SongBookDialog.setWindowTitle( - translate('SongsPlugin.SongBookForm', 'Edit Book')) + translate('SongsPlugin.SongBookForm', 'Song Book Maintenance')) self.NameLabel.setText(translate('SongsPlugin.SongBookForm', '&Name:')) self.PublisherLabel.setText( translate('SongsPlugin.SongBookForm', '&Publisher:')) diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 09db3dcb8..db66be4df 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -178,7 +178,8 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard): SettingsManager.get_last_dir(self.songsplugin.settingsSection, 1)) if filename: editbox.setText(filename) - self.config.set_last_dir(filename, 1) + SettingsManager.set_last_dir(self.songsplugin.settingsSection, + filename, 1) def incrementProgressBar(self, status_text): log.debug(u'IncrementBar %s', status_text) @@ -253,4 +254,4 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard): self.ImportProgressBar.setValue(self.ImportProgressBar.maximum()) self.finishButton.setVisible(True) self.cancelButton.setVisible(False) - Receiver.send_message(u'process_events') \ No newline at end of file + Receiver.send_message(u'process_events') diff --git a/openlp/plugins/songs/forms/songimportwizard.py b/openlp/plugins/songs/forms/songimportwizard.py index d740d42a5..57ad3d9cf 100644 --- a/openlp/plugins/songs/forms/songimportwizard.py +++ b/openlp/plugins/songs/forms/songimportwizard.py @@ -109,16 +109,16 @@ class Ui_SongImportWizard(object): self.OpenLyricsButtonLayout.setSpacing(8) self.OpenLyricsButtonLayout.setObjectName(u'OpenLyricsButtonLayout') self.OpenLyricsAddButton = QtGui.QPushButton(self.OpenLyricsPage) - self.OpenLyricsAddButton.setIcon( - build_icon(u':/general/general_open.png')) + openIcon = build_icon(u':/general/general_open.png') + self.OpenLyricsAddButton.setIcon(openIcon) self.OpenLyricsAddButton.setObjectName(u'OpenLyricsAddButton') self.OpenLyricsButtonLayout.addWidget(self.OpenLyricsAddButton) self.OpenLyricsButtonSpacer = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.OpenLyricsButtonLayout.addItem(self.OpenLyricsButtonSpacer) self.OpenLyricsRemoveButton = QtGui.QPushButton(self.OpenLyricsPage) - self.OpenLyricsRemoveButton.setIcon( - build_icon(u':/general/general_delete.png')) + deleteIcon = build_icon(u':/general/general_delete.png') + self.OpenLyricsRemoveButton.setIcon(deleteIcon) self.OpenLyricsRemoveButton.setObjectName(u'OpenLyricsRemoveButton') self.OpenLyricsButtonLayout.addWidget(self.OpenLyricsRemoveButton) self.OpenLyricsLayout.addLayout(self.OpenLyricsButtonLayout) @@ -136,14 +136,14 @@ class Ui_SongImportWizard(object): self.OpenSongButtonLayout.setSpacing(8) self.OpenSongButtonLayout.setObjectName(u'OpenSongButtonLayout') self.OpenSongAddButton = QtGui.QPushButton(self.OpenSongPage) - self.OpenSongAddButton.setIcon(self.OpenIcon) + self.OpenSongAddButton.setIcon(openIcon) self.OpenSongAddButton.setObjectName(u'OpenSongAddButton') self.OpenSongButtonLayout.addWidget(self.OpenSongAddButton) self.OpenSongButtonSpacer = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.OpenSongButtonLayout.addItem(self.OpenSongButtonSpacer) self.OpenSongRemoveButton = QtGui.QPushButton(self.OpenSongPage) - self.OpenSongRemoveButton.setIcon(self.DeleteIcon) + self.OpenSongRemoveButton.setIcon(deleteIcon) self.OpenSongRemoveButton.setObjectName(u'OpenSongRemoveButton') self.OpenSongButtonLayout.addWidget(self.OpenSongRemoveButton) self.OpenSongLayout.addLayout(self.OpenSongButtonLayout) @@ -161,14 +161,14 @@ class Ui_SongImportWizard(object): self.CCLIButtonLayout.setSpacing(8) self.CCLIButtonLayout.setObjectName(u'CCLIButtonLayout') self.CCLIAddButton = QtGui.QPushButton(self.CCLIPage) - self.CCLIAddButton.setIcon(self.OpenIcon) + self.CCLIAddButton.setIcon(openIcon) self.CCLIAddButton.setObjectName(u'CCLIAddButton') self.CCLIButtonLayout.addWidget(self.CCLIAddButton) self.CCLIButtonSpacer = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) self.CCLIButtonLayout.addItem(self.CCLIButtonSpacer) self.CCLIRemoveButton = QtGui.QPushButton(self.CCLIPage) - self.CCLIRemoveButton.setIcon(self.DeleteIcon) + self.CCLIRemoveButton.setIcon(deleteIcon) self.CCLIRemoveButton.setObjectName(u'CCLIRemoveButton') self.CCLIButtonLayout.addWidget(self.CCLIRemoveButton) self.CCLILayout.addLayout(self.CCLIButtonLayout) @@ -190,7 +190,7 @@ class Ui_SongImportWizard(object): self.CSVFilenameEdit.setObjectName(u'CSVFilenameEdit') self.CSVFileLayout.addWidget(self.CSVFilenameEdit) self.CSVBrowseButton = QtGui.QToolButton(self.CSVPage) - self.CSVBrowseButton.setIcon(self.OpenIcon) + self.CSVBrowseButton.setIcon(openIcon) self.CSVBrowseButton.setObjectName(u'CSVBrowseButton') self.CSVFileLayout.addWidget(self.CSVBrowseButton) self.CSVLayout.setLayout(0, QtGui.QFormLayout.FieldRole, @@ -213,14 +213,11 @@ class Ui_SongImportWizard(object): self.ImportProgressBar.setObjectName(u'ImportProgressBar') self.ImportLayout.addWidget(self.ImportProgressBar) SongImportWizard.addPage(self.ImportPage) - self.retranslateUi(SongImportWizard) self.FormatStackedWidget.setCurrentIndex(0) - QtCore.QObject.connect( - self.FormatComboBox, + QtCore.QObject.connect(self.FormatComboBox, QtCore.SIGNAL(u'currentIndexChanged(int)'), - self.FormatStackedWidget.setCurrentIndex - ) + self.FormatStackedWidget.setCurrentIndex) QtCore.QMetaObject.connectSlotsByName(SongImportWizard) def retranslateUi(self, SongImportWizard): @@ -275,4 +272,3 @@ class Ui_SongImportWizard(object): translate('SongsPlugin.ImportWizardForm', 'Ready.')) self.ImportProgressBar.setFormat( translate('SongsPlugin.ImportWizardForm', '%p%')) - diff --git a/openlp/plugins/songs/forms/songmaintenancedialog.py b/openlp/plugins/songs/forms/songmaintenancedialog.py index f86754e0c..3e2f37dd3 100644 --- a/openlp/plugins/songs/forms/songmaintenancedialog.py +++ b/openlp/plugins/songs/forms/songmaintenancedialog.py @@ -217,7 +217,7 @@ class Ui_SongMaintenanceDialog(object): self.TypeListWidget.item(1).setText( translate('SongsPlugin.SongMaintenanceForm', 'Topics')) self.TypeListWidget.item(2).setText( - translate('SongsPlugin.SongMaintenanceForm', 'Books/Hymnals')) + translate('SongsPlugin.SongMaintenanceForm', 'Song Books')) self.AuthorAddButton.setText( translate('SongsPlugin.SongMaintenanceForm', '&Add')) self.AuthorEditButton.setText( diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py index 2a848ad98..cc98f5cb5 100644 --- a/openlp/plugins/songs/forms/songmaintenanceform.py +++ b/openlp/plugins/songs/forms/songmaintenanceform.py @@ -26,9 +26,9 @@ from PyQt4 import QtGui, QtCore from sqlalchemy.sql import and_ -from openlp.core.lib import translate +from openlp.core.lib import Receiver, translate from openlp.plugins.songs.forms import AuthorsForm, TopicsForm, SongBookForm -from openlp.plugins.songs.lib.db import Author, Book, Topic +from openlp.plugins.songs.lib.db import Author, Book, Topic, Song from songmaintenancedialog import Ui_SongMaintenanceDialog class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): @@ -140,13 +140,10 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): Returns False if the given Author is already in the list otherwise True. """ - authors = self.songmanager.get_all_objects_filtered(Author, - and_( - Author.first_name == new_author.first_name, - Author.last_name == new_author.last_name, - Author.display_name == new_author.display_name - ) - ) + authors = self.songmanager.get_all_objects_filtered(Author, + and_(Author.first_name == new_author.first_name, + Author.last_name == new_author.last_name, + Author.display_name == new_author.display_name)) if len(authors) > 0: # If we edit an existing Author, we need to make sure that we do # not return False when nothing has changed (because this would @@ -186,7 +183,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): Returns False if the given Book is already in the list otherwise True. """ books = self.songmanager.get_all_objects_filtered(Book, - and_(Book.name == new_book.name, + and_(Book.name == new_book.name, Book.publisher == new_book.publisher)) if len(books) > 0: # If we edit an existing Book, we need to make sure that we do @@ -212,11 +209,16 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): if self.checkAuthor(author): if self.songmanager.save_object(author): self.resetAuthors() + else: + QtGui.QMessageBox.critical(self, + translate('SongsPlugin.SongMaintenanceForm', 'Error'), + translate('SongsPlugin.SongMaintenanceForm', + 'Could not add your author.')) else: QtGui.QMessageBox.critical(self, translate('SongsPlugin.SongMaintenanceForm', 'Error'), translate('SongsPlugin.SongMaintenanceForm', - 'Could not add your author.')) + 'This author already exists.')) def onTopicAddButtonClick(self): if self.topicform.exec_(): @@ -224,25 +226,34 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): if self.checkTopic(topic): if self.songmanager.save_object(topic): self.resetTopics() + else: + QtGui.QMessageBox.critical(self, + translate('SongsPlugin.SongMaintenanceForm', 'Error'), + translate('SongsPlugin.SongMaintenanceForm', + 'Could not add your topic.')) else: QtGui.QMessageBox.critical(self, translate('SongsPlugin.SongMaintenanceForm', 'Error'), translate('SongsPlugin.SongMaintenanceForm', - 'Could not add your topic.')) + 'This topic already exists.')) def onBookAddButtonClick(self): if self.bookform.exec_(): - book = Book.populate( - name=unicode(self.bookform.NameEdit.text()), + book = Book.populate(name=unicode(self.bookform.NameEdit.text()), publisher=unicode(self.bookform.PublisherEdit.text())) if self.checkBook(book): if self.songmanager.save_object(book): self.resetBooks() + else: + QtGui.QMessageBox.critical(self, + translate('SongsPlugin.SongMaintenanceForm', 'Error'), + translate('SongsPlugin.SongMaintenanceForm', + 'Could not add your book.')) else: QtGui.QMessageBox.critical(self, translate('SongsPlugin.SongMaintenanceForm', 'Error'), translate('SongsPlugin.SongMaintenanceForm', - 'Could not add your book.')) + 'This book already exists.')) def onAuthorEditButtonClick(self): author_id = self._getCurrentItemId(self.AuthorsListWidget) @@ -266,6 +277,24 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): if self.checkAuthor(author, True): if self.songmanager.save_object(author): self.resetAuthors() + Receiver.send_message(u'songs_load_list') + else: + QtGui.QMessageBox.critical(self, + translate('SongsPlugin.SongMaintenanceForm', + 'Error'), + translate('SongsPlugin.SongMaintenanceForm', + 'Could not save your changes.')) + elif QtGui.QMessageBox.critical(self, + translate('SongsPlugin.SongMaintenanceForm', 'Error'), + translate('SongsPlugin.SongMaintenanceForm', 'The author %s' + ' already exists. Would you like to make songs with author ' + '%s use the existing author %s?' % (author.display_name, + temp_display_name, author.display_name)), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | + QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.Yes: + self.mergeAuthors(author) + self.resetAuthors() + Receiver.send_message(u'songs_load_list') else: # We restore the author's old first and last name as well as # his display name. @@ -275,7 +304,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): QtGui.QMessageBox.critical(self, translate('SongsPlugin.SongMaintenanceForm', 'Error'), translate('SongsPlugin.SongMaintenanceForm', - 'Could not save your author.')) + 'Could not save your modified author, because he ' + 'already exists.')) def onTopicEditButtonClick(self): topic_id = self._getCurrentItemId(self.TopicsListWidget) @@ -289,13 +319,30 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): if self.checkTopic(topic, True): if self.songmanager.save_object(topic): self.resetTopics() + else: + QtGui.QMessageBox.critical(self, + translate('SongsPlugin.SongMaintenanceForm', + 'Error'), + translate('SongsPlugin.SongMaintenanceForm', + 'Could not save your changes.')) + elif QtGui.QMessageBox.critical(self, + translate('SongsPlugin.SongMaintenanceForm', 'Error'), + translate('SongsPlugin.SongMaintenanceForm', 'The topic %s ' + 'already exists. Would you like to make songs with topic %s' + ' use the existing topic %s?' % (topic.name, temp_name, + topic.name)), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | + QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.Yes: + self.mergeTopics(topic) + self.resetTopics() else: # We restore the topics's old name. topic.name = temp_name QtGui.QMessageBox.critical(self, translate('SongsPlugin.SongMaintenanceForm', 'Error'), translate('SongsPlugin.SongMaintenanceForm', - 'Could not save your topic.')) + 'Could not save your modified topic, because it ' + 'already exists.')) def onBookEditButtonClick(self): book_id = self._getCurrentItemId(self.BooksListWidget) @@ -313,18 +360,89 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): if self.checkBook(book, True): if self.songmanager.save_object(book): self.resetBooks() + else: + QtGui.QMessageBox.critical(self, + translate('SongsPlugin.SongMaintenanceForm', + 'Error'), + translate('SongsPlugin.SongMaintenanceForm', + 'Could not save your changes.')) + elif QtGui.QMessageBox.critical(self, + translate('SongsPlugin.SongMaintenanceForm', 'Error'), + translate('SongsPlugin.SongMaintenanceForm', 'The book %s ' + 'already exists. Would you like to make songs with book %s ' + 'use the existing book %s?' % (book.name, temp_name, + book.name)), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | + QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.Yes: + self.mergeBooks(book) + self.resetBooks() else: # We restore the book's old name and publisher. book.name = temp_name book.publisher = temp_publisher - QtGui.QMessageBox.critical(self, - translate('SongsPlugin.SongMaintenanceForm', 'Error'), - translate('SongsPlugin.SongMaintenanceForm', - 'Could not save your book.')) + + def mergeAuthors(self, old_author): + ''' + Merges two authors into one author. + + ``old_author`` + The author which will be deleted afterwards. + ''' + existing_author = self.songmanager.get_object_filtered(Author, + and_(Author.first_name == old_author.first_name, + Author.last_name == old_author.last_name, + Author.display_name == old_author.display_name)) + songs = self.songmanager.get_all_objects_filtered(Song, + Song.authors.contains(old_author)) + for song in songs: + # We check if the song has already existing_author as author. If + # that is not the case we add it. + if existing_author not in song.authors: + song.authors.append(existing_author) + song.authors.remove(old_author) + self.songmanager.save_object(song) + self.songmanager.delete_object(Author, old_author.id) + + def mergeTopics(self, old_topic): + ''' + Merges two topics into one topic. + + ``old_topic`` + The topic which will be deleted afterwards. + ''' + existing_topic = self.songmanager.get_object_filtered(Topic, + Topic.name == old_topic.name) + songs = self.songmanager.get_all_objects_filtered(Song, + Song.topics.contains(old_topic)) + for song in songs: + # We check if the song has already existing_topic as topic. If that + # is not the case we add it. + if existing_topic not in song.topics: + song.topics.append(existing_topic) + song.topics.remove(old_topic) + self.songmanager.save_object(song) + self.songmanager.delete_object(Topic, old_topic.id) + + def mergeBooks(self, old_book): + ''' + Merges two books into one book. + + ``old_book`` + The book which will be deleted afterwards. + ''' + existing_book = self.songmanager.get_object_filtered(Book, + and_(Book.name == old_book.name, + Book.publisher == old_book.publisher)) + songs = self.songmanager.get_all_objects_filtered(Song, + Song.song_book_id == old_book.id) + for song in songs: + song.song_book_id = existing_book.id + self.songmanager.save_object(song) + self.songmanager.delete_object(Book, old_book.id) def onAuthorDeleteButtonClick(self): """ - Delete the author if the author is not attached to any songs + Delete the author if the author is not attached to any songs. """ self._deleteItem(Author, self.AuthorsListWidget, self.resetAuthors, translate('SongsPlugin.SongMaintenanceForm', 'Delete Author'), @@ -337,7 +455,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): def onTopicDeleteButtonClick(self): """ - Delete the Book is the Book is not attached to any songs + Delete the Book is the Book is not attached to any songs. """ self._deleteItem(Topic, self.TopicsListWidget, self.resetTopics, translate('SongsPlugin.SongMaintenanceForm', 'Delete Topic'), @@ -350,7 +468,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog): def onBookDeleteButtonClick(self): """ - Delete the Book is the Book is not attached to any songs + Delete the Book is the Book is not attached to any songs. """ self._deleteItem(Book, self.BooksListWidget, self.resetBooks, translate('SongsPlugin.SongMaintenanceForm', 'Delete Book'), diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index ef7b8b7d3..8be820d82 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -141,6 +141,7 @@ from xml import LyricsXML, SongXMLBuilder, SongXMLParser from songstab import SongsTab from mediaitem import SongMediaItem from songimport import SongImport +from opensongimport import OpenSongImport try: from sofimport import SofImport from oooimport import OooImport diff --git a/openlp/plugins/songs/lib/opensongimport.py b/openlp/plugins/songs/lib/opensongimport.py new file mode 100644 index 000000000..69ae76f6d --- /dev/null +++ b/openlp/plugins/songs/lib/opensongimport.py @@ -0,0 +1,246 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2010 Raoul Snyman # +# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # +# Thompson, Jon Tibble, Carsten Tinggaard # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### + +import os +import re + +from zipfile import ZipFile + +from lxml.etree import Element +from lxml import objectify + +from openlp.plugins.songs.lib.songimport import SongImport + +import logging +log = logging.getLogger(__name__) + +class OpenSongImportError(Exception): + pass + +class OpenSongImport(object): + """ + Import songs exported from OpenSong - the format is described loosly here: + http://www.opensong.org/d/manual/song_file_format_specification + + However, it doesn't describe the section, so here's an attempt: + + Verses can be expressed in one of 2 ways: + + [v1]List of words + Another Line + + [v2]Some words for the 2nd verse + etc... + + + The 'v' can be left out - it is implied + or: + + [V] + 1List of words + 2Some words for the 2nd Verse + + 1Another Line + 2etc... + + + Either or both forms can be used in one song. The Number does not + necessarily appear at the start of the line + + The [v1] labels can have either upper or lower case Vs + Other labels can be used also: + C - Chorus + B - Bridge + + Guitar chords can be provided 'above' the lyrics (the line is + preceeded by a'.') and _s can be used to signify long-drawn-out + words: + + . A7 Bm + 1 Some____ Words + + Chords and _s are removed by this importer. + + The verses etc. are imported and tagged appropriately. + + The tag is used to populate the OpenLP verse + display order field. The Author and Copyright tags are also + imported to the appropriate places. + + """ + def __init__(self, songmanager): + """ + Initialise the class. Requires a songmanager class which + is passed to SongImport for writing song to disk + """ + self.songmanager = songmanager + self.song = None + + def do_import(self, filename, commit=True): + """ + Import either a single opensong file, or a zipfile + containing multiple opensong files If the commit parameter is + set False, the import will not be committed to the database + (useful for test scripts) + """ + ext = os.path.splitext(filename)[1] + if ext.lower() == ".zip": + log.info('Zipfile found %s', filename) + z = ZipFile(filename, u'r') + for song in z.infolist(): + parts = os.path.split(song.filename) + if parts[-1] == u'': + #No final part => directory + continue + songfile = z.open(song) + self.do_import_file(songfile) + if commit: + self.finish() + else: + log.info('Direct import %s', filename) + file = open(filename) + self.do_import_file(file) + if commit: + self.finish() + + + def do_import_file(self, file): + """ + Process the OpenSong file - pass in a file-like object, + not a filename + """ + self.song_import = SongImport(self.songmanager) + tree = objectify.parse(file) + root = tree.getroot() + fields = dir(root) + decode = {u'copyright':self.song_import.add_copyright, + u'ccli':u'ccli_number', + u'author':self.song_import.parse_author, + u'title':u'title', + u'aka':u'alternate_title', + u'hymn_number':u'song_number'} + for (attr, fn_or_string) in decode.items(): + if attr in fields: + ustring = unicode(root.__getattr__(attr)) + if type(fn_or_string) == type(u''): + self.song_import.__setattr__(fn_or_string, ustring) + else: + fn_or_string(ustring) + + res = [] + if u'theme' in fields: + self.song_import.topics.append(unicode(root.theme)) + if u'alttheme' in fields: + self.song_import.topics.append(unicode(root.alttheme)) + # data storage while importing + verses = {} + lyrics = unicode(root.lyrics) + # keep track of a "default" verse order, in case none is specified + our_verse_order = [] + verses_seen = {} + # in the absence of any other indication, verses are the default, + # erm, versetype! + versetype = u'V' + for thisline in lyrics.split(u'\n'): + # remove comments + semicolon = thisline.find(u';') + if semicolon >= 0: + thisline = thisline[:semicolon] + thisline = thisline.strip() + if len(thisline) == 0: + continue + # skip inthisline guitar chords and page and column breaks + if thisline[0] == u'.' or thisline.startswith(u'---') \ + or thisline.startswith(u'-!!'): + continue + + # verse/chorus/etc. marker + if thisline[0] == u'[': + versetype = thisline[1].upper() + if versetype.isdigit(): + versenum = versetype + versetype = u'V' + elif thisline[2] != u']': + # there's a number to go with it - extract that as well + right_bracket = thisline.find(u']') + versenum = thisline[2:right_bracket] + else: + # if there's no number, assume it's no.1 + versenum = u'1' + continue + words = None + + # number at start of line.. it's verse number + if thisline[0].isdigit(): + versenum = thisline[0] + words = thisline[1:].strip() + if words is None and \ + versenum is not None and \ + versetype is not None: + words = thisline + if versenum is not None: + versetag = u'%s%s'%(versetype,versenum) + if not verses.has_key(versetype): + verses[versetype] = {} + if not verses[versetype].has_key(versenum): + verses[versetype][versenum] = [] # storage for lines in this verse + if not verses_seen.has_key(versetag): + verses_seen[versetag] = 1 + our_verse_order.append(versetag) + if words: + # Tidy text and remove the ____s from extended words + words = self.song_import.tidy_text(words) + words = words.replace('_', '') + verses[versetype][versenum].append(words) + # done parsing + versetypes = verses.keys() + versetypes.sort() + versetags = {} + verse_renames = {} + for versetype in versetypes: + versenums = verses[versetype].keys() + versenums.sort() + for num in versenums: + versetag = u'%s%s' %(versetype,num) + lines = u'\n'.join(verses[versetype][num]) + self.song_import.verses.append([versetag, lines]) + versetags[versetag] = 1 # keep track of what we have for error checking later + # now figure out the presentation order + if u'presentation' in fields and root.presentation != u'': + order = unicode(root.presentation) + order = order.split() + else: + assert len(our_verse_order)>0 + order = our_verse_order + for tag in order: + if len(tag) == 1: + tag = tag + u'1' # Assume it's no.1 if it's not there + if not versetags.has_key(tag): + log.warn(u'Got order %s but not in versetags, skipping', tag) + else: + self.song_import.verse_order_list.append(tag) + def finish(self): + """ Separate function, allows test suite to not pollute database""" + self.song_import.finish() diff --git a/openlp/plugins/songs/lib/sofimport.py b/openlp/plugins/songs/lib/sofimport.py index da56580aa..52fd38634 100644 --- a/openlp/plugins/songs/lib/sofimport.py +++ b/openlp/plugins/songs/lib/sofimport.py @@ -142,7 +142,7 @@ class SofImport(OooImport): self.blanklines += 1 if self.blanklines > 1: return - if self.song.get_title() != u'': + if self.song.title != u'': self.finish_verse() return self.blanklines = 0 @@ -166,8 +166,8 @@ class SofImport(OooImport): self.finish_verse() self.song.repeat_verse() return - if self.song.get_title() == u'': - if self.song.get_copyright() == u'': + if self.song.title == u'': + if self.song.copyright == u'': self.add_author(text) else: self.song.add_copyright(text) @@ -187,10 +187,10 @@ class SofImport(OooImport): return text if textportion.CharWeight == BOLD: boldtext = text.strip() - if boldtext.isdigit() and self.song.get_song_number() == '': + if boldtext.isdigit() and self.song.song_number == '': self.add_songnumber(boldtext) return u'' - if self.song.get_title() == u'': + if self.song.title == u'': text = self.uncap_text(text) self.add_title(text) return text @@ -220,20 +220,17 @@ class SofImport(OooImport): Add a song number, store as alternate title. Also use the song number to work out which songbook we're in """ - self.song.set_song_number(song_no) - self.song.set_alternate_title(song_no + u'.') + self.song.song_number = song_no + self.song.alternate_title = song_no + u'.' + self.song.song_book_pub = u'Kingsway Publications' if int(song_no) <= 640: - self.song.set_song_book(u'Songs of Fellowship 1', - u'Kingsway Publications') + self.song.song_book = u'Songs of Fellowship 1' elif int(song_no) <= 1150: - self.song.set_song_book(u'Songs of Fellowship 2', - u'Kingsway Publications') + self.song.song_book = u'Songs of Fellowship 2' elif int(song_no) <= 1690: - self.song.set_song_book(u'Songs of Fellowship 3', - u'Kingsway Publications') + self.song.song_book = u'Songs of Fellowship 3' else: - self.song.set_song_book(u'Songs of Fellowship 4', - u'Kingsway Publications') + self.song.song_book = u'Songs of Fellowship 4' def add_title(self, text): """ @@ -245,7 +242,7 @@ class SofImport(OooImport): title = title[1:] if title.endswith(u','): title = title[:-1] - self.song.set_title(title) + self.song.title = title def add_author(self, text): """ @@ -283,7 +280,7 @@ class SofImport(OooImport): splitat = None else: versetag = u'V' - splitat = self.verse_splits(self.song.get_song_number()) + splitat = self.verse_splits(self.song.song_number) if splitat: ln = 0 verse = u'' @@ -538,4 +535,3 @@ class SofImport(OooImport): if song_number == 1119: return 7 return None - diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index bf3b404cd..9787d9e20 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -123,53 +123,10 @@ class SongImport(object): if len(lines) == 1: self.parse_author(lines[0]) return - if not self.get_title(): - self.set_title(lines[0]) + if not self.title: + self.title = lines[0] self.add_verse(text) - def get_title(self): - """ - Return the title - """ - return self.title - - def get_copyright(self): - """ - Return the copyright - """ - return self.copyright - - def get_song_number(self): - """ - Return the song number - """ - return self.song_number - - def set_title(self, title): - """ - Set the title - """ - self.title = title - - def set_alternate_title(self, title): - """ - Set the alternate title - """ - self.alternate_title = title - - def set_song_number(self, song_number): - """ - Set the song number - """ - self.song_number = song_number - - def set_song_book(self, song_book, publisher): - """ - Set the song book name and publisher - """ - self.song_book_name = song_book - self.song_book_pub = publisher - def add_copyright(self, copyright): """ Build the copyright field @@ -184,8 +141,8 @@ class SongImport(object): """ Add the author. OpenLP stores them individually so split by 'and', '&' and comma. - However need to check for "Mr and Mrs Smith" and turn it to - "Mr Smith" and "Mrs Smith". + However need to check for 'Mr and Mrs Smith' and turn it to + 'Mr Smith' and 'Mrs Smith'. """ for author in text.split(u','): authors = author.split(u'&') @@ -267,7 +224,7 @@ class SongImport(object): def commit_song(self): """ - Write the song and it's fields to disk + Write the song and its fields to disk """ song = Song() song.title = self.title @@ -315,11 +272,12 @@ class SongImport(object): publisher=self.song_book_pub) song.book = song_book for topictext in self.topics: - topic = self.manager.get_object_filtered(Topic, - Topic.name == topictext) + if len(topictext) == 0: + continue + topic = self.manager.get_object_filtered(Topic, Topic.name == topictext) if topic is None: topic = Topic.populate(name=topictext) - song.topics.append(topictext) + song.topics.append(topic) self.manager.save_object(song) def print_song(self): diff --git a/openlp/plugins/songs/lib/songxml.py b/openlp/plugins/songs/lib/songxml.py index 2965c579b..d529f3f31 100644 --- a/openlp/plugins/songs/lib/songxml.py +++ b/openlp/plugins/songs/lib/songxml.py @@ -60,175 +60,6 @@ class SongFeatureError(SongException): # TODO: Song: Import ChangingSong # TODO: Song: Export ChangingSong -_BLANK_OPENSONG_XML = \ -''' - - - - - - - - - - -''' - -class _OpenSong(object): - """ - Class for import of OpenSong - """ - def __init__(self, xmlContent = None): - """ - Initialize from given xml content - """ - self._set_from_xml(_BLANK_OPENSONG_XML, 'song') - if xmlContent: - self._set_from_xml(xmlContent, 'song') - - def _set_from_xml(self, xml, root_tag): - """ - Set song properties from given xml content. - - ``xml`` - Formatted xml tags and values. - ``root_tag`` - The root tag of the xml. - """ - root = ElementTree(element=XML(xml)) - xml_iter = root.getiterator() - for element in xml_iter: - if element.tag != root_tag: - text = element.text - if text is None: - val = text - elif isinstance(text, basestring): - # Strings need special handling to sort the colours out - if text[0] == u'$': - # This might be a hex number, let's try to convert it. - try: - val = int(text[1:], 16) - except ValueError: - pass - else: - # Let's just see if it's a integer. - try: - val = int(text) - except ValueError: - # Ok, it seems to be a string. - val = text - if hasattr(self, u'post_tag_hook'): - (element.tag, val) = \ - self.post_tag_hook(element.tag, val) - setattr(self, element.tag, val) - - def __str__(self): - """ - Return string with all public attributes - - The string is formatted with one attribute per line - If the string is split on newline then the length of the - list is equal to the number of attributes - """ - attributes = [] - for attrib in dir(self): - if not attrib.startswith(u'_'): - attributes.append( - u'%30s : %s' % (attrib, getattr(self, attrib))) - return u'\n'.join(attributes) - - def _get_as_string(self): - """ - Return one string with all public attributes - """ - result = u'' - for attrib in dir(self): - if not attrib.startswith(u'_'): - result += u'_%s_' % getattr(self, attrib) - return result - - def get_author_list(self): - """Convert author field to an authorlist - - in OpenSong an author list may be separated by '/' - return as a string - """ - if self.author: - list = self.author.split(u' and ') - res = [item.strip() for item in list] - return u', '.join(res) - - def get_category_array(self): - """Convert theme and alttheme into category_array - - return as a string - """ - res = [] - if self.theme: - res.append(self.theme) - if self.alttheme: - res.append(self.alttheme) - return u', u'.join(res) - - def _reorder_verse(self, tag, tmpVerse): - """ - Reorder the verse in case of first char is a number - tag -- the tag of this verse / verse group - tmpVerse -- list of strings - """ - res = [] - for digit in '1234567890 ': - tagPending = True - for line in tmpVerse: - if line.startswith(digit): - if tagPending: - tagPending = False - tagChar = tag.strip(u'[]').lower() - if 'v' == tagChar: - newtag = "Verse" - elif 'c' == tagChar: - newtag = "Chorus" - elif 'b' == tagChar: - newtag = "Bridge" - elif 'p' == tagChar: - newtag = "Pre-chorus" - else: - newtag = tagChar - tagString = (u'# %s %s' % (newtag, digit)).rstrip() - res.append(tagString) - res.append(line[1:]) - if (len(line) == 0) and (not tagPending): - res.append(line) - return res - - def get_lyrics(self): - """ - Convert the lyrics to openlp lyrics format - return as list of strings - """ - lyrics = self.lyrics.split(u'\n') - tmpVerse = [] - finalLyrics = [] - tag = "" - for lyric in lyrics: - line = lyric.rstrip() - if not line.startswith(u'.'): - # drop all chords - tmpVerse.append(line) - if line: - if line.startswith(u'['): - tag = line - else: - reorderedVerse = self._reorder_verse(tag, tmpVerse) - finalLyrics.extend(reorderedVerse) - tag = "" - tmpVerse = [] - # catch up final verse - reorderedVerse = self._reorder_verse(tag, tmpVerse) - finalLyrics.extend(reorderedVerse) - return finalLyrics - - class Song(object): """Handling song properties and methods @@ -275,7 +106,7 @@ class Song(object): show_author_list -- 0: no show, 1: show show_copyright -- 0: no show, 1: show show_song_cclino -- 0: no show, 1: show - theme -- name of theme or blank + theme_name -- name of theme or blank category_array -- list of user defined properties (hymn, gospel) song_book -- name of originating book song_number -- number of the song, related to a songbook @@ -298,7 +129,7 @@ class Song(object): self.show_copyright = 1 self.show_song_cclino = 1 self.show_title = 1 - self.theme = "" + self.theme_name = "" self.category_array = None self.song_book = "" self.song_number = "" @@ -307,40 +138,6 @@ class Song(object): self.set_lyrics(u'') return - def from_opensong_buffer(self, xmlcontent): - """Initialize from buffer(string) of xml lines in opensong format""" - self._reset() - opensong = _OpenSong(xmlcontent) - if opensong.title: - self.set_title(opensong.title) - if opensong.copyright: - self.set_copyright(opensong.copyright) - if opensong.presentation: - self.set_verse_order(opensong.presentation) - if opensong.ccli: - self.set_song_cclino(opensong.ccli) - self.set_author_list(opensong.get_author_list()) - self.set_category_array(opensong.get_category_array()) - self.set_lyrics(opensong.get_lyrics()) - - def from_opensong_file(self, xmlfilename): - """ - Initialize from file containing xml - xmlfilename -- path to xml file - """ - osfile = None - try: - osfile = open(xmlfilename, 'r') - list = [line for line in osfile] - osfile.close() - xml = "".join(list) - self.from_opensong_buffer(xml) - except IOError: - log.exception(u'Failed to load opensong xml file') - finally: - if osfile: - osfile.close() - def _remove_punctuation(self, title): """Remove the puntuation chars from title @@ -366,10 +163,6 @@ class Song(object): if len(self.search_title) < 1: raise SongTitleError(u'The searchable title is empty') - def get_title(self): - """Return title value""" - return self.title - def from_ccli_text_buffer(self, textList): """ Create song from a list of texts (strings) - CCLI text format expected @@ -424,7 +217,7 @@ class Song(object): self.set_title(sName) self.set_author_list(author_list) self.set_copyright(sCopyright) - self.set_song_cclino(sCcli) + self.set_ccli_number(sCcli) self.set_lyrics(lyrics) def from_ccli_text_file(self, textFileName): @@ -479,21 +272,21 @@ class Song(object): """Set the copyright string""" self.copyright = copyright - def get_song_cclino(self): + def get_ccli_number(self): """Return the songCclino""" - return self._assure_string(self.song_cclino) + return self._assure_string(self.ccli_number) - def set_song_cclino(self, song_cclino): - """Set the song_cclino""" - self.song_cclino = song_cclino + def set_ccli_number(self, ccli_number): + """Set the ccli_number""" + self.ccli_number = ccli_number - def get_theme(self): + def get_theme_name(self): """Return the theme name for the song""" - return self._assure_string(self.theme) + return self._assure_string(self.theme_name) - def set_theme(self, theme): + def set_theme_name(self, theme_name): """Set the theme name (string)""" - self.theme = theme + self.theme_name = theme_name def get_song_book(self): """Return the song_book (string)""" @@ -532,9 +325,9 @@ class Song(object): asOneString True -- string: - "John Newton, A Parker" + 'John Newton, A Parker' False -- list of strings - ["John Newton", u'A Parker"] + ['John Newton', u'A Parker'] """ if asOneString: res = self._assure_string(self.author_list) @@ -557,9 +350,9 @@ class Song(object): asOneString True -- string: - "Hymn, Gospel" + 'Hymn, Gospel' False -- list of strings - ["Hymn", u'Gospel"] + ['Hymn', u'Gospel'] """ if asOneString: res = self._assure_string(self.category_array) @@ -601,13 +394,13 @@ class Song(object): """Set the show_copyright flag (bool)""" self.show_copyright = show_copyright - def get_show_song_cclino(self): + def get_show_ccli_number(self): """Return the showSongCclino (string)""" - return self.show_song_cclino + return self.show_ccli_number - def set_show_song_cclino(self, show_song_cclino): - """Set the show_song_cclino flag (bool)""" - self.show_song_cclino = show_song_cclino + def set_show_ccli_number(self, show_ccli_number): + """Set the show_ccli_number flag (bool)""" + self.show_ccli_number = show_ccli_number def get_lyrics(self): """Return the lyrics as a list of strings @@ -674,7 +467,7 @@ class Song(object): slideNumber -- 1 .. numberOfSlides Returns a list as: - [theme (string), + [theme_name (string), title (string), authorlist (string), copyright (string), @@ -688,7 +481,7 @@ class Song(object): raise SongSlideError(u'Slide number too high') res = [] if self.show_title: - title = self.get_title() + title = self.title else: title = "" if self.show_author_list: @@ -699,13 +492,13 @@ class Song(object): cpright = self.get_copyright() else: cpright = "" - if self.show_song_cclino: - ccli = self.get_song_cclino() + if self.show_ccli_number: + ccli = self.get_ccli_number() else: ccli = "" - theme = self.get_theme() + theme_name = self.get_theme_name() # examine the slide for a theme - res.append(theme) + res.append(theme_name) res.append(title) res.append(author) res.append(cpright) diff --git a/openlp/plugins/songs/lib/test/test.opensong b/openlp/plugins/songs/lib/test/test.opensong new file mode 100644 index 000000000..1ce60cf3b --- /dev/null +++ b/openlp/plugins/songs/lib/test/test.opensong @@ -0,0 +1,54 @@ + + + Martins Test + MartiÑ Thómpson + 2010 Martin Thompson + 1 + V1 C V2 C2 V3 B1 V1 + Blah + + + + + + + + TestTheme + TestAltTheme + + + ;Comment +. A B C +1 v1 Line 1___ +2 v2 Line 1___ +. A B C7 +1 V1 Line 2 +2 V2 Line 2 + +[3] + V3 Line 1 + V3 Line 2 + +[b1] + Bridge 1 +--- +-!! + Bridge 1 line 2 + +[C] +. A B + Chorus 1 + +[C2] +. A B + Chorus 2 + + diff --git a/openlp/plugins/songs/lib/test/test.opensong.zip b/openlp/plugins/songs/lib/test/test.opensong.zip new file mode 100644 index 000000000..62588c890 Binary files /dev/null and b/openlp/plugins/songs/lib/test/test.opensong.zip differ diff --git a/openlp/plugins/songs/lib/test/test2.opensong b/openlp/plugins/songs/lib/test/test2.opensong new file mode 100644 index 000000000..e6a3b4913 --- /dev/null +++ b/openlp/plugins/songs/lib/test/test2.opensong @@ -0,0 +1,45 @@ + + + Martins 2nd Test + Martin Thompson + 2010 Martin Thompson + 2 + + Blah + + + + + + + + + + + ;Comment +[V] +. A B C +1 v1 Line 1___ +2 v2 Line 1___ +. A B C7 +1 V1 Line 2 +2 V2 Line 2 + +[b1] + Bridge 1 + Bridge 1 line 2 +[C1] + Chorus 1 + +[C2] + Chorus 2 + + diff --git a/openlp/plugins/songs/lib/test/test_importing_lots.py b/openlp/plugins/songs/lib/test/test_importing_lots.py new file mode 100644 index 000000000..9f1908bca --- /dev/null +++ b/openlp/plugins/songs/lib/test/test_importing_lots.py @@ -0,0 +1,57 @@ +from openlp.plugins.songs.lib.opensongimport import OpenSongImport +from openlp.plugins.songs.lib.db import init_schema +from openlp.core.lib.db import Manager +from glob import glob +from zipfile import ZipFile +import os +from traceback import print_exc +import sys +import codecs + +def opensong_import_lots(): + ziploc = u'/home/mjt/openlp/OpenSong_Data/' + files = [] + #files = [u'test.opensong.zip', ziploc+u'ADond.zip'] + files.extend(glob(ziploc+u'Songs.zip')) + #files.extend(glob(ziploc+u'SOF.zip')) + #files.extend(glob(ziploc+u'spanish_songs_for_opensong.zip')) +# files.extend(glob(ziploc+u'opensong_*.zip')) + errfile = codecs.open(u'import_lots_errors.txt', u'w', u'utf8') + manager = Manager(u'songs', init_schema) + for file in files: + print u'Importing', file + z = ZipFile(file, u'r') + for song in z.infolist(): + # need to handle unicode filenames (CP437 - Winzip does this) + filename = song.filename#.decode('cp852') + parts = os.path.split(filename) + if parts[-1] == u'': + #No final part => directory + continue + print " ", file, ":",filename, + songfile = z.open(song) + #z.extract(song) + #songfile=open(filename, u'r') + o = OpenSongImport(manager) + try: + o.do_import_file(songfile) + # o.song_import.print_song() + except: + print "Failure", + + errfile.write(u'Failure: %s:%s\n' %(file, filename.decode('cp437'))) + songfile = z.open(song) + for l in songfile.readlines(): + l = l.decode('utf8') + print(u' |%s\n' % l.strip()) + errfile.write(u' |%s\n'%l.strip()) + print_exc(3, file = errfile) + print_exc(3) + sys.exit(1) + # continue + #o.finish() + print "OK" + #os.unlink(filename) + # o.song_import.print_song() +if __name__ == "__main__": + opensong_import_lots() diff --git a/openlp/plugins/songs/lib/test/test_opensongimport.py b/openlp/plugins/songs/lib/test/test_opensongimport.py new file mode 100644 index 000000000..8c974adbc --- /dev/null +++ b/openlp/plugins/songs/lib/test/test_opensongimport.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2010 Raoul Snyman # +# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael # +# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin # +# Thompson, Jon Tibble, Carsten Tinggaard # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### +from openlp.plugins.songs.lib.opensongimport import OpenSongImport +from openlp.core.lib.db import Manager +from openlp.plugins.songs.lib.db import init_schema +from openlp.plugins.songs.songsplugin import SongsPlugin +import sys + +def test(): + manager = Manager(u'songs', init_schema) + o = OpenSongImport(manager) + o.do_import(u'test.opensong', commit=False) + o.song_import.print_song() + assert o.song_import.copyright == u'2010 Martin Thompson' + assert o.song_import.authors == [u'MartiÑ Thómpson'] + assert o.song_import.title == u'Martins Test' + assert o.song_import.alternate_title == u'' + assert o.song_import.song_number == u'1' + assert [u'C1', u'Chorus 1'] in o.song_import.verses + assert [u'C2', u'Chorus 2'] in o.song_import.verses + assert not [u'C3', u'Chorus 3'] in o.song_import.verses + assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.song_import.verses + assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.song_import.verses + assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.song_import.verses + assert o.song_import.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3', u'B1', u'V1'] + assert o.song_import.ccli_number == u'Blah' + assert o.song_import.topics == [u'TestTheme', u'TestAltTheme'] + o.do_import(u'test.opensong.zip', commit=False) + o.song_import.print_song() + o.finish() + assert o.song_import.copyright == u'2010 Martin Thompson' + assert o.song_import.authors == [u'MartiÑ Thómpson'] + assert o.song_import.title == u'Martins Test' + assert o.song_import.alternate_title == u'' + assert o.song_import.song_number == u'1' + assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.song_import.verses + assert [u'C1', u'Chorus 1'] in o.song_import.verses + assert [u'C2', u'Chorus 2'] in o.song_import.verses + assert not [u'C3', u'Chorus 3'] in o.song_import.verses + assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.song_import.verses + assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.song_import.verses + assert o.song_import.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3', u'B1', u'V1'] + + o = OpenSongImport(manager) + o.do_import(u'test2.opensong', commit=False) + # o.finish() + o.song_import.print_song() + assert o.song_import.copyright == u'2010 Martin Thompson' + assert o.song_import.authors == [u'Martin Thompson'] + assert o.song_import.title == u'Martins 2nd Test' + assert o.song_import.alternate_title == u'' + assert o.song_import.song_number == u'2' + print o.song_import.verses + assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.song_import.verses + assert [u'C1', u'Chorus 1'] in o.song_import.verses + assert [u'C2', u'Chorus 2'] in o.song_import.verses + assert not [u'C3', u'Chorus 3'] in o.song_import.verses + assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.song_import.verses + assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.song_import.verses + print o.song_import.verse_order_list + assert o.song_import.verse_order_list == [u'V1', u'V2', u'B1', u'C1', u'C2'] + + print "Tests passed" + pass + +if __name__ == "__main__": + test() diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 359c28eda..336c1ebf1 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -76,8 +76,9 @@ class SongXMLBuilder(object): ``content`` The actual text of the verse to be stored. """ - #log.debug(u'add_verse_to_lyrics %s, %s\n%s' % (type, number, content)) - verse = etree.Element(u'verse', type=type, label=number) + # log.debug(u'add_verse_to_lyrics %s, %s\n%s' % (type, number, content)) + verse = etree.Element(u'verse', type = unicode(type), + label = unicode(number)) verse.text = etree.CDATA(content) self.lyrics.append(verse) diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index e7be7cae3..ab36d13d0 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -39,6 +39,8 @@ try: except ImportError: OOo_available = False +from openlp.plugins.songs.lib import OpenSongImport + log = logging.getLogger(__name__) class SongsPlugin(Plugin): @@ -137,6 +139,25 @@ class SongsPlugin(Plugin): QtCore.SIGNAL(u'triggered()'), self.onImportSofItemClick) QtCore.QObject.connect(self.ImportOooItem, QtCore.SIGNAL(u'triggered()'), self.onImportOooItemClick) + # OpenSong import menu item - will be removed and the + # functionality will be contained within the import wizard + self.ImportOpenSongItem = QtGui.QAction(import_menu) + self.ImportOpenSongItem.setObjectName(u'ImportOpenSongItem') + self.ImportOpenSongItem.setText( + translate('SongsPlugin', + 'OpenSong (temp menu item)')) + self.ImportOpenSongItem.setToolTip( + translate('SongsPlugin', + 'Import songs from OpenSong files' + + '(either raw text or ZIPfiles)')) + self.ImportOpenSongItem.setStatusTip( + translate('SongsPlugin', + 'Import songs from OpenSong files' + + '(either raw text or ZIPfiles)')) + import_menu.addAction(self.ImportOpenSongItem) + QtCore.QObject.connect(self.ImportOpenSongItem, + QtCore.SIGNAL(u'triggered()'), self.onImportOpenSongItemClick) + def addExportMenuItem(self, export_menu): """ @@ -177,6 +198,26 @@ class SongsPlugin(Plugin): QtGui.QMessageBox.Ok) Receiver.send_message(u'songs_load_list') + def onImportOpenSongItemClick(self): + filenames = QtGui.QFileDialog.getOpenFileNames( + None, translate('SongsPlugin', + 'Open OpenSong file'), + u'', u'All files (*.*)') + try: + for filename in filenames: + importer = OpenSongImport(self.manager) + importer.do_import(unicode(filename)) + except: + log.exception('Could not import OpenSong file') + QtGui.QMessageBox.critical(None, + translate('SongsPlugin', + 'Import Error'), + translate('SongsPlugin', + 'Error importing OpenSong file'), + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok), + QtGui.QMessageBox.Ok) + Receiver.send_message(u'songs_load_list') + def onImportOooItemClick(self): filenames = QtGui.QFileDialog.getOpenFileNames( None, translate('SongsPlugin',