← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~raoul-snyman/openlp/cleanups into lp:openlp

 

Raoul Snyman has proposed merging lp:~raoul-snyman/openlp/cleanups into lp:openlp.

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

For more details, see:
https://code.launchpad.net/~raoul-snyman/openlp/cleanups/+merge/152292

Various cleanups of naming conventions, minor tweaks, some tests, and a bug fix.
-- 
https://code.launchpad.net/~raoul-snyman/openlp/cleanups/+merge/152292
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/lib/screen.py'
--- openlp/core/lib/screen.py	2013-02-10 16:05:52 +0000
+++ openlp/core/lib/screen.py	2013-03-07 21:58:22 +0000
@@ -74,8 +74,8 @@
         screen_list.display_count = 0
         screen_list.screen_count_changed()
         screen_list.load_screen_settings()
-        QtCore.QObject.connect(desktop, QtCore.SIGNAL(u'resized(int)'), screen_list.screen_resolution_changed)
-        QtCore.QObject.connect(desktop, QtCore.SIGNAL(u'screenCountChanged(int)'), screen_list.screen_count_changed)
+        desktop.resized.connect(screen_list.screen_resolution_changed)
+        desktop.screenCountChanged.connect(screen_list.screen_count_changed)
         return screen_list
 
     def screen_resolution_changed(self, number):

=== modified file 'openlp/core/ui/mainwindow.py'
--- openlp/core/ui/mainwindow.py	2013-03-05 17:11:58 +0000
+++ openlp/core/ui/mainwindow.py	2013-03-07 21:58:22 +0000
@@ -888,7 +888,7 @@
             export_file_name += u'.conf'
         temp_file = os.path.join(unicode(gettempdir(),
             get_filesystem_encoding()), u'openlp', u'exportConf.tmp')
-        self.saveSettings()
+        self.save_settings()
         setting_sections = []
         # Add main sections.
         setting_sections.extend([self.generalSettingsSection])
@@ -1054,7 +1054,7 @@
         self.plugin_manager.finalise_plugins()
         if save_settings:
             # Save settings
-            self.saveSettings()
+            self.save_settings()
         # Check if we need to change the data directory
         if self.new_data_path:
             self.changeDataDirectory()
@@ -1205,7 +1205,7 @@
         self.controlSplitter.restoreState(settings.value(u'main window splitter geometry'))
         settings.endGroup()
 
-    def saveSettings(self):
+    def save_settings(self):
         """
         Save the main window settings.
         """

=== modified file 'openlp/core/ui/serviceitemeditdialog.py'
--- openlp/core/ui/serviceitemeditdialog.py	2013-02-01 21:34:23 +0000
+++ openlp/core/ui/serviceitemeditdialog.py	2013-03-07 21:58:22 +0000
@@ -58,9 +58,9 @@
             click=serviceItemEditDialog.on_delete_button_clicked)
         self.button_layout.addWidget(self.delete_button)
         self.button_layout.addStretch()
