diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 5247ae938..80bf4a67b 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -239,7 +239,8 @@ def resize_image(image, width, height, background=QtCore.Qt.black): Resize an image to fit on the current screen. ``image`` - The image to resize. + The image to resize. It has to be either a ``QImage`` instance or the + path to the image. ``width`` The new image width. diff --git a/openlp/core/lib/imagemanager.py b/openlp/core/lib/imagemanager.py index fb242602a..0a76ce834 100644 --- a/openlp/core/lib/imagemanager.py +++ b/openlp/core/lib/imagemanager.py @@ -85,8 +85,7 @@ class ImageManager(QtCore.QObject): for key in self._cache.keys(): image = self._cache[key] image.dirty = True - image.image = resize_image(image.path, - self.width, self.height) + image.image = resize_image(image.path, self.width, self.height) self._cache_dirty = True # only one thread please if not self._thread_running: @@ -128,8 +127,7 @@ class ImageManager(QtCore.QObject): image = Image() image.name = name image.path = path - image.image = resize_image(path, - self.width, self.height) + image.image = resize_image(path, self.width, self.height) self._cache[name] = image else: log.debug(u'Image in cache %s:%s' % (name, path)) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index d4fdfff17..c2c2987c2 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -240,7 +240,7 @@ class MediaManagerItem(QtGui.QWidget): Creates the main widget for listing items the media item is tracking """ # Add the List widget - self.listView = ListWidgetWithDnD(self, self.title) + self.listView = ListWidgetWithDnD(self, self.plugin.name) self.listView.uniformItemSizes = True self.listView.setSpacing(1) self.listView.setSelectionMode( diff --git a/openlp/core/lib/rendermanager.py b/openlp/core/lib/rendermanager.py index 32a29915f..860a52b60 100644 --- a/openlp/core/lib/rendermanager.py +++ b/openlp/core/lib/rendermanager.py @@ -203,12 +203,12 @@ class RenderManager(object): # set the default image size for previews self.calculate_default(self.screens.preview[u'size']) verse = u'The Lord said to {r}Noah{/r}: \n' \ - 'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n' \ - 'The Lord said to {g}Noah{/g}:\n' \ - 'There\'s gonna be a {st}floody{/st}, {it}floody{/it}\n' \ - 'Get those children out of the muddy, muddy \n' \ - '{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}' \ - 'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n' + 'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n' \ + 'The Lord said to {g}Noah{/g}:\n' \ + 'There\'s gonna be a {st}floody{/st}, {it}floody{/it}\n' \ + 'Get those children out of the muddy, muddy \n' \ + '{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}' \ + 'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n' # make big page for theme edit dialog to get line count if self.force_page: verse = verse + verse + verse diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py index 3f41d377a..4e20671c5 100644 --- a/openlp/core/ui/shortcutlistdialog.py +++ b/openlp/core/ui/shortcutlistdialog.py @@ -36,7 +36,7 @@ class Ui_ShortcutListDialog(object): self.treeWidget = QtGui.QTreeWidget(shortcutListDialog) self.treeWidget.setAlternatingRowColors(True) self.treeWidget.setObjectName(u'treeWidget') - self.treeWidget.setColumnCount(2) + self.treeWidget.setColumnCount(3) self.dialogLayout.addWidget(self.treeWidget) self.defaultButton = QtGui.QRadioButton(shortcutListDialog) self.defaultButton.setChecked(True) @@ -78,7 +78,8 @@ class Ui_ShortcutListDialog(object): translate('OpenLP.ShortcutListDialog', 'Customize Shortcuts')) self.treeWidget.setHeaderLabels([ translate('OpenLP.ShortcutListDialog', 'Action'), - translate('OpenLP.ShortcutListDialog', 'Shortcut')]) + translate('OpenLP.ShortcutListDialog', 'Shortcut'), + translate('OpenLP.ShortcutListDialog', 'Alternate')]) self.defaultButton.setText( translate('OpenLP.ShortcutListDialog', 'Default: %s')) self.customButton.setText( diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index 9d2b31853..0de4bea7f 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -95,8 +95,13 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): item = QtGui.QTreeWidgetItem([category.name]) for action in category.actions: actionText = REMOVE_AMPERSAND.sub('', unicode(action.text())) - shortcutText = action.shortcut().toString() - actionItem = QtGui.QTreeWidgetItem([actionText, shortcutText]) + if (len(action.shortcuts()) == 2): + shortcutText = action.shortcuts()[0].toString() + alternateText = action.shortcuts()[1].toString() + else: + shortcutText = action.shortcut().toString() + alternateText = u'' + actionItem = QtGui.QTreeWidgetItem([actionText, shortcutText, alternateText]) actionItem.setIcon(0, action.icon()) item.addChild(actionItem) item.setExpanded(True) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 52626f24f..9177e313f 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -380,7 +380,7 @@ class SlideController(QtGui.QWidget): self.previousItem.setShortcuts([QtCore.Qt.Key_Up, QtCore.Qt.Key_PageUp]) self.previousItem.setShortcutContext( QtCore.Qt.WidgetWithChildrenShortcut) - actionList.add_action(self.nextItem, u'Live') + actionList.add_action(self.previousItem, u'Live') self.nextItem.setShortcuts([QtCore.Qt.Key_Down, QtCore.Qt.Key_PageDown]) self.nextItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) actionList.add_action(self.nextItem, u'Live') @@ -603,14 +603,15 @@ class SlideController(QtGui.QWidget): slideHeight = 0 if self.serviceItem.is_text(): if frame[u'verseTag']: - bits = frame[u'verseTag'].split(u':') - tag = u'%s\n%s' % (bits[0][0], bits[1][0:] ) - tag1 = u'%s%s' % (bits[0][0], bits[1][0:] ) - row = tag + # These tags are already translated. + verse_def = frame[u'verseTag'] + verse_def = u'%s%s' % (verse_def[0].upper(), verse_def[1:]) + two_line_def = u'%s\n%s' % (verse_def[0], verse_def[1:] ) + row = two_line_def if self.isLive: - if tag1 not in self.slideList: - self.slideList[tag1] = framenumber - self.songMenu.menu().addAction(tag1, + if verse_def not in self.slideList: + self.slideList[verse_def] = framenumber + self.songMenu.menu().addAction(verse_def, self.onSongBarHandler) else: row += 1 diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 739de7182..78c4596e3 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -405,8 +405,8 @@ class ThemeManager(QtGui.QWidget): files = QtGui.QFileDialog.getOpenFileNames(self, translate('OpenLP.ThemeManager', 'Select Theme Import File'), SettingsManager.get_last_dir(self.settingsSection), - unicode(translate('OpenLP.ThemeManager', 'Theme v1 (*.theme);;' - 'Theme v2 (*.otz);;%s (*.*)')) % UiStrings.AllFiles) + unicode(translate('OpenLP.ThemeManager', + 'OpenLP Themes (*.theme *.otz)'))) log.info(u'New Themes %s', unicode(files)) if files: for file in files: @@ -529,6 +529,18 @@ class ThemeManager(QtGui.QWidget): else: outfile = open(fullpath, u'wb') outfile.write(zip.read(file)) + except (IOError, NameError): + critical_error_message_box( + translate('OpenLP.ThemeManager', 'Validation Error'), + translate('OpenLP.ThemeManager', 'File is not a valid theme.')) + log.exception(u'Importing theme from zip failed %s' % filename) + finally: + # Close the files, to be able to continue creating the theme. + if zip: + zip.close() + if outfile: + outfile.close() + # As all files are closed, we can create the Theme. if filexml: theme = self._createThemeFromXml(filexml, self.path) self.generateAndSaveImage(dir, themename, theme) @@ -539,17 +551,6 @@ class ThemeManager(QtGui.QWidget): 'File is not a valid theme.')) log.exception(u'Theme file does not contain XML data %s' % filename) - except (IOError, NameError): - critical_error_message_box( - translate('OpenLP.ThemeManager', 'Validation Error'), - translate('OpenLP.ThemeManager', - 'File is not a valid theme.')) - log.exception(u'Importing theme from zip failed %s' % filename) - finally: - if zip: - zip.close() - if outfile: - outfile.close() def checkIfThemeExists(self, themeName): """ diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 9db744460..3f5ee90c2 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -164,27 +164,34 @@ def _get_os_dir_path(dir_type): """ Return a path based on which OS and environment we are running in. """ + encoding = sys.getfilesystemencoding() if sys.platform == u'win32': if dir_type == AppLocation.DataDir: - return os.path.join(os.getenv(u'APPDATA'), u'openlp', u'data') - return os.path.join(os.getenv(u'APPDATA'), u'openlp') + return os.path.join(unicode(os.getenv(u'APPDATA'), encoding), + u'openlp', u'data') + return os.path.join(unicode(os.getenv(u'APPDATA'), encoding), + u'openlp') elif sys.platform == u'darwin': if dir_type == AppLocation.DataDir: - return os.path.join(os.getenv(u'HOME'), u'Library', - u'Application Support', u'openlp', u'Data') - return os.path.join(os.getenv(u'HOME'), u'Library', - u'Application Support', u'openlp') + return os.path.join(unicode(os.getenv(u'HOME'), encoding), + u'Library', u'Application Support', u'openlp', u'Data') + return os.path.join(unicode(os.getenv(u'HOME'), encoding), + u'Library', u'Application Support', u'openlp') else: if XDG_BASE_AVAILABLE: if dir_type == AppLocation.ConfigDir: - return os.path.join(BaseDirectory.xdg_config_home, u'openlp') + return os.path.join(unicode(BaseDirectory.xdg_config_home, + encoding), u'openlp') elif dir_type == AppLocation.DataDir: - return os.path.join(BaseDirectory.xdg_data_home, u'openlp') + return os.path.join( + unicode(BaseDirectory.xdg_data_home, encoding), u'openlp') elif dir_type == AppLocation.CacheDir: - return os.path.join(BaseDirectory.xdg_cache_home, u'openlp') + return os.path.join(unicode(BaseDirectory.xdg_cache_home, + encoding), u'openlp') if dir_type == AppLocation.DataDir: - return os.path.join(os.getenv(u'HOME'), u'.openlp', u'data') - return os.path.join(os.getenv(u'HOME'), u'.openlp') + return os.path.join(unicode(os.getenv(u'HOME'), encoding), + u'.openlp', u'data') + return os.path.join(unicode(os.getenv(u'HOME'), encoding), u'.openlp') def _get_frozen_path(frozen_option, non_frozen_option): """ @@ -375,13 +382,13 @@ def get_uno_command(): """ Returns the UNO command to launch an openoffice.org instance. """ + COMMAND = u'soffice' + OPTIONS = u'-nologo -norestore -minimized -invisible -nofirststartwizard' if UNO_CONNECTION_TYPE == u'pipe': - return u'openoffice.org -nologo -norestore -minimized -invisible ' \ - + u'-nofirststartwizard -accept=pipe,name=openlp_pipe;urp;' + CONNECTION = u'"-accept=pipe,name=openlp_pipe;urp;"' else: - return u'openoffice.org -nologo -norestore -minimized ' \ - + u'-invisible -nofirststartwizard ' \ - + u'-accept=socket,host=localhost,port=2002;urp;' + CONNECTION = u'"-accept=socket,host=localhost,port=2002;urp;"' + return u'%s %s %s' % (COMMAND, OPTIONS, CONNECTION) def get_uno_instance(resolver): """ diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index 65e9f35ff..eb00da255 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -147,8 +147,10 @@ class PowerpointDocument(PresentationDocument): """ if self.check_thumbnails(): return - self.presentation.Export(os.path.join(self.get_thumbnail_folder(), ''), - 'png', 320, 240) + for num in range(0, self.presentation.Slides.Count): + self.presentation.Slides(num + 1).Export(os.path.join( + self.get_thumbnail_folder(), 'slide%d.png' % (num + 1)), + 'png', 320, 240) def close_presentation(self): """ diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index a64cd31dd..fc839195c 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -154,8 +154,9 @@ class PptviewDocument(PresentationDocument): being shut down """ log.debug(u'ClosePresentation') - self.controller.process.ClosePPT(self.pptid) - self.pptid = -1 + if self.controller.process: + self.controller.process.ClosePPT(self.pptid) + self.pptid = -1 self.controller.remove_doc(self) def is_loaded(self): diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 39f1ba256..77caf012e 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -227,10 +227,6 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.copyrightEdit.setText(u'') self.verseListWidget.clear() self.verseListWidget.setRowCount(0) - if self.song.verse_order: - self.verseOrderEdit.setText(self.song.verse_order) - else: - self.verseOrderEdit.setText(u'') if self.song.comments: self.commentsEdit.setPlainText(self.song.comments) else: @@ -250,15 +246,31 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): # This is just because occasionally the lyrics come back as a "buffer" if isinstance(self.song.lyrics, buffer): self.song.lyrics = unicode(self.song.lyrics) + verse_tags_translated = False if self.song.lyrics.startswith(u' 1: + index = VerseType.from_translated_string(verse_tag) + if index is None: + index = VerseType.from_string(verse_tag) + else: + verse_tags_translated = True + if index is None: + index = VerseType.from_tag(verse_tag) + if index is None: + index = VerseType.Other + verse[0][u'type'] = VerseType.Tags[index] + verse_def = u'%s%s' % (verse[0][u'type'], verse[0][u'label']) item = QtGui.QTableWidgetItem(verse[1]) - item.setData(QtCore.Qt.UserRole, QtCore.QVariant(variant)) + item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def)) self.verseListWidget.setItem(count, 0, item) else: verses = self.song.lyrics.split(u'\n\n') @@ -266,10 +278,24 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): self.verseListWidget.setRowCount( self.verseListWidget.rowCount() + 1) item = QtGui.QTableWidgetItem(verse) - variant = u'%s:%s' % \ - (VerseType.to_string(VerseType.Verse), unicode(count + 1)) - item.setData(QtCore.Qt.UserRole, QtCore.QVariant(variant)) + verse_def = u'%s%s' % \ + (VerseType.Tags[VerseType.Verse], unicode(count + 1)) + item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def)) self.verseListWidget.setItem(count, 0, item) + if self.song.verse_order: + # we translate verse order + translated = [] + for verse_def in self.song.verse_order.split(): + verse_index = None + if verse_tags_translated: + verse_index = VerseType.from_translated_tag(verse_def[0]) + if verse_index is None: + verse_index = VerseType.from_tag(verse_def[0]) + verse_tag = VerseType.TranslatedTags[verse_index].upper() + translated.append(u'%s%s' % (verse_tag, verse_def[1:])) + self.verseOrderEdit.setText(u' '.join(translated)) + else: + self.verseOrderEdit.setText(u'') self.verseListWidget.resizeRowsToContents() self.tagRows() # clear the results @@ -294,14 +320,14 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): """ Tag the Song List rows based on the verse list """ - rowLabel = [] + row_label = [] for row in range(0, self.verseListWidget.rowCount()): item = self.verseListWidget.item(row, 0) - data = unicode(item.data(QtCore.Qt.UserRole).toString()) - bit = data.split(u':') - rowTag = u'%s%s' % (bit[0][0:1], bit[1]) - rowLabel.append(rowTag) - self.verseListWidget.setVerticalHeaderLabels(rowLabel) + verse_def = unicode(item.data(QtCore.Qt.UserRole).toString()) + verse_tag = VerseType.translated_tag(verse_def[0]) + row_def = u'%s%s' % (verse_tag, verse_def[1:]) + row_label.append(row_def) + self.verseListWidget.setVerticalHeaderLabels(row_label) def onAuthorAddButtonClicked(self): item = int(self.authorsComboBox.currentIndex()) @@ -420,11 +446,11 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): def onVerseAddButtonClicked(self): self.verse_form.setVerse(u'', True) if self.verse_form.exec_(): - afterText, verse, subVerse = self.verse_form.getVerse() - data = u'%s:%s' % (verse, subVerse) - item = QtGui.QTableWidgetItem(afterText) - item.setData(QtCore.Qt.UserRole, QtCore.QVariant(data)) - item.setText(afterText) + after_text, verse_tag, verse_num = self.verse_form.getVerse() + verse_def = u'%s%s' % (verse_tag, verse_num) + item = QtGui.QTableWidgetItem(after_text) + item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def)) + item.setText(after_text) self.verseListWidget.setRowCount( self.verseListWidget.rowCount() + 1) self.verseListWidget.setItem( @@ -440,12 +466,12 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): verseId = unicode(item.data(QtCore.Qt.UserRole).toString()) self.verse_form.setVerse(tempText, True, verseId) if self.verse_form.exec_(): - afterText, verse, subVerse = self.verse_form.getVerse() - data = u'%s:%s' % (verse, subVerse) - item.setData(QtCore.Qt.UserRole, QtCore.QVariant(data)) - item.setText(afterText) + after_text, verse_tag, verse_num = self.verse_form.getVerse() + verse_def = u'%s%s' % (verse_tag, verse_num) + item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def)) + item.setText(after_text) # number of lines has change so repaint the list moving the data - if len(tempText.split(u'\n')) != len(afterText.split(u'\n')): + if len(tempText.split(u'\n')) != len(after_text.split(u'\n')): tempList = {} tempId = {} for row in range(0, self.verseListWidget.rowCount()): @@ -467,7 +493,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): for row in range(0, self.verseListWidget.rowCount()): item = self.verseListWidget.item(row, 0) field = unicode(item.data(QtCore.Qt.UserRole).toString()) - verse_list += u'---[%s]---\n' % field + verse_tag = VerseType.translated_name(field[0]) + verse_num = field[1:] + verse_list += u'---[%s:%s]---\n' % (verse_tag, verse_num) verse_list += item.text() verse_list += u'\n' self.verse_form.setVerse(verse_list) @@ -483,15 +511,32 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): for count, parts in enumerate(match.split(u']---\n')): if len(parts) > 1: if count == 0: - # make sure the tag is correctly cased - variant = u'%s%s' % \ - (parts[0:1].upper(), parts[1:].lower()) + # handling carefully user inputted versetags + separator = parts.find(u':') + if separator >= 0: + verse_name = parts[0:separator].strip() + verse_num = parts[separator+1:].strip() + else: + verse_name = parts + verse_num = u'1' + verse_index = \ + VerseType.from_loose_input(verse_name) + verse_tag = VerseType.Tags[verse_index] + # Later we need to handle v1a as well. + #regex = re.compile(r'(\d+\w.)') + regex = re.compile(r'\D*(\d+)\D*') + match = regex.match(verse_num) + if match: + verse_num = match.group(1) + else: + verse_num = u'1' + verse_def = u'%s%s' % (verse_tag, verse_num) else: if parts.endswith(u'\n'): parts = parts.rstrip(u'\n') item = QtGui.QTableWidgetItem(parts) item.setData(QtCore.Qt.UserRole, - QtCore.QVariant(variant)) + QtCore.QVariant(verse_def)) self.verseListWidget.setRowCount( self.verseListWidget.rowCount() + 1) self.verseListWidget.setItem( @@ -543,25 +588,31 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): order_names = unicode(self.verseOrderEdit.text()).split() for item in order_names: if len(item) == 1: - order.append(item.lower() + u'1') + verse_index = VerseType.from_translated_tag(item) + if verse_index is not None: + order.append(VerseType.Tags[verse_index] + u'1') + else: + order.append(u'') # it matches no verses anyway else: - order.append(item.lower()) + verse_index = VerseType.from_translated_tag(item[0]) + if verse_index is None: + order.append(u'') # same as above + else: + verse_tag = VerseType.Tags[verse_index] + verse_num = item[1:].lower() + order.append(verse_tag + verse_num) verses = [] verse_names = [] - for index in range (0, self.verseListWidget.rowCount()): + for index in range(0, self.verseListWidget.rowCount()): verse = self.verseListWidget.item(index, 0) verse = unicode(verse.data(QtCore.Qt.UserRole).toString()) if verse not in verse_names: - verses.append( - re.sub(r'(.)[^:]*:(.*)', r'\1\2', verse.lower())) - verse_names.append(verse) + verses.append(verse) + verse_names.append(u'%s%s' % ( + VerseType.translated_tag(verse[0]), verse[1:])) for count, item in enumerate(order): if item not in verses: - self.songTabWidget.setCurrentIndex(0) - self.verseOrderEdit.setFocus() - valid = verses.pop(0) - for verse in verses: - valid = valid + u', ' + verse + valid = u', '.join(verse_names) critical_error_message_box( message=unicode(translate('SongsPlugin.EditSongForm', 'The verse order is invalid. There is no verse ' @@ -577,7 +628,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): unicode(translate('SongsPlugin.EditSongForm', 'You have not used %s anywhere in the verse ' 'order. Are you sure you want to save the song ' - 'like this?')) % verse_names[count].replace(u':', u' '), + 'like this?')) % verse_names[count], QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) if answer == QtGui.QMessageBox.No: return False @@ -684,7 +735,14 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): else: self.song.search_title = self.song.title self.song.comments = unicode(self.commentsEdit.toPlainText()) - self.song.verse_order = unicode(self.verseOrderEdit.text()) + ordertext = unicode(self.verseOrderEdit.text()) + order = [] + for item in ordertext.split(): + verse_tag = VerseType.Tags[ + VerseType.from_translated_tag(item[0])] + verse_num = item[1:].lower() + order.append(u'%s%s' % (verse_tag, verse_num)) + self.song.verse_order = u' '.join(order) self.song.ccli_number = unicode(self.CCLNumberEdit.text()) self.song.song_number = unicode(self.songBookNumberEdit.text()) book_name = unicode(self.songBookComboBox.currentText()) @@ -727,12 +785,14 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): for i in range(0, self.verseListWidget.rowCount()): item = self.verseListWidget.item(i, 0) verseId = unicode(item.data(QtCore.Qt.UserRole).toString()) - bits = verseId.split(u':') - sxml.add_verse_to_lyrics(bits[0], bits[1], unicode(item.text())) + verse_tag = verseId[0] + verse_num = verseId[1:] + sxml.add_verse_to_lyrics(verse_tag, verse_num, + unicode(item.text())) text = text + self.whitespace.sub(u' ', unicode(self.verseListWidget.item(i, 0).text())) + u' ' - if (bits[1] > u'1') and (bits[0][0] not in multiple): - multiple.append(bits[0][0]) + if (verse_num > u'1') and (verse_tag not in multiple): + multiple.append(verse_tag) self.song.search_lyrics = text.lower() self.song.lyrics = unicode(sxml.extract_xml(), u'utf-8') for verse in multiple: diff --git a/openlp/plugins/songs/forms/editversedialog.py b/openlp/plugins/songs/forms/editversedialog.py index 7caf782e6..64da3e89e 100644 --- a/openlp/plugins/songs/forms/editversedialog.py +++ b/openlp/plugins/songs/forms/editversedialog.py @@ -70,19 +70,19 @@ class Ui_EditVerseDialog(object): translate('SongsPlugin.EditVerseForm', 'Edit Verse')) self.verseTypeLabel.setText( translate('SongsPlugin.EditVerseForm', '&Verse type:')) - self.verseTypeComboBox.setItemText(0, - VerseType.to_string(VerseType.Verse)) - self.verseTypeComboBox.setItemText(1, - VerseType.to_string(VerseType.Chorus)) - self.verseTypeComboBox.setItemText(2, - VerseType.to_string(VerseType.Bridge)) - self.verseTypeComboBox.setItemText(3, - VerseType.to_string(VerseType.PreChorus)) - self.verseTypeComboBox.setItemText(4, - VerseType.to_string(VerseType.Intro)) - self.verseTypeComboBox.setItemText(5, - VerseType.to_string(VerseType.Ending)) - self.verseTypeComboBox.setItemText(6, - VerseType.to_string(VerseType.Other)) + self.verseTypeComboBox.setItemText(VerseType.Verse, + VerseType.TranslatedNames[VerseType.Verse]) + self.verseTypeComboBox.setItemText(VerseType.Chorus, + VerseType.TranslatedNames[VerseType.Chorus]) + self.verseTypeComboBox.setItemText(VerseType.Bridge, + VerseType.TranslatedNames[VerseType.Bridge]) + self.verseTypeComboBox.setItemText(VerseType.PreChorus, + VerseType.TranslatedNames[VerseType.PreChorus]) + self.verseTypeComboBox.setItemText(VerseType.Intro, + VerseType.TranslatedNames[VerseType.Intro]) + self.verseTypeComboBox.setItemText(VerseType.Ending, + VerseType.TranslatedNames[VerseType.Ending]) + self.verseTypeComboBox.setItemText(VerseType.Other, + VerseType.TranslatedNames[VerseType.Other]) self.insertButton.setText( translate('SongsPlugin.EditVerseForm', '&Insert')) diff --git a/openlp/plugins/songs/forms/editverseform.py b/openlp/plugins/songs/forms/editverseform.py index e67e0733a..6156e6821 100644 --- a/openlp/plugins/songs/forms/editverseform.py +++ b/openlp/plugins/songs/forms/editverseform.py @@ -57,22 +57,23 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog): QtCore.QObject.connect(self.verseTypeComboBox, QtCore.SIGNAL(u'currentIndexChanged(int)'), self.onVerseTypeComboBoxChanged) - self.verse_regex = re.compile(r'---\[([-\w]+):([\d]+)\]---') + self.verse_regex = re.compile(r'---\[(.+):\D*(\d*)\D*.*\]---') def contextMenu(self, point): item = self.serviceManagerList.itemAt(point) - def insertVerse(self, title, num=1): + def insertVerse(self, verse_tag, verse_num=1): if self.verseTextEdit.textCursor().columnNumber() != 0: self.verseTextEdit.insertPlainText(u'\n') - self.verseTextEdit.insertPlainText(u'---[%s:%s]---\n' % (title, num)) + verse_tag = VerseType.translated_name(verse_tag) + self.verseTextEdit.insertPlainText(u'---[%s:%s]---\n' % + (verse_tag, verse_num)) self.verseTextEdit.setFocus() def onInsertButtonClicked(self): - verse_type = self.verseTypeComboBox.currentIndex() - if VerseType.to_string(verse_type) is not None: - self.insertVerse(VerseType.to_string(verse_type), - self.verseNumberBox.value()) + verse_type_index = self.verseTypeComboBox.currentIndex() + self.insertVerse(VerseType.Tags[verse_type_index], + self.verseNumberBox.value()) def onVerseTypeComboBoxChanged(self): """ @@ -81,10 +82,11 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog): """ position = self.verseTextEdit.textCursor().position() text = unicode(self.verseTextEdit.toPlainText()) - verse_type = VerseType.to_string(self.verseTypeComboBox.currentIndex()) + verse_name = VerseType.TranslatedNames[ + self.verseTypeComboBox.currentIndex()] if not text: return - position = text.rfind(u'---[%s' % verse_type, 0, position) + position = text.rfind(u'---[%s' % verse_name, 0, position) if position == -1: self.verseNumberBox.setValue(1) return @@ -95,11 +97,11 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog): text = text[:position + 4] match = self.verse_regex.match(text) if match: - verse_type = match.group(1) - verse_number = int(match.group(2)) - verse_type_index = VerseType.from_string(verse_type) + verse_tag = match.group(1) + verse_num = int(match.group(2)) + verse_type_index = VerseType.from_loose_input(verse_tag) if verse_type_index is not None: - self.verseNumberBox.setValue(verse_number) + self.verseNumberBox.setValue(verse_num) def onCursorPositionChanged(self): """ @@ -124,25 +126,26 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog): match = self.verse_regex.match(text) if match: verse_type = match.group(1) + verse_type_index = VerseType.from_loose_input(verse_type) verse_number = int(match.group(2)) - verse_type_index = VerseType.from_string(verse_type) if verse_type_index is not None: self.verseTypeComboBox.setCurrentIndex(verse_type_index) self.verseNumberBox.setValue(verse_number) def setVerse(self, text, single=False, - tag=u'%s:1' % VerseType.to_string(VerseType.Verse)): + tag=u'%s1' % VerseType.Tags[VerseType.Verse]): self.hasSingleVerse = single if single: - verse_type, verse_number = tag.split(u':') - verse_type_index = VerseType.from_string(verse_type) + verse_type_index = VerseType.from_tag(tag[0]) + verse_number = tag[1:] if verse_type_index is not None: self.verseTypeComboBox.setCurrentIndex(verse_type_index) self.verseNumberBox.setValue(int(verse_number)) self.insertButton.setVisible(False) else: if not text: - text = u'---[%s:1]---\n' % VerseType.to_string(VerseType.Verse) + text = u'---[%s:1]---\n' % \ + VerseType.TranslatedNames[VerseType.Verse] self.verseTypeComboBox.setCurrentIndex(0) self.verseNumberBox.setValue(1) self.insertButton.setVisible(True) @@ -152,14 +155,14 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog): def getVerse(self): return self.verseTextEdit.toPlainText(), \ - VerseType.to_string(self.verseTypeComboBox.currentIndex()), \ + VerseType.Tags[self.verseTypeComboBox.currentIndex()], \ unicode(self.verseNumberBox.value()) def getVerseAll(self): text = self.verseTextEdit.toPlainText() if not text.startsWith(u'---['): - text = u'---[%s:1]---\n%s' % (VerseType.to_string(VerseType.Verse), - text) + text = u'---[%s:1]---\n%s' % \ + (VerseType.TranslatedNames[VerseType.Verse], text) return text def accept(self): diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 849a1ad1e..f331fbcb9 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -252,6 +252,7 @@ class SongExportForm(OpenLPWizard): self.availableListWidget.clear() self.selectedListWidget.clear() self.directoryLineEdit.clear() + self.searchLineEdit.clear() # Load the list of songs. Receiver.send_message(u'cursor_busy') songs = self.plugin.manager.get_all_objects(Song) @@ -340,19 +341,21 @@ class SongExportForm(OpenLPWizard): def onUncheckButtonClicked(self): """ - The *uncheckButton* has been clicked. Set all songs unchecked. + The *uncheckButton* has been clicked. Set all visible songs unchecked. """ for row in range(self.availableListWidget.count()): item = self.availableListWidget.item(row) - item.setCheckState(QtCore.Qt.Unchecked) + if not item.isHidden(): + item.setCheckState(QtCore.Qt.Unchecked) def onCheckButtonClicked(self): """ - The *checkButton* has been clicked. Set all songs checked. + The *checkButton* has been clicked. Set all visible songs checked. """ for row in range(self.availableListWidget.count()): item = self.availableListWidget.item(row) - item.setCheckState(QtCore.Qt.Checked) + if not item.isHidden(): + item.setCheckState(QtCore.Qt.Checked) def onDirectoryButtonClicked(self): """ diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py index c763d70b9..95fae92a8 100644 --- a/openlp/plugins/songs/lib/__init__.py +++ b/openlp/plugins/songs/lib/__init__.py @@ -40,68 +40,147 @@ class VerseType(object): Ending = 5 Other = 6 - @staticmethod - def to_string(verse_type): - """ - Return a string for a given VerseType + Names = [ + u'Verse', + u'Chorus', + u'Bridge', + u'Pre-Chorus', + u'Intro', + u'Ending', + u'Other'] + Tags = [name[0].lower() for name in Names] - ``verse_type`` - The type to return a string for - """ - if not isinstance(verse_type, int): - verse_type = verse_type.lower() - if verse_type == VerseType.Verse or verse_type == \ - unicode(VerseType.to_string(VerseType.Verse)).lower()[0]: - return translate('SongsPlugin.VerseType', 'Verse') - elif verse_type == VerseType.Chorus or verse_type == \ - unicode(VerseType.to_string(VerseType.Chorus)).lower()[0]: - return translate('SongsPlugin.VerseType', 'Chorus') - elif verse_type == VerseType.Bridge or verse_type == \ - unicode(VerseType.to_string(VerseType.Bridge)).lower()[0]: - return translate('SongsPlugin.VerseType', 'Bridge') - elif verse_type == VerseType.PreChorus or verse_type == \ - unicode(VerseType.to_string(VerseType.PreChorus)).lower()[0]: - return translate('SongsPlugin.VerseType', 'Pre-Chorus') - elif verse_type == VerseType.Intro or verse_type == \ - unicode(VerseType.to_string(VerseType.Intro)).lower()[0]: - return translate('SongsPlugin.VerseType', 'Intro') - elif verse_type == VerseType.Ending or verse_type == \ - unicode(VerseType.to_string(VerseType.Ending)).lower()[0]: - return translate('SongsPlugin.VerseType', 'Ending') - elif verse_type == VerseType.Other or verse_type == \ - unicode(VerseType.to_string(VerseType.Other)).lower()[0]: - return translate('SongsPlugin.VerseType', 'Other') + TranslatedNames = [ + unicode(translate('SongsPlugin.VerseType', 'Verse')), + unicode(translate('SongsPlugin.VerseType', 'Chorus')), + unicode(translate('SongsPlugin.VerseType', 'Bridge')), + unicode(translate('SongsPlugin.VerseType', 'Pre-Chorus')), + unicode(translate('SongsPlugin.VerseType', 'Intro')), + unicode(translate('SongsPlugin.VerseType', 'Ending')), + unicode(translate('SongsPlugin.VerseType', 'Other'))] + TranslatedTags = [name[0].lower() for name in TranslatedNames] @staticmethod - def from_string(verse_type): + def translated_tag(verse_tag, strict=False): + """ + Return the translated UPPERCASE tag for a given tag, + used to show translated verse tags in UI + + ``verse_tag`` + The string to return a VerseType for + + ``strict`` + Determines if the default Other or None should be returned + """ + if strict: + not_found_value = None + else: + not_found_value = VerseType.TranslatedTags[VerseType.Other].upper() + verse_tag = verse_tag[0].lower() + for num, tag in enumerate(VerseType.Tags): + if verse_tag == tag: + return VerseType.TranslatedTags[num].upper() + return not_found_value + + @staticmethod + def translated_name(verse_tag, strict=False): + """ + Return the translated name for a given tag + + ``verse_tag`` + The string to return a VerseType for + + ``strict`` + Determines if the default Other or None should be returned + """ + if strict: + not_found_value = None + else: + not_found_value = VerseType.TranslatedNames[VerseType.Other] + verse_tag = verse_tag[0].lower() + for num, tag in enumerate(VerseType.Tags): + if verse_tag == tag: + return VerseType.TranslatedNames[num] + return not_found_value + + @staticmethod + def from_tag(verse_tag, strict=False): + """ + Return the VerseType for a given tag + + ``verse_tag`` + The string to return a VerseType for + + ``strict`` + Determines if the default Other or None should be returned + """ + if strict: + no_return_value = None + else: + no_return_value = VerseType.Other + verse_tag = verse_tag[0].lower() + for num, tag in enumerate(VerseType.Tags): + if verse_tag == tag: + return num + return no_return_value + + @staticmethod + def from_translated_tag(verse_tag): + """ + Return the VerseType for a given tag + + ``verse_tag`` + The string to return a VerseType for + """ + verse_tag = verse_tag[0].lower() + for num, tag in enumerate(VerseType.TranslatedTags): + if verse_tag == tag: + return num + + @staticmethod + def from_string(verse_name): """ Return the VerseType for a given string - ``verse_type`` + ``verse_name`` The string to return a VerseType for """ - verse_type = verse_type.lower() - if verse_type == unicode(VerseType.to_string(VerseType.Verse)).lower(): - return VerseType.Verse - elif verse_type == \ - unicode(VerseType.to_string(VerseType.Chorus)).lower(): - return VerseType.Chorus - elif verse_type == \ - unicode(VerseType.to_string(VerseType.Bridge)).lower(): - return VerseType.Bridge - elif verse_type == \ - unicode(VerseType.to_string(VerseType.PreChorus)).lower(): - return VerseType.PreChorus - elif verse_type == \ - unicode(VerseType.to_string(VerseType.Intro)).lower(): - return VerseType.Intro - elif verse_type == \ - unicode(VerseType.to_string(VerseType.Ending)).lower(): - return VerseType.Ending - elif verse_type == \ - unicode(VerseType.to_string(VerseType.Other)).lower(): - return VerseType.Other + verse_name = verse_name.lower() + for num, name in enumerate(VerseType.Names): + if verse_name == name.lower(): + return num + @staticmethod + def from_translated_string(verse_name): + """ + Return the VerseType for a given string + + ``verse_name`` + The string to return a VerseType for + """ + verse_name = verse_name.lower() + for num, translation in enumerate(VerseType.TranslatedNames): + if verse_name == translation.lower(): + return num + + @staticmethod + def from_loose_input(verse_name): + """ + Return the VerseType for a given string, Other if not found + + ``verse_name`` + The string to return a VerseType for + """ + verse_index = None + if len(verse_name) > 1: + verse_index = VerseType.from_translated_string(verse_name) + if verse_index is None: + verse_index = VerseType.from_string(verse_name) + if verse_index is None: + verse_index = VerseType.from_translated_tag(verse_name) + if verse_index is None: + verse_index = VerseType.from_tag(verse_name) + return verse_index def retrieve_windows_encoding(recommendation=None): """ diff --git a/openlp/plugins/songs/lib/easislidesimport.py b/openlp/plugins/songs/lib/easislidesimport.py index 5d56af8ce..b31e50862 100644 --- a/openlp/plugins/songs/lib/easislidesimport.py +++ b/openlp/plugins/songs/lib/easislidesimport.py @@ -81,14 +81,16 @@ class EasiSlidesImport(SongImport): def _parse_song(self, song): self._success = True - self._add_unicode_attribute(self.title, song.Title1, True) - self._add_unicode_attribute(self.alternate_title, song.Title2) - self._add_unicode_attribute(self.song_number, song.SongNumber) + self._add_unicode_attribute(u'title', song.Title1, True) + self._add_unicode_attribute(u'alternate_title', song.Title2) + self._add_unicode_attribute(u'song_number', song.SongNumber) if self.song_number == u'0': self.song_number = u'' self._add_authors(song) - self._add_copyright(song) - self._add_unicode_attribute(self.song_book_name, song.BookReference) + self._add_copyright(song.Copyright) + self._add_copyright(song.LicenceAdmin1) + self._add_copyright(song.LicenceAdmin2) + self._add_unicode_attribute(u'song_book_name', song.BookReference) self._parse_and_add_lyrics(song) return self._success @@ -110,7 +112,7 @@ class EasiSlidesImport(SongImport): Signals that this attribute must exist in a valid song. """ try: - self_attribute = unicode(import_attribute).strip() + setattr(self, self_attribute, unicode(import_attribute).strip()) except UnicodeDecodeError: log.exception(u'UnicodeDecodeError decoding %s' % import_attribute) self._success = False @@ -124,7 +126,7 @@ class EasiSlidesImport(SongImport): authors = unicode(song.Writer).split(u',') for author in authors: author = author.strip() - if len(author) > 0: + if len(author): self.authors.append(author) except UnicodeDecodeError: log.exception(u'Unicode decode error while decoding Writer') @@ -132,35 +134,18 @@ class EasiSlidesImport(SongImport): except AttributeError: pass - def _add_copyright(self, song): - """ - Assign the copyright information from the import to the song being - created. - - ``song`` - The current song being imported. - """ - copyright_list = [] - self.__add_copyright_element(copyright_list, song.Copyright) - self.__add_copyright_element(copyright_list, song.LicenceAdmin1) - self.__add_copyright_element(copyright_list, song.LicenceAdmin2) - self.add_copyright(u' '.join(copyright_list)) - - def __add_copyright_element(self, copyright_list, element): + def _add_copyright(self, element): """ Add a piece of copyright to the total copyright information for the song. - ``copyright_list`` - The array to add the information to. - ``element`` The imported variable to get the data from. """ try: - copyright_list.append(unicode(element).strip()) + self.add_copyright(unicode(element).strip()) except UnicodeDecodeError: - log.exception(u'Unicode error decoding %s' % element) + log.exception(u'Unicode error on decoding copyright: %s' % element) self._success = False except AttributeError: pass @@ -285,10 +270,12 @@ class EasiSlidesImport(SongImport): # as these appeared originally in the file for [reg, vt, vn, inst] in our_verse_order: if self._listHas(verses, [reg, vt, vn, inst]): + # this is false, but needs user input + lang = None versetag = u'%s%s' % (vt, vn) versetags.append(versetag) lines = u'\n'.join(verses[reg][vt][vn][inst]) - self.verses.append([versetag, lines]) + self.verses.append([versetag, lines, lang]) SeqTypes = { u'p': u'P1', diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 283aa6c03..c7e9bae3b 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -36,7 +36,7 @@ from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \ from openlp.core.lib.ui import UiStrings from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \ SongImportForm, SongExportForm -from openlp.plugins.songs.lib import OpenLyrics, SongXML +from openlp.plugins.songs.lib import OpenLyrics, SongXML, VerseType from openlp.plugins.songs.lib.db import Author, Song from openlp.core.lib.searchedit import SearchEdit @@ -344,24 +344,49 @@ class SongMediaItem(MediaManagerItem): if song.lyrics.startswith(u' 1: + verse_index = \ + VerseType.from_translated_string(verse_tag) + if verse_index is None: + verse_index = VerseType.from_string(verse_tag) + if verse_index is None: + verse_index = VerseType.from_tag(verse_tag) + verse_tag = VerseType.TranslatedTags[verse_index].upper() + verse_def = u'%s%s' % (verse_tag, verse[0][u'label']) service_item.add_from_text( - verse[1][:30], unicode(verse[1]), verseTag) + verse[1][:30], unicode(verse[1]), verse_def) else: # Loop through the verse list and expand the song accordingly. - for order in song.verse_order.upper().split(): + for order in song.verse_order.lower().split(): if len(order) == 0: break for verse in verseList: - if verse[0][u'type'][0] == order[0] and \ - (verse[0][u'label'] == order[1:] or not order[1:]): - verseTag = u'%s:%s' % \ - (verse[0][u'type'], verse[0][u'label']) + if verse[0][u'type'][0].lower() == order[0] and \ + (verse[0][u'label'].lower() == order[1:] or \ + not order[1:]): + if verse_tags_translated: + verse_index = VerseType.from_translated_tag( + verse[0][u'type']) + else: + verse_index = VerseType.from_tag( + verse[0][u'type']) + if verse_index is None: + verse_index = VerseType.Other + verse_tag = VerseType.TranslatedTags[verse_index] + verse_def = u'%s%s' % (verse_tag, + verse[0][u'label']) service_item.add_from_text( - verse[1][:30], verse[1], verseTag) + verse[1][:30], verse[1], verse_def) else: verses = song.lyrics.split(u'\n\n') for slide in verses: diff --git a/openlp/plugins/songs/lib/opensongimport.py b/openlp/plugins/songs/lib/opensongimport.py index 8c6d283e3..00fdbf044 100644 --- a/openlp/plugins/songs/lib/opensongimport.py +++ b/openlp/plugins/songs/lib/opensongimport.py @@ -149,23 +149,25 @@ class OpenSongImport(SongImport): unicode(translate('SongsPlugin.ImportWizardForm', 'Importing %s...')) % parts[-1]) songfile = z.open(song) - self.do_import_file(songfile) - if self.commit: + if self.do_import_file(songfile) and self.commit and \ + not self.stop_import_flag: self.finish() - if self.stop_import_flag: - success = False - break + else: + success = False + break else: # not a zipfile log.info(u'Direct import %s', filename) self.import_wizard.incrementProgressBar( unicode(translate('SongsPlugin.ImportWizardForm', 'Importing %s...')) % os.path.split(filename)[-1]) - file = open(filename) - self.do_import_file(file) - if self.commit: + song_file = open(filename) + if self.do_import_file(song_file) and self.commit and \ + not self.stop_import_flag: self.finish() - + else: + success = False + break return success def do_import_file(self, file): @@ -178,7 +180,7 @@ class OpenSongImport(SongImport): tree = objectify.parse(file) except (Error, LxmlError): log.exception(u'Error parsing XML') - return + return False root = tree.getroot() fields = dir(root) decode = { @@ -196,114 +198,103 @@ class OpenSongImport(SongImport): setattr(self, fn_or_string, ustring) else: fn_or_string(ustring) + if not len(self.title): + # to prevent creation of empty songs from wrong files + return False if u'theme' in fields and unicode(root.theme) not in self.topics: self.topics.append(unicode(root.theme)) if u'alttheme' in fields and unicode(root.alttheme) not in self.topics: self.topics.append(unicode(root.alttheme)) # data storage while importing verses = {} - # keep track of a "default" verse order, in case none is specified + # keep track of verses appearance order our_verse_order = [] - verses_seen = {} - # in the absence of any other indication, verses are the default, - # erm, versetype! - versetype = u'V' - versenum = None + # default verse + verse_tag = u'v' + verse_num = u'1' + # for the case where song has several sections with same marker + inst = 1 lyrics = unicode(root.lyrics) - for thisline in lyrics.split(u'\n'): + for this_line in lyrics.split(u'\n'): # remove comments - semicolon = thisline.find(u';') + semicolon = this_line.find(u';') if semicolon >= 0: - thisline = thisline[:semicolon] - thisline = thisline.strip() - if len(thisline) == 0: + this_line = this_line[:semicolon] + this_line = this_line.strip() + if not len(this_line): continue - # skip inthisline guitar chords and page and column breaks - if thisline[0] == u'.' or thisline.startswith(u'---') \ - or thisline.startswith(u'-!!'): + # skip guitar chords and page and column breaks + if this_line.startswith(u'.') or this_line.startswith(u'---') \ + or this_line.startswith(u'-!!'): continue # verse/chorus/etc. marker - if thisline[0] == u'[': + if this_line.startswith(u'['): # drop the square brackets - right_bracket = thisline.find(u']') - content = thisline[1:right_bracket].upper() + right_bracket = this_line.find(u']') + content = this_line[1:right_bracket].lower() # have we got any digits? - # If so, versenumber is everything from the digits + # If so, verse number is everything from the digits # to the end (even if there are some alpha chars on the end) match = re.match(u'(.*)(\d+.*)', content) if match is not None: - versetype = match.group(1) - versenum = match.group(2) + verse_tag = match.group(1) + verse_num = match.group(2) else: # otherwise we assume number 1 and take the whole prefix as - # the versetype - versetype = content - versenum = u'1' + # the verse tag + verse_tag = content + verse_num = u'1' + inst = 1 + if [verse_tag, verse_num, inst] in our_verse_order \ + and verses.has_key(verse_tag) \ + and verses[verse_tag].has_key(verse_num): + inst = len(verses[verse_tag][verse_num])+1 + our_verse_order.append([verse_tag, verse_num, inst]) 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: - words = thisline - if not versenum: - versenum = u'1' - 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): - # storage for lines in this verse - verses[versetype][versenum] = [] - 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.tidy_text(words) - words = words.replace('_', '') - verses[versetype][versenum].append(words) + if this_line[0].isdigit(): + verse_num = this_line[0] + this_line = this_line[1:].strip() + our_verse_order.append([verse_tag, verse_num, inst]) + if not verses.has_key(verse_tag): + verses[verse_tag] = {} + if not verses[verse_tag].has_key(verse_num): + verses[verse_tag][verse_num] = {} + if not verses[verse_tag][verse_num].has_key(inst): + verses[verse_tag][verse_num][inst] = [] + # Tidy text and remove the ____s from extended words + this_line = self.tidy_text(this_line) + this_line = this_line.replace(u'_', u'') + this_line = this_line.replace(u'|', u'\n') + verses[verse_tag][verse_num][inst].append(this_line) # done parsing - versetypes = verses.keys() - versetypes.sort() - versetags = {} - for versetype in versetypes: - our_verse_type = versetype - if our_verse_type == u'': - our_verse_type = u'V' - versenums = verses[versetype].keys() - versenums.sort() - for num in versenums: - versetag = u'%s%s' % (our_verse_type, num) - lines = u'\n'.join(verses[versetype][num]) - self.add_verse(lines, versetag) - # Keep track of what we have for error checking later - versetags[versetag] = 1 - # now figure out the presentation order - order = [] + # add verses in original order + for (verse_tag, verse_num, inst) in our_verse_order: + verse_def = u'%s%s' % (verse_tag, verse_num) + lines = u'\n'.join(verses[verse_tag][verse_num][inst]) + self.add_verse(lines, verse_def) + # figure out the presentation order, if present if u'presentation' in fields and root.presentation != u'': order = unicode(root.presentation) - # We make all the tags in the lyrics upper case, so match that here + # We make all the tags in the lyrics lower case, so match that here # and then split into a list on the whitespace - order = order.upper().split() - else: - if len(our_verse_order) > 0: - order = our_verse_order - 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 - tag = u'V' + tag - elif not re.search('\d+', tag): - # Assume it's no.1 if there's no digits - tag = tag + u'1' - if not versetags.has_key(tag): - log.info(u'Got order %s but not in versetags, dropping this' - u'item from presentation order', tag) - else: - self.verse_order_list.append(tag) + order = order.lower().split() + for verse_def in order: + match = re.match(u'(.*)(\d+.*)', verse_def) + if match is not None: + verse_tag = match.group(1) + verse_num = match.group(2) + if not len(verse_tag): + verse_tag = u'v' + else: + # Assume it's no.1 if there are no digits + verse_tag = verse_def + verse_num = u'1' + verse_def = u'%s%s' % (verse_tag, verse_num) + if verses.has_key(verse_tag) \ + and verses[verse_tag].has_key(verse_num): + self.verse_order_list.append(verse_def) + else: + log.info(u'Got order %s but not in verse tags, dropping' + u'this item from presentation order', verse_def) + return True diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py index da017d4f5..e0e078c43 100644 --- a/openlp/plugins/songs/lib/songimport.py +++ b/openlp/plugins/songs/lib/songimport.py @@ -75,9 +75,11 @@ class SongImport(QtCore.QObject): self.media_files = [] self.song_book_name = u'' self.song_book_pub = u'' + self.verse_order_list_generated_useful = False + self.verse_order_list_generated = [] self.verse_order_list = [] self.verses = [] - self.versecounts = {} + self.verse_counts = {} self.copyright_string = unicode(translate( 'SongsPlugin.SongImport', 'copyright')) self.copyright_symbol = unicode(translate( @@ -128,20 +130,20 @@ class SongImport(QtCore.QObject): return text def process_song_text(self, text): - versetexts = text.split(u'\n\n') - for versetext in versetexts: - if versetext.strip() != u'': - self.process_verse_text(versetext.strip()) + verse_texts = text.split(u'\n\n') + for verse_text in verse_texts: + if verse_text.strip() != u'': + self.process_verse_text(verse_text.strip()) def process_verse_text(self, text): lines = text.split(u'\n') if text.lower().find(self.copyright_string) >= 0 \ - or text.lower().find(self.copyright_symbol) >= 0: + or text.find(self.copyright_symbol) >= 0: copyright_found = False for line in lines: if (copyright_found or line.lower().find(self.copyright_string) >= 0 or - line.lower().find(self.copyright_symbol) >= 0): + line.find(self.copyright_symbol) >= 0): copyright_found = True self.add_copyright(line) else: @@ -198,45 +200,46 @@ class SongImport(QtCore.QObject): return self.media_files.append(filename) - def add_verse(self, versetext, versetag=u'V', lang=None): + def add_verse(self, verse_text, verse_def=u'v', lang=None): """ Add a verse. This is the whole verse, lines split by \\n. It will also attempt to detect duplicates. In this case it will just add to the verse order. - ``versetext`` + ``verse_text`` The text of the verse. - ``versetag`` - The verse tag can be V1/C1/B etc, or 'V' and 'C' (will count the + ``verse_def`` + The verse tag can be v1/c1/b etc, or 'v' and 'c' (will count the verses/choruses itself) or None, where it will assume verse. ``lang`` The language code (ISO-639) of the verse, for example *en* or *de*. """ - for (oldversetag, oldverse, oldlang) in self.verses: - if oldverse.strip() == versetext.strip(): - self.verse_order_list.append(oldversetag) + for (old_verse_def, old_verse, old_lang) in self.verses: + if old_verse.strip() == verse_text.strip(): + self.verse_order_list_generated.append(old_verse_def) + self.verse_order_list_generated_useful = True return - if versetag[0] in self.versecounts: - self.versecounts[versetag[0]] += 1 + if verse_def[0] in self.verse_counts: + self.verse_counts[verse_def[0]] += 1 else: - self.versecounts[versetag[0]] = 1 - if len(versetag) == 1: - versetag += unicode(self.versecounts[versetag[0]]) - elif int(versetag[1:]) > self.versecounts[versetag[0]]: - self.versecounts[versetag[0]] = int(versetag[1:]) - self.verses.append([versetag, versetext.rstrip(), lang]) - self.verse_order_list.append(versetag) - if versetag.startswith(u'V') and u'C1' in self.verse_order_list: - self.verse_order_list.append(u'C1') + self.verse_counts[verse_def[0]] = 1 + if len(verse_def) == 1: + verse_def += unicode(self.verse_counts[verse_def[0]]) + elif int(verse_def[1:]) > self.verse_counts[verse_def[0]]: + self.verse_counts[verse_def[0]] = int(verse_def[1:]) + self.verses.append([verse_def, verse_text.rstrip(), lang]) + self.verse_order_list_generated.append(verse_def) def repeat_verse(self): """ Repeat the previous verse in the verse order """ - self.verse_order_list.append(self.verse_order_list[-1]) + self.verse_order_list_generated.append( + self.verse_order_list_generated[-1]) + self.verse_order_list_generated_useful = True def check_complete(self): """ @@ -273,34 +276,29 @@ class SongImport(QtCore.QObject): verses_changed_to_other = {} sxml = SongXML() other_count = 1 - for (versetag, versetext, lang) in self.verses: - if versetag[0] == u'C': - versetype = VerseType.to_string(VerseType.Chorus) - elif versetag[0] == u'V': - versetype = VerseType.to_string(VerseType.Verse) - elif versetag[0] == u'B': - versetype = VerseType.to_string(VerseType.Bridge) - elif versetag[0] == u'I': - versetype = VerseType.to_string(VerseType.Intro) - elif versetag[0] == u'P': - versetype = VerseType.to_string(VerseType.PreChorus) - elif versetag[0] == u'E': - versetype = VerseType.to_string(VerseType.Ending) + for (verse_def, verse_text, lang) in self.verses: + if verse_def[0].lower() in VerseType.Tags: + verse_tag = verse_def[0].lower() else: - newversetag = u'O%d' % other_count - verses_changed_to_other[versetag] = newversetag + new_verse_def = u'%s%d' % (VerseType.Tags[VerseType.Other], + other_count) + verses_changed_to_other[verse_def] = new_verse_def other_count += 1 - versetype = VerseType.to_string(VerseType.Other) - log.info(u'Versetype %s changing to %s' , versetag, newversetag) - versetag = newversetag - sxml.add_verse_to_lyrics(versetype, versetag[1:], versetext, lang) - song.search_lyrics += u' ' + self.remove_punctuation(versetext) + verse_tag = VerseType.Tags[VerseType.Other] + log.info(u'Versetype %s changing to %s' , verse_def, + new_verse_def) + verse_def = new_verse_def + sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], verse_text, lang) + song.search_lyrics += u' ' + self.remove_punctuation(verse_text) song.search_lyrics = song.search_lyrics.lower() song.lyrics = unicode(sxml.extract_xml(), u'utf-8') - for i, current_verse_tag in enumerate(self.verse_order_list): - if verses_changed_to_other.has_key(current_verse_tag): + if not len(self.verse_order_list) and \ + self.verse_order_list_generated_useful: + self.verse_order_list = self.verse_order_list_generated + for i, current_verse_def in enumerate(self.verse_order_list): + if verses_changed_to_other.has_key(current_verse_def): self.verse_order_list[i] = \ - verses_changed_to_other[current_verse_tag] + verses_changed_to_other[current_verse_def] song.verse_order = u' '.join(self.verse_order_list) song.copyright = self.copyright song.comments = self.comments @@ -345,9 +343,10 @@ class SongImport(QtCore.QObject): + u'========================================' print u'TITLE: ' + self.title print u'ALT TITLE: ' + self.alternate_title - for (versetag, versetext, lang) in self.verses: - print u'VERSE ' + versetag + u': ' + versetext + for (verse_def, verse_text, lang) in self.verses: + print u'VERSE ' + verse_def + u': ' + verse_text print u'ORDER: ' + u' '.join(self.verse_order_list) + print u'GENERATED ORDER: ' + u' '.join(self.verse_order_list_generated) for author in self.authors: print u'AUTHOR: ' + author if self.copyright: diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index b96e79961..e5c3963c0 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -464,7 +464,8 @@ 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])) + verse_type_index = VerseType.from_tag(verse_name[0]) + verse_type = VerseType.Names[verse_type_index] 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". diff --git a/resources/windows/OpenLP-2.0.iss b/resources/windows/OpenLP-2.0.iss index a85b41187..c64e2b488 100644 --- a/resources/windows/OpenLP-2.0.iss +++ b/resources/windows/OpenLP-2.0.iss @@ -68,6 +68,7 @@ Source: ..\..\dist\OpenLP\*; DestDir: {app}; Flags: ignoreversion recursesubdirs [Icons] Name: {group}\{#AppName}; Filename: {app}\{#AppExeName} +Name: {group}\{#AppName} (Debug); Filename: {app}\{#AppExeName}; Parameters: -l debug Name: {group}\{cm:ProgramOnTheWeb,{#AppName}}; Filename: {#AppURL} Name: {group}\{cm:UninstallProgram,{#AppName}}; Filename: {uninstallexe} Name: {commondesktop}\{#AppName}; Filename: {app}\{#AppExeName}; Tasks: desktopicon