← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~mahfiaz/openlp/opensongfixes into lp:openlp

 

mahfiaz has proposed merging lp:~mahfiaz/openlp/opensongfixes into lp:openlp.

Requested reviews:
  Raoul Snyman (raoul-snyman)
  Tim Bentley (trb143)
  Jon Tibble (meths)

For more details, see:
https://code.launchpad.net/~mahfiaz/openlp/opensongfixes/+merge/50260

More changes, now internally tags are held not as Salm:1 (previously), nor v:1 (in the meantime), but just as in openlyrics format, v1. The v1a is not yet supported, it requires spinboxes to be changed first.
-- 
https://code.launchpad.net/~mahfiaz/openlp/opensongfixes/+merge/50260
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py	2011-02-14 21:06:48 +0000
+++ openlp/core/ui/slidecontroller.py	2011-02-18 00:52:58 +0000
@@ -603,14 +603,15 @@
             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

=== modified file 'openlp/plugins/songs/forms/editsongform.py'
--- openlp/plugins/songs/forms/editsongform.py	2011-02-15 01:58:18 +0000
+++ openlp/plugins/songs/forms/editsongform.py	2011-02-18 00:52:58 +0000
@@ -227,10 +227,6 @@
             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 @@
         # 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'<?xml version='):
             songXML = SongXML()
             verseList = songXML.get_verses(self.song.lyrics)
             for count, verse in enumerate(verseList):
                 self.verseListWidget.setRowCount(
                     self.verseListWidget.rowCount() + 1)
-                variant = u'%s:%s' % (verse[0][u'type'], verse[0][u'label'])
+                # This silently migrates from localized verse type markup.
+                # If we trusted the database, this would be unnecessary.
+                verse_tag = verse[0][u'type']
+                index = None
+                if len(verse_tag) > 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 @@
                 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 @@
         """
         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 @@
     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)
+            after_text, verse_tag, verse_num = self.verse_form.getVerse()
+            verse_def = u'%s%s' % (verse_tag, verse_num)
             item = QtGui.QTableWidgetItem(afterText)
-            item.setData(QtCore.Qt.UserRole, QtCore.QVariant(data))
-            item.setText(afterText)
+            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 @@
             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 @@
             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 @@
                     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 @@
             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 @@
                         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 @@
         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 @@
             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_type, 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:

=== modified file 'openlp/plugins/songs/forms/editversedialog.py'
--- openlp/plugins/songs/forms/editversedialog.py	2011-02-10 00:36:00 +0000
+++ openlp/plugins/songs/forms/editversedialog.py	2011-02-18 00:52:58 +0000
@@ -70,19 +70,19 @@
             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'))

=== modified file 'openlp/plugins/songs/forms/editverseform.py'
--- openlp/plugins/songs/forms/editverseform.py	2011-02-02 23:12:31 +0000
+++ openlp/plugins/songs/forms/editverseform.py	2011-02-18 00:52:58 +0000
@@ -57,22 +57,23 @@
         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 @@
         """
         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 @@
         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,27 @@
         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_type_index = VerseType.from_loose_input(verse_type)
+            regex = re.compile(r'(\d+)')
+            verse_num = int(match.group(2))
             if verse_type_index is not None:
                 self.verseTypeComboBox.setCurrentIndex(verse_type_index)
-                self.verseNumberBox.setValue(verse_number)
+                self.verseNumberBox.setValue(verse_num)
 
     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 +156,14 @@
 
     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):

=== modified file 'openlp/plugins/songs/lib/__init__.py'
--- openlp/plugins/songs/lib/__init__.py	2011-02-07 16:29:06 +0000
+++ openlp/plugins/songs/lib/__init__.py	2011-02-18 00:52:58 +0000
@@ -40,68 +40,147 @@
     Ending = 5
     Other = 6
 