-        self.up_button = create_button(serviceItemEditDialog, u'upButton', role=u'up',
+        self.up_button = create_button(serviceItemEditDialog, u'up_button', role=u'up',
             click=serviceItemEditDialog.on_up_button_clicked)
-        self.down_button = create_button(serviceItemEditDialog, u'downButton', role=u'down',
+        self.down_button = create_button(serviceItemEditDialog, u'down_button', role=u'down',
             click=serviceItemEditDialog.on_down_button_clicked)
         self.button_layout.addWidget(self.up_button)
         self.button_layout.addWidget(self.down_button)

=== modified file 'openlp/plugins/bibles/forms/editbibledialog.py'
--- openlp/plugins/bibles/forms/editbibledialog.py	2013-01-27 20:36:18 +0000
+++ openlp/plugins/bibles/forms/editbibledialog.py	2013-03-07 21:58:22 +0000
@@ -65,7 +65,7 @@
         self.copyrightLabel = QtGui.QLabel(self.licenseDetailsGroupBox)
         self.copyrightLabel.setObjectName(u'copyrightLabel')
         self.copyrightEdit = QtGui.QLineEdit(self.licenseDetailsGroupBox)
-        self.copyrightEdit.setObjectName(u'copyrightEdit')
+        self.copyrightEdit.setObjectName(u'copyright_edit')
         self.copyrightLabel.setBuddy(self.copyrightEdit)
         self.licenseDetailsLayout.addRow(self.copyrightLabel, self.copyrightEdit)
         self.permissionsLabel = QtGui.QLabel(self.licenseDetailsGroupBox)

=== modified file 'openlp/plugins/bibles/lib/csvbible.py'
--- openlp/plugins/bibles/lib/csvbible.py	2013-02-05 06:47:03 +0000
+++ openlp/plugins/bibles/lib/csvbible.py	2013-03-07 21:58:22 +0000
@@ -55,7 +55,7 @@
         or
         "Genesis",1,2,"And the earth was without form, and void; and...."
 
-All CSV files are expected to use a comma (',') as the delimeter and double
+All CSV files are expected to use a comma (',') as the delimiter and double
 quotes ('"') as the quote symbol.
 """
 import logging

=== modified file 'openlp/plugins/songs/forms/__init__.py'
--- openlp/plugins/songs/forms/__init__.py	2013-01-17 21:58:38 +0000
+++ openlp/plugins/songs/forms/__init__.py	2013-03-07 21:58:22 +0000
@@ -52,14 +52,3 @@
 them separate from the functionality, so that it is easier to recreate the GUI
 from the .ui files later if necessary.
 """
-
-from mediafilesform import MediaFilesForm
-from authorsform import AuthorsForm
-from topicsform import TopicsForm
-from songbookform import SongBookForm
-from editverseform import EditVerseForm
-from editsongform import EditSongForm
-from songmaintenanceform import SongMaintenanceForm
-from songimportform import SongImportForm
-from songexportform import SongExportForm
-

=== modified file 'openlp/plugins/songs/forms/authorsdialog.py'
--- openlp/plugins/songs/forms/authorsdialog.py	2013-01-27 20:36:18 +0000
+++ openlp/plugins/songs/forms/authorsdialog.py	2013-03-07 21:58:22 +0000
@@ -29,43 +29,56 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.lib import translate
+from openlp.core.lib import translate, build_icon
 from openlp.core.lib.ui import create_button_box
 
+
 class Ui_AuthorsDialog(object):
-    def setupUi(self, authorsDialog):
-        authorsDialog.setObjectName(u'AuthorsDialog')
-        authorsDialog.resize(300, 10)
-        self.dialogLayout = QtGui.QVBoxLayout(authorsDialog)
-        self.dialogLayout.setObjectName(u'dialog_layout')
-        self.authorLayout = QtGui.QFormLayout()
-        self.authorLayout.setObjectName(u'authorLayout')
-        self.firstNameLabel = QtGui.QLabel(authorsDialog)
-        self.firstNameLabel.setObjectName(u'firstNameLabel')
-        self.firstNameEdit = QtGui.QLineEdit(authorsDialog)
-        self.firstNameEdit.setObjectName(u'firstNameEdit')
-        self.firstNameLabel.setBuddy(self.firstNameEdit)
-        self.authorLayout.addRow(self.firstNameLabel, self.firstNameEdit)
-        self.lastNameLabel = QtGui.QLabel(authorsDialog)
-        self.lastNameLabel.setObjectName(u'lastNameLabel')
-        self.lastNameEdit = QtGui.QLineEdit(authorsDialog)
-        self.lastNameEdit.setObjectName(u'lastNameEdit')
-        self.lastNameLabel.setBuddy(self.lastNameEdit)
-        self.authorLayout.addRow(self.lastNameLabel, self.lastNameEdit)
-        self.displayLabel = QtGui.QLabel(authorsDialog)
-        self.displayLabel.setObjectName(u'displayLabel')
-        self.displayEdit = QtGui.QLineEdit(authorsDialog)
-        self.displayEdit.setObjectName(u'displayEdit')
-        self.displayLabel.setBuddy(self.displayEdit)
-        self.authorLayout.addRow(self.displayLabel, self.displayEdit)
-        self.dialogLayout.addLayout(self.authorLayout)
-        self.button_box = create_button_box(authorsDialog, u'button_box', [u'cancel', u'save'])
-        self.dialogLayout.addWidget(self.button_box)
-        self.retranslateUi(authorsDialog)
-        authorsDialog.setMaximumHeight(authorsDialog.sizeHint().height())
+    """
+    The :class:`~openlp.plugins.songs.forms.authorsdialog.Ui_AuthorsDialog` class defines the user interface for the
+    AuthorsForm dialog.
+    """
+    def setupUi(self, authors_dialog):
+        """
+        Set up the UI for the dialog.
+        """
+        authors_dialog.setObjectName(u'authors_dialog')
+        authors_dialog.resize(300, 10)
+        authors_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo-16x16.png'))
+        authors_dialog.setModal(True)
+        self.dialog_layout = QtGui.QVBoxLayout(authors_dialog)
+        self.dialog_layout.setObjectName(u'dialog_layout')
+        self.author_layout = QtGui.QFormLayout()
+        self.author_layout.setObjectName(u'authorLayout')
+        self.first_name_label = QtGui.QLabel(authors_dialog)
+        self.first_name_label.setObjectName(u'first_name_label')
+        self.first_name_edit = QtGui.QLineEdit(authors_dialog)
+        self.first_name_edit.setObjectName(u'first_name_edit')
+        self.first_name_label.setBuddy(self.first_name_edit)
+        self.author_layout.addRow(self.first_name_label, self.first_name_edit)
+        self.last_name_label = QtGui.QLabel(authors_dialog)
+        self.last_name_label.setObjectName(u'last_name_label')
+        self.last_name_edit = QtGui.QLineEdit(authors_dialog)
+        self.last_name_edit.setObjectName(u'last_name_edit')
+        self.last_name_label.setBuddy(self.last_name_edit)
+        self.author_layout.addRow(self.last_name_label, self.last_name_edit)
+        self.display_label = QtGui.QLabel(authors_dialog)
+        self.display_label.setObjectName(u'display_label')
+        self.display_edit = QtGui.QLineEdit(authors_dialog)
+        self.display_edit.setObjectName(u'display_edit')
+        self.display_label.setBuddy(self.display_edit)
+        self.author_layout.addRow(self.display_label, self.display_edit)
+        self.dialog_layout.addLayout(self.author_layout)
+        self.button_box = create_button_box(authors_dialog, u'button_box', [u'cancel', u'save'])
+        self.dialog_layout.addWidget(self.button_box)
+        self.retranslateUi(authors_dialog)
+        authors_dialog.setMaximumHeight(authors_dialog.sizeHint().height())
 
-    def retranslateUi(self, authorsDialog):
-        authorsDialog.setWindowTitle(translate('SongsPlugin.AuthorsForm', 'Author Maintenance'))
-        self.displayLabel.setText(translate('SongsPlugin.AuthorsForm', 'Display name:'))
-        self.firstNameLabel.setText(translate('SongsPlugin.AuthorsForm', 'First name:'))
-        self.lastNameLabel.setText(translate('SongsPlugin.AuthorsForm', 'Last name:'))
+    def retranslateUi(self, authors_dialog):
+        """
+        Translate the UI on the fly.
+        """
+        authors_dialog.setWindowTitle(translate('SongsPlugin.AuthorsForm', 'Author Maintenance'))
+        self.display_label.setText(translate('SongsPlugin.AuthorsForm', 'Display name:'))
+        self.first_name_label.setText(translate('SongsPlugin.AuthorsForm', 'First name:'))
+        self.last_name_label.setText(translate('SongsPlugin.AuthorsForm', 'Last name:'))

=== modified file 'openlp/plugins/songs/forms/authorsform.py'
--- openlp/plugins/songs/forms/authorsform.py	2013-01-05 22:17:30 +0000
+++ openlp/plugins/songs/forms/authorsform.py	2013-03-07 21:58:22 +0000
@@ -27,12 +27,13 @@
 # Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
 ###############################################################################
 
-from PyQt4 import QtGui, QtCore
+from PyQt4 import QtGui
 
 from openlp.core.lib import translate
 from openlp.core.lib.ui import critical_error_message_box
 from openlp.plugins.songs.forms.authorsdialog import Ui_AuthorsDialog
 
+
 class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog):
     """
     Class to control the Maintenance of Authors Dialog
@@ -43,60 +44,115 @@
         """
         QtGui.QDialog.__init__(self, parent)
         self.setupUi(self)
-        self._autoDisplayName = False
-        QtCore.QObject.connect(self.firstNameEdit, QtCore.SIGNAL(u'textEdited(QString)'),
-            self.onFirstNameEditTextEdited)
-        QtCore.QObject.connect(self.lastNameEdit, QtCore.SIGNAL(u'textEdited(QString)'),
-            self.onLastNameEditTextEdited)
+        self.auto_display_name = False
+        self.first_name_edit.textEdited.connect(self.on_first_name_edited)
+        self.last_name_edit.textEdited.connect(self.on_last_name_edited)
 
     def exec_(self, clear=True):
+        """
+        Execute the dialog.
+
+        ``clear``
+            Clear the form fields before displaying the dialog.
+        """
         if clear:
-            self.firstNameEdit.clear()
-            self.lastNameEdit.clear()
-            self.displayEdit.clear()
-        self.firstNameEdit.setFocus()
+            self.first_name_edit.clear()
+            self.last_name_edit.clear()
+            self.display_edit.clear()
+        self.first_name_edit.setFocus()
         return QtGui.QDialog.exec_(self)
 
-    def onFirstNameEditTextEdited(self, display_name):
-        if not self._autoDisplayName:
-            return
-        if self.lastNameEdit.text():
-            display_name = display_name + u' ' + self.lastNameEdit.text()
-        self.displayEdit.setText(display_name)
-
-    def onLastNameEditTextEdited(self, display_name):
-        if not self._autoDisplayName:
-            return
-        if self.firstNameEdit.text():
-            display_name = self.firstNameEdit.text() + u' ' + display_name
-        self.displayEdit.setText(display_name)
-
-    def autoDisplayName(self):
-        return self._autoDisplayName
-
-    def setAutoDisplayName(self, on):
-        self._autoDisplayName = on
+    def on_first_name_edited(self, display_name):
+        """
+        Slot for when the first name is edited.
+
+        When the first name is edited and the setting to automatically create a display name is True, then try to create
+        a display name from the first and last names.
+
+        ``display_name``
+            The text from the first_name_edit widget.
+        """
+        if not self.auto_display_name:
+            return
+        if self.last_name_edit.text():
+            display_name = display_name + u' ' + self.last_name_edit.text()
+        self.display_edit.setText(display_name)
+
+    def on_last_name_edited(self, display_name):
+        """
+        Slot for when the last name is edited.
+
+        When the last name is edited and the setting to automatically create a display name is True, then try to create
+        a display name from the first and last names.
+
+        ``display_name``
+            The text from the last_name_edit widget.
+        """
+        if not self.auto_display_name:
+            return
+        if self.first_name_edit.text():
+            display_name = self.first_name_edit.text() + u' ' + display_name
+        self.display_edit.setText(display_name)
 
     def accept(self):
-        if not self.firstNameEdit.text():
+        """
+        Override the QDialog's accept() method to do some validation before the dialog can be closed.
+        """
+        if not self.first_name_edit.text():
             critical_error_message_box(
                 message=translate('SongsPlugin.AuthorsForm', 'You need to type in the first name of the author.'))
-            self.firstNameEdit.setFocus()
+            self.first_name_edit.setFocus()
             return False
-        elif not self.lastNameEdit.text():
+        elif not self.last_name_edit.text():
             critical_error_message_box(
                 message=translate('SongsPlugin.AuthorsForm', 'You need to type in the last name of the author.'))
-            self.lastNameEdit.setFocus()
+            self.last_name_edit.setFocus()
             return False
-        elif not self.displayEdit.text():
+        elif not self.display_edit.text():
             if critical_error_message_box(
                 message=translate('SongsPlugin.AuthorsForm',
                     'You have not set a display name for the author, combine the first and last names?'),
                 parent=self, question=True) == QtGui.QMessageBox.Yes:
-                self.displayEdit.setText(self.firstNameEdit.text() + u' ' + self.lastNameEdit.text())
+                self.display_edit.setText(self.first_name_edit.text() + u' ' + self.last_name_edit.text())
                 return QtGui.QDialog.accept(self)
             else:
-                self.displayEdit.setFocus()
+                self.display_edit.setFocus()
                 return False
         else:
             return QtGui.QDialog.accept(self)
+
+    def _get_first_name(self):
+        """
+        Get the value of the first name from the UI widget.
+        """
+        return self.first_name_edit.text()
+
+    def _set_first_name(self, value):
+        """
+        Set the value of the first name in the UI widget.
+        """
+        self.first_name_edit.setText(value)
+
+    first_name = property(_get_first_name, _set_first_name)
+
+    def _get_last_name(self):
+        """
+        Get the value of the last name from the UI widget.
+        """
+        return self.last_name_edit.text()
+
+    def _set_last_name(self, value):
+        """
+        Set the value of the last name in the UI widget.
+        """
+        self.last_name_edit.setText(value)
+
+    last_name = property(_get_last_name, _set_last_name)
+
+    def _get_display_name(self):
+        return self.display_edit.text()
+
+    def _set_display_name(self, value):
+        self.display_edit.setText(value)
+
+    display_name = property(_get_display_name, _set_display_name)

=== modified file 'openlp/plugins/songs/forms/editsongdialog.py'
--- openlp/plugins/songs/forms/editsongdialog.py	2013-02-05 08:05:28 +0000
+++ openlp/plugins/songs/forms/editsongdialog.py	2013-03-07 21:58:22 +0000
@@ -33,302 +33,318 @@
 from openlp.core.lib.ui import create_button_box, create_button
 from openlp.plugins.songs.lib.ui import SongStrings
 
+
 class Ui_EditSongDialog(object):
-    def setupUi(self, editSongDialog):
-        editSongDialog.setObjectName(u'editSongDialog')
-        editSongDialog.resize(650, 400)
-        editSongDialog.setWindowIcon(build_icon(u':/icon/openlp-logo-16x16.png'))
-        editSongDialog.setModal(True)
-        self.dialogLayout = QtGui.QVBoxLayout(editSongDialog)
-        self.dialogLayout.setSpacing(8)
-        self.dialogLayout.setContentsMargins(8, 8, 8, 8)
-        self.dialogLayout.setObjectName(u'dialog_layout')
-        self.songTabWidget = QtGui.QTabWidget(editSongDialog)
-        self.songTabWidget.setObjectName(u'songTabWidget')
+    """
+    The :class:`~openlp.plugins.songs.forms.editsongdialog.Ui_EditSongDialog` class defines the user interface for the
+    EditSongForm dialog.
+    """
+    def setupUi(self, edit_song_dialog):
+        edit_song_dialog.setObjectName(u'edit_song_dialog')
+        edit_song_dialog.resize(650, 400)
+        edit_song_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo-16x16.png'))
+        edit_song_dialog.setModal(True)
+        self.dialog_layout = QtGui.QVBoxLayout(edit_song_dialog)
+        self.dialog_layout.setSpacing(8)
+        self.dialog_layout.setContentsMargins(8, 8, 8, 8)
+        self.dialog_layout.setObjectName(u'dialog_layout')
+        self.song_tab_widget = QtGui.QTabWidget(edit_song_dialog)
+        self.song_tab_widget.setObjectName(u'song_tab_widget')
         # lyrics tab
-        self.lyricsTab = QtGui.QWidget()
-        self.lyricsTab.setObjectName(u'lyricsTab')
-        self.lyricsTabLayout = QtGui.QGridLayout(self.lyricsTab)
-        self.lyricsTabLayout.setObjectName(u'lyricsTabLayout')
-        self.titleLabel = QtGui.QLabel(self.lyricsTab)
-        self.titleLabel.setObjectName(u'titleLabel')
-        self.lyricsTabLayout.addWidget(self.titleLabel, 0, 0)
-        self.titleEdit = QtGui.QLineEdit(self.lyricsTab)
-        self.titleEdit.setObjectName(u'titleEdit')
-        self.titleLabel.setBuddy(self.titleEdit)
-        self.lyricsTabLayout.addWidget(self.titleEdit, 0, 1, 1, 2)
-        self.alternativeTitleLabel = QtGui.QLabel(self.lyricsTab)
-        self.alternativeTitleLabel.setObjectName(u'alternativeTitleLabel')
-        self.lyricsTabLayout.addWidget(self.alternativeTitleLabel, 1, 0)
-        self.alternativeEdit = QtGui.QLineEdit(self.lyricsTab)
-        self.alternativeEdit.setObjectName(u'alternativeEdit')
-        self.alternativeTitleLabel.setBuddy(self.alternativeEdit)
-        self.lyricsTabLayout.addWidget(self.alternativeEdit, 1, 1, 1, 2)
-        self.lyricsLabel = QtGui.QLabel(self.lyricsTab)
-        self.lyricsLabel.setFixedHeight(self.titleEdit.sizeHint().height())
-        self.lyricsLabel.setObjectName(u'lyricsLabel')
-        self.lyricsTabLayout.addWidget(self.lyricsLabel, 2, 0, QtCore.Qt.AlignTop)
-        self.verseListWidget = SingleColumnTableWidget(self.lyricsTab)
-        self.verseListWidget.setAlternatingRowColors(True)
-        self.verseListWidget.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
-        self.verseListWidget.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
-        self.verseListWidget.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
-        self.verseListWidget.setObjectName(u'verseListWidget')
-        self.lyricsLabel.setBuddy(self.verseListWidget)
-        self.lyricsTabLayout.addWidget(self.verseListWidget, 2, 1)
-        self.verseOrderLabel = QtGui.QLabel(self.lyricsTab)
-        self.verseOrderLabel.setObjectName(u'verseOrderLabel')
-        self.lyricsTabLayout.addWidget(self.verseOrderLabel, 3, 0)
-        self.verseOrderEdit = QtGui.QLineEdit(self.lyricsTab)
-        self.verseOrderEdit.setObjectName(u'verseOrderEdit')
-        self.verseOrderLabel.setBuddy(self.verseOrderEdit)
-        self.lyricsTabLayout.addWidget(self.verseOrderEdit, 3, 1, 1, 2)
-        self.verseButtonsLayout = QtGui.QVBoxLayout()
-        self.verseButtonsLayout.setObjectName(u'verseButtonsLayout')
-        self.verseAddButton = QtGui.QPushButton(self.lyricsTab)
-        self.verseAddButton.setObjectName(u'verseAddButton')
-        self.verseButtonsLayout.addWidget(self.verseAddButton)
-        self.verseEditButton = QtGui.QPushButton(self.lyricsTab)
-        self.verseEditButton.setObjectName(u'verseEditButton')
-        self.verseButtonsLayout.addWidget(self.verseEditButton)
-        self.verseEditAllButton = QtGui.QPushButton(self.lyricsTab)
-        self.verseEditAllButton.setObjectName(u'verseEditAllButton')
-        self.verseButtonsLayout.addWidget(self.verseEditAllButton)
-        self.verseDeleteButton = QtGui.QPushButton(self.lyricsTab)
-        self.verseDeleteButton.setObjectName(u'verseDeleteButton')
-        self.verseButtonsLayout.addWidget(self.verseDeleteButton)
-        self.verseButtonsLayout.addStretch()
-        self.lyricsTabLayout.addLayout(self.verseButtonsLayout, 2, 2)
-        self.songTabWidget.addTab(self.lyricsTab, u'')
+        self.lyrics_tab = QtGui.QWidget()
+        self.lyrics_tab.setObjectName(u'lyrics_tab')
+        self.lyrics_tab_layout = QtGui.QGridLayout(self.lyrics_tab)
+        self.lyrics_tab_layout.setObjectName(u'lyrics_tab_layout')
+        self.title_label = QtGui.QLabel(self.lyrics_tab)
+        self.title_label.setObjectName(u'title_label')
+        self.lyrics_tab_layout.addWidget(self.title_label, 0, 0)
+        self.title_edit = QtGui.QLineEdit(self.lyrics_tab)
+        self.title_edit.setObjectName(u'title_edit')
+        self.title_label.setBuddy(self.title_edit)
+        self.lyrics_tab_layout.addWidget(self.title_edit, 0, 1, 1, 2)
+        self.alternative_title_label = QtGui.QLabel(self.lyrics_tab)
+        self.alternative_title_label.setObjectName(u'alternative_title_label')
+        self.lyrics_tab_layout.addWidget(self.alternative_title_label, 1, 0)
+        self.alternative_edit = QtGui.QLineEdit(self.lyrics_tab)
+        self.alternative_edit.setObjectName(u'alternative_edit')
+        self.alternative_title_label.setBuddy(self.alternative_edit)
+        self.lyrics_tab_layout.addWidget(self.alternative_edit, 1, 1, 1, 2)
+        self.lyrics_label = QtGui.QLabel(self.lyrics_tab)
+        self.lyrics_label.setFixedHeight(self.title_edit.sizeHint().height())
+        self.lyrics_label.setObjectName(u'lyrics_label')
+        self.lyrics_tab_layout.addWidget(self.lyrics_label, 2, 0, QtCore.Qt.AlignTop)
+        self.verse_list_widget = SingleColumnTableWidget(self.lyrics_tab)
+        self.verse_list_widget.setAlternatingRowColors(True)
+        self.verse_list_widget.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
+        self.verse_list_widget.setSelectionMode(QtGui.QAbstractItemView.SingleSelection)
+        self.verse_list_widget.setEditTriggers(QtGui.QAbstractItemView.NoEditTriggers)
+        self.verse_list_widget.setObjectName(u'verse_list_widget')
+        self.lyrics_label.setBuddy(self.verse_list_widget)
+        self.lyrics_tab_layout.addWidget(self.verse_list_widget, 2, 1)
+        self.verse_order_label = QtGui.QLabel(self.lyrics_tab)
+        self.verse_order_label.setObjectName(u'verse_order_label')
+        self.lyrics_tab_layout.addWidget(self.verse_order_label, 3, 0)
+        self.verse_order_edit = QtGui.QLineEdit(self.lyrics_tab)
+        self.verse_order_edit.setObjectName(u'verse_order_edit')
+        self.verse_order_label.setBuddy(self.verse_order_edit)
+        self.lyrics_tab_layout.addWidget(self.verse_order_edit, 3, 1, 1, 2)
+        self.verse_buttons_layout = QtGui.QVBoxLayout()
+        self.verse_buttons_layout.setObjectName(u'verse_buttons_layout')
+        self.verse_add_button = QtGui.QPushButton(self.lyrics_tab)
+        self.verse_add_button.setObjectName(u'verse_add_button')
+        self.verse_buttons_layout.addWidget(self.verse_add_button)
+        self.verse_edit_button = QtGui.QPushButton(self.lyrics_tab)
+        self.verse_edit_button.setObjectName(u'verse_edit_button')
+        self.verse_buttons_layout.addWidget(self.verse_edit_button)
+        self.verse_edit_all_button = QtGui.QPushButton(self.lyrics_tab)
+        self.verse_edit_all_button.setObjectName(u'verse_edit_all_button')
+        self.verse_buttons_layout.addWidget(self.verse_edit_all_button)
+        self.verse_delete_button = QtGui.QPushButton(self.lyrics_tab)
+        self.verse_delete_button.setObjectName(u'verse_delete_button')
+        self.verse_buttons_layout.addWidget(self.verse_delete_button)
+        self.verse_buttons_layout.addStretch()
+        self.lyrics_tab_layout.addLayout(self.verse_buttons_layout, 2, 2)
+        self.song_tab_widget.addTab(self.lyrics_tab, u'')
         # authors tab
-        self.authorsTab = QtGui.QWidget()
-        self.authorsTab.setObjectName(u'authorsTab')
-        self.authorsTabLayout = QtGui.QHBoxLayout(self.authorsTab)
-        self.authorsTabLayout.setObjectName(u'authorsTabLayout')
-        self.authorsLeftLayout = QtGui.QVBoxLayout()
-        self.authorsLeftLayout.setObjectName(u'authorsLeftLayout')
-        self.authorsGroupBox = QtGui.QGroupBox(self.authorsTab)
-        self.authorsGroupBox.setObjectName(u'authorsGroupBox')
-        self.authorsLayout = QtGui.QVBoxLayout(self.authorsGroupBox)
-        self.authorsLayout.setObjectName(u'authorsLayout')
-        self.authorAddLayout = QtGui.QHBoxLayout()
-        self.authorAddLayout.setObjectName(u'authorAddLayout')
-        self.authorsComboBox = editSongDialogComboBox(self.authorsGroupBox, u'authorsComboBox')
-        self.authorAddLayout.addWidget(self.authorsComboBox)
-        self.authorAddButton = QtGui.QPushButton(self.authorsGroupBox)
-        self.authorAddButton.setObjectName(u'authorAddButton')
-        self.authorAddLayout.addWidget(self.authorAddButton)
-        self.authorsLayout.addLayout(self.authorAddLayout)
-        self.authorsListView = QtGui.QListWidget(self.authorsGroupBox)
-        self.authorsListView.setAlternatingRowColors(True)
-        self.authorsListView.setObjectName(u'authorsListView')
-        self.authorsLayout.addWidget(self.authorsListView)
-        self.authorRemoveLayout = QtGui.QHBoxLayout()
-        self.authorRemoveLayout.setObjectName(u'authorRemoveLayout')
-        self.authorRemoveLayout.addStretch()
-        self.authorRemoveButton = QtGui.QPushButton(self.authorsGroupBox)
-        self.authorRemoveButton.setObjectName(u'authorRemoveButton')
-        self.authorRemoveLayout.addWidget(self.authorRemoveButton)
-        self.authorsLayout.addLayout(self.authorRemoveLayout)
-        self.authorsLeftLayout.addWidget(self.authorsGroupBox)
-        self.maintenanceLayout = QtGui.QHBoxLayout()
-        self.maintenanceLayout.setObjectName(u'maintenanceLayout')
-        self.maintenanceButton = QtGui.QPushButton(self.authorsTab)
-        self.maintenanceButton.setObjectName(u'maintenanceButton')
-        self.maintenanceLayout.addWidget(self.maintenanceButton)
-        self.maintenanceLayout.addStretch()
-        self.authorsLeftLayout.addLayout(self.maintenanceLayout)
-        self.authorsTabLayout.addLayout(self.authorsLeftLayout)
-        self.authorsRightLayout = QtGui.QVBoxLayout()
-        self.authorsRightLayout.setObjectName(u'authorsRightLayout')
-        self.topicsGroupBox = QtGui.QGroupBox(self.authorsTab)
-        self.topicsGroupBox.setObjectName(u'topicsGroupBox')
-        self.topicsLayout = QtGui.QVBoxLayout(self.topicsGroupBox)
-        self.topicsLayout.setObjectName(u'topicsLayout')
-        self.topicAddLayout = QtGui.QHBoxLayout()
-        self.topicAddLayout.setObjectName(u'topicAddLayout')
-        self.topicsComboBox = editSongDialogComboBox(self.topicsGroupBox, u'topicsComboBox')
-        self.topicAddLayout.addWidget(self.topicsComboBox)
-        self.topicAddButton = QtGui.QPushButton(self.topicsGroupBox)
-        self.topicAddButton.setObjectName(u'topicAddButton')
-        self.topicAddLayout.addWidget(self.topicAddButton)
-        self.topicsLayout.addLayout(self.topicAddLayout)
-        self.topicsListView = QtGui.QListWidget(self.topicsGroupBox)
-        self.topicsListView.setAlternatingRowColors(True)
-        self.topicsListView.setObjectName(u'topicsListView')
-        self.topicsLayout.addWidget(self.topicsListView)
-        self.topicRemoveLayout = QtGui.QHBoxLayout()
-        self.topicRemoveLayout.setObjectName(u'topicRemoveLayout')
-        self.topicRemoveLayout.addStretch()
-        self.topicRemoveButton = QtGui.QPushButton(self.topicsGroupBox)
-        self.topicRemoveButton.setObjectName(u'topicRemoveButton')
-        self.topicRemoveLayout.addWidget(self.topicRemoveButton)
-        self.topicsLayout.addLayout(self.topicRemoveLayout)
-        self.authorsRightLayout.addWidget(self.topicsGroupBox)
-        self.songBookGroupBox = QtGui.QGroupBox(self.authorsTab)
-        self.songBookGroupBox.setObjectName(u'songBookGroupBox')
-        self.songBookLayout = QtGui.QFormLayout(self.songBookGroupBox)
-        self.songBookLayout.setObjectName(u'songBookLayout')
-        self.songBookNameLabel = QtGui.QLabel(self.songBookGroupBox)
-        self.songBookNameLabel.setObjectName(u'songBookNameLabel')
-        self.songBookComboBox = editSongDialogComboBox(self.songBookGroupBox, u'songBookComboBox')
-        self.songBookNameLabel.setBuddy(self.songBookComboBox)
-        self.songBookLayout.addRow(self.songBookNameLabel, self.songBookComboBox)
-        self.songBookNumberLabel = QtGui.QLabel(self.songBookGroupBox)
-        self.songBookNumberLabel.setObjectName(u'songBookNumberLabel')
-        self.songBookNumberEdit = QtGui.QLineEdit(self.songBookGroupBox)
-        self.songBookNumberEdit.setObjectName(u'songBookNumberEdit')
-        self.songBookNumberLabel.setBuddy(self.songBookNumberEdit)
-        self.songBookLayout.addRow(self.songBookNumberLabel, self.songBookNumberEdit)
-        self.authorsRightLayout.addWidget(self.songBookGroupBox)
-        self.authorsTabLayout.addLayout(self.authorsRightLayout)
-        self.songTabWidget.addTab(self.authorsTab, u'')
+        self.authors_tab = QtGui.QWidget()
+        self.authors_tab.setObjectName(u'authors_tab')
+        self.authors_tab_layout = QtGui.QHBoxLayout(self.authors_tab)
+        self.authors_tab_layout.setObjectName(u'authors_tab_layout')
+        self.authors_left_layout = QtGui.QVBoxLayout()
+        self.authors_left_layout.setObjectName(u'authors_left_layout')
+        self.authors_group_box = QtGui.QGroupBox(self.authors_tab)
+        self.authors_group_box.setObjectName(u'authors_group_box')
+        self.authors_layout = QtGui.QVBoxLayout(self.authors_group_box)
+        self.authors_layout.setObjectName(u'authors_layout')
+        self.author_add_layout = QtGui.QHBoxLayout()
+        self.author_add_layout.setObjectName(u'author_add_layout')
+        self.authors_combo_box = create_combo_box(self.authors_group_box, u'authors_combo_box')
+        self.author_add_layout.addWidget(self.authors_combo_box)
+        self.author_add_button = QtGui.QPushButton(self.authors_group_box)
+        self.author_add_button.setObjectName(u'author_add_button')
+        self.author_add_layout.addWidget(self.author_add_button)
+        self.authors_layout.addLayout(self.author_add_layout)
+        self.authors_list_view = QtGui.QListWidget(self.authors_group_box)
+        self.authors_list_view.setAlternatingRowColors(True)
+        self.authors_list_view.setObjectName(u'authors_list_view')
+        self.authors_layout.addWidget(self.authors_list_view)
+        self.author_remove_layout = QtGui.QHBoxLayout()
+        self.author_remove_layout.setObjectName(u'author_remove_layout')
+        self.author_remove_layout.addStretch()
+        self.author_remove_button = QtGui.QPushButton(self.authors_group_box)
+        self.author_remove_button.setObjectName(u'author_remove_button')
+        self.author_remove_layout.addWidget(self.author_remove_button)
+        self.authors_layout.addLayout(self.author_remove_layout)
+        self.authors_left_layout.addWidget(self.authors_group_box)
+        self.maintenance_layout = QtGui.QHBoxLayout()
+        self.maintenance_layout.setObjectName(u'maintenance_layout')
+        self.maintenance_button = QtGui.QPushButton(self.authors_tab)
+        self.maintenance_button.setObjectName(u'maintenance_button')
+        self.maintenance_layout.addWidget(self.maintenance_button)
+        self.maintenance_layout.addStretch()
+        self.authors_left_layout.addLayout(self.maintenance_layout)
+        self.authors_tab_layout.addLayout(self.authors_left_layout)
+        self.authors_right_layout = QtGui.QVBoxLayout()
+        self.authors_right_layout.setObjectName(u'authors_right_layout')
+        self.topics_group_box = QtGui.QGroupBox(self.authors_tab)
+        self.topics_group_box.setObjectName(u'topics_group_box')
+        self.topics_layout = QtGui.QVBoxLayout(self.topics_group_box)
+        self.topics_layout.setObjectName(u'topics_layout')
+        self.topic_add_layout = QtGui.QHBoxLayout()
+        self.topic_add_layout.setObjectName(u'topic_add_layout')
+        self.topicsComboBox = create_combo_box(self.topics_group_box, u'topicsComboBox')
+        self.topic_add_layout.addWidget(self.topicsComboBox)
+        self.topic_add_button = QtGui.QPushButton(self.topics_group_box)
+        self.topic_add_button.setObjectName(u'topic_add_button')
+        self.topic_add_layout.addWidget(self.topic_add_button)
+        self.topics_layout.addLayout(self.topic_add_layout)
+        self.topics_list_view = QtGui.QListWidget(self.topics_group_box)
+        self.topics_list_view.setAlternatingRowColors(True)
+        self.topics_list_view.setObjectName(u'topics_list_view')
+        self.topics_layout.addWidget(self.topics_list_view)
+        self.topic_remove_layout = QtGui.QHBoxLayout()
+        self.topic_remove_layout.setObjectName(u'topic_remove_layout')
+        self.topic_remove_layout.addStretch()
+        self.topic_remove_button = QtGui.QPushButton(self.topics_group_box)
+        self.topic_remove_button.setObjectName(u'topic_remove_button')
+        self.topic_remove_layout.addWidget(self.topic_remove_button)
+        self.topics_layout.addLayout(self.topic_remove_layout)
+        self.authors_right_layout.addWidget(self.topics_group_box)
+        self.song_book_group_box = QtGui.QGroupBox(self.authors_tab)
+        self.song_book_group_box.setObjectName(u'song_book_group_box')
+        self.song_book_layout = QtGui.QFormLayout(self.song_book_group_box)
+        self.song_book_layout.setObjectName(u'song_book_layout')
+        self.song_book_name_label = QtGui.QLabel(self.song_book_group_box)
+        self.song_book_name_label.setObjectName(u'song_book_name_label')
+        self.song_book_combo_box = create_combo_box(self.song_book_group_box, u'song_book_combo_box')
+        self.song_book_name_label.setBuddy(self.song_book_combo_box)
+        self.song_book_layout.addRow(self.song_book_name_label, self.song_book_combo_box)
+        self.song_book_number_label = QtGui.QLabel(self.song_book_group_box)
+        self.song_book_number_label.setObjectName(u'song_book_number_label')
+        self.song_book_number_edit = QtGui.QLineEdit(self.song_book_group_box)
+        self.song_book_number_edit.setObjectName(u'song_book_number_edit')
+        self.song_book_number_label.setBuddy(self.song_book_number_edit)
+        self.song_book_layout.addRow(self.song_book_number_label, self.song_book_number_edit)
+        self.authors_right_layout.addWidget(self.song_book_group_box)
+        self.authors_tab_layout.addLayout(self.authors_right_layout)
+        self.song_tab_widget.addTab(self.authors_tab, u'')
         # theme tab
-        self.themeTab = QtGui.QWidget()
-        self.themeTab.setObjectName(u'themeTab')
-        self.themeTabLayout = QtGui.QHBoxLayout(self.themeTab)
-        self.themeTabLayout.setObjectName(u'themeTabLayout')
-        self.themeLeftLayout = QtGui.QVBoxLayout()
-        self.themeLeftLayout.setObjectName(u'themeLeftLayout')
-        self.themeGroupBox = QtGui.QGroupBox(self.themeTab)
-        self.themeGroupBox.setObjectName(u'themeGroupBox')
-        self.themeLayout = QtGui.QHBoxLayout(self.themeGroupBox)
-        self.themeLayout.setObjectName(u'themeLayout')
-        self.themeComboBox = editSongDialogComboBox(self.themeGroupBox, u'themeComboBox')
-        self.themeLayout.addWidget(self.themeComboBox)
-        self.themeAddButton = QtGui.QPushButton(self.themeGroupBox)
-        self.themeAddButton.setObjectName(u'themeAddButton')
-        self.themeLayout.addWidget(self.themeAddButton)
-        self.themeLeftLayout.addWidget(self.themeGroupBox)
-        self.rightsGroupBox = QtGui.QGroupBox(self.themeTab)
-        self.rightsGroupBox.setObjectName(u'rightsGroupBox')
-        self.rightsLayout = QtGui.QVBoxLayout(self.rightsGroupBox)
-        self.rightsLayout.setObjectName(u'rightsLayout')
-        self.copyrightLayout = QtGui.QHBoxLayout()
-        self.copyrightLayout.setObjectName(u'copyrightLayout')
-        self.copyrightEdit = QtGui.QLineEdit(self.rightsGroupBox)
-        self.copyrightEdit.setObjectName(u'copyrightEdit')
-        self.copyrightLayout.addWidget(self.copyrightEdit)
-        self.copyrightInsertButton = QtGui.QToolButton(self.rightsGroupBox)
-        self.copyrightInsertButton.setObjectName(u'copyrightInsertButton')
-        self.copyrightLayout.addWidget(self.copyrightInsertButton)
-        self.rightsLayout.addLayout(self.copyrightLayout)
-        self.CCLILayout = QtGui.QHBoxLayout()
-        self.CCLILayout.setObjectName(u'CCLILayout')
-        self.CCLILabel = QtGui.QLabel(self.rightsGroupBox)
-        self.CCLILabel.setObjectName(u'CCLILabel')
-        self.CCLILayout.addWidget(self.CCLILabel)
-        self.CCLNumberEdit = QtGui.QLineEdit(self.rightsGroupBox)
-        self.CCLNumberEdit.setValidator(QtGui.QIntValidator())
-        self.CCLNumberEdit.setObjectName(u'CCLNumberEdit')
-        self.CCLILayout.addWidget(self.CCLNumberEdit)
-        self.rightsLayout.addLayout(self.CCLILayout)
-        self.themeLeftLayout.addWidget(self.rightsGroupBox)
-        self.themeLeftLayout.addStretch()
-        self.themeTabLayout.addLayout(self.themeLeftLayout)
-        self.commentsGroupBox = QtGui.QGroupBox(self.themeTab)
-        self.commentsGroupBox.setObjectName(u'commentsGroupBox')
-        self.commentsLayout = QtGui.QVBoxLayout(self.commentsGroupBox)
-        self.commentsLayout.setObjectName(u'commentsLayout')
-        self.commentsEdit = QtGui.QTextEdit(self.commentsGroupBox)
-        self.commentsEdit.setObjectName(u'commentsEdit')
-        self.commentsLayout.addWidget(self.commentsEdit)
-        self.themeTabLayout.addWidget(self.commentsGroupBox)
-        self.songTabWidget.addTab(self.themeTab, u'')
+        self.theme_tab = QtGui.QWidget()
+        self.theme_tab.setObjectName(u'theme_tab')
+        self.theme_tab_layout = QtGui.QHBoxLayout(self.theme_tab)
+        self.theme_tab_layout.setObjectName(u'theme_tab_layout')
+        self.theme_left_layout = QtGui.QVBoxLayout()
+        self.theme_left_layout.setObjectName(u'theme_left_layout')
+        self.theme_group_box = QtGui.QGroupBox(self.theme_tab)
+        self.theme_group_box.setObjectName(u'theme_group_box')
+        self.theme_layout = QtGui.QHBoxLayout(self.theme_group_box)
+        self.theme_layout.setObjectName(u'theme_layout')
+        self.theme_combo_box = create_combo_box(self.theme_group_box, u'theme_combo_box')
+        self.theme_layout.addWidget(self.theme_combo_box)
+        self.theme_add_button = QtGui.QPushButton(self.theme_group_box)
+        self.theme_add_button.setObjectName(u'theme_add_button')
+        self.theme_layout.addWidget(self.theme_add_button)
+        self.theme_left_layout.addWidget(self.theme_group_box)
+        self.rights_group_box = QtGui.QGroupBox(self.theme_tab)
+        self.rights_group_box.setObjectName(u'rights_group_box')
+        self.rights_layout = QtGui.QVBoxLayout(self.rights_group_box)
+        self.rights_layout.setObjectName(u'rights_layout')
+        self.copyright_layout = QtGui.QHBoxLayout()
+        self.copyright_layout.setObjectName(u'copyright_layout')
+        self.copyright_edit = QtGui.QLineEdit(self.rights_group_box)
+        self.copyright_edit.setObjectName(u'copyright_edit')
+        self.copyright_layout.addWidget(self.copyright_edit)
+        self.copyright_insert_button = QtGui.QToolButton(self.rights_group_box)
+        self.copyright_insert_button.setObjectName(u'copyright_insert_button')
+        self.copyright_layout.addWidget(self.copyright_insert_button)
+        self.rights_layout.addLayout(self.copyright_layout)
+        self.ccli_layout = QtGui.QHBoxLayout()
+        self.ccli_layout.setObjectName(u'ccli_layout')
+        self.ccli_label = QtGui.QLabel(self.rights_group_box)
+        self.ccli_label.setObjectName(u'ccli_label')
+        self.ccli_layout.addWidget(self.ccli_label)
+        self.ccli_number_edit = QtGui.QLineEdit(self.rights_group_box)
+        self.ccli_number_edit.setValidator(QtGui.QIntValidator())
+        self.ccli_number_edit.setObjectName(u'ccli_number_edit')
+        self.ccli_layout.addWidget(self.ccli_number_edit)
+        self.rights_layout.addLayout(self.ccli_layout)
+        self.theme_left_layout.addWidget(self.rights_group_box)
+        self.theme_left_layout.addStretch()
+        self.theme_tab_layout.addLayout(self.theme_left_layout)
+        self.comments_group_box = QtGui.QGroupBox(self.theme_tab)
+        self.comments_group_box.setObjectName(u'comments_group_box')
+        self.comments_layout = QtGui.QVBoxLayout(self.comments_group_box)
+        self.comments_layout.setObjectName(u'comments_layout')
+        self.comments_edit = QtGui.QTextEdit(self.comments_group_box)
+        self.comments_edit.setObjectName(u'comments_edit')
+        self.comments_layout.addWidget(self.comments_edit)
+        self.theme_tab_layout.addWidget(self.comments_group_box)
+        self.song_tab_widget.addTab(self.theme_tab, u'')
         # audio tab
-        self.audioTab = QtGui.QWidget()
-        self.audioTab.setObjectName(u'audioTab')
-        self.audioLayout = QtGui.QHBoxLayout(self.audioTab)
-        self.audioLayout.setObjectName(u'audioLayout')
-        self.audioListWidget = QtGui.QListWidget(self.audioTab)
-        self.audioListWidget.setObjectName(u'audioListWidget')
-        self.audioLayout.addWidget(self.audioListWidget)
-        self.audioButtonsLayout = QtGui.QVBoxLayout()
-        self.audioButtonsLayout.setObjectName(u'audioButtonsLayout')
-        self.audioAddFromFileButton = QtGui.QPushButton(self.audioTab)
-        self.audioAddFromFileButton.setObjectName(u'audioAddFromFileButton')
-        self.audioButtonsLayout.addWidget(self.audioAddFromFileButton)
-        self.audioAddFromMediaButton = QtGui.QPushButton(self.audioTab)
-        self.audioAddFromMediaButton.setObjectName(u'audioAddFromMediaButton')
-        self.audioButtonsLayout.addWidget(self.audioAddFromMediaButton)
-        self.audioRemoveButton = QtGui.QPushButton(self.audioTab)
-        self.audioRemoveButton.setObjectName(u'audioRemoveButton')
-        self.audioButtonsLayout.addWidget(self.audioRemoveButton)
-        self.audioRemoveAllButton = QtGui.QPushButton(self.audioTab)
-        self.audioRemoveAllButton.setObjectName(u'audioRemoveAllButton')
-        self.audioButtonsLayout.addWidget(self.audioRemoveAllButton)
-        self.audioButtonsLayout.addStretch(1)
-        self.upButton = create_button(self, u'upButton', role=u'up', click=self.onUpButtonClicked)
-        self.downButton = create_button(self, u'downButton', role=u'down', click=self.onDownButtonClicked)
-        self.audioButtonsLayout.addWidget(self.upButton)
-        self.audioButtonsLayout.addWidget(self.downButton)
-        self.audioLayout.addLayout(self.audioButtonsLayout)
-        self.songTabWidget.addTab(self.audioTab, u'')
+        self.audio_tab = QtGui.QWidget()
+        self.audio_tab.setObjectName(u'audio_tab')
+        self.audio_layout = QtGui.QHBoxLayout(self.audio_tab)
+        self.audio_layout.setObjectName(u'audio_layout')
+        self.audio_list_widget = QtGui.QListWidget(self.audio_tab)
+        self.audio_list_widget.setObjectName(u'audio_list_widget')
+        self.audio_layout.addWidget(self.audio_list_widget)
+        self.audio_buttons_layout = QtGui.QVBoxLayout()
+        self.audio_buttons_layout.setObjectName(u'audio_buttons_layout')
+        self.from_file_button = QtGui.QPushButton(self.audio_tab)
+        self.from_file_button.setObjectName(u'from_file_button')
+        self.audio_buttons_layout.addWidget(self.from_file_button)
+        self.from_media_button = QtGui.QPushButton(self.audio_tab)
+        self.from_media_button.setObjectName(u'from_media_button')
+        self.audio_buttons_layout.addWidget(self.from_media_button)
+        self.audio_remove_button = QtGui.QPushButton(self.audio_tab)
+        self.audio_remove_button.setObjectName(u'audio_remove_button')
+        self.audio_buttons_layout.addWidget(self.audio_remove_button)
+        self.audio_remove_all_button = QtGui.QPushButton(self.audio_tab)
+        self.audio_remove_all_button.setObjectName(u'audio_remove_all_button')
+        self.audio_buttons_layout.addWidget(self.audio_remove_all_button)
+        self.audio_buttons_layout.addStretch(1)
+        self.up_button = create_button(self, u'up_button', role=u'up', click=self.on_up_button_clicked)
+        self.down_button = create_button(self, u'down_button', role=u'down', click=self.on_down_button_clicked)
+        self.audio_buttons_layout.addWidget(self.up_button)
+        self.audio_buttons_layout.addWidget(self.down_button)
+        self.audio_layout.addLayout(self.audio_buttons_layout)
+        self.song_tab_widget.addTab(self.audio_tab, u'')
         # Last few bits
-        self.dialogLayout.addWidget(self.songTabWidget)
-        self.bottomLayout = QtGui.QHBoxLayout()
-        self.bottomLayout.setObjectName(u'bottomLayout')
-        self.warningLabel = QtGui.QLabel(editSongDialog)
-        self.warningLabel.setObjectName(u'warningLabel')
-        self.warningLabel.setVisible(False)
-        self.bottomLayout.addWidget(self.warningLabel)
-        self.button_box = create_button_box(editSongDialog, u'button_box', [u'cancel', u'save'])
-        self.bottomLayout.addWidget(self.button_box)
-        self.dialogLayout.addLayout(self.bottomLayout)
-        self.retranslateUi(editSongDialog)
+        self.dialog_layout.addWidget(self.song_tab_widget)
+        self.bottom_layout = QtGui.QHBoxLayout()
+        self.bottom_layout.setObjectName(u'bottom_layout')
+        self.warning_label = QtGui.QLabel(edit_song_dialog)
+        self.warning_label.setObjectName(u'warning_label')
+        self.warning_label.setVisible(False)
+        self.bottom_layout.addWidget(self.warning_label)
+        self.button_box = create_button_box(edit_song_dialog, u'button_box', [u'cancel', u'save'])
+        self.bottom_layout.addWidget(self.button_box)
+        self.dialog_layout.addLayout(self.bottom_layout)
+        self.retranslateUi(edit_song_dialog)
 
-    def retranslateUi(self, editSongDialog):
-        editSongDialog.setWindowTitle(translate('SongsPlugin.EditSongForm', 'Song Editor'))
-        self.titleLabel.setText(translate('SongsPlugin.EditSongForm', '&Title:'))
-        self.alternativeTitleLabel.setText(translate('SongsPlugin.EditSongForm', 'Alt&ernate title:'))
-        self.lyricsLabel.setText(translate('SongsPlugin.EditSongForm', '&Lyrics:'))
-        self.verseOrderLabel.setText(translate('SongsPlugin.EditSongForm', '&Verse order:'))
-        self.verseAddButton.setText(UiStrings().Add)
-        self.verseEditButton.setText(UiStrings().Edit)
-        self.verseEditAllButton.setText(translate('SongsPlugin.EditSongForm', 'Ed&it All'))
-        self.verseDeleteButton.setText(UiStrings().Delete)
-        self.songTabWidget.setTabText(self.songTabWidget.indexOf(self.lyricsTab),
+    def retranslateUi(self, edit_song_dialog):
+        """
+        Translate the UI on the fly.
+        """
+        edit_song_dialog.setWindowTitle(translate('SongsPlugin.EditSongForm', 'Song Editor'))
+        self.title_label.setText(translate('SongsPlugin.EditSongForm', '&Title:'))
+        self.alternative_title_label.setText(translate('SongsPlugin.EditSongForm', 'Alt&ernate title:'))
+        self.lyrics_label.setText(translate('SongsPlugin.EditSongForm', '&Lyrics:'))
+        self.verse_order_label.setText(translate('SongsPlugin.EditSongForm', '&Verse order:'))
+        self.verse_add_button.setText(UiStrings().Add)
+        self.verse_edit_button.setText(UiStrings().Edit)
+        self.verse_edit_all_button.setText(translate('SongsPlugin.EditSongForm', 'Ed&it All'))
+        self.verse_delete_button.setText(UiStrings().Delete)
+        self.song_tab_widget.setTabText(self.song_tab_widget.indexOf(self.lyrics_tab),
             translate('SongsPlugin.EditSongForm', 'Title && Lyrics'))
-        self.authorsGroupBox.setTitle(SongStrings.Authors)
-        self.authorAddButton.setText(translate('SongsPlugin.EditSongForm', '&Add to Song'))
-        self.authorRemoveButton.setText(translate('SongsPlugin.EditSongForm', '&Remove'))
-        self.maintenanceButton.setText(translate('SongsPlugin.EditSongForm', '&Manage Authors, Topics, Song Books'))
-        self.topicsGroupBox.setTitle(SongStrings.Topic)
-        self.topicAddButton.setText(translate('SongsPlugin.EditSongForm', 'A&dd to Song'))
-        self.topicRemoveButton.setText(translate('SongsPlugin.EditSongForm', 'R&emove'))
-        self.songBookGroupBox.setTitle(SongStrings.SongBook)
-        self.songBookNameLabel.setText(translate('SongsPlugin.EditSongForm', 'Book:'))
-        self.songBookNumberLabel.setText(translate('SongsPlugin.EditSongForm', 'Number:'))
-        self.songTabWidget.setTabText(self.songTabWidget.indexOf(self.authorsTab),
+        self.authors_group_box.setTitle(SongStrings.Authors)
+        self.author_add_button.setText(translate('SongsPlugin.EditSongForm', '&Add to Song'))
+        self.author_remove_button.setText(translate('SongsPlugin.EditSongForm', '&Remove'))
+        self.maintenance_button.setText(translate('SongsPlugin.EditSongForm', '&Manage Authors, Topics, Song Books'))
+        self.topics_group_box.setTitle(SongStrings.Topic)
+        self.topic_add_button.setText(translate('SongsPlugin.EditSongForm', 'A&dd to Song'))
+        self.topic_remove_button.setText(translate('SongsPlugin.EditSongForm', 'R&emove'))
+        self.song_book_group_box.setTitle(SongStrings.SongBook)
+        self.song_book_name_label.setText(translate('SongsPlugin.EditSongForm', 'Book:'))
+        self.song_book_number_label.setText(translate('SongsPlugin.EditSongForm', 'Number:'))
+        self.song_tab_widget.setTabText(self.song_tab_widget.indexOf(self.authors_tab),
             translate('SongsPlugin.EditSongForm', 'Authors, Topics && Song Book'))
-        self.themeGroupBox.setTitle(UiStrings().Theme)
-        self.themeAddButton.setText(translate('SongsPlugin.EditSongForm', 'New &Theme'))
-        self.rightsGroupBox.setTitle(translate('SongsPlugin.EditSongForm', 'Copyright Information'))
-        self.copyrightInsertButton.setText(SongStrings.CopyrightSymbol)
-        self.CCLILabel.setText(UiStrings().CCLINumberLabel)
-        self.commentsGroupBox.setTitle(translate('SongsPlugin.EditSongForm', 'Comments'))
-        self.songTabWidget.setTabText(self.songTabWidget.indexOf(self.themeTab),
+        self.theme_group_box.setTitle(UiStrings().Theme)
+        self.theme_add_button.setText(translate('SongsPlugin.EditSongForm', 'New &Theme'))
+        self.rights_group_box.setTitle(translate('SongsPlugin.EditSongForm', 'Copyright Information'))
+        self.copyright_insert_button.setText(SongStrings.CopyrightSymbol)
+        self.ccli_label.setText(UiStrings().CCLINumberLabel)
+        self.comments_group_box.setTitle(translate('SongsPlugin.EditSongForm', 'Comments'))
+        self.song_tab_widget.setTabText(self.song_tab_widget.indexOf(self.theme_tab),
             translate('SongsPlugin.EditSongForm', 'Theme, Copyright Info && Comments'))
-        self.songTabWidget.setTabText(self.songTabWidget.indexOf(self.audioTab),
+        self.song_tab_widget.setTabText(self.song_tab_widget.indexOf(self.audio_tab),
             translate('SongsPlugin.EditSongForm', 'Linked Audio'))
-        self.audioAddFromFileButton.setText(translate('SongsPlugin.EditSongForm', 'Add &File(s)'))
-        self.audioAddFromMediaButton.setText(translate('SongsPlugin.EditSongForm', 'Add &Media'))
-        self.audioRemoveButton.setText(translate('SongsPlugin.EditSongForm', '&Remove'))
-        self.audioRemoveAllButton.setText(translate('SongsPlugin.EditSongForm', 'Remove &All'))
-        self.warningLabel.setText(
+        self.from_file_button.setText(translate('SongsPlugin.EditSongForm', 'Add &File(s)'))
+        self.from_media_button.setText(translate('SongsPlugin.EditSongForm', 'Add &Media'))
+        self.audio_remove_button.setText(translate('SongsPlugin.EditSongForm', '&Remove'))
+        self.audio_remove_all_button.setText(translate('SongsPlugin.EditSongForm', 'Remove &All'))
+        self.warning_label.setText(
             translate('SongsPlugin.EditSongForm', '<strong>Warning:</strong> Not all of the verses are in use.'))
 
-def editSongDialogComboBox(parent, name):
+
+def create_combo_box(parent, name):
     """
     Utility method to generate a standard combo box for this dialog.
+
+    ``parent``
+        The parent widget for this combo box.
+
+    ``name``
+        The object name.
     """
-    comboBox = QtGui.QComboBox(parent)
-    comboBox.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToMinimumContentsLength)
-    comboBox.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
-    comboBox.setEditable(True)
-    comboBox.setInsertPolicy(QtGui.QComboBox.NoInsert)
-    comboBox.setObjectName(name)
-    return comboBox
+    combo_box = QtGui.QComboBox(parent)
+    combo_box.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToMinimumContentsLength)
+    combo_box.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
+    combo_box.setEditable(True)
+    combo_box.setInsertPolicy(QtGui.QComboBox.NoInsert)
+    combo_box.setObjectName(name)
+    return combo_box
+
 
 class SingleColumnTableWidget(QtGui.QTableWidget):
     """

=== modified file 'openlp/plugins/songs/forms/editsongform.py'
--- openlp/plugins/songs/forms/editsongform.py	2013-03-04 20:58:28 +0000
+++ openlp/plugins/songs/forms/editsongform.py	2013-03-07 21:58:22 +0000
@@ -38,19 +38,21 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.lib import Registry, PluginStatus, MediaType, translate, create_separated_list, \
-    check_directory_exists, UiStrings
-from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, \
-    find_and_set_in_combo_box
+from openlp.core.lib import Registry, PluginStatus, MediaType, UiStrings, translate, create_separated_list, \
+    check_directory_exists
+from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box
 from openlp.core.utils import AppLocation
-from openlp.plugins.songs.forms import EditVerseForm, MediaFilesForm
-from openlp.plugins.songs.lib import SongXML, VerseType, clean_song
+from openlp.plugins.songs.lib import VerseType, clean_song
 from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile
 from openlp.plugins.songs.lib.ui import SongStrings
+from openlp.plugins.songs.lib.xml import SongXML
 from openlp.plugins.songs.forms.editsongdialog import Ui_EditSongDialog
+from openlp.plugins.songs.forms.editverseform import EditVerseForm
+from openlp.plugins.songs.forms.mediafilesform import MediaFilesForm
 
 log = logging.getLogger(__name__)
 
+
 class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
     """
     Class to manage the editing of a song
@@ -68,54 +70,183 @@
         self.width = 400
         self.setupUi(self)
         # Connecting signals and slots
-        QtCore.QObject.connect(self.authorAddButton, QtCore.SIGNAL(u'clicked()'), self.onAuthorAddButtonClicked)
-        QtCore.QObject.connect(self.authorRemoveButton, QtCore.SIGNAL(u'clicked()'), self.onAuthorRemoveButtonClicked)
-        QtCore.QObject.connect(self.authorsListView, QtCore.SIGNAL(u'itemClicked(QListWidgetItem*)'),
-            self.onAuthorsListViewClicked)
-        QtCore.QObject.connect(self.topicAddButton, QtCore.SIGNAL(u'clicked()'), self.onTopicAddButtonClicked)
-        QtCore.QObject.connect(self.topicRemoveButton, QtCore.SIGNAL(u'clicked()'), self.onTopicRemoveButtonClicked)
-        QtCore.QObject.connect(self.topicsListView, QtCore.SIGNAL(u'itemClicked(QListWidgetItem*)'),
-            self.onTopicListViewClicked)
-        QtCore.QObject.connect(self.copyrightInsertButton, QtCore.SIGNAL(u'clicked()'),
-            self.onCopyrightInsertButtonTriggered)
-        QtCore.QObject.connect(self.verseAddButton, QtCore.SIGNAL(u'clicked()'), self.onVerseAddButtonClicked)
-        QtCore.QObject.connect(self.verseListWidget, QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
-            self.onVerseEditButtonClicked)
-        QtCore.QObject.connect(self.verseEditButton, QtCore.SIGNAL(u'clicked()'), self.onVerseEditButtonClicked)
-        QtCore.QObject.connect(self.verseEditAllButton, QtCore.SIGNAL(u'clicked()'), self.onVerseEditAllButtonClicked)
-        QtCore.QObject.connect(self.verseDeleteButton, QtCore.SIGNAL(u'clicked()'), self.onVerseDeleteButtonClicked)
-        QtCore.QObject.connect(self.verseListWidget, QtCore.SIGNAL(u'itemClicked(QTableWidgetItem*)'),
-            self.onVerseListViewClicked)
-        QtCore.QObject.connect(self.verseOrderEdit, QtCore.SIGNAL(u'textChanged(QString)'),
-            self.onVerseOrderTextChanged)
-        QtCore.QObject.connect(self.themeAddButton, QtCore.SIGNAL(u'clicked()'), self.theme_manager.onAddTheme)
-        QtCore.QObject.connect(self.maintenanceButton, QtCore.SIGNAL(u'clicked()'), self.onMaintenanceButtonClicked)
-        QtCore.QObject.connect(self.audioAddFromFileButton, QtCore.SIGNAL(u'clicked()'),
-            self.onAudioAddFromFileButtonClicked)
-        QtCore.QObject.connect(self.audioAddFromMediaButton, QtCore.SIGNAL(u'clicked()'),
-            self.onAudioAddFromMediaButtonClicked)
-        QtCore.QObject.connect(self.audioRemoveButton, QtCore.SIGNAL(u'clicked()'), self.onAudioRemoveButtonClicked)
-        QtCore.QObject.connect(self.audioRemoveAllButton, QtCore.SIGNAL(u'clicked()'),
-            self.onAudioRemoveAllButtonClicked)
+        self.author_add_button.clicked.connect(self.on_author_add_button_clicked)
+        self.author_remove_button.clicked.connect(self.on_author_remove_button_clicked)
+        self.authors_list_view.itemClicked.connect(self.on_authors_list_view_clicked)
+        self.topic_add_button.clicked.connect(self.on_topic_add_button_clicked)
+        self.topic_remove_button.clicked.connect(self.on_topic_remove_button_clicked)
+        self.topics_list_view.itemClicked.connect(self.on_topic_list_view_clicked)
+        self.copyright_insert_button.clicked.connect(self.on_copyright_insert_button_triggered)
+        self.verse_add_button.clicked.connect(self.on_verse_add_button_clicked)
+        self.verse_list_widget.doubleClicked.connect(self.on_verse_edit_button_clicked)
+        self.verse_edit_button.clicked.connect(self.on_verse_edit_button_clicked)
+        self.verse_edit_all_button.clicked.connect(self.on_verse_edit_all_button_clicked)
+        self.verse_delete_button.clicked.connect(self.on_verse_delete_button_clicked)
+        self.verse_list_widget.itemClicked.connect(self.on_verse_list_view_clicked)
+        self.verse_order_edit.textChanged.connect(self.on_verse_order_text_changed)
+        self.theme_add_button.clicked.connect(self.theme_manager.onAddTheme)
+        self.maintenance_button.clicked.connect(self.on_maintenance_button_clicked)
+        self.from_file_button.clicked.connect(self.on_audio_add_from_file_button_clicked)
+        self.from_media_button.clicked.connect(self.on_audio_add_from_media_button_clicked)
+        self.audio_remove_button.clicked.connect(self.on_audio_remove_button_clicked)
+        self.audio_remove_all_button.clicked.connect(self.on_audio_remove_all_button_clicked)
         Registry().register_function(u'theme_update_list', self.load_themes)
-        self.previewButton = QtGui.QPushButton()
-        self.previewButton.setObjectName(u'previewButton')
-        self.previewButton.setText(UiStrings().SaveAndPreview)
-        self.button_box.addButton(self.previewButton, QtGui.QDialogButtonBox.ActionRole)
-        QtCore.QObject.connect(self.button_box, QtCore.SIGNAL(u'clicked(QAbstractButton*)'), self.onPreview)
+        self.preview_button = QtGui.QPushButton()
+        self.preview_button.setObjectName(u'preview_button')
+        self.preview_button.setText(UiStrings().SaveAndPreview)
+        self.button_box.addButton(self.preview_button, QtGui.QDialogButtonBox.ActionRole)
+        self.button_box.clicked.connect(self.on_preview)
         # Create other objects and forms
         self.manager = manager
-        self.verseForm = EditVerseForm(self)
-        self.mediaForm = MediaFilesForm(self)
+        self.verse_form = EditVerseForm(self)
+        self.media_form = MediaFilesForm(self)
         self.initialise()
-        self.authorsListView.setSortingEnabled(False)
-        self.authorsListView.setAlternatingRowColors(True)
-        self.topicsListView.setSortingEnabled(False)
-        self.topicsListView.setAlternatingRowColors(True)
-        self.audioListWidget.setAlternatingRowColors(True)
-        self.findVerseSplit = re.compile(u'---\[\]---\n', re.UNICODE)
+        self.authors_list_view.setSortingEnabled(False)
+        self.authors_list_view.setAlternatingRowColors(True)
+        self.topics_list_view.setSortingEnabled(False)
+        self.topics_list_view.setAlternatingRowColors(True)
+        self.audio_list_widget.setAlternatingRowColors(True)
+        self.find_verse_split = re.compile(u'---\[\]---\n', re.UNICODE)
         self.whitespace = re.compile(r'\W+', re.UNICODE)
 
+    def _load_objects(self, cls, combo, cache):
+        """
+        Generically load a set of objects into a cache and a combobox.
+        """
+        objects = self.manager.get_all_objects(cls, order_by_ref=cls.name)
+        combo.clear()
+        combo.addItem(u'')
+        for obj in objects:
+            row = combo.count()
+            combo.addItem(obj.name)
+            cache.append(obj.name)
+            combo.setItemData(row, obj.id)
+        set_case_insensitive_completer(cache, combo)
+
+    def _add_author_to_list(self, author):
+        """
+        Add an author to the author list.
+        """
+        author_item = QtGui.QListWidgetItem(unicode(author.display_name))
+        author_item.setData(QtCore.Qt.UserRole, author.id)
+        self.authors_list_view.addItem(author_item)
+
+    def _extract_verse_order(self, verse_order):
+        order = []
+        order_names = unicode(verse_order).split()
+        for item in order_names:
+            if len(item) == 1:
+                verse_index = VerseType.from_translated_tag(item, None)
+                if verse_index is not None:
+                    order.append(VerseType.tags[verse_index] + u'1')
+                else:
+                    # it matches no verses anyway
+                    order.append(u'')
+            else:
+                verse_index = VerseType.from_translated_tag(item[0], None)
+                if verse_index is None:
+                    # it matches no verses anyway
+                    order.append(u'')
+                else:
+                    verse_tag = VerseType.tags[verse_index]
+                    verse_num = item[1:].lower()
+                    order.append(verse_tag + verse_num)
+        return order
+
+    def _validate_verse_list(self, verse_order, verse_count):
+        verses = []
+        invalid_verses = []
+        verse_names = []
+        order_names = unicode(verse_order).split()
+        order = self._extract_verse_order(verse_order)
+        for index in range(verse_count):
+            verse = self.verse_list_widget.item(index, 0)
+            verse = verse.data(QtCore.Qt.UserRole)
+            if verse not in verse_names:
+                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:
+                invalid_verses.append(order_names[count])
+        if invalid_verses:
+            valid = create_separated_list(verse_names)
+            if len(invalid_verses) > 1:
+                critical_error_message_box(message=translate('SongsPlugin.EditSongForm',
+                    'The verse order is invalid. There are no verses corresponding to %s. Valid entries are %s.') %
+                    (u', '.join(invalid_verses), valid))
+            else:
+                critical_error_message_box(message=translate('SongsPlugin.EditSongForm',
+                    'The verse order is invalid. There is no verse corresponding to %s. Valid entries are %s.') %
+                    (invalid_verses[0], valid))
+        return len(invalid_verses) == 0
+
+    def _validate_song(self):
+        """
+        Check the validity of the song.
+        """
+        # This checks data in the form *not* self.song. self.song is still
+        # None at this point.
+        log.debug(u'Validate Song')
+        # Lets be nice and assume the data is correct.
+        if not self.title_edit.text():
+            self.song_tab_widget.setCurrentIndex(0)
+            self.title_edit.setFocus()
+            critical_error_message_box(
+                message=translate('SongsPlugin.EditSongForm', 'You need to type in a song title.'))
+            return False
+        if self.verse_list_widget.rowCount() == 0:
+            self.song_tab_widget.setCurrentIndex(0)
+            self.verse_list_widget.setFocus()
+            critical_error_message_box(
+                message=translate('SongsPlugin.EditSongForm', 'You need to type in at least one verse.'))
+            return False
+        if self.authors_list_view.count() == 0:
+            self.song_tab_widget.setCurrentIndex(1)
+            self.authors_list_view.setFocus()
+            critical_error_message_box(
+                message=translate('SongsPlugin.EditSongForm', 'You need to have an author for this song.'))
+            return False
+        if self.verse_order_edit.text():
+            result = self._validate_verse_list(self.verse_order_edit.text(), self.verse_list_widget.rowCount())
+            if not result:
+                return False
+        text = self.song_book_combo_box.currentText()
+        if self.song_book_combo_box.findText(text, QtCore.Qt.MatchExactly) < 0:
+            if QtGui.QMessageBox.question(self, translate('SongsPlugin.EditSongForm', 'Add Book'),
+                    translate('SongsPlugin.EditSongForm', 'This song book does not exist, do you want to add it?'),
+                    QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes:
+                book = Book.populate(name=text, publisher=u'')
+                self.manager.save_object(book)
+            else:
+                return False
+        return True
+
+    def _process_lyrics(self):
+        """
+        Process the lyric data entered by the user into the OpenLP XML format.
+        """
+        # This method must only be run after the self.song = Song() assignment.
+        log.debug(u'_processLyrics')
+        sxml = None
+        try:
+            sxml = SongXML()
+            multiple = []
+            for i in range(self.verse_list_widget.rowCount()):
+                item = self.verse_list_widget.item(i, 0)
+                verse_id = item.data(QtCore.Qt.UserRole)
+                verse_tag = verse_id[0]
+                verse_num = verse_id[1:]
+                sxml.add_verse_to_lyrics(verse_tag, verse_num, item.text())
+                if verse_num > u'1' and verse_tag not in multiple:
+                    multiple.append(verse_tag)
+            self.song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
+            for verse in multiple:
+                self.song.verse_order = re.sub(u'([' + verse.upper() + verse.lower() + u'])(\W|$)',
+                    r'\g<1>1\2', self.song.verse_order)
+        except:
+            log.exception(u'Problem processing song Lyrics \n%s', sxml.dump_xml())
+            raise
+
     def keyPressEvent(self, event):
         """
         Reimplement the keyPressEvent to react on Return/Enter keys. When some combo boxes have focus we do not want
@@ -129,7 +260,7 @@
                 self.onAuthorAddButtonClicked()
                 return
             if self.topicsComboBox.hasFocus() and self.topicsComboBox.currentText():
-                self.onTopicAddButtonClicked()
+                self.on_topic_add_button_clicked()
                 return
         QtGui.QDialog.keyPressEvent(self, event)
 
@@ -137,110 +268,95 @@
         """
         Set up the form for when it is displayed.
         """
-        self.verseEditButton.setEnabled(False)
-        self.verseDeleteButton.setEnabled(False)
-        self.authorRemoveButton.setEnabled(False)
-        self.topicRemoveButton.setEnabled(False)
+        self.verse_edit_button.setEnabled(False)
+        self.verse_delete_button.setEnabled(False)
+        self.author_remove_button.setEnabled(False)
+        self.topic_remove_button.setEnabled(False)
 
-    def loadAuthors(self):
+    def load_authors(self):
         """
         Load the authors from the database into the combobox.
         """
-        authors = self.manager.get_all_objects(Author,
-            order_by_ref=Author.display_name)
-        self.authorsComboBox.clear()
-        self.authorsComboBox.addItem(u'')
+        authors = self.manager.get_all_objects(Author, order_by_ref=Author.display_name)
+        self.authors_combo_box.clear()
+        self.authors_combo_box.addItem(u'')
         self.authors = []
         for author in authors:
-            row = self.authorsComboBox.count()
-            self.authorsComboBox.addItem(author.display_name)
-            self.authorsComboBox.setItemData(row, author.id)
+            row = self.authors_combo_box.count()
+            self.authors_combo_box.addItem(author.display_name)
+            self.authors_combo_box.setItemData(row, author.id)
             self.authors.append(author.display_name)
-        set_case_insensitive_completer(self.authors, self.authorsComboBox)
+        set_case_insensitive_completer(self.authors, self.authors_combo_box)
 
-    def loadTopics(self):
+    def load_topics(self):
         """
         Load the topics into the combobox.
         """
         self.topics = []
-        self.__loadObjects(Topic, self.topicsComboBox, self.topics)
+        self._load_objects(Topic, self.topicsComboBox, self.topics)
 
-    def loadBooks(self):
+    def load_books(self):
         """
         Load the song books into the combobox
         """
         self.books = []
-        self.__loadObjects(Book, self.songBookComboBox, self.books)
-
-    def __loadObjects(self, cls, combo, cache):
-        """
-        Generically load a set of objects into a cache and a combobox.
-        """
-        objects = self.manager.get_all_objects(cls, order_by_ref=cls.name)
-        combo.clear()
-        combo.addItem(u'')
-        for object in objects:
-            row = combo.count()
-            combo.addItem(object.name)
-            cache.append(object.name)
-            combo.setItemData(row, object.id)
-        set_case_insensitive_completer(cache, combo)
+        self._load_objects(Book, self.song_book_combo_box, self.books)
 
     def load_themes(self, theme_list):
         """
         Load the themes into a combobox.
         """
-        self.themeComboBox.clear()
-        self.themeComboBox.addItem(u'')
+        self.theme_combo_box.clear()
+        self.theme_combo_box.addItem(u'')
         self.themes = theme_list
-        self.themeComboBox.addItems(theme_list)
-        set_case_insensitive_completer(self.themes, self.themeComboBox)
+        self.theme_combo_box.addItems(theme_list)
+        set_case_insensitive_completer(self.themes, self.theme_combo_box)
 
-    def loadMediaFiles(self):
+    def load_media_files(self):
         """
         Load the media files into a combobox.
         """
-        self.audioAddFromMediaButton.setVisible(False)
+        self.from_media_button.setVisible(False)
         for plugin in self.plugin_manager.plugins:
             if plugin.name == u'media' and plugin.status == PluginStatus.Active:
-                self.audioAddFromMediaButton.setVisible(True)
-                self.mediaForm.populateFiles(plugin.mediaItem.getList(MediaType.Audio))
+                self.from_media_button.setVisible(True)
+                self.media_form.populateFiles(plugin.mediaItem.getList(MediaType.Audio))
                 break
 
-    def newSong(self):
+    def new_song(self):
         """
         Blank the edit form out in preparation for a new song.
         """
         log.debug(u'New Song')
         self.song = None
         self.initialise()
-        self.songTabWidget.setCurrentIndex(0)
-        self.titleEdit.clear()
-        self.alternativeEdit.clear()
-        self.copyrightEdit.clear()
-        self.verseOrderEdit.clear()
-        self.commentsEdit.clear()
-        self.CCLNumberEdit.clear()
-        self.verseListWidget.clear()
-        self.verseListWidget.setRowCount(0)
-        self.authorsListView.clear()
-        self.topicsListView.clear()
-        self.audioListWidget.clear()
-        self.titleEdit.setFocus()
-        self.songBookNumberEdit.clear()
-        self.loadAuthors()
-        self.loadTopics()
-        self.loadBooks()
-        self.loadMediaFiles()
-        self.themeComboBox.setCurrentIndex(0)
+        self.song_tab_widget.setCurrentIndex(0)
+        self.title_edit.clear()
+        self.alternative_edit.clear()
+        self.copyright_edit.clear()
+        self.verse_order_edit.clear()
+        self.comments_edit.clear()
+        self.ccli_number_edit.clear()
+        self.verse_list_widget.clear()
+        self.verse_list_widget.setRowCount(0)
+        self.authors_list_view.clear()
+        self.topics_list_view.clear()
+        self.audio_list_widget.clear()
+        self.title_edit.setFocus()
+        self.song_book_number_edit.clear()
+        self.load_authors()
+        self.load_topics()
+        self.load_books()
+        self.load_media_files()
+        self.theme_combo_box.setCurrentIndex(0)
         # it's a new song to preview is not possible
-        self.previewButton.setVisible(False)
+        self.preview_button.setVisible(False)
 
-    def loadSong(self, id, preview=False):
+    def load_song(self, song_id, preview=False):
         """
         Loads a song.
 
-        ``id``
+        ``song_id``
             The song id (int).
 
         ``preview``
@@ -248,27 +364,27 @@
         """
         log.debug(u'Load Song')
         self.initialise()
-        self.songTabWidget.setCurrentIndex(0)
-        self.loadAuthors()
-        self.loadTopics()
-        self.loadBooks()
-        self.loadMediaFiles()
-        self.song = self.manager.get_object(Song, id)
-        self.titleEdit.setText(self.song.title)
-        self.alternativeEdit.setText(
+        self.song_tab_widget.setCurrentIndex(0)
+        self.load_authors()
+        self.load_topics()
+        self.load_books()
+        self.load_media_files()
+        self.song = self.manager.get_object(Song, song_id)
+        self.title_edit.setText(self.song.title)
+        self.alternative_edit.setText(
             self.song.alternate_title if self.song.alternate_title else u'')
         if self.song.song_book_id != 0:
             book_name = self.manager.get_object(Book, self.song.song_book_id)
-            find_and_set_in_combo_box(self.songBookComboBox, unicode(book_name.name))
+            find_and_set_in_combo_box(self.song_book_combo_box, unicode(book_name.name))
         if self.song.theme_name:
-            find_and_set_in_combo_box(self.themeComboBox, unicode(self.song.theme_name))
-        self.copyrightEdit.setText(self.song.copyright if self.song.copyright else u'')
-        self.commentsEdit.setPlainText(self.song.comments if self.song.comments else u'')
-        self.CCLNumberEdit.setText(self.song.ccli_number if self.song.ccli_number else u'')
-        self.songBookNumberEdit.setText(self.song.song_number if self.song.song_number else u'')
+            find_and_set_in_combo_box(self.theme_combo_box, unicode(self.song.theme_name))
+        self.copyright_edit.setText(self.song.copyright if self.song.copyright else u'')
+        self.comments_edit.setPlainText(self.song.comments if self.song.comments else u'')
+        self.ccli_number_edit.setText(self.song.ccli_number if self.song.ccli_number else u'')
+        self.song_book_number_edit.setText(self.song.song_number if self.song.song_number else u'')
         # lazy xml migration for now
-        self.verseListWidget.clear()
-        self.verseListWidget.setRowCount(0)
+        self.verse_list_widget.clear()
+        self.verse_list_widget.setRowCount(0)
         # 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)
@@ -277,8 +393,7 @@
             songXML = SongXML()
             verse_list = songXML.get_verses(self.song.lyrics)
             for count, verse in enumerate(verse_list):
-                self.verseListWidget.setRowCount(
-                    self.verseListWidget.rowCount() + 1)
+                self.verse_list_widget.setRowCount(self.verse_list_widget.rowCount() + 1)
                 # This silently migrates from localized verse type markup.
                 # If we trusted the database, this would be unnecessary.
                 verse_tag = verse[0][u'type']
@@ -297,15 +412,15 @@
                 verse_def = u'%s%s' % (verse[0][u'type'], verse[0][u'label'])
                 item = QtGui.QTableWidgetItem(verse[1])
                 item.setData(QtCore.Qt.UserRole, verse_def)
-                self.verseListWidget.setItem(count, 0, item)
+                self.verse_list_widget.setItem(count, 0, item)
         else:
             verses = self.song.lyrics.split(u'\n\n')
             for count, verse in enumerate(verses):
-                self.verseListWidget.setRowCount(self.verseListWidget.rowCount() + 1)
+                self.verse_list_widget.setRowCount(self.verse_list_widget.rowCount() + 1)
                 item = QtGui.QTableWidgetItem(verse)
                 verse_def = u'%s%s' % (VerseType.tags[VerseType.Verse], unicode(count + 1))
                 item.setData(QtCore.Qt.UserRole, verse_def)
-                self.verseListWidget.setItem(count, 0, item)
+                self.verse_list_widget.setItem(count, 0, item)
         if self.song.verse_order:
             # we translate verse order
             translated = []
@@ -317,52 +432,52 @@
                     verse_index = VerseType.from_tag(verse_def[0])
                 verse_tag = VerseType.translated_tags[verse_index].upper()
                 translated.append(u'%s%s' % (verse_tag, verse_def[1:]))
-            self.verseOrderEdit.setText(u' '.join(translated))
+            self.verse_order_edit.setText(u' '.join(translated))
         else:
-            self.verseOrderEdit.setText(u'')
-        self.tagRows()
+            self.verse_order_edit.setText(u'')
+        self.tag_rows()
         # clear the results
-        self.authorsListView.clear()
+        self.authors_list_view.clear()
         for author in self.song.authors:
             author_name = QtGui.QListWidgetItem(unicode(author.display_name))
             author_name.setData(QtCore.Qt.UserRole, author.id)
-            self.authorsListView.addItem(author_name)
+            self.authors_list_view.addItem(author_name)
         # clear the results
-        self.topicsListView.clear()
+        self.topics_list_view.clear()
         for topic in self.song.topics:
             topic_name = QtGui.QListWidgetItem(unicode(topic.name))
             topic_name.setData(QtCore.Qt.UserRole, topic.id)
-            self.topicsListView.addItem(topic_name)
-        self.audioListWidget.clear()
+            self.topics_list_view.addItem(topic_name)
+        self.audio_list_widget.clear()
         for media in self.song.media_files:
             media_file = QtGui.QListWidgetItem(os.path.split(media.file_name)[1])
             media_file.setData(QtCore.Qt.UserRole, media.file_name)
-            self.audioListWidget.addItem(media_file)
-        self.titleEdit.setFocus()
+            self.audio_list_widget.addItem(media_file)
+        self.title_edit.setFocus()
         # Hide or show the preview button.
-        self.previewButton.setVisible(preview)
+        self.preview_button.setVisible(preview)
 
-    def tagRows(self):
+    def tag_rows(self):
         """
         Tag the Song List rows based on the verse list
         """
         row_label = []
-        for row in range(self.verseListWidget.rowCount()):
-            item = self.verseListWidget.item(row, 0)
+        for row in range(self.verse_list_widget.rowCount()):
+            item = self.verse_list_widget.item(row, 0)
             verse_def = item.data(QtCore.Qt.UserRole)
             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)
-        self.verseListWidget.resizeRowsToContents()
-        self.verseListWidget.repaint()
+        self.verse_list_widget.setVerticalHeaderLabels(row_label)
+        self.verse_list_widget.resizeRowsToContents()
+        self.verse_list_widget.repaint()
 
-    def onAuthorAddButtonClicked(self):
+    def on_author_add_button_clicked(self):
         """
         Add the author to the list of authors associated with this song when the button is clicked.
         """
-        item = int(self.authorsComboBox.currentIndex())
-        text = self.authorsComboBox.currentText().strip(u' \r\n\t')
+        item = int(self.authors_combo_box.currentIndex())
+        text = self.authors_combo_box.currentText().strip(u' \r\n\t')
         # This if statement is for OS X, which doesn't seem to work well with
         # the QCompleter autocompletion class. See bug #812628.
         if text in self.authors:
@@ -379,52 +494,44 @@
                     author = Author.populate(first_name=text.rsplit(u' ', 1)[0],
                         last_name=text.rsplit(u' ', 1)[1], display_name=text)
                 self.manager.save_object(author)
-                self.__addAuthorToList(author)
-                self.loadAuthors()
-                self.authorsComboBox.setCurrentIndex(0)
+                self._add_author_to_list(author)
+                self.load_authors()
+                self.authors_combo_box.setCurrentIndex(0)
             else:
                 return
         elif item > 0:
-            item_id = (self.authorsComboBox.itemData(item))
+            item_id = (self.authors_combo_box.itemData(item))
             author = self.manager.get_object(Author, item_id)
-            if self.authorsListView.findItems(unicode(author.display_name),
+            if self.authors_list_view.findItems(unicode(author.display_name),
                 QtCore.Qt.MatchExactly):
                 critical_error_message_box(
                     message=translate('SongsPlugin.EditSongForm', 'This author is already in the list.'))
             else:
-                self.__addAuthorToList(author)
-            self.authorsComboBox.setCurrentIndex(0)
+                self._add_author_to_list(author)
+            self.authors_combo_box.setCurrentIndex(0)
         else:
             QtGui.QMessageBox.warning(self, UiStrings().NISs,
                 translate('SongsPlugin.EditSongForm', 'You have not selected a valid author. Either select an author '
                     'from the list, or type in a new author and click the "Add Author to Song" button to add '
                     'the new author.'))
 
-    def __addAuthorToList(self, author):
-        """
-        Add an author to the author list.
-        """
-        author_item = QtGui.QListWidgetItem(unicode(author.display_name))
-        author_item.setData(QtCore.Qt.UserRole, author.id)
-        self.authorsListView.addItem(author_item)
-
-    def onAuthorsListViewClicked(self):
+    def on_authors_list_view_clicked(self):
         """
         Run a set of actions when an author in the list is selected (mainly enable the delete button).
         """
-        if self.authorsListView.count() > 1:
-            self.authorRemoveButton.setEnabled(True)
+        if self.authors_list_view.count() > 1:
+            self.author_remove_button.setEnabled(True)
 
-    def onAuthorRemoveButtonClicked(self):
+    def on_author_remove_button_clicked(self):
         """
         Remove the author from the list when the delete button is clicked.
         """
-        self.authorRemoveButton.setEnabled(False)
-        item = self.authorsListView.currentItem()
-        row = self.authorsListView.row(item)
-        self.authorsListView.takeItem(row)
+        self.author_remove_button.setEnabled(False)
+        item = self.authors_list_view.currentItem()
+        row = self.authors_list_view.row(item)
+        self.authors_list_view.takeItem(row)
 
-    def onTopicAddButtonClicked(self):
+    def on_topic_add_button_clicked(self):
         item = int(self.topicsComboBox.currentIndex())
         text = self.topicsComboBox.currentText()
         if item == 0 and text:
@@ -435,63 +542,63 @@
                 self.manager.save_object(topic)
                 topic_item = QtGui.QListWidgetItem(unicode(topic.name))
                 topic_item.setData(QtCore.Qt.UserRole, topic.id)
-                self.topicsListView.addItem(topic_item)
-                self.loadTopics()
+                self.topics_list_view.addItem(topic_item)
+                self.load_topics()
                 self.topicsComboBox.setCurrentIndex(0)
             else:
                 return
         elif item > 0:
             item_id = (self.topicsComboBox.itemData(item))
             topic = self.manager.get_object(Topic, item_id)
-            if self.topicsListView.findItems(unicode(topic.name),
+            if self.topics_list_view.findItems(unicode(topic.name),
                 QtCore.Qt.MatchExactly):
                 critical_error_message_box(
                     message=translate('SongsPlugin.EditSongForm', 'This topic is already in the list.'))
             else:
                 topic_item = QtGui.QListWidgetItem(unicode(topic.name))
                 topic_item.setData(QtCore.Qt.UserRole, topic.id)
-                self.topicsListView.addItem(topic_item)
+                self.topics_list_view.addItem(topic_item)
             self.topicsComboBox.setCurrentIndex(0)
         else:
             QtGui.QMessageBox.warning(self, UiStrings().NISs,
                 translate('SongsPlugin.EditSongForm', 'You have not selected a valid topic. Either select a topic '
                 'from the list, or type in a new topic and click the "Add Topic to Song" button to add the new topic.'))
 
-    def onTopicListViewClicked(self):
-        self.topicRemoveButton.setEnabled(True)
-
-    def onTopicRemoveButtonClicked(self):
-        self.topicRemoveButton.setEnabled(False)
-        item = self.topicsListView.currentItem()
-        row = self.topicsListView.row(item)
-        self.topicsListView.takeItem(row)
-
-    def onVerseListViewClicked(self):
-        self.verseEditButton.setEnabled(True)
-        self.verseDeleteButton.setEnabled(True)
-
-    def onVerseAddButtonClicked(self):
-        self.verseForm.setVerse(u'', True)
-        if self.verseForm.exec_():
-            after_text, verse_tag, verse_num = self.verseForm.getVerse()
+    def on_topic_list_view_clicked(self):
+        self.topic_remove_button.setEnabled(True)
+
+    def on_topic_remove_button_clicked(self):
+        self.topic_remove_button.setEnabled(False)
+        item = self.topics_list_view.currentItem()
+        row = self.topics_list_view.row(item)
+        self.topics_list_view.takeItem(row)
+
+    def on_verse_list_view_clicked(self):
+        self.verse_edit_button.setEnabled(True)
+        self.verse_delete_button.setEnabled(True)
+
+    def on_verse_add_button_clicked(self):
+        self.verse_form.setVerse(u'', True)
+        if self.verse_form.exec_():
+            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, verse_def)
             item.setText(after_text)
-            self.verseListWidget.setRowCount(self.verseListWidget.rowCount() + 1)
-            self.verseListWidget.setItem(self.verseListWidget.rowCount() - 1, 0, item)
-        self.tagRows()
+            self.verse_list_widget.setRowCount(self.verse_list_widget.rowCount() + 1)
+            self.verse_list_widget.setItem(self.verse_list_widget.rowCount() - 1, 0, item)
+        self.tag_rows()
         # Check if all verse tags are used.
-        self.onVerseOrderTextChanged(self.verseOrderEdit.text())
+        self.on_verse_order_text_changed(self.verse_order_edit.text())
 
-    def onVerseEditButtonClicked(self):
-        item = self.verseListWidget.currentItem()
+    def on_verse_edit_button_clicked(self):
+        item = self.verse_list_widget.currentItem()
         if item:
             temp_text = item.text()
             verse_id = item.data(QtCore.Qt.UserRole)
-            self.verseForm.setVerse(temp_text, True, verse_id)
-            if self.verseForm.exec_():
-                after_text, verse_tag, verse_num = self.verseForm.getVerse()
+            self.verse_form.setVerse(temp_text, True, verse_id)
+            if self.verse_form.exec_():
+                after_text, verse_tag, verse_num = self.verse_form.getVerse()
                 verse_def = u'%s%s' % (verse_tag, verse_num)
                 item.setData(QtCore.Qt.UserRole, verse_def)
                 item.setText(after_text)
@@ -499,40 +606,40 @@
                 if len(temp_text.split(u'\n')) != len(after_text.split(u'\n')):
                     temp_list = []
                     temp_ids = []
-                    for row in range(self.verseListWidget.rowCount()):
-                        item = self.verseListWidget.item(row, 0)
+                    for row in range(self.verse_list_widget.rowCount()):
+                        item = self.verse_list_widget.item(row, 0)
                         temp_list.append(item.text())
                         temp_ids.append(item.data(QtCore.Qt.UserRole))
-                    self.verseListWidget.clear()
+                    self.verse_list_widget.clear()
                     for row, entry in enumerate(temp_list):
                         item = QtGui.QTableWidgetItem(entry, 0)
                         item.setData(QtCore.Qt.UserRole, temp_ids[row])
-                        self.verseListWidget.setItem(row, 0, item)
-        self.tagRows()
+                        self.verse_list_widget.setItem(row, 0, item)
+        self.tag_rows()
         # Check if all verse tags are used.
-        self.onVerseOrderTextChanged(self.verseOrderEdit.text())
+        self.on_verse_order_text_changed(self.verse_order_edit.text())
 
-    def onVerseEditAllButtonClicked(self):
+    def on_verse_edit_all_button_clicked(self):
         verse_list = u''
-        if self.verseListWidget.rowCount() > 0:
-            for row in range(self.verseListWidget.rowCount()):
-                item = self.verseListWidget.item(row, 0)
+        if self.verse_list_widget.rowCount() > 0:
+            for row in range(self.verse_list_widget.rowCount()):
+                item = self.verse_list_widget.item(row, 0)
                 field = item.data(QtCore.Qt.UserRole)
                 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.verseForm.setVerse(verse_list)
+            self.verse_form.setVerse(verse_list)
         else:
-            self.verseForm.setVerse(u'')
-        if not self.verseForm.exec_():
+            self.verse_form.setVerse(u'')
+        if not self.verse_form.exec_():
             return
-        verse_list = self.verseForm.getVerseAll()
+        verse_list = self.verse_form.getVerseAll()
         verse_list = unicode(verse_list.replace(u'\r\n', u'\n'))
-        self.verseListWidget.clear()
-        self.verseListWidget.setRowCount(0)
-        for row in self.findVerseSplit.split(verse_list):
+        self.verse_list_widget.clear()
+        self.verse_list_widget.setRowCount(0)
+        for row in self.find_verse_split.split(verse_list):
             for match in row.split(u'---['):
                 for count, parts in enumerate(match.split(u']---\n')):
                     if count == 0:
@@ -562,26 +669,26 @@
                             parts = parts.rstrip(u'\n')
                         item = QtGui.QTableWidgetItem(parts)
                         item.setData(QtCore.Qt.UserRole, verse_def)
-                        self.verseListWidget.setRowCount(self.verseListWidget.rowCount() + 1)
-                        self.verseListWidget.setItem(self.verseListWidget.rowCount() - 1, 0, item)
-        self.tagRows()
-        self.verseEditButton.setEnabled(False)
-        self.verseDeleteButton.setEnabled(False)
+                        self.verse_list_widget.setRowCount(self.verse_list_widget.rowCount() + 1)
+                        self.verse_list_widget.setItem(self.verse_list_widget.rowCount() - 1, 0, item)
+        self.tag_rows()
+        self.verse_edit_button.setEnabled(False)
+        self.verse_delete_button.setEnabled(False)
         # Check if all verse tags are used.
-        self.onVerseOrderTextChanged(self.verseOrderEdit.text())
-
-    def onVerseDeleteButtonClicked(self):
-        self.verseListWidget.removeRow(self.verseListWidget.currentRow())
-        if not self.verseListWidget.selectedItems():
-            self.verseEditButton.setEnabled(False)
-            self.verseDeleteButton.setEnabled(False)
-
-    def onVerseOrderTextChanged(self, text):
+        self.on_verse_order_text_changed(self.verse_order_edit.text())
+
+    def on_verse_delete_button_clicked(self):
+        self.verse_list_widget.removeRow(self.verse_list_widget.currentRow())
+        if not self.verse_list_widget.selectedItems():
+            self.verse_edit_button.setEnabled(False)
+            self.verse_delete_button.setEnabled(False)
+
+    def on_verse_order_text_changed(self, text):
         verses = []
         verse_names = []
-        order = self.__extractVerseOrder(text)
-        for index in range(self.verseListWidget.rowCount()):
-            verse = self.verseListWidget.item(index, 0)
+        order = self._extract_verse_order(text)
+        for index in range(self.verse_list_widget.rowCount()):
+            verse = self.verse_list_widget.item(index, 0)
             verse = verse.data(QtCore.Qt.UserRole)
             if verse not in verse_names:
                 verses.append(verse)
@@ -590,122 +697,31 @@
         for verse in verses:
             if not verse in order:
                 verses_not_used.append(verse)
-        self.warningLabel.setVisible(len(verses_not_used) > 0)
-
-    def __extractVerseOrder(self, verse_order):
-        order = []
-        order_names = unicode(verse_order).split()
-        for item in order_names:
-            if len(item) == 1:
-                verse_index = VerseType.from_translated_tag(item, None)
-                if verse_index is not None:
-                    order.append(VerseType.tags[verse_index] + u'1')
-                else:
-                    # it matches no verses anyway
-                    order.append(u'')
-            else:
-                verse_index = VerseType.from_translated_tag(item[0], None)
-                if verse_index is None:
-                    # it matches no verses anyway
-                    order.append(u'')
-                else:
-                    verse_tag = VerseType.tags[verse_index]
-                    verse_num = item[1:].lower()
-                    order.append(verse_tag + verse_num)
-        return order
-
-    def __validateVerseList(self, verse_order, verse_count):
-        verses = []
-        invalid_verses = []
-        verse_names = []
-        order_names = unicode(verse_order).split()
-        order = self.__extractVerseOrder(verse_order)
-        for index in range(verse_count):
-            verse = self.verseListWidget.item(index, 0)
-            verse = verse.data(QtCore.Qt.UserRole)
-            if verse not in verse_names:
-                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:
-                invalid_verses.append(order_names[count])
-        if invalid_verses:
-            valid = create_separated_list(verse_names)
-            if len(invalid_verses) > 1:
-                critical_error_message_box(message=translate('SongsPlugin.EditSongForm',
-                    'The verse order is invalid. There are no verses corresponding to %s. Valid entries are %s.') %
-                    (u', '.join(invalid_verses), valid))
-            else:
-                critical_error_message_box(message=translate('SongsPlugin.EditSongForm',
-                    'The verse order is invalid. There is no verse corresponding to %s. Valid entries are %s.') %
-                    (invalid_verses[0], valid))
-        return len(invalid_verses) == 0
-
-    def __validateSong(self):
-        """
-        Check the validity of the song.
-        """
-        # This checks data in the form *not* self.song. self.song is still
-        # None at this point.
-        log.debug(u'Validate Song')
-        # Lets be nice and assume the data is correct.
-        if not self.titleEdit.text():
-            self.songTabWidget.setCurrentIndex(0)
-            self.titleEdit.setFocus()
-            critical_error_message_box(
-                message=translate('SongsPlugin.EditSongForm', 'You need to type in a song title.'))
-            return False
-        if self.verseListWidget.rowCount() == 0:
-            self.songTabWidget.setCurrentIndex(0)
-            self.verseListWidget.setFocus()
-            critical_error_message_box(
-                message=translate('SongsPlugin.EditSongForm', 'You need to type in at least one verse.'))
-            return False
-        if self.authorsListView.count() == 0:
-            self.songTabWidget.setCurrentIndex(1)
-            self.authorsListView.setFocus()
-            critical_error_message_box(
-                message=translate('SongsPlugin.EditSongForm', 'You need to have an author for this song.'))
-            return False
-        if self.verseOrderEdit.text():
-            result = self.__validateVerseList(self.verseOrderEdit.text(),
-                self.verseListWidget.rowCount())
-            if not result:
-                return False
-        text = self.songBookComboBox.currentText()
-        if self.songBookComboBox.findText(text, QtCore.Qt.MatchExactly) < 0:
-            if QtGui.QMessageBox.question(self, translate('SongsPlugin.EditSongForm', 'Add Book'),
-                    translate('SongsPlugin.EditSongForm', 'This song book does not exist, do you want to add it?'),
-                    QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes:
-                book = Book.populate(name=text, publisher=u'')
-                self.manager.save_object(book)
-            else:
-                return False
-        return True
-
-    def onCopyrightInsertButtonTriggered(self):
-        text = self.copyrightEdit.text()
-        pos = self.copyrightEdit.cursorPosition()
+        self.warning_label.setVisible(len(verses_not_used) > 0)
+
+    def on_copyright_insert_button_triggered(self):
+        text = self.copyright_edit.text()
+        pos = self.copyright_edit.cursorPosition()
         sign = SongStrings.CopyrightSymbol
         text = text[:pos] + sign + text[pos:]
-        self.copyrightEdit.setText(text)
-        self.copyrightEdit.setFocus()
-        self.copyrightEdit.setCursorPosition(pos + len(sign))
+        self.copyright_edit.setText(text)
+        self.copyright_edit.setFocus()
+        self.copyright_edit.setCursorPosition(pos + len(sign))
 
-    def onMaintenanceButtonClicked(self):
+    def on_maintenance_button_clicked(self):
         temp_song_book = None
-        item = int(self.songBookComboBox.currentIndex())
-        text = self.songBookComboBox.currentText()
+        item = int(self.song_book_combo_box.currentIndex())
+        text = self.song_book_combo_box.currentText()
         if item == 0 and text:
             temp_song_book = text
         self.mediaitem.songMaintenanceForm.exec_(True)
-        self.loadAuthors()
-        self.loadBooks()
-        self.loadTopics()
+        self.load_authors()
+        self.load_books()
+        self.load_topics()
         if temp_song_book:
-            self.songBookComboBox.setEditText(temp_song_book)
+            self.song_book_combo_box.setEditText(temp_song_book)
 
-    def onPreview(self, button):
+    def on_preview(self, button):
         """
         Save and Preview button clicked.
         The Song is valid so as the plugin to add it to preview to see.
@@ -714,11 +730,11 @@
             A button (QPushButton).
         """
         log.debug(u'onPreview')
-        if button.objectName() == u'previewButton':
-            self.saveSong(True)
+        if button.objectName() == u'preview_button':
+            self.save_song(True)
             Registry().execute(u'songs_preview')
 
-    def onAudioAddFromFileButtonClicked(self):
+    def on_audio_add_from_file_button_clicked(self):
         """
         Loads file(s) from the filesystem.
         """
@@ -728,56 +744,56 @@
         for filename in filenames:
             item = QtGui.QListWidgetItem(os.path.split(unicode(filename))[1])
             item.setData(QtCore.Qt.UserRole, filename)
-            self.audioListWidget.addItem(item)
+            self.audio_list_widget.addItem(item)
 
-    def onAudioAddFromMediaButtonClicked(self):
+    def on_audio_add_from_media_button_clicked(self):
         """
         Loads file(s) from the media plugin.
         """
-        if self.mediaForm.exec_():
-            for filename in self.mediaForm.getSelectedFiles():
+        if self.media_form.exec_():
+            for filename in self.media_form.getSelectedFiles():
                 item = QtGui.QListWidgetItem(os.path.split(unicode(filename))[1])
                 item.setData(QtCore.Qt.UserRole, filename)
-                self.audioListWidget.addItem(item)
+                self.audio_list_widget.addItem(item)
 
-    def onAudioRemoveButtonClicked(self):
+    def on_audio_remove_button_clicked(self):
         """
         Removes a file from the list.
         """
-        row = self.audioListWidget.currentRow()
+        row = self.audio_list_widget.currentRow()
         if row == -1:
             return
-        self.audioListWidget.takeItem(row)
+        self.audio_list_widget.takeItem(row)
 
-    def onAudioRemoveAllButtonClicked(self):
+    def on_audio_remove_all_button_clicked(self):
         """
         Removes all files from the list.
         """
-        self.audioListWidget.clear()
+        self.audio_list_widget.clear()
 
-    def onUpButtonClicked(self):
+    def on_up_button_clicked(self):
         """
         Moves a file up when the user clicks the up button on the audio tab.
         """
-        row = self.audioListWidget.currentRow()
+        row = self.audio_list_widget.currentRow()
         if row <= 0:
             return
-        item = self.audioListWidget.takeItem(row)
-        self.audioListWidget.insertItem(row - 1, item)
-        self.audioListWidget.setCurrentRow(row - 1)
+        item = self.audio_list_widget.takeItem(row)
+        self.audio_list_widget.insertItem(row - 1, item)
+        self.audio_list_widget.setCurrentRow(row - 1)
 
-    def onDownButtonClicked(self):
+    def on_down_button_clicked(self):
         """
         Moves a file down when the user clicks the up button on the audio tab.
         """
-        row = self.audioListWidget.currentRow()
-        if row == -1 or row > self.audioListWidget.count() - 1:
+        row = self.audio_list_widget.currentRow()
+        if row == -1 or row > self.audio_list_widget.count() - 1:
             return
-        item = self.audioListWidget.takeItem(row)
-        self.audioListWidget.insertItem(row + 1, item)
-        self.audioListWidget.setCurrentRow(row + 1)
+        item = self.audio_list_widget.takeItem(row)
+        self.audio_list_widget.insertItem(row + 1, item)
+        self.audio_list_widget.setCurrentRow(row + 1)
 
-    def clearCaches(self):
+    def clear_caches(self):
         """
         Free up autocompletion memory on dialog exit
         """
@@ -792,7 +808,7 @@
         Exit Dialog and do not save
         """
         log.debug (u'SongEditForm.reject')
-        self.clearCaches()
+        self.clear_caches()
         QtGui.QDialog.reject(self)
 
     def accept(self):
@@ -800,13 +816,13 @@
         Exit Dialog and save song if valid
         """
         log.debug(u'SongEditForm.accept')
-        self.clearCaches()
-        if self.__validateSong():
-            self.saveSong()
+        self.clear_caches()
+        if self._validate_song():
+            self.save_song()
             self.song = None
             QtGui.QDialog.accept(self)
 
-    def saveSong(self, preview=False):
+    def save_song(self, preview=False):
         """
         Get all the data from the widgets on the form, and then save it to the
         database. The form has been validated and all reference items
@@ -820,45 +836,45 @@
         # Song() is in a partially complete state.
         if not self.song:
             self.song = Song()
-        self.song.title = self.titleEdit.text()
-        self.song.alternate_title = self.alternativeEdit.text()
-        self.song.copyright = self.copyrightEdit.text()
+        self.song.title = self.title_edit.text()
+        self.song.alternate_title = self.alternative_edit.text()
+        self.song.copyright = self.copyright_edit.text()
         # Values will be set when cleaning the song.
         self.song.search_title = u''
         self.song.search_lyrics = u''
         self.song.verse_order = u''
-        self.song.comments = self.commentsEdit.toPlainText()
-        ordertext = self.verseOrderEdit.text()
+        self.song.comments = self.comments_edit.toPlainText()
+        ordertext = self.verse_order_edit.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 = self.CCLNumberEdit.text()
-        self.song.song_number = self.songBookNumberEdit.text()
-        book_name = self.songBookComboBox.currentText()
+        self.song.ccli_number = self.ccli_number_edit.text()
+        self.song.song_number = self.song_book_number_edit.text()
+        book_name = self.song_book_combo_box.currentText()
         if book_name:
             self.song.book = self.manager.get_object_filtered(Book,
                 Book.name == book_name)
         else:
             self.song.book = None
-        theme_name = self.themeComboBox.currentText()
+        theme_name = self.theme_combo_box.currentText()
         if theme_name:
             self.song.theme_name = theme_name
         else:
             self.song.theme_name = None
-        self._processLyrics()
+        self._process_lyrics()
         self.song.authors = []
-        for row in xrange(self.authorsListView.count()):
-            item = self.authorsListView.item(row)
+        for row in xrange(self.authors_list_view.count()):
+            item = self.authors_list_view.item(row)
             authorId = (item.data(QtCore.Qt.UserRole))
             author = self.manager.get_object(Author, authorId)
             if author is not None:
                 self.song.authors.append(author)
         self.song.topics = []
-        for row in xrange(self.topicsListView.count()):
-            item = self.topicsListView.item(row)
+        for row in xrange(self.topics_list_view.count()):
+            item = self.topics_list_view.item(row)
             topicId = (item.data(QtCore.Qt.UserRole))
             topic = self.manager.get_object(Topic, topicId)
             if topic is not None:
@@ -873,8 +889,8 @@
         check_directory_exists(save_path)
         self.song.media_files = []
         files = []
-        for row in xrange(self.audioListWidget.count()):
-            item = self.audioListWidget.item(row)
+        for row in xrange(self.audio_list_widget.count()):
+            item = self.audio_list_widget.item(row)
             filename = item.data(QtCore.Qt.UserRole)
             if not filename.startswith(save_path):
                 oldfile, filename = filename, os.path.join(save_path, os.path.split(filename)[1])
@@ -900,31 +916,6 @@
         self.manager.save_object(self.song)
         self.mediaitem.autoSelectId = self.song.id
 
-    def _processLyrics(self):
-        """
-        Process the lyric data entered by the user into the OpenLP XML format.
-        """
-        # This method must only be run after the self.song = Song() assignment.
-        log.debug(u'_processLyrics')
-        try:
-            sxml = SongXML()
-            multiple = []
-            for i in range(self.verseListWidget.rowCount()):
-                item = self.verseListWidget.item(i, 0)
-                verse_id = item.data(QtCore.Qt.UserRole)
-                verse_tag = verse_id[0]
-                verse_num = verse_id[1:]
-                sxml.add_verse_to_lyrics(verse_tag, verse_num, item.text())
-                if verse_num > u'1' and verse_tag not in multiple:
-                    multiple.append(verse_tag)
-            self.song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
-            for verse in multiple:
-                self.song.verse_order = re.sub(u'([' + verse.upper() + verse.lower() + u'])(\W|$)', r'\g<1>1\2',
-                    self.song.verse_order)
-        except:
-            log.exception(u'Problem processing song Lyrics \n%s', sxml.dump_xml())
-            raise
-
     def _get_plugin_manager(self):
         """
         Adds the plugin manager to the class dynamically
@@ -944,4 +935,3 @@
         return self._theme_manager
 
     theme_manager = property(_get_theme_manager)
-

=== modified file 'openlp/plugins/songs/forms/editverseform.py'
--- openlp/plugins/songs/forms/editverseform.py	2013-02-24 18:13:50 +0000
+++ openlp/plugins/songs/forms/editverseform.py	2013-03-07 21:58:22 +0000
@@ -33,13 +33,13 @@
 from PyQt4 import QtCore, QtGui
 
 from openlp.plugins.songs.lib import VerseType
-
 from editversedialog import Ui_EditVerseDialog
 
 log = logging.getLogger(__name__)
 
 VERSE_REGEX = re.compile(r'---\[(.+):\D*(\d*)\D*.*\]---')
 
+
 class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
     """
     This is the form that is used to edit the verses of the song.

=== modified file 'openlp/plugins/songs/forms/mediafilesform.py'
--- openlp/plugins/songs/forms/mediafilesform.py	2013-01-05 22:17:30 +0000
+++ openlp/plugins/songs/forms/mediafilesform.py	2013-03-07 21:58:22 +0000
@@ -36,6 +36,7 @@
 
 log = logging.getLogger(__name__)
 
+
 class MediaFilesForm(QtGui.QDialog, Ui_MediaFilesDialog):
     """
     Class to show a list of files from the

=== modified file 'openlp/plugins/songs/forms/songmaintenancedialog.py'
--- openlp/plugins/songs/forms/songmaintenancedialog.py	2013-02-05 08:05:28 +0000
+++ openlp/plugins/songs/forms/songmaintenancedialog.py	2013-03-07 21:58:22 +0000
@@ -57,7 +57,7 @@
         self.authorsPage = QtGui.QWidget(songMaintenanceDialog)
         self.authorsPage.setObjectName(u'authorsPage')
         self.authorsLayout = QtGui.QVBoxLayout(self.authorsPage)
-        self.authorsLayout.setObjectName(u'authorsLayout')
+        self.authorsLayout.setObjectName(u'authors_layout')
         self.authorsListWidget = QtGui.QListWidget(self.authorsPage)
         self.authorsListWidget.setObjectName(u'authorsListWidget')
         self.authorsLayout.addWidget(self.authorsListWidget)
@@ -82,7 +82,7 @@
         self.topicsPage = QtGui.QWidget(songMaintenanceDialog)
         self.topicsPage.setObjectName(u'topicsPage')
         self.topicsLayout = QtGui.QVBoxLayout(self.topicsPage)
-        self.topicsLayout.setObjectName(u'topicsLayout')
+        self.topicsLayout.setObjectName(u'topics_layout')
         self.topicsListWidget = QtGui.QListWidget(self.topicsPage)
         self.topicsListWidget.setObjectName(u'topicsListWidget')
         self.topicsLayout.addWidget(self.topicsListWidget)

=== modified file 'openlp/plugins/songs/forms/songmaintenanceform.py'
--- openlp/plugins/songs/forms/songmaintenanceform.py	2013-02-07 08:42:17 +0000
+++ openlp/plugins/songs/forms/songmaintenanceform.py	2013-03-07 21:58:22 +0000
@@ -33,7 +33,9 @@
 
 from openlp.core.lib import Registry, UiStrings, translate
 from openlp.core.lib.ui import critical_error_message_box
-from openlp.plugins.songs.forms import AuthorsForm, TopicsForm, SongBookForm
+from openlp.plugins.songs.forms.authorsform import AuthorsForm
+from openlp.plugins.songs.forms.topicsform import TopicsForm
+from openlp.plugins.songs.forms.songbookform import SongBookForm
 from openlp.plugins.songs.lib.db import Author, Book, Topic, Song
 from songmaintenancedialog import Ui_SongMaintenanceDialog
 
@@ -209,12 +211,13 @@
         """
         Add an author to the list.
         """
-        self.authorform.setAutoDisplayName(True)
+        self.authorform.auto_display_name = True
         if self.authorform.exec_():
             author = Author.populate(
-                first_name=self.authorform.firstNameEdit.text(),
-                last_name=self.authorform.lastNameEdit.text(),
-                display_name=self.authorform.displayEdit.text())
+                first_name=self.authorform.first_name,
+                last_name=self.authorform.last_name,
+                display_name=self.authorform.display_name
+            )
             if self.checkAuthor(author):
                 if self.manager.save_object(author):
                     self.resetAuthors()
@@ -266,19 +269,19 @@
         if author_id == -1:
             return
         author = self.manager.get_object(Author, author_id)
-        self.authorform.setAutoDisplayName(False)
-        self.authorform.firstNameEdit.setText(author.first_name)
-        self.authorform.lastNameEdit.setText(author.last_name)
-        self.authorform.displayEdit.setText(author.display_name)
+        self.authorform.auto_display_name = False
+        self.authorform.first_name_edit.setText(author.first_name)
+        self.authorform.last_name_edit.setText(author.last_name)
+        self.authorform.display_edit.setText(author.display_name)
         # Save the author's first and last name as well as the display name
         # for the case that they have to be restored.
         temp_first_name = author.first_name
         temp_last_name = author.last_name
         temp_display_name = author.display_name
         if self.authorform.exec_(False):
-            author.first_name = self.authorform.firstNameEdit.text()
-            author.last_name = self.authorform.lastNameEdit.text()
-            author.display_name = self.authorform.displayEdit.text()
+            author.first_name = self.authorform.first_name_edit.text()
+            author.last_name = self.authorform.last_name_edit.text()
+            author.display_name = self.authorform.display_edit.text()
             if self.checkAuthor(author, True):
                 if self.manager.save_object(author):
                     self.resetAuthors()

=== modified file 'openlp/plugins/songs/lib/__init__.py'
--- openlp/plugins/songs/lib/__init__.py	2013-02-27 12:41:04 +0000
+++ openlp/plugins/songs/lib/__init__.py	2013-03-07 21:58:22 +0000
@@ -26,6 +26,9 @@
 # with this program; if not, write to the Free Software Foundation, Inc., 59  #
 # Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
 ###############################################################################
+"""
+The :mod:`~openlp.plugins.songs.lib` module contains a number of library functions and classes used in the Songs plugin.
+"""
 import re
 
 from PyQt4 import QtGui
@@ -182,8 +185,10 @@
         for num, tag in enumerate(VerseType.tags):
             if verse_tag == tag:
                 return VerseType.translated_tags[num].upper()
-        if default in VerseType.translated_tags:
+        if len(VerseType.names) > default:
             return VerseType.translated_tags[default].upper()
+        else:
+            return VerseType.translated_tags[VerseType.Other].upper()
 
     @staticmethod
     def translated_name(verse_tag, default=Other):
@@ -200,8 +205,10 @@
         for num, tag in enumerate(VerseType.tags):
             if verse_tag == tag:
                 return VerseType.translated_names[num]
-        if default in VerseType.translated_names:
+        if len(VerseType.names) > default:
             return VerseType.translated_names[default]
+        else:
+            return VerseType.translated_names[VerseType.Other]
 
     @staticmethod
     def from_tag(verse_tag, default=Other):
@@ -218,7 +225,10 @@
         for num, tag in enumerate(VerseType.tags):
             if verse_tag == tag:
                 return num
-        return default
+        if len(VerseType.names) > default:
+            return default
+        else:
+            return VerseType.Other
 
     @staticmethod
     def from_translated_tag(verse_tag, default=Other):
@@ -235,7 +245,10 @@
         for num, tag in enumerate(VerseType.translated_tags):
             if verse_tag == tag:
                 return num
-        return default
+        if len(VerseType.names) > default:
+            return default
+        else:
+            return VerseType.Other
 
     @staticmethod
     def from_string(verse_name, default=Other):
@@ -611,7 +624,3 @@
             if result != 0:
                 return result
         return 1
-
-from xml import OpenLyrics, SongXML
-from songstab import SongsTab
-from mediaitem import SongMediaItem

=== modified file 'openlp/plugins/songs/lib/mediaitem.py'
--- openlp/plugins/songs/lib/mediaitem.py	2013-02-24 18:13:50 +0000
+++ openlp/plugins/songs/lib/mediaitem.py	2013-03-07 21:58:22 +0000
@@ -39,10 +39,14 @@
     UiStrings, translate, check_item_selected, create_separated_list, check_directory_exists
 from openlp.core.lib.ui import create_widget_action
 from openlp.core.utils import AppLocation
-from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, SongImportForm, SongExportForm
-from openlp.plugins.songs.lib import OpenLyrics, SongXML, VerseType, clean_string, natcmp
+from openlp.plugins.songs.forms.editsongform import EditSongForm
+from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm
+from openlp.plugins.songs.forms.songimportform import SongImportForm
+from openlp.plugins.songs.forms.songexportform import SongExportForm
+from openlp.plugins.songs.lib import VerseType, clean_string, natcmp
 from openlp.plugins.songs.lib.db import Author, Song, Book, MediaFile
 from openlp.plugins.songs.lib.ui import SongStrings
+from openlp.plugins.songs.lib.xml import OpenLyrics, SongXML
 
 log = logging.getLogger(__name__)
 
@@ -302,7 +306,7 @@
 
     def onNewClick(self):
         log.debug(u'onNewClick')
-        self.editSongForm.newSong()
+        self.editSongForm.new_song()
         self.editSongForm.exec_()
         self.onClearTextButtonClick()
         self.onSelectionChange()
@@ -321,7 +325,7 @@
         song_id = int(song_id)
         valid = self.plugin.manager.get_object(Song, song_id)
         if valid:
-            self.editSongForm.loadSong(song_id, preview)
+            self.editSongForm.load_song(song_id, preview)
             if self.editSongForm.exec_() == QtGui.QDialog.Accepted:
                 self.autoSelectId = -1
                 self.on_song_list_load()
@@ -342,7 +346,7 @@
         if check_item_selected(self.listView, UiStrings().SelectEdit):
             self.editItem = self.listView.currentItem()
             item_id = self.editItem.data(QtCore.Qt.UserRole)
-            self.editSongForm.loadSong(item_id, False)
+            self.editSongForm.load_song(item_id, False)
             self.editSongForm.exec_()
             self.autoSelectId = -1
             self.on_song_list_load()

=== modified file 'openlp/plugins/songs/lib/openlyricsexport.py'
--- openlp/plugins/songs/lib/openlyricsexport.py	2013-02-03 19:23:12 +0000
+++ openlp/plugins/songs/lib/openlyricsexport.py	2013-03-07 21:58:22 +0000
@@ -37,10 +37,11 @@
 
 from openlp.core.lib import Registry, check_directory_exists, translate
 from openlp.core.utils import clean_filename
-from openlp.plugins.songs.lib import OpenLyrics
+from openlp.plugins.songs.lib.xml import OpenLyrics
 
 log = logging.getLogger(__name__)
 
+
 class OpenLyricsExport(object):
     """
     This provides the Openlyrics export.

=== modified file 'openlp/plugins/songs/lib/openlyricsimport.py'
--- openlp/plugins/songs/lib/openlyricsimport.py	2013-02-07 11:33:47 +0000
+++ openlp/plugins/songs/lib/openlyricsimport.py	2013-03-07 21:58:22 +0000
@@ -39,11 +39,11 @@
 from openlp.core.ui.wizard import WizardStrings
 from openlp.plugins.songs.lib.songimport import SongImport
 from openlp.plugins.songs.lib.ui import SongStrings
-from openlp.plugins.songs.lib import OpenLyrics
-from openlp.plugins.songs.lib.xml import OpenLyricsError
+from openlp.plugins.songs.lib.xml import OpenLyrics, OpenLyricsError
 
 log = logging.getLogger(__name__)
 
+
 class OpenLyricsImport(SongImport):
     """
     This provides the Openlyrics import.

=== modified file 'openlp/plugins/songs/songsplugin.py'
--- openlp/plugins/songs/songsplugin.py	2013-02-19 21:23:56 +0000
+++ openlp/plugins/songs/songsplugin.py	2013-03-07 21:58:22 +0000
@@ -43,11 +43,13 @@
 from openlp.core.lib.ui import create_action
 from openlp.core.utils import get_filesystem_encoding
 from openlp.core.utils.actions import ActionList
-from openlp.plugins.songs.lib import clean_song, upgrade, SongMediaItem, SongsTab
+from openlp.plugins.songs.lib import clean_song, upgrade
 from openlp.plugins.songs.lib.db import init_schema, Song
 from openlp.plugins.songs.lib.mediaitem import SongSearch
 from openlp.plugins.songs.lib.importer import SongFormat
 from openlp.plugins.songs.lib.olpimport import OpenLPSongImport
+from openlp.plugins.songs.lib.mediaitem import SongMediaItem
+from openlp.plugins.songs.lib.songstab import SongsTab
 
 log = logging.getLogger(__name__)
 __default_settings__ = {

=== modified file 'tests/functional/openlp_core_lib/test_screen.py'
--- tests/functional/openlp_core_lib/test_screen.py	2013-02-27 21:40:55 +0000
+++ tests/functional/openlp_core_lib/test_screen.py	2013-03-07 21:58:22 +0000
@@ -23,11 +23,17 @@
         """
         Set up the components need for all tests.
         """
+        # Mocked out desktop object
+        self.desktop = MagicMock()
+        self.desktop.primaryScreen.return_value = SCREEN[u'primary']
+        self.desktop.screenCount.return_value = SCREEN[u'number']
+        self.desktop.screenGeometry.return_value = SCREEN[u'size']
+
         self.application = QtGui.QApplication.instance()
         Registry.create()
         self.application.setOrganizationName(u'OpenLP-tests')
         self.application.setOrganizationDomain(u'openlp.org')
-        self.screens = ScreenList.create(self.application.desktop())
+        self.screens = ScreenList.create(self.desktop)
 
     def tearDown(self):
         """
@@ -38,21 +44,16 @@
 
     def add_desktop_test(self):
         """
-        Test the ScreenList class' - screen_count_changed method to check if new monitors are detected by OpenLP.
+        Test the ScreenList.screen_count_changed method to check if new monitors are detected by OpenLP.
         """
-        # GIVEN: The screen list.
-        old_screens = copy.deepcopy(self.screens.screen_list)
-        # Mock the attributes.
-        self.screens.desktop.primaryScreen = MagicMock(return_value=SCREEN[u'primary'])
-        self.screens.desktop.screenCount = MagicMock(return_value=SCREEN[u'number'] + 1)
-        self.screens.desktop.screenGeometry = MagicMock(return_value=SCREEN[u'size'])
-
-        # WHEN: Add a new screen.
-        self.screens.screen_count_changed(len(old_screens))
-
-        # THEN: The screen should have been added.
-        new_screens = self.screens.screen_list
-        assert len(old_screens) + 1 == len(new_screens), u'The new_screens list should be bigger.'
-
-        # THEN: The screens should be identical.
-        assert SCREEN == new_screens.pop(), u'The new screen should be identical to the screen defined above.'
+        # GIVEN: The screen list at its current size
+        old_screen_count = len(self.screens.screen_list)
+
+        # WHEN: We add a new screen
+        self.desktop.screenCount.return_value = SCREEN[u'number'] + 1
+        self.screens.screen_count_changed(old_screen_count)
+
+        # THEN: The screen should have been added and the screens should be identical
+        new_screen_count = len(self.screens.screen_list)
+        assert old_screen_count + 1 == new_screen_count, u'The new_screens list should be bigger'
+        assert SCREEN == self.screens.screen_list.pop(), u'The 2nd screen should be identical to the first screen'

=== modified file 'tests/functional/openlp_core_utils/__init__.py'
--- tests/functional/openlp_core_utils/__init__.py	2012-12-05 18:52:31 +0000
+++ tests/functional/openlp_core_utils/__init__.py	2013-03-07 21:58:22 +0000
@@ -1,1 +0,0 @@
-__author__ = 'raoul'

=== added directory 'tests/functional/openlp_plugins'
=== added file 'tests/functional/openlp_plugins/__init__.py'
--- tests/functional/openlp_plugins/__init__.py	1970-01-01 00:00:00 +0000
+++ tests/functional/openlp_plugins/__init__.py	2013-03-07 21:58:22 +0000
@@ -0,0 +1,1 @@
+__author__ = 'raoul'

=== added directory 'tests/functional/openlp_plugins/songs'
=== added file 'tests/functional/openlp_plugins/songs/__init__.py'
=== added file 'tests/functional/openlp_plugins/songs/test_lib.py'
--- tests/functional/openlp_plugins/songs/test_lib.py	1970-01-01 00:00:00 +0000
+++ tests/functional/openlp_plugins/songs/test_lib.py	2013-03-07 21:58:22 +0000
@@ -0,0 +1,226 @@
+"""
+This module contains tests for the lib submodule of the Songs plugin.
+"""
+
+from unittest import TestCase
+
+from mock import patch
+
+from openlp.plugins.songs.lib import VerseType, clean_string, clean_title
+
+
+class TestLib(TestCase):
+    """
+    Test the functions in the :mod:`lib` module.
+    """
+    def clean_string_test(self):
+        """
+        Test the clean_string() function
+        """
+        # GIVEN: A "dirty" string
+        dirty_string = u'Ain\'t gonna   find\t you there.'
+
+        # WHEN: We run the string through the function
+        result = clean_string(dirty_string)
+
+        # THEN: The string should be cleaned up and lower-cased
+        self.assertEqual(result, u'aint gonna find you there ', u'The string should be cleaned up properly')
+
+    def clean_title_test(self):
+        """
+        Test the clean_title() function
+        """
+        # GIVEN: A "dirty" string
+        dirty_string = u'This\u0000 is a\u0014 dirty \u007Fstring\u009F'
+
+        # WHEN: We run the string through the function
+        result = clean_title(dirty_string)
+
+        # THEN: The string should be cleaned up
+        self.assertEqual(result, u'This is a dirty string', u'The title should be cleaned up properly: "%s"' % result)
+
+
+class TestVerseType(TestCase):
+    """
+    This is a test case to test various methods in the VerseType enumeration class.
+    """
+
+    def translated_tag_test(self):
+        """
+        Test that the translated_tag() method returns the correct tags
+        """
+        # GIVEN: A mocked out translate() function that just returns what it was given
+        with patch(u'openlp.plugins.songs.lib.translate') as mocked_translate:
+            mocked_translate.side_effect = lambda x, y: y
+
+            # WHEN: We run the translated_tag() method with a "verse"
+            result = VerseType.translated_tag(u'v')
+
+            # THEN: The result should be "V"
+            self.assertEqual(result, u'V', u'The result should be "V"')
+
+            # WHEN: We run the translated_tag() method with a "chorus"
+            result = VerseType.translated_tag(u'c')
+
+            # THEN: The result should be "C"
+            self.assertEqual(result, u'C', u'The result should be "C"')
+
+    def translated_invalid_tag_test(self):
+        """
+        Test that the translated_tag() method returns the default tag when passed an invalid tag
+        """
+        # GIVEN: A mocked out translate() function that just returns what it was given
+        with patch(u'openlp.plugins.songs.lib.translate') as mocked_translate:
+            mocked_translate.side_effect = lambda x, y: y
+
+            # WHEN: We run the translated_tag() method with an invalid verse type
+            result = VerseType.translated_tag(u'z')
+
+            # THEN: The result should be "O"
+            self.assertEqual(result, u'O', u'The result should be "O", but was "%s"' % result)
+
+    def translated_invalid_tag_with_specified_default_test(self):
+        """
+        Test that the translated_tag() method returns the specified default tag when passed an invalid tag
+        """
+        # GIVEN: A mocked out translate() function that just returns what it was given
+        with patch(u'openlp.plugins.songs.lib.translate') as mocked_translate:
+            mocked_translate.side_effect = lambda x, y: y
+
+            # WHEN: We run the translated_tag() method with an invalid verse type and specify a default
+            result = VerseType.translated_tag(u'q', VerseType.Bridge)
+
+            # THEN: The result should be "B"
+            self.assertEqual(result, u'B', u'The result should be "B", but was "%s"' % result)
+
+    def translated_invalid_tag_with_invalid_default_test(self):
+        """
+        Test that the translated_tag() method returns a sane default tag when passed an invalid default
+        """
+        # GIVEN: A mocked out translate() function that just returns what it was given
+        with patch(u'openlp.plugins.songs.lib.translate') as mocked_translate:
+            mocked_translate.side_effect = lambda x, y: y
+
+            # WHEN: We run the translated_tag() method with an invalid verse type and an invalid default
+            result = VerseType.translated_tag(u'q', 29)
+
+            # THEN: The result should be "O"
+            self.assertEqual(result, u'O', u'The result should be "O", but was "%s"' % result)
+
+    def translated_name_test(self):
+        """
+        Test that the translated_name() method returns the correct name
+        """
+        # GIVEN: A mocked out translate() function that just returns what it was given
+        with patch(u'openlp.plugins.songs.lib.translate') as mocked_translate:
+            mocked_translate.side_effect = lambda x, y: y
+
+            # WHEN: We run the translated_name() method with a "verse"
+            result = VerseType.translated_name(u'v')
+
+            # THEN: The result should be "Verse"
+            self.assertEqual(result, u'Verse', u'The result should be "Verse"')
+
+            # WHEN: We run the translated_name() method with a "chorus"
+            result = VerseType.translated_name(u'c')
+
+            # THEN: The result should be "Chorus"
+            self.assertEqual(result, u'Chorus', u'The result should be "Chorus"')
+
+    def translated_invalid_name_test(self):
+        """
+        Test that the translated_name() method returns the default name when passed an invalid tag
+        """
+        # GIVEN: A mocked out translate() function that just returns what it was given
+        with patch(u'openlp.plugins.songs.lib.translate') as mocked_translate:
+            mocked_translate.side_effect = lambda x, y: y
+
+            # WHEN: We run the translated_name() method with an invalid verse type
+            result = VerseType.translated_name(u'z')
+
+            # THEN: The result should be "Other"
+            self.assertEqual(result, u'Other', u'The result should be "Other", but was "%s"' % result)
+
+    def translated_invalid_name_with_specified_default_test(self):
+        """
+        Test that the translated_name() method returns the specified default name when passed an invalid tag
+        """
+        # GIVEN: A mocked out translate() function that just returns what it was given
+        with patch(u'openlp.plugins.songs.lib.translate') as mocked_translate:
+            mocked_translate.side_effect = lambda x, y: y
+
+            # WHEN: We run the translated_name() method with an invalid verse type and specify a default
+            result = VerseType.translated_name(u'q', VerseType.Bridge)
+
+            # THEN: The result should be "Bridge"
+            self.assertEqual(result, u'Bridge', u'The result should be "Bridge", but was "%s"' % result)
+
+    def translated_invalid_name_with_invalid_default_test(self):
+        """
+        Test that the translated_name() method returns the specified default tag when passed an invalid tag
+        """
+        # GIVEN: A mocked out translate() function that just returns what it was given
+        with patch(u'openlp.plugins.songs.lib.translate') as mocked_translate:
+            mocked_translate.side_effect = lambda x, y: y
+
+            # WHEN: We run the translated_name() method with an invalid verse type and specify an invalid default
+            result = VerseType.translated_name(u'q', 29)
+
+            # THEN: The result should be "Other"
+            self.assertEqual(result, u'Other', u'The result should be "Other", but was "%s"' % result)
+
+    def from_tag_test(self):
+        """
+        Test that the from_tag() method returns the correct VerseType.
+        """
+        # GIVEN: A mocked out translate() function that just returns what it was given
+        with patch(u'openlp.plugins.songs.lib.translate') as mocked_translate:
+            mocked_translate.side_effect = lambda x, y: y
+
+            # WHEN: We run the from_tag() method with a valid verse type, we get the name back
+            result = VerseType.from_tag(u'v')
+
+            # THEN: The result should be VerseType.Verse
+            self.assertEqual(result, VerseType.Verse, u'The result should be VerseType.Verse, but was "%s"' % result)
+
+    def from_tag_with_invalid_tag_test(self):
+        """
+        Test that the from_tag() method returns the default VerseType when it is passed an invalid tag.
+        """
+        # GIVEN: A mocked out translate() function that just returns what it was given
+        with patch(u'openlp.plugins.songs.lib.translate') as mocked_translate:
+            mocked_translate.side_effect = lambda x, y: y
+
+            # WHEN: We run the from_tag() method with a valid verse type, we get the name back
+            result = VerseType.from_tag(u'w')
+
+            # THEN: The result should be VerseType.Other
+            self.assertEqual(result, VerseType.Other, u'The result should be VerseType.Other, but was "%s"' % result)
+
+    def from_tag_with_specified_default_test(self):
+        """
+        Test that the from_tag() method returns the specified default when passed an invalid tag.
+        """
+        # GIVEN: A mocked out translate() function that just returns what it was given
+        with patch(u'openlp.plugins.songs.lib.translate') as mocked_translate:
+            mocked_translate.side_effect = lambda x, y: y
+
+            # WHEN: We run the from_tag() method with an invalid verse type, we get the specified default back
+            result = VerseType.from_tag(u'x', VerseType.Chorus)
+
+            # THEN: The result should be VerseType.Chorus
+            self.assertEqual(result, VerseType.Chorus, u'The result should be VerseType.Chorus, but was "%s"' % result)
+
+    def from_tag_with_invalid_default_test(self):
+        """
+        Test that the from_tag() method returns a sane default when passed an invalid tag and an invalid default.
+        """
+        # GIVEN: A mocked out translate() function that just returns what it was given
+        with patch(u'openlp.plugins.songs.lib.translate') as mocked_translate:
+            mocked_translate.side_effect = lambda x, y: y
+
+            # WHEN: We run the from_tag() method with an invalid verse type, we get the specified default back
+            result = VerseType.from_tag(u'm', 29)
+
+            # THEN: The result should be VerseType.Other
+            self.assertEqual(result, VerseType.Other, u'The result should be VerseType.Other, but was "%s"' % result)

=== modified file 'tests/interfaces/__init__.py'
--- tests/interfaces/__init__.py	2013-02-17 20:11:37 +0000
+++ tests/interfaces/__init__.py	2013-03-07 21:58:22 +0000
@@ -9,5 +9,5 @@
 
 from PyQt4 import QtGui
 
-# Only one QApplication can be created. Use QtGui.QApplication.instance() when you need to "create" a  QApplication.
+# Only one QApplication can be created. Use QtGui.QApplication.instance() when you need to "create" an QApplication.
 application = QtGui.QApplication([])

=== modified file 'tests/interfaces/openlp_core_lib/test_pluginmanager.py'
--- tests/interfaces/openlp_core_lib/test_pluginmanager.py	2013-02-18 20:39:48 +0000
+++ tests/interfaces/openlp_core_lib/test_pluginmanager.py	2013-03-07 21:58:22 +0000
@@ -3,7 +3,8 @@
 """
 import os
 import sys
-from tempfile import mkstemp
+import shutil
+from tempfile import mkstemp, mkdtemp
 from unittest import TestCase
 
 from mock import MagicMock
@@ -23,7 +24,9 @@
         Some pre-test setup required.
         """
         fd, self.ini_file = mkstemp(u'.ini')
+        self.temp_dir = mkdtemp(u'openlp')
         Settings().set_filename(self.ini_file)
+        Settings().setValue(u'advanced/data path', self.temp_dir)
         Registry.create()
         Registry().register(u'service_list', MagicMock())
         self.app = QtGui.QApplication.instance()
@@ -31,9 +34,11 @@
         Registry().register(u'main_window', self.main_window)
 
     def tearDown(self):
-        os.unlink(self.ini_file)
         del self.app
         del self.main_window
+        Settings().remove(u'advanced/data path')
+        shutil.rmtree(self.temp_dir)
+        os.unlink(self.ini_file)
 
     def find_plugins_test(self):
         """

=== modified file 'tests/interfaces/openlp_core_ui/test_servicenotedialog.py'
--- tests/interfaces/openlp_core_ui/test_servicenotedialog.py	2013-02-17 20:11:37 +0000
+++ tests/interfaces/openlp_core_ui/test_servicenotedialog.py	2013-03-07 21:58:22 +0000
@@ -1,5 +1,5 @@
 """
-    Package to test the openlp.core.ui package.
+Package to test the openlp.core.ui package.
 """
 from unittest import TestCase
 from mock import patch
@@ -10,6 +10,7 @@
 from openlp.core.ui import servicenoteform
 
 
+
 class TestStartNoteDialog(TestCase):
 
     def setUp(self):

=== modified file 'tests/interfaces/openlp_core_ui/test_starttimedialog.py'
--- tests/interfaces/openlp_core_ui/test_starttimedialog.py	2013-02-17 20:11:37 +0000
+++ tests/interfaces/openlp_core_ui/test_starttimedialog.py	2013-03-07 21:58:22 +0000
@@ -1,5 +1,5 @@
 """
-    Package to test the openlp.core.ui package.
+Package to test the openlp.core.ui package.
 """
 from unittest import TestCase
 from mock import MagicMock, patch
@@ -10,6 +10,7 @@
 from openlp.core.ui import starttimeform
 
 
+
 class TestStartTimeDialog(TestCase):
 
     def setUp(self):

=== added directory 'tests/interfaces/openlp_plugins_songs_forms'
=== added file 'tests/interfaces/openlp_plugins_songs_forms/__init__.py'
=== added file 'tests/interfaces/openlp_plugins_songs_forms/test_authorsform.py'
--- tests/interfaces/openlp_plugins_songs_forms/test_authorsform.py	1970-01-01 00:00:00 +0000
+++ tests/interfaces/openlp_plugins_songs_forms/test_authorsform.py	2013-03-07 21:58:22 +0000
@@ -0,0 +1,117 @@
+"""
+Package to test the openlp.plugins.songs.forms.authorsform package.
+"""
+from unittest import TestCase
+
+from PyQt4 import QtGui
+
+from openlp.core.lib import Registry
+from openlp.plugins.songs.forms.authorsform import AuthorsForm
+
+
+class TestAuthorsForm(TestCase):
+
+    def setUp(self):
+        """
+        Create the UI
+        """
+        Registry.create()
+        self.app = QtGui.QApplication.instance()
+        self.main_window = QtGui.QMainWindow()
+        Registry().register(u'main_window', self.main_window)
+        self.form = AuthorsForm()
+
+    def tearDown(self):
+        """
+        Delete all the C++ objects at the end so that we don't have a segfault
+        """
+        del self.form
+        del self.main_window
+        del self.app
+
+    def ui_defaults_test(self):
+        """
+        Test the AuthorForm defaults are correct
+        """
+        self.assertEqual(self.form.first_name_edit.text(), u'', u'The first name edit should be empty')
+        self.assertEqual(self.form.last_name_edit.text(), u'', u'The last name edit should be empty')
+        self.assertEqual(self.form.display_edit.text(), u'', u'The display name edit should be empty')
+
+    def get_first_name_property_test(self):
+        """
+        Test that getting the first name property on the AuthorForm works correctly.
+        """
+        # GIVEN: A first name to set
+        first_name = u'John'
+
+        # WHEN: The first_name_edit's text is set
+        self.form.first_name_edit.setText(first_name)
+
+        # THEN: The first_name property should have the correct value
+        self.assertEqual(self.form.first_name, first_name, u'The first name property should be correct')
+
+    def set_first_name_property_test(self):
+        """
+        Test that setting the first name property on the AuthorForm works correctly.
+        """
+        # GIVEN: A first name to set
+        first_name = u'James'
+
+        # WHEN: The first_name property is set
+        self.form.first_name = first_name
+
+        # THEN: The first_name_edit should have the correct value
+        self.assertEqual(self.form.first_name_edit.text(), first_name, u'The first name should be set correctly')
+
+    def get_last_name_property_test(self):
+        """
+        Test that getting the last name property on the AuthorForm works correctly.
+        """
+        # GIVEN: A last name to set
+        last_name = u'Smith'
+
+        # WHEN: The last_name_edit's text is set
+        self.form.last_name_edit.setText(last_name)
+
+        # THEN: The last_name property should have the correct value
+        self.assertEqual(self.form.last_name, last_name, u'The last name property should be correct')
+
+    def set_last_name_property_test(self):
+        """
+        Test that setting the last name property on the AuthorForm works correctly.
+        """
+        # GIVEN: A last name to set
+        last_name = u'Potter'
+
+        # WHEN: The last_name property is set
+        self.form.last_name = last_name
+
+        # THEN: The last_name_edit should have the correct value
+        self.assertEqual(self.form.last_name_edit.text(), last_name, u'The last name should be set correctly')
+
+    def get_display_name_property_test(self):
+        """
+        Test that getting the display name property on the AuthorForm works correctly.
+        """
+        # GIVEN: A display name to set
+        display_name = u'John'
+
+        # WHEN: The display_name_edit's text is set
+        self.form.display_edit.setText(display_name)
+
+        # THEN: The display_name property should have the correct value
+        self.assertEqual(self.form.display_name, display_name, u'The display name property should be correct')
+
+    def set_display_name_property_test(self):
+        """
+        Test that setting the display name property on the AuthorForm works correctly.
+        """
+        # GIVEN: A display name to set
+        display_name = u'John'
+
+        # WHEN: The display_name property is set
+        self.form.display_name = display_name
+
+        # THEN: The display_name_edit should have the correct value
+        self.assertEqual(self.form.display_edit.text(), display_name, u'The display name should be set correctly')
+

=== added file 'tests/interfaces/openlp_plugins_songs_forms/test_editsongform.py'
--- tests/interfaces/openlp_plugins_songs_forms/test_editsongform.py	1970-01-01 00:00:00 +0000
+++ tests/interfaces/openlp_plugins_songs_forms/test_editsongform.py	2013-03-07 21:58:22 +0000
@@ -0,0 +1,40 @@
+"""
+Package to test the openlp.plugins.songs.forms.editsongform package.
+"""
+from mock import MagicMock
+from unittest import TestCase
+
+from PyQt4 import QtGui
+
+from openlp.core.lib import Registry
+from openlp.plugins.songs.forms.editsongform import EditSongForm
+
+
+class TestEditSongForm(TestCase):
+    def setUp(self):
+        """
+        Create the UI
+        """
+        Registry.create()
+        self.app = QtGui.QApplication.instance()
+        self.main_window = QtGui.QMainWindow()
+        Registry().register(u'main_window', self.main_window)
+        Registry().register(u'theme_manager', MagicMock())
+        self.form = EditSongForm(MagicMock(), self.main_window, MagicMock())
+
+    def tearDown(self):
+        """
+        Delete all the C++ objects at the end so that we don't have a segfault
+        """
+        del self.form
+        del self.main_window
+        del self.app
+
+    def ui_defaults_test(self):
+        """
+        Test that the EditSongForm defaults are correct
+        """
+        self.assertFalse(self.form.verse_edit_button.isEnabled(), u'The verse edit button should not be enabled')
+        self.assertFalse(self.form.verse_delete_button.isEnabled(), u'The verse delete button should not be enabled')
+        self.assertFalse(self.form.author_remove_button.isEnabled(), u'The author remove button should not be enabled')
+        self.assertFalse(self.form.topic_remove_button.isEnabled(), u'The topic remove button should not be enabled')

=== added file 'tests/interfaces/openlp_plugins_songs_forms/test_editverseform.py'
--- tests/interfaces/openlp_plugins_songs_forms/test_editverseform.py	1970-01-01 00:00:00 +0000
+++ tests/interfaces/openlp_plugins_songs_forms/test_editverseform.py	2013-03-07 21:58:22 +0000
@@ -0,0 +1,35 @@
+"""
+Package to test the openlp.plugins.songs.forms.editverseform package.
+"""
+from unittest import TestCase
+
+from PyQt4 import QtGui
+
+from openlp.core.lib import Registry
+from openlp.plugins.songs.forms.editverseform import EditVerseForm
+
+
+class TestEditVerseForm(TestCase):
+    def setUp(self):
+        """
+        Create the UI
+        """
+        Registry.create()
+        self.app = QtGui.QApplication.instance()
+        self.main_window = QtGui.QMainWindow()
+        Registry().register(u'main_window', self.main_window)
+        self.form = EditVerseForm()
+
+    def tearDown(self):
+        """
+        Delete all the C++ objects at the end so that we don't have a segfault
+        """
+        del self.form
+        del self.main_window
+        del self.app
+
+    def ui_defaults_test(self):
+        """
+        Test the EditVerseForm defaults are correct
+        """
+        self.assertEqual(self.form.verseTextEdit.toPlainText(), u'', u'The verse edit box is empty.')


Follow ups