-    @staticmethod
-    def to_string(verse_type):
-        """
-        Return a string for a given VerseType
-
-        ``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')
-
-    @staticmethod
-    def from_string(verse_type):
-        """
-        Return the VerseType for a given string
-
-        ``verse_type``
-            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
-
+    Names = [
+        u'Verse',
+        u'Chorus',
+        u'Bridge',
+        u'Pre-Chorus',
+        u'Intro',
+        u'Ending',
+        u'Other']
+    Tags = [name[0].lower() for name in Names]
+
+    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 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_name``
+            The string to return a VerseType for
+        """
+        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:
+                verseIndex = VerseType.from_string(verse_name)
+        if verse_index is None:
+            verse_index = VerseType.from_translated_tag(verse_name)
+        elif verse_index is None:
+            verse_index = VerseType.from_tag(verse_name)
+        return verse_index
 
 def retrieve_windows_encoding(recommendation=None):
     """

=== modified file 'openlp/plugins/songs/lib/mediaitem.py'
--- openlp/plugins/songs/lib/mediaitem.py	2011-02-10 18:38:40 +0000
+++ openlp/plugins/songs/lib/mediaitem.py	2011-02-18 00:52:58 +0000
@@ -36,7 +36,7 @@
 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 @@
         if song.lyrics.startswith(u'<?xml version='):
             verseList = SongXML().get_verses(song.lyrics)
             # no verse list or only 1 space (in error)
-            if not song.verse_order or not song.verse_order.strip():
+            verse_tags_translated = False
+            if VerseType.from_translated_string(unicode(
+                verseList[0][0][u'type'])) is not None:
+                verse_tags_translated = True
+            if not song.verse_order.strip():
                 for verse in verseList:
-                    verseTag = u'%s:%s' % (
-                        verse[0][u'type'], verse[0][u'label'])
+                    # We cannot use from_loose_input() here, because database
+                    # is supposed to contain English lowercase singlechar tags.
+                    verse_tag = verse[0][u'type']
+                    verse_index = None
+                    if len(verse_tag) > 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:

=== modified file 'openlp/plugins/songs/lib/opensongimport.py'
--- openlp/plugins/songs/lib/opensongimport.py	2011-02-08 16:25:46 +0000
+++ openlp/plugins/songs/lib/opensongimport.py	2011-02-18 00:52:58 +0000
@@ -149,23 +149,25 @@
                         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:
+                songfile = open(filename)
+                if self.do_import_file(songfile) 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 @@
             tree = objectify.parse(file)
         except (Error, LxmlError):
             log.exception(u'Error parsing XML')
-            return
+            return False
         root = tree.getroot()
         fields = dir(root)
         decode = {
@@ -196,19 +198,22 @@
                     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 versetype
+        versetype = u'v'
+        versenum = 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'):
             # remove comments
@@ -216,17 +221,17 @@
             if semicolon >= 0:
                 thisline = thisline[:semicolon]
             thisline = thisline.strip()
-            if len(thisline) == 0:
+            if not len(thisline):
                 continue
-            # skip inthisline guitar chords and page and column breaks
-            if thisline[0] == u'.' or thisline.startswith(u'---') \
+            # skip guitar chords and page and column breaks
+            if thisline.startswith(u'.') or thisline.startswith(u'---') \
                 or thisline.startswith(u'-!!'):
                 continue
             # verse/chorus/etc. marker
-            if thisline[0] == u'[':
+            if thisline.startswith(u'['):
                 # drop the square brackets
                 right_bracket = thisline.find(u']')
-                content = thisline[1:right_bracket].upper()
+                content = thisline[1:right_bracket].lower()
                 # have we got any digits?
                 # If so, versenumber is everything from the digits
                 # to the end (even if there are some alpha chars on the end)
@@ -239,71 +244,57 @@
                     # the versetype
                     versetype = content
                     versenum = u'1'
+                inst = 1
+                if [versetype, versenum, inst] in our_verse_order \
+                    and verses.has_key(versetype) \
+                    and verses[versetype].has_key(versenum):
+                    inst = len(verses[versetype][versenum])+1
+                our_verse_order.append([versetype, versenum, 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)
+                thisline = thisline[1:].strip()
+                our_verse_order.append([versetype, versenum, inst])
+            if not verses.has_key(versetype):
+                verses[versetype] = {}
+            if not verses[versetype].has_key(versenum):
+                verses[versetype][versenum] = {}
+            if not verses[versetype][versenum].has_key(inst):
+                verses[versetype][versenum][inst] = []
+            # Tidy text and remove the ____s from extended words
+            thisline = self.tidy_text(thisline)
+            thisline = thisline.replace(u'_', u'')
+            thisline = thisline.replace(u'|', u'\n')
+            verses[versetype][versenum][inst].append(thisline)
         # 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 (versetype, versenum, inst) in our_verse_order:
+            vtag = u'%s%s' % (versetype, versenum)
+            lines = u'\n'.join(verses[versetype][versenum][inst])
+            self.add_verse(lines, vtag)
+        # 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 tag in order:
+                match = re.match(u'(.*)(\d+.*)', tag)
+                if match is not None:
+                    versetype = match.group(1)
+                    versenum = match.group(2)
+                    if not len(versetype):
+                        versetype = u'v'
+                else:
+                    # Assume it's no.1 if there are no digits
+                    versetype = tag
+                    versenum = u'1'
+                vtagString = u'%s%s' % (versetype, versenum)
+                if verses.has_key(versetype) \
+                    and verses[versetype].has_key(versenum):
+                    self.verse_order_list.append(vtagString)
+                else:
+                    log.info(u'Got order %s but not in versetags, dropping'
+                        u'this item from presentation order', vtagString)
+        return True

=== modified file 'openlp/plugins/songs/lib/songimport.py'
--- openlp/plugins/songs/lib/songimport.py	2011-02-10 05:25:08 +0000
+++ openlp/plugins/songs/lib/songimport.py	2011-02-18 00:52:58 +0000
@@ -75,6 +75,8 @@
         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 = {}
@@ -136,12 +138,12 @@
     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,7 +200,7 @@
             return
         self.media_files.append(filename)
 
-    def add_verse(self, versetext, versetag=u'V', lang=None):
+    def add_verse(self, versetext, versetag=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
@@ -217,7 +219,9 @@
         """
         for (oldversetag, oldverse, oldlang) in self.verses:
             if oldverse.strip() == versetext.strip():
-                self.verse_order_list.append(oldversetag)
+                # this verse is already present
+                self.verse_order_list_generated.append(oldversetag)
+                self.verse_order_list_generated_useful = True
                 return
         if versetag[0] in self.versecounts:
             self.versecounts[versetag[0]] += 1
@@ -228,15 +232,15 @@
         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_order_list_generated.append(versetag)
 
     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):
         """
@@ -274,29 +278,23 @@
         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)
+            if versetag[0].lower() in VerseType.Tags:
+                versetype = versetag[0].lower()
             else:
-                newversetag = u'O%d' % other_count
+                newversetag = u'%s%d' % (VerseType.Tags[VerseType.Other],
+                    other_count)
                 verses_changed_to_other[versetag] = newversetag
                 other_count += 1
-                versetype = VerseType.to_string(VerseType.Other)
+                versetype = VerseType.Tags[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)
         song.search_lyrics = song.search_lyrics.lower()
         song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
+        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_tag in enumerate(self.verse_order_list):
             if verses_changed_to_other.has_key(current_verse_tag):
                 self.verse_order_list[i] = \
@@ -348,6 +346,7 @@
         for (versetag, versetext, lang) in self.verses:
             print u'VERSE ' + versetag + u': ' + versetext
         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:

=== modified file 'openlp/plugins/songs/lib/xml.py'
--- openlp/plugins/songs/lib/xml.py	2011-02-10 17:42:13 +0000
+++ openlp/plugins/songs/lib/xml.py	2011-02-18 00:52:58 +0000
@@ -464,7 +464,8 @@
                     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".


Follow ups