← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~trb143/openlp/refactor1 into lp:openlp

 

Tim Bentley has proposed merging lp:~trb143/openlp/refactor1 into lp:openlp.

Requested reviews:
  OpenLP Core (openlp-core)

For more details, see:
https://code.launchpad.net/~trb143/openlp/refactor1/+merge/200250

Cleaned up Theme and Service Managers Now working the same way.

Fixed up tests for both classes

PEP8 changes for Alerts and Custom + various other bits in passing.
-- 
https://code.launchpad.net/~trb143/openlp/refactor1/+merge/200250
Your team OpenLP Core is requested to review the proposed merge of lp:~trb143/openlp/refactor1 into lp:openlp.
=== modified file 'openlp/core/__init__.py'
--- openlp/core/__init__.py	2013-12-28 21:33:38 +0000
+++ openlp/core/__init__.py	2014-01-01 11:50:01 +0000
@@ -137,6 +137,7 @@
         self.main_window = MainWindow()
         Registry().execute('bootstrap_initialise')
         Registry().execute('bootstrap_post_set_up')
+        Registry().initialise = False
         self.main_window.show()
         if show_splash:
             # now kill the splashscreen

=== modified file 'openlp/core/common/openlpmixin.py'
--- openlp/core/common/openlpmixin.py	2013-12-23 20:43:35 +0000
+++ openlp/core/common/openlpmixin.py	2014-01-01 11:50:01 +0000
@@ -4,8 +4,8 @@
 ###############################################################################
 # OpenLP - Open Source Lyrics Projection                                      #
 # --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2013 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan      #
+# Copyright (c) 2008-2014 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan      #
 # Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
 # Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
 # Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #
@@ -33,7 +33,7 @@
 import inspect
 
 from openlp.core.common import trace_error_handler
-DO_NOT_TRACE_EVENTS = ['timerEvent', 'paintEvent']
+DO_NOT_TRACE_EVENTS = ['timerEvent', 'paintEvent', 'drag_enter_event', 'drop_event']
 
 
 class OpenLPMixin(object):
@@ -68,13 +68,13 @@
 
     def log_debug(self, message):
         """
-        Common log debug handler which prints the calling path
+        Common log debug handler
         """
         self.logger.debug(message)
 
     def log_info(self, message):
         """
-        Common log info handler which prints the calling path
+        Common log info handler
         """
         self.logger.info(message)
 

=== modified file 'openlp/core/common/registry.py'
--- openlp/core/common/registry.py	2013-12-28 21:33:38 +0000
+++ openlp/core/common/registry.py	2014-01-01 11:50:01 +0000
@@ -63,6 +63,7 @@
         registry.service_list = {}
         registry.functions_list = {}
         registry.running_under_test = False
+        registry.initialising = True
         # Allow the tests to remove Registry entries but not the live system
         if 'nose' in sys.argv[0]:
             registry.running_under_test = True
@@ -78,9 +79,10 @@
         if key in self.service_list:
             return self.service_list[key]
         else:
-            trace_error_handler(log)
-            log.error('Service %s not found in list' % key)
-            #raise KeyError('Service %s not found in list' % key)
+            if not self.initialising:
+                trace_error_handler(log)
+                log.error('Service %s not found in list' % key)
+                raise KeyError('Service %s not found in list' % key)
 
     def register(self, key, reference):
         """
@@ -170,5 +172,5 @@
                     log.exception('Exception for function %s', function)
         else:
             trace_error_handler(log)
-            log.error("Event %s not called by not registered" % event)
+            log.error("Event %s called but not registered" % event)
         return results

=== modified file 'openlp/core/common/registrymixin.py'
--- openlp/core/common/registrymixin.py	2013-12-19 20:17:06 +0000
+++ openlp/core/common/registrymixin.py	2014-01-01 11:50:01 +0000
@@ -4,8 +4,8 @@
 ###############################################################################
 # OpenLP - Open Source Lyrics Projection                                      #
 # --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2013 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan      #
+# Copyright (c) 2008-2014 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan      #
 # Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
 # Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
 # Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #

=== modified file 'openlp/core/lib/__init__.py'
--- openlp/core/lib/__init__.py	2013-12-28 21:33:38 +0000
+++ openlp/core/lib/__init__.py	2014-01-01 11:50:01 +0000
@@ -89,8 +89,8 @@
     returns False. If there is an error loading the file or the content can't be decoded then the function will return
     None.
 
-    ``textfile``
-        The name of the file.
+    :param text_file: The name of the file.
+    :return The file as a single string
     """
     if not os.path.isfile(text_file):
         return False
@@ -114,8 +114,8 @@
     """
     Convert a string version of a boolean into a real boolean.
 
-    ``string_value``
-        The string value to examine and convert to a boolean type.
+    :param string_value: The string value to examine and convert to a boolean type.
+    :return The correct boolean value
     """
     if isinstance(string_value, bool):
         return string_value
@@ -127,9 +127,10 @@
     Build a QIcon instance from an existing QIcon, a resource location, or a physical file location. If the icon is a
     QIcon instance, that icon is simply returned. If not, it builds a QIcon instance from the resource or file name.
 
-    ``icon``
+    :param icon:
         The icon to build. This can be a QIcon, a resource string in the form ``:/resource/file.png``, or a file
         location like ``/path/to/file.png``. However, the **recommended** way is to specify a resource string.
+    :return The build icon.
     """
     button_icon = QtGui.QIcon()
     if isinstance(icon, QtGui.QIcon):
@@ -148,8 +149,7 @@
     """
     Resize an image to fit on the current screen for the web and returns it as a byte stream.
 
-    ``image``
-        The image to converted.
+    :param image: The image to converted.
     """
     log.debug('image_to_byte - start')
     byte_array = QtCore.QByteArray()
@@ -166,18 +166,12 @@
     """
     Create a thumbnail from the given image path and depending on ``return_icon`` it returns an icon from this thumb.
 
-    ``image_path``
-        The image file to create the icon from.
-
-    ``thumb_path``
-        The filename to save the thumbnail to.
-
-    ``return_icon``
-        States if an icon should be build and returned from the thumb. Defaults to ``True``.
-
-    ``size``
-        Allows to state a own size (QtCore.QSize) to use. Defaults to ``None``, which means that a default height of 88
-        is used.
+    :param image_path: The image file to create the icon from.
+    :param thumb_path: The filename to save the thumbnail to.
+    :param return_icon: States if an icon should be build and returned from the thumb. Defaults to ``True``.
+    :param size: Allows to state a own size (QtCore.QSize) to use. Defaults to ``None``, which means that a default
+     height of 88 is used.
+    :return The final icon.
     """
     ext = os.path.splitext(thumb_path)[1].lower()
     reader = QtGui.QImageReader(image_path)
@@ -201,11 +195,9 @@
     Validates whether an file's thumb still exists and if is up to date. **Note**, you must **not** call this function,
     before checking the existence of the file.
 
-    ``file_path``
-        The path to the file. The file **must** exist!
-
-    ``thumb_path``
-        The path to the thumb.
+    :param file_path: The path to the file. The file **must** exist!
+    :param thumb_path: The path to the thumb.
+    :return True, False if the image has changed since the thumb was created.
     """
     if not os.path.exists(thumb_path):
         return False
@@ -218,19 +210,12 @@
     """
     Resize an image to fit on the current screen.
 
-    ``image_path``
-        The path to the image to resize.
-
-    ``width``
-        The new image width.
-
-    ``height``
-        The new image height.
-
-    ``background``
-        The background colour. Defaults to black.
-
     DO NOT REMOVE THE DEFAULT BACKGROUND VALUE!
+
+    :param image_path: The path to the image to resize.
+    :param width: The new image width.
+    :param height: The new image height.
+    :param background: The background colour. Defaults to black.
     """
     log.debug('resize_image - start')
     reader = QtGui.QImageReader(image_path)
@@ -265,11 +250,8 @@
     """
     Check if a list item is selected so an action may be performed on it
 
-    ``list_widget``
-        The list to check for selected items
-
-    ``message``
-        The message to give the user if no item is selected
+    :param list_widget: The list to check for selected items
+    :param message: The message to give the user if no item is selected
     """
     if not list_widget.selectedIndexes():
         QtGui.QMessageBox.information(list_widget.parent(),
@@ -281,6 +263,8 @@
 def clean_tags(text):
     """
     Remove Tags from text for display
+
+    :param text: Text to be cleaned
     """
     text = text.replace('<br>', '\n')
     text = text.replace('{br}', '\n')
@@ -294,6 +278,8 @@
 def expand_tags(text):
     """
     Expand tags HTML for display
+
+    :param text: The text to be expanded.
     """
     for tag in FormattingTags.get_html_tags():
         text = text.replace(tag['start tag'], tag['start html'])
@@ -304,11 +290,11 @@
 def create_separated_list(string_list):
     """
     Returns a string that represents a join of a list of strings with a localized separator. This function corresponds
+
     to QLocale::createSeparatedList which was introduced in Qt 4.8 and implements the algorithm from
     http://www.unicode.org/reports/tr35/#ListPatterns
 
-    ``string_list``
-        List of unicode strings
+     :param string_list: List of unicode strings
     """
     if LooseVersion(Qt.PYQT_VERSION_STR) >= LooseVersion('4.9') and \
             LooseVersion(Qt.qVersion()) >= LooseVersion('4.8'):

=== modified file 'openlp/core/ui/__init__.py'
--- openlp/core/ui/__init__.py	2013-12-28 21:33:38 +0000
+++ openlp/core/ui/__init__.py	2014-01-01 11:50:01 +0000
@@ -99,11 +99,10 @@
 from .shortcutlistform import ShortcutListForm
 from .mediadockmanager import MediaDockManager
 from .servicemanager import ServiceManager
-from .thememanagerhelper import ThemeManagerHelper
 from .thememanager import ThemeManager
 
 __all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainDisplay', 'SlideController', 'ServiceManager',
     'ThemeManager', 'MediaDockManager', 'ServiceItemEditForm', 'FirstTimeForm', 'FirstTimeLanguageForm', 'ThemeForm',
     'ThemeLayoutForm', 'FileRenameForm', 'StartTimeForm', 'MainDisplay', 'Display', 'ServiceNoteForm',
     'SlideController', 'DisplayController', 'GeneralTab', 'ThemesTab', 'AdvancedTab', 'PluginForm',
-    'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController', 'ThemeManagerHelper']
+    'FormattingTagForm', 'ShortcutListForm', 'FormattingTagController']

=== modified file 'openlp/core/ui/media/mediacontroller.py'
--- openlp/core/ui/media/mediacontroller.py	2013-12-28 21:33:38 +0000
+++ openlp/core/ui/media/mediacontroller.py	2014-01-01 11:50:01 +0000
@@ -35,7 +35,7 @@
 import datetime
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import Registry, Settings, UiStrings, translate
+from openlp.core.common import Registry, RegistryMixin, Settings, UiStrings, translate
 from openlp.core.lib import OpenLPToolbar
 from openlp.core.lib.ui import critical_error_message_box
 from openlp.core.ui.media import MediaState, MediaInfo, MediaType, get_media_players, set_media_players
@@ -99,7 +99,7 @@
         Constructor
         """
         Registry().register('media_controller', self)
-        Registry().register_function('bootstrap_initialise', self.check_available_media_players)
+        Registry().register_function('bootstrap_initialise', self.bootstrap_initialise)
         self.media_players = {}
         self.display_controllers = {}
         self.current_media_players = {}
@@ -134,20 +134,22 @@
         """
         Set the active players and available media files
         """
+        suffix_list = []
         self.audio_extensions_list = []
         for player in list(self.media_players.values()):
             if player.is_active:
                 for item in player.audio_extensions_list:
                     if not item in self.audio_extensions_list:
                         self.audio_extensions_list.append(item)
-                        self.service_manager.supported_suffixes(item[2:])
+                        suffix_list.append(item[2:])
         self.video_extensions_list = []
         for player in list(self.media_players.values()):
             if player.is_active:
                 for item in player.video_extensions_list:
                     if item not in self.video_extensions_list:
                         self.video_extensions_list.extend(item)
-                        self.service_manager.supported_suffixes(item[2:])
+                        suffix_list.append(item[2:])
+        self.service_manager.supported_suffixes(suffix_list)
 
     def register_players(self, player):
         """
@@ -159,7 +161,7 @@
         """
         self.media_players[player.name] = player
 
-    def check_available_media_players(self):
+    def bootstrap_initialise(self):
         """
         Check to see if we have any media Player's available.
         """
@@ -169,27 +171,28 @@
             if filename.endswith('player.py') and not filename == 'mediaplayer.py':
                 path = os.path.join(controller_dir, filename)
                 if os.path.isfile(path):
-                    modulename = 'openlp.core.ui.media.' + os.path.splitext(filename)[0]
-                    log.debug('Importing controller %s', modulename)
+                    module_name = 'openlp.core.ui.media.' + os.path.splitext(filename)[0]
+                    log.debug('Importing controller %s', module_name)
                     try:
-                        __import__(modulename, globals(), locals(), [])
+                        __import__(module_name, globals(), locals(), [])
                     # On some platforms importing vlc.py might cause
                     # also OSError exceptions. (e.g. Mac OS X)
                     except (ImportError, OSError):
-                        log.warn('Failed to import %s on path %s', modulename, path)
+                        log.warn('Failed to import %s on path %s', module_name, path)
         player_classes = MediaPlayer.__subclasses__()
         for player_class in player_classes:
             player = player_class(self)
             self.register_players(player)
         if not self.media_players:
             return False
-        savedPlayers, overriddenPlayer = get_media_players()
-        invalid_media_players = [mediaPlayer for mediaPlayer in savedPlayers
-            if not mediaPlayer in self.media_players or not self.media_players[mediaPlayer].check_available()]
+        saved_players, overridden_player = get_media_players()
+        invalid_media_players = \
+            [mediaPlayer for mediaPlayer in saved_players if not mediaPlayer in self.media_players or
+                not self.media_players[mediaPlayer].check_available()]
         if invalid_media_players:
             for invalidPlayer in invalid_media_players:
-                savedPlayers.remove(invalidPlayer)
-            set_media_players(savedPlayers, overriddenPlayer)
+                saved_players.remove(invalidPlayer)
+            set_media_players(saved_players, overridden_player)
         self._set_active_players()
         self._generate_extensions_lists()
         return True
@@ -270,14 +273,17 @@
         # Build a Media ToolBar
         controller.mediabar = OpenLPToolbar(controller)
         controller.mediabar.add_toolbar_action('playbackPlay', text='media_playback_play',
-            icon=':/slides/media_playback_start.png',
-            tooltip=translate('OpenLP.SlideController', 'Start playing media.'), triggers=controller.send_to_plugins)
+                                               icon=':/slides/media_playback_start.png',
+                                               tooltip=translate('OpenLP.SlideController', 'Start playing media.'),
+                                               triggers=controller.send_to_plugins)
         controller.mediabar.add_toolbar_action('playbackPause', text='media_playback_pause',
-            icon=':/slides/media_playback_pause.png',
-            tooltip=translate('OpenLP.SlideController', 'Pause playing media.'), triggers=controller.send_to_plugins)
+                                               icon=':/slides/media_playback_pause.png',
+                                               tooltip=translate('OpenLP.SlideController', 'Pause playing media.'),
+                                               triggers=controller.send_to_plugins)
         controller.mediabar.add_toolbar_action('playbackStop', text='media_playback_stop',
-            icon=':/slides/media_playback_stop.png',
-            tooltip=translate('OpenLP.SlideController', 'Stop playing media.'), triggers=controller.send_to_plugins)
+                                               icon=':/slides/media_playback_stop.png',
+                                               tooltip=translate('OpenLP.SlideController', 'Stop playing media.'),
+                                               triggers=controller.send_to_plugins)
         # Build the seek_slider.
         controller.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, controller)
         controller.seek_slider.setMaximum(1000)
@@ -445,11 +451,11 @@
         if not self._check_file_type(controller, display, service_item):
             # Media could not be loaded correctly
             critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
-                translate('MediaPlugin.MediaItem', 'Unsupported File'))
+                                       translate('MediaPlugin.MediaItem', 'Unsupported File'))
             return False
         if not self.media_play(controller):
             critical_error_message_box(translate('MediaPlugin.MediaItem', 'Unsupported File'),
-                translate('MediaPlugin.MediaItem', 'Unsupported File'))
+                                       translate('MediaPlugin.MediaItem', 'Unsupported File'))
             return False
         service_item.set_media_length(controller.media_info.length)
         self.media_stop(controller)

=== modified file 'openlp/core/ui/media/mediaplayer.py'
--- openlp/core/ui/media/mediaplayer.py	2013-12-28 21:33:38 +0000
+++ openlp/core/ui/media/mediaplayer.py	2014-01-01 11:50:01 +0000
@@ -37,8 +37,7 @@
 
 class MediaPlayer(object):
     """
-    This is the base class media Player class to provide OpenLP with a
-    pluggable media display framework.
+    This is the base class media Player class to provide OpenLP with a pluggable media display framework.
     """
 
     def __init__(self, parent, name='media_player'):

=== modified file 'openlp/core/ui/media/phononplayer.py'
--- openlp/core/ui/media/phononplayer.py	2013-12-24 08:56:50 +0000
+++ openlp/core/ui/media/phononplayer.py	2014-01-01 11:50:01 +0000
@@ -80,12 +80,12 @@
         self.parent = parent
         self.additional_extensions = ADDITIONAL_EXT
         mimetypes.init()
-        for mimetype in Phonon.BackendCapabilities.availableMimeTypes():
-            mimetype = str(mimetype)
-            if mimetype.startswith('audio/'):
-                self._addToList(self.audio_extensions_list, mimetype)
-            elif mimetype.startswith('video/'):
-                self._addToList(self.video_extensions_list, mimetype)
+        for mime_type in Phonon.BackendCapabilities.availableMimeTypes():
+            mime_type = str(mime_type)
+            if mime_type.startswith('audio/'):
+                self._addToList(self.audio_extensions_list, mime_type)
+            elif mime_type.startswith('video/'):
+                self._addToList(self.video_extensions_list, mime_type)
 
     def _addToList(self, mimetype_list, mimetype):
         """
@@ -144,14 +144,14 @@
         self.volume(display, volume)
         return True
 
-    def media_state_wait(self, display, mediaState):
+    def media_state_wait(self, display, media_state):
         """
         Wait for the video to change its state
         Wait no longer than 5 seconds.
         """
         start = datetime.now()
         current_state = display.media_object.state()
-        while current_state != mediaState:
+        while current_state != media_state:
             current_state = display.media_object.state()
             if current_state == Phonon.ErrorState:
                 return False
@@ -172,8 +172,7 @@
         """
         controller = display.controller
         start_time = 0
-        if display.media_object.state() != Phonon.PausedState and \
-            controller.media_info.start_time > 0:
+        if display.media_object.state() != Phonon.PausedState and controller.media_info.start_time > 0:
             start_time = controller.media_info.start_time
         display.media_object.play()
         if not self.media_state_wait(display, Phonon.PlayingState):
@@ -262,8 +261,8 @@
         Return some info about this player
         """
         return(translate('Media.player', 'Phonon is a media player which '
-            'interacts with the operating system to provide media capabilities.') +
-            '<br/> <strong>' + translate('Media.player', 'Audio') +
-            '</strong><br/>' + str(self.audio_extensions_list) +
-            '<br/><strong>' + translate('Media.player', 'Video') +
-            '</strong><br/>' + str(self.video_extensions_list) + '<br/>')
+               'interacts with the operating system to provide media capabilities.') +
+               '<br/> <strong>' + translate('Media.player', 'Audio') +
+               '</strong><br/>' + str(self.audio_extensions_list) +
+               '<br/><strong>' + translate('Media.player', 'Video') +
+               '</strong><br/>' + str(self.video_extensions_list) + '<br/>')

=== modified file 'openlp/core/ui/media/playertab.py'
--- openlp/core/ui/media/playertab.py	2013-12-28 21:33:38 +0000
+++ openlp/core/ui/media/playertab.py	2014-01-01 11:50:01 +0000
@@ -39,13 +39,13 @@
 
 class MediaQCheckBox(QtGui.QCheckBox):
     """
-    MediaQCheckBox adds an extra property, playerName to the QCheckBox class.
+    MediaQCheckBox adds an extra property, player_name to the QCheckBox class.
     """
     def set_player_name(self, name):
         """
         Set the player name
         """
-        self.playerName = name
+        self.player_name = name
 
 
 class PlayerTab(SettingsTab):
@@ -113,9 +113,9 @@
         self.ordering_button_layout.setObjectName('ordering_button_layout')
         self.ordering_button_layout.addStretch(1)
         self.ordering_up_button = create_button(self, 'ordering_up_button', role='up',
-            click=self.on_up_button_clicked)
+                                                click=self.on_up_button_clicked)
         self.ordering_down_button = create_button(self, 'ordering_down_button', role='down',
-            click=self.on_down_button_clicked)
+                                                  click=self.on_down_button_clicked)
         self.ordering_button_layout.addWidget(self.ordering_up_button)
         self.ordering_button_layout.addWidget(self.ordering_down_button)
         self.ordering_button_layout.addStretch(1)
@@ -135,8 +135,8 @@
         self.background_color_group_box.setTitle(UiStrings().BackgroundColor)
         self.background_color_label.setText(UiStrings().DefaultColor)
         self.information_label.setText(translate('OpenLP.PlayerTab',
-            'Visible background for videos with aspect ratio different to screen.'))
-        self.retranslatePlayers()
+                                       'Visible background for videos with aspect ratio different to screen.'))
+        self.retranslate_players()
 
     def on_background_color_button_clicked(self):
         """
@@ -151,7 +151,7 @@
         """
         Add or remove players depending on their status
         """
-        player = self.sender().playerName
+        player = self.sender().player_name
         if check_state == QtCore.Qt.Checked:
             if player not in self.used_players:
                 self.used_players.append(player)
@@ -249,9 +249,9 @@
             else:
                 checkbox.setChecked(False)
         self.update_player_list()
-        self.retranslatePlayers()
+        self.retranslate_players()
 
-    def retranslatePlayers(self):
+    def retranslate_players(self):
         """
         Translations for players is dependent on  their setup as well
          """

=== modified file 'openlp/core/ui/media/vlcplayer.py'
--- openlp/core/ui/media/vlcplayer.py	2013-12-24 08:56:50 +0000
+++ openlp/core/ui/media/vlcplayer.py	2014-01-01 11:50:01 +0000
@@ -118,35 +118,35 @@
         """
         Set up the media player
         """
-        display.vlcWidget = QtGui.QFrame(display)
-        display.vlcWidget.setFrameStyle(QtGui.QFrame.NoFrame)
+        display.vlc_widget = QtGui.QFrame(display)
+        display.vlc_widget.setFrameStyle(QtGui.QFrame.NoFrame)
         # creating a basic vlc instance
         command_line_options = '--no-video-title-show'
         if not display.has_audio:
             command_line_options += ' --no-audio --no-video-title-show'
         if Settings().value('advanced/hide mouse') and display.controller.is_live:
             command_line_options += ' --mouse-hide-timeout=0'
-        display.vlcInstance = vlc.Instance(command_line_options)
+        display.vlc_instance = vlc.Instance(command_line_options)
         # creating an empty vlc media player
-        display.vlcMediaPlayer = display.vlcInstance.media_player_new()
-        display.vlcWidget.resize(display.size())
-        display.vlcWidget.raise_()
-        display.vlcWidget.hide()
+        display.vlc_media_player = display.vlc_instance.media_player_new()
+        display.vlc_widget.resize(display.size())
+        display.vlc_widget.raise_()
+        display.vlc_widget.hide()
         # The media player has to be 'connected' to the QFrame.
         # (otherwise a video would be displayed in it's own window)
         # This is platform specific!
         # You have to give the id of the QFrame (or similar object)
         # to vlc, different platforms have different functions for this.
-        win_id = int(display.vlcWidget.winId())
+        win_id = int(display.vlc_widget.winId())
         if sys.platform == "win32":
-            display.vlcMediaPlayer.set_hwnd(win_id)
+            display.vlc_media_player.set_hwnd(win_id)
         elif sys.platform == "darwin":
             # We have to use 'set_nsobject' since Qt4 on OSX uses Cocoa
             # framework and not the old Carbon.
-            display.vlcMediaPlayer.set_nsobject(win_id)
+            display.vlc_media_player.set_nsobject(win_id)
         else:
             # for Linux using the X Server
-            display.vlcMediaPlayer.set_xwindow(win_id)
+            display.vlc_media_player.set_xwindow(win_id)
         self.has_own_widget = True
 
     def check_available(self):
@@ -165,18 +165,18 @@
         file_path = str(controller.media_info.file_info.absoluteFilePath())
         path = os.path.normcase(file_path)
         # create the media
-        display.vlcMedia = display.vlcInstance.media_new_path(path)
+        display.vlc_media = display.vlc_instance.media_new_path(path)
         # put the media in the media player
-        display.vlcMediaPlayer.set_media(display.vlcMedia)
+        display.vlc_media_player.set_media(display.vlc_media)
         # parse the metadata of the file
-        display.vlcMedia.parse()
+        display.vlc_media.parse()
         self.volume(display, volume)
         # We need to set media_info.length during load because we want
         # to avoid start and stop the video twice. Once for real playback
         # and once to just get media length.
         #
         # Media plugin depends on knowing media length before playback.
-        controller.media_info.length = int(display.vlcMediaPlayer.get_media().get_duration() / 1000)
+        controller.media_info.length = int(display.vlc_media_player.get_media().get_duration() / 1000)
         return True
 
     def media_state_wait(self, display, media_state):
@@ -185,8 +185,8 @@
         Wait no longer than 60 seconds. (loading an iso file needs a long time)
         """
         start = datetime.now()
-        while not media_state == display.vlcMedia.get_state():
-            if display.vlcMedia.get_state() == vlc.State.Error:
+        while not media_state == display.vlc_media.get_state():
+            if display.vlc_media.get_state() == vlc.State.Error:
                 return False
             self.application.process_events()
             if (datetime.now() - start).seconds > 60:
@@ -197,7 +197,7 @@
         """
         Resize the player
         """
-        display.vlcWidget.resize(display.size())
+        display.vlc_widget.resize(display.size())
 
     def play(self, display):
         """
@@ -207,25 +207,25 @@
         start_time = 0
         if self.state != MediaState.Paused and controller.media_info.start_time > 0:
             start_time = controller.media_info.start_time
-        display.vlcMediaPlayer.play()
+        display.vlc_media_player.play()
         if not self.media_state_wait(display, vlc.State.Playing):
             return False
         self.volume(display, controller.media_info.volume)
         if start_time > 0:
             self.seek(display, controller.media_info.start_time * 1000)
-        controller.media_info.length = int(display.vlcMediaPlayer.get_media().get_duration() / 1000)
+        controller.media_info.length = int(display.vlc_media_player.get_media().get_duration() / 1000)
         controller.seek_slider.setMaximum(controller.media_info.length * 1000)
         self.state = MediaState.Playing
-        display.vlcWidget.raise_()
+        display.vlc_widget.raise_()
         return True
 
     def pause(self, display):
         """
         Pause the current item
         """
-        if display.vlcMedia.get_state() != vlc.State.Playing:
+        if display.vlc_media.get_state() != vlc.State.Playing:
             return
-        display.vlcMediaPlayer.pause()
+        display.vlc_media_player.pause()
         if self.media_state_wait(display, vlc.State.Paused):
             self.state = MediaState.Paused
 
@@ -233,7 +233,7 @@
         """
         Stop the current item
         """
-        display.vlcMediaPlayer.stop()
+        display.vlc_media_player.stop()
         self.state = MediaState.Stopped
 
     def volume(self, display, vol):
@@ -241,21 +241,21 @@
         Set the volume
         """
         if display.has_audio:
-            display.vlcMediaPlayer.audio_set_volume(vol)
+            display.vlc_media_player.audio_set_volume(vol)
 
     def seek(self, display, seek_value):
         """
         Go to a particular position
         """
-        if display.vlcMediaPlayer.is_seekable():
-            display.vlcMediaPlayer.set_time(seek_value)
+        if display.vlc_media_player.is_seekable():
+            display.vlc_media_player.set_time(seek_value)
 
     def reset(self, display):
         """
         Reset the player
         """
-        display.vlcMediaPlayer.stop()
-        display.vlcWidget.setVisible(False)
+        display.vlc_media_player.stop()
+        display.vlc_widget.setVisible(False)
         self.state = MediaState.Off
 
     def set_visible(self, display, status):
@@ -263,23 +263,23 @@
         Set the visibility
         """
         if self.has_own_widget:
-            display.vlcWidget.setVisible(status)
+            display.vlc_widget.setVisible(status)
 
     def update_ui(self, display):
         """
         Update the UI
         """
         # Stop video if playback is finished.
-        if display.vlcMedia.get_state() == vlc.State.Ended:
+        if display.vlc_media.get_state() == vlc.State.Ended:
             self.stop(display)
         controller = display.controller
         if controller.media_info.end_time > 0:
-            if display.vlcMediaPlayer.get_time() > controller.media_info.end_time * 1000:
+            if display.vlc_media_player.get_time() > controller.media_info.end_time * 1000:
                 self.stop(display)
                 self.set_visible(display, False)
         if not controller.seek_slider.isSliderDown():
             controller.seek_slider.blockSignals(True)
-            controller.seek_slider.setSliderPosition(display.vlcMediaPlayer.get_time())
+            controller.seek_slider.setSliderPosition(display.vlc_media_player.get_time())
             controller.seek_slider.blockSignals(False)
 
     def get_info(self):
@@ -287,8 +287,8 @@
         Return some information about this player
         """
         return(translate('Media.player', 'VLC is an external player which '
-            'supports a number of different formats.') +
-            '<br/> <strong>' + translate('Media.player', 'Audio') +
-            '</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' +
-            translate('Media.player', 'Video') + '</strong><br/>' +
-            str(VIDEO_EXT) + '<br/>')
+               'supports a number of different formats.') +
+               '<br/> <strong>' + translate('Media.player', 'Audio') +
+               '</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' +
+               translate('Media.player', 'Video') + '</strong><br/>' +
+               str(VIDEO_EXT) + '<br/>')
\ No newline at end of file

=== modified file 'openlp/core/ui/media/webkitplayer.py'
--- openlp/core/ui/media/webkitplayer.py	2013-12-24 08:56:50 +0000
+++ openlp/core/ui/media/webkitplayer.py	2014-01-01 11:50:01 +0000
@@ -376,9 +376,9 @@
         else:
             is_visible = "hidden"
         if controller.media_info.is_flash:
-            display.frame.evaluateJavaScript('show_flash("setVisible", null, null, "%s");' % (is_visible))
+            display.frame.evaluateJavaScript('show_flash("setVisible", null, null, "%s");' % is_visible)
         else:
-            display.frame.evaluateJavaScript('show_video("setVisible", null, null, null, "%s");' % (is_visible))
+            display.frame.evaluateJavaScript('show_video("setVisible", null, null, null, "%s");' % is_visible)
 
     def update_ui(self, display):
         """
@@ -412,9 +412,9 @@
         Return some information about this player
         """
         return(translate('Media.player', 'Webkit is a media player which runs '
-            'inside a web browser. This player allows text over video to be '
-            'rendered.') +
-            '<br/> <strong>' + translate('Media.player', 'Audio') +
-            '</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' +
-            translate('Media.player', 'Video') + '</strong><br/>' +
-            str(VIDEO_EXT) + '<br/>')
+               'inside a web browser. This player allows text over video to be '
+               'rendered.') +
+               '<br/> <strong>' + translate('Media.player', 'Audio') +
+               '</strong><br/>' + str(AUDIO_EXT) + '<br/><strong>' +
+               translate('Media.player', 'Video') + '</strong><br/>' +
+               str(VIDEO_EXT) + '<br/>')

=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py	2013-12-28 21:33:38 +0000
+++ openlp/core/ui/servicemanager.py	2014-01-01 11:50:01 +0000
@@ -4,8 +4,8 @@
 ###############################################################################
 # OpenLP - Open Source Lyrics Projection                                      #
 # --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2013 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan      #
+# Copyright (c) 2008-2014 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan      #
 # Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
 # Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
 # Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #
@@ -30,7 +30,6 @@
 The service manager sets up, loads, saves and manages services.
 """
 import html
-import logging
 import os
 import shutil
 import zipfile
@@ -38,11 +37,10 @@
 from tempfile import mkstemp
 from datetime import datetime, timedelta
 
-log = logging.getLogger(__name__)
-
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import Registry, AppLocation, Settings, ThemeLevel, check_directory_exists, UiStrings, translate
+from openlp.core.common import Registry, AppLocation, Settings, ThemeLevel, OpenLPMixin, RegistryMixin, \
+    check_directory_exists, UiStrings, translate
 from openlp.core.lib import OpenLPToolbar, ServiceItem, ItemCapabilities, PluginStatus, build_icon
 from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box
 from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm
@@ -65,6 +63,7 @@
     def keyPressEvent(self, event):
         """
         Capture Key press and respond accordingly.
+        :param event:
         """
         if isinstance(event, QtGui.QKeyEvent):
             # here accept the event and do something
@@ -75,7 +74,7 @@
                 self.service_manager.on_move_selection_down()
                 event.accept()
             elif event.key() == QtCore.Qt.Key_Delete:
-                self.service_manager.onDeleteFromService()
+                self.service_manager.on_delete_from_service()
                 event.accept()
             event.ignore()
         else:
@@ -85,6 +84,7 @@
         """
         Drag and drop event does not care what data is selected as the recipient will use events to request the data
         move just tell it what plugin to call
+        :param event:
         """
         if event.buttons() != QtCore.Qt.LeftButton:
             event.ignore()
@@ -99,16 +99,21 @@
         drag.start(QtCore.Qt.CopyAction)
 
 
-class ServiceManagerDialog(object):
+class Ui_ServiceManager(object):
     """
     UI part of the Service Manager
     """
     def setup_ui(self, widget):
         """
         Define the UI
+        :param widget:
         """
+        # start with the layout
+        self.layout = QtGui.QVBoxLayout(widget)
+        self.layout.setSpacing(0)
+        self.layout.setMargin(0)
         # Create the top toolbar
-        self.toolbar = OpenLPToolbar(self)
+        self.toolbar = OpenLPToolbar(widget)
         self.toolbar.add_toolbar_action('newService', text=UiStrings().NewService, icon=':/general/general_new.png',
                                         tooltip=UiStrings().CreateService, triggers=self.on_new_service_clicked)
         self.toolbar.add_toolbar_action('openService', text=UiStrings().OpenService,
@@ -120,7 +125,7 @@
                                         tooltip=translate('OpenLP.ServiceManager', 'Save this service.'),
                                         triggers=self.decide_save_method)
         self.toolbar.addSeparator()
-        self.theme_label = QtGui.QLabel('%s:' % UiStrings().Theme, self)
+        self.theme_label = QtGui.QLabel('%s:' % UiStrings().Theme, widget)
         self.theme_label.setMargin(3)
         self.theme_label.setObjectName('theme_label')
         self.toolbar.add_toolbar_widget(self.theme_label)
@@ -133,7 +138,7 @@
         self.toolbar.setObjectName('toolbar')
         self.layout.addWidget(self.toolbar)
         # Create the service manager list
-        self.service_manager_list = ServiceManagerList(self)
+        self.service_manager_list = ServiceManagerList(widget)
         self.service_manager_list.setEditTriggers(
             QtGui.QAbstractItemView.CurrentChanged |
             QtGui.QAbstractItemView.DoubleClicked |
@@ -151,51 +156,61 @@
         self.service_manager_list.__class__.dropEvent = self.drop_event
         self.layout.addWidget(self.service_manager_list)
         # Add the bottom toolbar
-        self.order_toolbar = OpenLPToolbar(self)
+        self.order_toolbar = OpenLPToolbar(widget)
         action_list = ActionList.get_instance()
         action_list.add_category(UiStrings().Service, CategoryOrder.standard_toolbar)
-        self.service_manager_list.move_top = self.order_toolbar.add_toolbar_action('moveTop',
+        self.service_manager_list.move_top = self.order_toolbar.add_toolbar_action(
+            'moveTop',
             text=translate('OpenLP.ServiceManager', 'Move to &top'), icon=':/services/service_top.png',
             tooltip=translate('OpenLP.ServiceManager', 'Move item to the top of the service.'),
-            can_shortcuts=True, category=UiStrings().Service, triggers=self.onServiceTop)
-        self.service_manager_list.move_up = self.order_toolbar.add_toolbar_action('moveUp',
+            can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_top)
+        self.service_manager_list.move_up = self.order_toolbar.add_toolbar_action(
+            'moveUp',
             text=translate('OpenLP.ServiceManager', 'Move &up'), icon=':/services/service_up.png',
             tooltip=translate('OpenLP.ServiceManager', 'Move item up one position in the service.'),
-            can_shortcuts=True, category=UiStrings().Service, triggers=self.onServiceUp)
-        self.service_manager_list.move_down = self.order_toolbar.add_toolbar_action('moveDown',
+            can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_up)
+        self.service_manager_list.move_down = self.order_toolbar.add_toolbar_action(
+            'moveDown',
             text=translate('OpenLP.ServiceManager', 'Move &down'), icon=':/services/service_down.png',
             tooltip=translate('OpenLP.ServiceManager', 'Move item down one position in the service.'),
-            can_shortcuts=True, category=UiStrings().Service, triggers=self.onServiceDown)
-        self.service_manager_list.move_bottom = self.order_toolbar.add_toolbar_action('moveBottom',
+            can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_down)
+        self.service_manager_list.move_bottom = self.order_toolbar.add_toolbar_action(
+            'moveBottom',
             text=translate('OpenLP.ServiceManager', 'Move to &bottom'), icon=':/services/service_bottom.png',
             tooltip=translate('OpenLP.ServiceManager', 'Move item to the end of the service.'),
-            can_shortcuts=True, category=UiStrings().Service, triggers=self.onServiceEnd)
-        self.service_manager_list.down = self.order_toolbar.add_toolbar_action('down',
+            can_shortcuts=True, category=UiStrings().Service, triggers=self.on_service_end)
+        self.service_manager_list.down = self.order_toolbar.add_toolbar_action(
+            'down',
             text=translate('OpenLP.ServiceManager', 'Move &down'), can_shortcuts=True,
             tooltip=translate('OpenLP.ServiceManager', 'Moves the selection down the window.'), visible=False,
             triggers=self.on_move_selection_down)
         action_list.add_action(self.service_manager_list.down)
-        self.service_manager_list.up = self.order_toolbar.add_toolbar_action('up',
+        self.service_manager_list.up = self.order_toolbar.add_toolbar_action(
+            'up',
             text=translate('OpenLP.ServiceManager', 'Move up'), can_shortcuts=True,
             tooltip=translate('OpenLP.ServiceManager', 'Moves the selection up the window.'), visible=False,
             triggers=self.on_move_selection_up)
         action_list.add_action(self.service_manager_list.up)
         self.order_toolbar.addSeparator()
-        self.service_manager_list.delete = self.order_toolbar.add_toolbar_action('delete', can_shortcuts=True,
+        self.service_manager_list.delete = self.order_toolbar.add_toolbar_action(
+            'delete', can_shortcuts=True,
             text=translate('OpenLP.ServiceManager', '&Delete From Service'), icon=':/general/general_delete.png',
             tooltip=translate('OpenLP.ServiceManager', 'Delete the selected item from the service.'),
-            triggers=self.onDeleteFromService)
+            triggers=self.on_delete_from_service)
         self.order_toolbar.addSeparator()
-        self.service_manager_list.expand = self.order_toolbar.add_toolbar_action('expand', can_shortcuts=True,
+        self.service_manager_list.expand = self.order_toolbar.add_toolbar_action(
+            'expand', can_shortcuts=True,
             text=translate('OpenLP.ServiceManager', '&Expand all'), icon=':/services/service_expand_all.png',
             tooltip=translate('OpenLP.ServiceManager', 'Expand all the service items.'),
             category=UiStrings().Service, triggers=self.on_expand_all)
-        self.service_manager_list.collapse = self.order_toolbar.add_toolbar_action('collapse', can_shortcuts=True,
+        self.service_manager_list.collapse = self.order_toolbar.add_toolbar_action(
+            'collapse', can_shortcuts=True,
             text=translate('OpenLP.ServiceManager', '&Collapse all'), icon=':/services/service_collapse_all.png',
             tooltip=translate('OpenLP.ServiceManager', 'Collapse all the service items.'),
             category=UiStrings().Service, triggers=self.on_collapse_all)
         self.order_toolbar.addSeparator()
-        self.service_manager_list.make_live = self.order_toolbar.add_toolbar_action('make_live', can_shortcuts=True,
+        self.service_manager_list.make_live = self.order_toolbar.add_toolbar_action(
+            'make_live', can_shortcuts=True,
             text=translate('OpenLP.ServiceManager', 'Go Live'), icon=':/general/general_live.png',
             tooltip=translate('OpenLP.ServiceManager', 'Send the selected item to Live.'),
             category=UiStrings().Service,
@@ -276,7 +291,7 @@
              self.service_manager_list.down,
              self.service_manager_list.expand,
              self.service_manager_list.collapse
-            ])
+             ])
         Registry().register_function('theme_update_list', self.update_theme_list)
         Registry().register_function('config_screen_changed', self.regenerate_service_items)
         Registry().register_function('theme_update_global', self.theme_change)
@@ -286,13 +301,12 @@
         """
         Accept Drag events
 
-        ``event``
-            Handle of the event pint passed
+        :param event: Handle of the event passed
         """
         event.accept()
 
 
-class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
+class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManager):
     """
     Manages the services. This involves taking text strings from plugins and adding them to the service. This service
     can then be zipped up with all the resources used into one OSZ or oszl file for use on any OpenLP v2 installation.
@@ -305,7 +319,6 @@
         super(ServiceManager, self).__init__(parent)
         self.active = build_icon(':/media/auto-start_active.png')
         self.inactive = build_icon(':/media/auto-start_inactive.png')
-        Registry().register('service_manager', self)
         self.service_items = []
         self.suffixes = []
         self.drop_position = 0
@@ -314,20 +327,28 @@
         self._modified = False
         self._file_name = ''
         self.service_has_all_original_files = True
+
+    def bootstrap_initialise(self):
+        """
+        To be called as part of initialisation
+        """
+        self.setup_ui(self)
+        # Need to use event as called across threads and UI is updated
+        QtCore.QObject.connect(self, QtCore.SIGNAL('servicemanager_set_item'), self.on_set_item)
+
+    def bootstrap_post_set_up(self):
+        """
+        Can be set up as a late setup
+        """
         self.service_note_form = ServiceNoteForm()
         self.service_item_edit_form = ServiceItemEditForm()
         self.start_time_form = StartTimeForm()
-        # start with the layout
-        self.layout = QtGui.QVBoxLayout(self)
-        self.layout.setSpacing(0)
-        self.layout.setMargin(0)
-        self.setup_ui(self)
-        # Need to use event as called across threads and UI is updated
-        QtCore.QObject.connect(self, QtCore.SIGNAL('servicemanager_set_item'), self.on_set_item)
 
     def set_modified(self, modified=True):
         """
         Setter for property "modified". Sets whether or not the current service has been modified.
+
+        :param modified: Indicates if the service has new or removed items.  Used to trigger a remote update.
         """
         if modified:
             self.service_id += 1
@@ -344,6 +365,8 @@
     def set_file_name(self, file_name):
         """
         Setter for service file.
+
+        :param file_name: The service file name
         """
         self._file_name = str(file_name)
         self.main_window.set_service_modified(self.is_modified(), self.short_file_name())
@@ -369,19 +392,20 @@
         """
         self.suffixes = []
 
-    def supported_suffixes(self, suffix):
+    def supported_suffixes(self, suffix_list):
         """
         Adds Suffixes supported to the master list. Called from Plugins.
 
-        ``suffix``
-            New Suffix to be supported
+        :param suffix_list: New Suffix's to be supported
         """
-        if not suffix in self.suffixes:
-            self.suffixes.append(suffix)
+        for suffix in suffix_list:
+            if not suffix in self.suffixes:
+                self.suffixes.append(suffix)
 
-    def on_new_service_clicked(self):
+    def on_new_service_clicked(self, field=None):
         """
         Create a new service.
+        :param field:
         """
         if self.is_modified():
             result = self.save_modified_service()
@@ -396,8 +420,7 @@
         """
         Loads the service file and saves the existing one it there is one unchanged.
 
-        ``load_file``
-            The service file to the loaded.  Will be None is from menu so selection will be required.
+        :param load_file: The service file to the loaded.  Will be None is from menu so selection will be required.
         """
         if self.is_modified():
             result = self.save_modified_service()
@@ -432,9 +455,10 @@
                                           QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard |
                                           QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Save)
 
-    def on_recent_service_clicked(self):
+    def on_recent_service_clicked(self, field=None):
         """
         Load a recent file as the service triggered by mainwindow recent service list.
+        :param field:
         """
         sender = self.sender()
         self.load_file(sender.data())
@@ -464,12 +488,12 @@
         temp_file, temp_file_name = mkstemp('.osz', 'openlp_')
         # We don't need the file handle.
         os.close(temp_file)
-        log.debug(temp_file_name)
+        self.log_debug(temp_file_name)
         path_file_name = str(self.file_name())
         path, file_name = os.path.split(path_file_name)
         base_name = os.path.splitext(file_name)[0]
         service_file_name = '%s.osj' % base_name
-        log.debug('ServiceManager.save_file - %s', path_file_name)
+        self.log_debug('ServiceManager.save_file - %s' % path_file_name)
         Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', path)
         service = []
         write_list = []
@@ -522,11 +546,11 @@
         for file_item in write_list:
             file_size = os.path.getsize(file_item)
             total_size += file_size
-        log.debug('ServiceManager.save_file - ZIP contents size is %i bytes' % total_size)
+        self.log_debug('ServiceManager.save_file - ZIP contents size is %i bytes' % total_size)
         service_content = json.dumps(service)
         # Usual Zip file cannot exceed 2GiB, file with Zip64 cannot be extracted using unzip in UNIX.
         allow_zip_64 = (total_size > 2147483648 + len(service_content))
-        log.debug('ServiceManager.save_file - allowZip64 is %s' % allow_zip_64)
+        self.log_debug('ServiceManager.save_file - allowZip64 is %s' % allow_zip_64)
         zip_file = None
         success = True
         self.main_window.increment_progress_bar()
@@ -549,7 +573,7 @@
                     shutil.copy(audio_from, save_file)
                 zip_file.write(audio_from, audio_to)
         except IOError:
-            log.exception('Failed to save service to disk: %s', temp_file_name)
+            self.log_exception('Failed to save service to disk: %s' % temp_file_name)
             self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
                                            translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
             success = False
@@ -578,12 +602,12 @@
         temp_file, temp_file_name = mkstemp('.oszl', 'openlp_')
         # We don't need the file handle.
         os.close(temp_file)
-        log.debug(temp_file_name)
+        self.log_debug(temp_file_name)
         path_file_name = str(self.file_name())
         path, file_name = os.path.split(path_file_name)
         base_name = os.path.splitext(file_name)[0]
         service_file_name = '%s.osj' % base_name
-        log.debug('ServiceManager.save_file - %s', path_file_name)
+        self.log_debug('ServiceManager.save_file - %s', path_file_name)
         Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', path)
         service = []
         self.application.set_busy_cursor()
@@ -604,7 +628,7 @@
             # First we add service contents.
             zip_file.writestr(service_file_name, service_content)
         except IOError:
-            log.exception('Failed to save service to disk: %s', temp_file_name)
+            self.log_exception('Failed to save service to disk: %s', temp_file_name)
             self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
                                            translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
             success = False
@@ -656,8 +680,8 @@
                                                           '(*.oszl)'))
         else:
             file_name = QtGui.QFileDialog.getSaveFileName(self.main_window, UiStrings().SaveService, path,
-                                                          translate('OpenLP.ServiceManager', 'OpenLP Service Files (*'
-                                                                                             '.osz);;'))
+                                                          translate('OpenLP.ServiceManager',
+                                                                    'OpenLP Service Files (*.osz);;'))
         if not file_name:
             return False
         if os.path.splitext(file_name)[1] == '':
@@ -668,9 +692,10 @@
         self.set_file_name(file_name)
         self.decide_save_method()
 
-    def decide_save_method(self):
+    def decide_save_method(self, field=None):
         """
         Determine which type of save method to use.
+        :param field:
         """
         if not self.file_name():
             return self.save_file_as()
@@ -682,6 +707,7 @@
     def load_file(self, file_name):
         """
         Load an existing service file
+        :param file_name:
         """
         if not file_name:
             return False
@@ -697,14 +723,14 @@
                 try:
                     ucs_file = zip_info.filename
                 except UnicodeDecodeError:
-                    log.exception('file_name "%s" is not valid UTF-8' % zip_info.file_name)
+                    self.log_exception('file_name "%s" is not valid UTF-8' % zip_info.file_name)
                     critical_error_message_box(message=translate('OpenLP.ServiceManager',
                                                'File is not a valid service.\n The content encoding is not UTF-8.'))
                     continue
                 os_file = ucs_file.replace('/', os.path.sep)
                 if not os_file.startswith('audio'):
                     os_file = os.path.split(os_file)[1]
-                log.debug('Extract file: %s', os_file)
+                self.log_debug('Extract file: %s' % os_file)
                 zip_info.filename = os_file
                 zip_file.extract(zip_info, self.service_path)
                 if os_file.endswith('osj') or os_file.endswith('osd'):
@@ -741,19 +767,19 @@
                 Settings().setValue('servicemanager/last file', file_name)
             else:
                 critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File is not a valid service.'))
-                log.error('File contains no service data')
+                self.log_error('File contains no service data')
         except (IOError, NameError, zipfile.BadZipfile):
-            log.exception('Problem loading service file %s' % file_name)
+            self.log_exception('Problem loading service file %s' % file_name)
             critical_error_message_box(message=translate('OpenLP.ServiceManager',
                                        'File could not be opened because it is corrupt.'))
         except zipfile.BadZipfile:
             if os.path.getsize(file_name) == 0:
-                log.exception('Service file is zero sized: %s' % file_name)
+                self.log_exception('Service file is zero sized: %s' % file_name)
                 QtGui.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Empty File'),
                                               translate('OpenLP.ServiceManager', 'This service file does not contain '
                                                                                  'any data.'))
             else:
-                log.exception('Service file is cannot be extracted as zip: %s' % file_name)
+                self.log_exception('Service file is cannot be extracted as zip: %s' % file_name)
                 QtGui.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Corrupt File'),
                                               translate('OpenLP.ServiceManager',
                                               'This file is either corrupt or it is not an OpenLP 2 service file.'))
@@ -780,6 +806,8 @@
     def context_menu(self, point):
         """
         The Right click context menu from the Serviceitem list
+
+        :param point: The location of the cursor.
         """
         item = self.service_manager_list.itemAt(point)
         if item is None:
@@ -843,9 +871,10 @@
                 theme_action.setChecked(True)
         self.menu.exec_(self.service_manager_list.mapToGlobal(point))
 
-    def on_service_item_note_form(self):
+    def on_service_item_note_form(self, field=None):
         """
         Allow the service note to be edited
+        :param field:
         """
         item = self.find_service_item()[0]
         self.service_note_form.text_edit.setPlainText(self.service_items[item]['service_item'].notes)
@@ -854,18 +883,20 @@
             self.repaint_service_list(item, -1)
             self.set_modified()
 
-    def on_start_time_form(self):
+    def on_start_time_form(self, field=None):
         """
         Opens a dialog to type in service item notes.
+        :param field:
         """
         item = self.find_service_item()[0]
         self.start_time_form.item = self.service_items[item]
         if self.start_time_form.exec_():
             self.repaint_service_list(item, -1)
 
-    def toggle_auto_play_slides_once(self):
+    def toggle_auto_play_slides_once(self, field=None):
         """
         Toggle Auto play slide once. Inverts auto play once option for the item
+        :param field:
         """
         item = self.find_service_item()[0]
         service_item = self.service_items[item]['service_item']
@@ -878,9 +909,10 @@
                 self.main_window.general_settings_section + '/loop delay')
         self.set_modified()
 
-    def toggle_auto_play_slides_loop(self):
+    def toggle_auto_play_slides_loop(self, field=None):
         """
         Toggle Auto play slide loop.
+        :param field:
         """
         item = self.find_service_item()[0]
         service_item = self.service_items[item]['service_item']
@@ -893,9 +925,10 @@
                 self.main_window.general_settings_section + '/loop delay')
         self.set_modified()
 
-    def on_timed_slide_interval(self):
+    def on_timed_slide_interval(self, field=None):
         """
         Shows input dialog for enter interval in seconds for delay
+        :param field:
         """
         item = self.find_service_item()[0]
         service_item = self.service_items[item]['service_item']
@@ -926,9 +959,10 @@
         self.service_items[item]['service_item'].will_auto_start = \
             not self.service_items[item]['service_item'].will_auto_start
 
-    def on_service_item_edit_form(self):
+    def on_service_item_edit_form(self, field=None):
         """
         Opens a dialog to edit the service item and update the service display if changes are saved.
+        :param field:
         """
         item = self.find_service_item()[0]
         self.service_item_edit_form.set_service_item(self.service_items[item]['service_item'])
@@ -941,11 +975,8 @@
         Called by the SlideController to request a preview item be made live and allows the next preview to be updated
         if relevant.
 
-        ``unique_identifier``
-            Reference to the service_item
-
-        ``row``
-            individual row number
+        :param unique_identifier: Reference to the service_item
+        :param row: individual row number
         """
         for sitem in self.service_items:
             if sitem['service_item'].unique_identifier == unique_identifier:
@@ -976,9 +1007,7 @@
         """
         Called by the SlideController to select the previous service item.
 
-        ``last_slide``
-            Is this the last slide in the service_item
-
+        :param last_slide: Is this the last slide in the service_item.
         """
         if not self.service_manager_list.selected_items():
             return
@@ -1006,17 +1035,22 @@
                 prev_item_last_slide = service_iterator.value()
             service_iterator += 1
 
-    def on_set_item(self, message):
+    def on_set_item(self, message, field=None):
         """
         Called by a signal to select a specific item and make it live usually from remote.
+
+        :param field:
+        :param message: The data passed in from a remove message
         """
         self.set_item(int(message))
 
     def set_item(self, index):
         """
         Makes a specific item in the service live.
+
+        :param index: The index of the service item list to be actioned.
         """
-        if index >= 0 and index < self.service_manager_list.topLevelItemCount():
+        if 0 >= index < self.service_manager_list.topLevelItemCount():
             item = self.service_manager_list.topLevelItem(index)
             self.service_manager_list.setCurrentItem(item)
             self.make_live()
@@ -1041,9 +1075,10 @@
             return
         self.service_manager_list.setCurrentItem(item_after)
 
-    def on_collapse_all(self):
+    def on_collapse_all(self, field=None):
         """
         Collapse all the service items.
+        :param field:
         """
         for item in self.service_items:
             item['expanded'] = False
@@ -1052,13 +1087,16 @@
     def collapsed(self, item):
         """
         Record if an item is collapsed. Used when repainting the list to get the correct state.
+
+        :param item: The service item to be checked
         """
         pos = item.data(0, QtCore.Qt.UserRole)
         self.service_items[pos - 1]['expanded'] = False
 
-    def on_expand_all(self):
+    def on_expand_all(self, field=None):
         """
         Collapse all the service items.
+        :param field:
         """
         for item in self.service_items:
             item['expanded'] = True
@@ -1067,13 +1105,16 @@
     def expanded(self, item):
         """
         Record if an item is collapsed. Used when repainting the list to get the correct state.
+
+        :param item: The service item to be checked
         """
         pos = item.data(0, QtCore.Qt.UserRole)
         self.service_items[pos - 1]['expanded'] = True
 
-    def onServiceTop(self):
+    def on_service_top(self, field=None):
         """
         Move the current ServiceItem to the top of the list.
+        :param field:
         """
         item, child = self.find_service_item()
         if item < len(self.service_items) and item != -1:
@@ -1083,9 +1124,10 @@
             self.repaint_service_list(0, child)
             self.set_modified()
 
-    def onServiceUp(self):
+    def on_service_up(self, field=None):
         """
         Move the current ServiceItem one position up in the list.
+        :param field:
         """
         item, child = self.find_service_item()
         if item > 0:
@@ -1095,9 +1137,10 @@
             self.repaint_service_list(item - 1, child)
             self.set_modified()
 
-    def onServiceDown(self):
+    def on_service_down(self, field=None):
         """
         Move the current ServiceItem one position down in the list.
+        :param field:
         """
         item, child = self.find_service_item()
         if item < len(self.service_items) and item != -1:
@@ -1107,9 +1150,10 @@
             self.repaint_service_list(item + 1, child)
             self.set_modified()
 
-    def onServiceEnd(self):
+    def on_service_end(self, field=None):
         """
         Move the current ServiceItem to the bottom of the list.
+        :param field:
         """
         item, child = self.find_service_item()
         if item < len(self.service_items) and item != -1:
@@ -1119,9 +1163,10 @@
             self.repaint_service_list(len(self.service_items) - 1, child)
             self.set_modified()
 
-    def onDeleteFromService(self):
+    def on_delete_from_service(self, field=None):
         """
         Remove the current ServiceItem from the list.
+        :param field:
         """
         item = self.find_service_item()[0]
         if item != -1:
@@ -1134,11 +1179,8 @@
         Clear the existing service list and prepaint all the items. This is used when moving items as the move takes
         place in a supporting list, and when regenerating all the items due to theme changes.
 
-        ``service_item``
-            The item which changed. (int)
-
-        ``service_item_child``
-            The child of the ``service_item``, which will be selected. (int)
+        :param service_item: The item which changed. (int)
+        :param service_item_child: The child of the ``service_item``, which will be selected. (int)
         """
         # Correct order of items in array
         count = 1
@@ -1211,18 +1253,18 @@
         """
         Empties the service_path of temporary files on system exit.
         """
-        log.debug('Cleaning up service_path')
         for file_name in os.listdir(self.service_path):
             file_path = os.path.join(self.service_path, file_name)
             delete_file(file_path)
         if os.path.exists(os.path.join(self.service_path, 'audio')):
             shutil.rmtree(os.path.join(self.service_path, 'audio'), True)
 
-    def on_theme_combo_box_selected(self, currentIndex):
+    def on_theme_combo_box_selected(self, current_index):
         """
         Set the theme for the current service.
+
+        :param current_index: The combo box index for the selected item
         """
-        log.debug('on_theme_combo_box_selected')
         self.service_theme = self.theme_combo_box.currentText()
         self.renderer.set_service_theme(self.service_theme)
         Settings().setValue(self.main_window.service_manager_settings_section + '/service theme', self.service_theme)
@@ -1232,7 +1274,6 @@
         """
         The theme may have changed in the settings dialog so make sure the theme combo box is in the correct state.
         """
-        log.debug('theme_change')
         visible = self.renderer.theme_level == ThemeLevel.Global
         self.theme_label.setVisible(visible)
         self.theme_combo_box.setVisible(visible)
@@ -1240,9 +1281,10 @@
     def regenerate_service_items(self, changed=False):
         """
         Rebuild the service list as things have changed and a repaint is the easiest way to do this.
+
+        :param changed: True if the list has changed for new / removed items. False for a theme change.
         """
         self.application.set_busy_cursor()
-        log.debug('regenerate_service_items')
         # force reset of renderer as theme data has changed
         self.service_has_all_original_files = True
         if self.service_items:
@@ -1274,46 +1316,49 @@
             self.repaint_service_list(-1, -1)
         self.application.set_normal_cursor()
 
-    def replace_service_item(self, newItem):
+    def replace_service_item(self, new_item):
         """
         Using the service item passed replace the one with the same edit id if found.
+
+        :param new_item: a new service item to up date an existing one.
         """
         for item_count, item in enumerate(self.service_items):
-            if item['service_item'].edit_id == newItem.edit_id and item['service_item'].name == newItem.name:
-                newItem.render()
-                newItem.merge(item['service_item'])
-                item['service_item'] = newItem
+            if item['service_item'].edit_id == new_item.edit_id and item['service_item'].name == new_item.name:
+                new_item.render()
+                new_item.merge(item['service_item'])
+                item['service_item'] = new_item
                 self.repaint_service_list(item_count + 1, 0)
-                self.live_controller.replace_service_manager_item(newItem)
+                self.live_controller.replace_service_manager_item(new_item)
                 self.set_modified()
 
     def add_service_item(self, item, rebuild=False, expand=None, replace=False, repaint=True, selected=False):
         """
         Add a Service item to the list
 
-        ``item``
-            Service Item to be added
-
-        ``expand``
-            Override the default expand settings. (Tristate)
+        :param item: Service Item to be added
+        :param rebuild: Do we need to rebuild the live display (Default False)
+        :param expand: Override the default expand settings. (Tristate)
+        :param replace: Is the service item a replacement (Default False)
+        :param repaint: Do we need to repaint the service item list (Default True)
+        :param selected: Has the item been selected (Default False)
         """
         # if not passed set to config value
         if expand is None:
             expand = Settings().value('advanced/expand service item')
         item.from_service = True
         if replace:
-            sitem, child = self.find_service_item()
-            item.merge(self.service_items[sitem]['service_item'])
-            self.service_items[sitem]['service_item'] = item
-            self.repaint_service_list(sitem, child)
+            s_item, child = self.find_service_item()
+            item.merge(self.service_items[s_item]['service_item'])
+            self.service_items[s_item]['service_item'] = item
+            self.repaint_service_list(s_item, child)
             self.live_controller.replace_service_manager_item(item)
         else:
             item.render()
             # nothing selected for dnd
             if self.drop_position == 0:
                 if isinstance(item, list):
-                    for inditem in item:
-                        self.service_items.append({'service_item': inditem,
+                    for ind_item in item:
+                        self.service_items.append({'service_item': ind_item,
                                                    'order': len(self.service_items) + 1,
                                                    'expanded': expand, 'selected': selected})
                 else:
@@ -1324,8 +1369,8 @@
                     self.repaint_service_list(len(self.service_items) - 1, -1)
             else:
                 self.service_items.insert(self.drop_position,
-                    {'service_item': item, 'order': self.drop_position,
-                     'expanded': expand, 'selected': selected})
+                                          {'service_item': item, 'order': self.drop_position,
+                                           'expanded': expand, 'selected': selected})
                 self.repaint_service_list(self.drop_position, -1)
             # if rebuilding list make sure live is fixed.
             if rebuild:
@@ -1357,9 +1402,10 @@
         else:
             return self.service_items[item]['service_item']
 
-    def on_make_live(self):
+    def on_make_live(self, field=None):
         """
         Send the current item to the Live slide controller but triggered by a tablewidget click event.
+        :param field:
         """
         self.make_live()
 
@@ -1367,8 +1413,7 @@
         """
         Send the current item to the Live slide controller
 
-        ``row``
-            Row number to be displayed if from preview. -1 is passed if the value is not set
+        :param row: Row number to be displayed if from preview. -1 is passed if the value is not set
         """
         item, child = self.find_service_item()
         # No items in service
@@ -1394,9 +1439,10 @@
                                                  'is missing or inactive'))
         self.application.set_normal_cursor()
 
-    def remote_edit(self):
+    def remote_edit(self, field=None):
         """
         Triggers a remote edit to a plugin to allow item to be edited.
+        :param field:
         """
         item = self.find_service_item()[0]
         if self.service_items[item]['service_item'].is_capable(ItemCapabilities.CanEdit):
@@ -1405,9 +1451,10 @@
             if new_item:
                 self.add_service_item(new_item, replace=True)
 
-    def create_custom(self):
+    def create_custom(self, field=None):
         """
         Saves the current text item as a custom slide
+        :param field:
         """
         item = self.find_service_item()[0]
         Registry().execute('custom_create_from_service', self.service_items[item]['service_item'])
@@ -1441,8 +1488,7 @@
         Receive drop event and trigger an internal event to get the plugins to build and push the correct service item.
         The drag event payload carries the plugin name
 
-        ``event``
-            Handle of the event pint passed
+        :param event: Handle of the event passed
         """
         link = event.mimeData()
         if link.hasUrls():
@@ -1501,8 +1547,7 @@
         """
         Called from ThemeManager when the Themes have changed
 
-        ``theme_list``
-            A list of current themes to be displayed
+        :param theme_list: A list of current themes to be displayed
         """
         self.theme_combo_box.clear()
         self.theme_menu.clear()
@@ -1525,9 +1570,11 @@
         self.renderer.set_service_theme(self.service_theme)
         self.regenerate_service_items()
 
-    def on_theme_change_action(self):
+    def on_theme_change_action(self, field=None):
         """
         Handles theme change events
+
+        :param field:
         """
         theme = self.sender().objectName()
         # No object name means that the "Default" theme is supposed to be used.
@@ -1540,6 +1587,8 @@
     def _get_parent_item_data(self, item):
         """
         Finds and returns the parent item for any item
+
+        :param item: The service item list item to be checked.
         """
         parent_item = item.parent()
         if parent_item is None:

=== modified file 'openlp/core/ui/settingsform.py'
--- openlp/core/ui/settingsform.py	2013-12-28 21:33:38 +0000
+++ openlp/core/ui/settingsform.py	2014-01-01 11:50:01 +0000
@@ -51,7 +51,7 @@
         Initialise the settings form
         """
         Registry().register('settings_form', self)
-        Registry().register_function('bootstrap_post_set_up', self.post_set_up)
+        Registry().register_function('bootstrap_post_set_up', self.bootstrap_post_set_up)
         super(SettingsForm, self).__init__(parent)
         self.processes = []
         self.setupUi(self)
@@ -117,7 +117,7 @@
             self.stacked_layout.widget(tabIndex).cancel()
         return QtGui.QDialog.reject(self)
 
-    def post_set_up(self):
+    def bootstrap_post_set_up(self):
         """
         Run any post-setup code for the tabs on the form
         """

=== modified file 'openlp/core/ui/thememanager.py'
--- openlp/core/ui/thememanager.py	2013-12-28 21:33:38 +0000
+++ openlp/core/ui/thememanager.py	2014-01-01 11:50:01 +0000
@@ -42,28 +42,25 @@
     check_item_selected, create_thumb, validate_thumb
 from openlp.core.lib.theme import ThemeXML, BackgroundType
 from openlp.core.lib.ui import critical_error_message_box, create_widget_action
-from openlp.core.ui import FileRenameForm, ThemeForm, ThemeManagerHelper
+from openlp.core.ui import FileRenameForm, ThemeForm
 from openlp.core.utils import delete_file, get_locale_key, get_filesystem_encoding
 
 
-class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper):
-    """
-    Manages the orders of Theme.
-    """
-    def __init__(self, parent=None):
-        """
-        Constructor
-        """
-        super(ThemeManager, self).__init__(parent)
-        self.settings_section = 'themes'
-        self.theme_form = ThemeForm(self)
-        self.file_rename_form = FileRenameForm()
+class Ui_ThemeManager(object):
+    """
+    UI part of the Theme Manager
+    """
+    def setup_ui(self, widget):
+        """
+        Define the UI
+        :param widget: The screen object the the dialog is to be attached to.
+        """
         # start with the layout
-        self.layout = QtGui.QVBoxLayout(self)
+        self.layout = QtGui.QVBoxLayout(widget)
         self.layout.setSpacing(0)
         self.layout.setMargin(0)
         self.layout.setObjectName('layout')
-        self.toolbar = OpenLPToolbar(self)
+        self.toolbar = OpenLPToolbar(widget)
         self.toolbar.setObjectName('toolbar')
         self.toolbar.add_toolbar_action('newTheme',
                                         text=UiStrings().NewTheme, icon=':/themes/theme_new.png',
@@ -96,7 +93,7 @@
         self.theme_widget = QtGui.QWidgetAction(self.toolbar)
         self.theme_widget.setObjectName('theme_widget')
         # create theme manager list
-        self.theme_list_widget = QtGui.QListWidget(self)
+        self.theme_list_widget = QtGui.QListWidget(widget)
         self.theme_list_widget.setAlternatingRowColors(True)
         self.theme_list_widget.setIconSize(QtCore.QSize(88, 50))
         self.theme_list_widget.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
@@ -128,7 +125,18 @@
         # Signals
         self.theme_list_widget.doubleClicked.connect(self.change_global_from_screen)
         self.theme_list_widget.currentItemChanged.connect(self.check_list_state)
-        Registry().register_function('theme_update_global', self.change_global_from_tab)
+
+
+class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager):
+    """
+    Manages the orders of Theme.
+    """
+    def __init__(self, parent=None):
+        """
+        Constructor
+        """
+        super(ThemeManager, self).__init__(parent)
+        self.settings_section = 'themes'
         # Variables
         self.theme_list = []
         self.old_background_image = None
@@ -137,7 +145,7 @@
         """
         process the bootstrap initialise setup request
         """
-        self.log_debug('initialise called')
+        self.setup_ui(self)
         self.global_theme = Settings().value(self.settings_section + '/global theme')
         self.build_theme_path()
         self.load_first_time_themes()
@@ -146,12 +154,28 @@
         """
         process the bootstrap post setup request
         """
+        self.theme_form = ThemeForm(self)
+        self.theme_form.path = self.path
+        self.file_rename_form = FileRenameForm()
+        Registry().register_function('theme_update_global', self.change_global_from_tab)
         self._push_themes()
 
+    def build_theme_path(self):
+        """
+        Set up the theme path variables
+        """
+        self.path = AppLocation.get_section_data_path(self.settings_section)
+        check_directory_exists(self.path)
+        self.thumb_path = os.path.join(self.path, 'thumbnails')
+        check_directory_exists(self.thumb_path)
+
     def check_list_state(self, item, field=None):
         """
         If Default theme selected remove delete button.
         Note for some reason a dummy field is required.  Nothing is passed!
+
+        :param field:
+        :param item: Service Item to process
         """
         if item is None:
             return
@@ -165,8 +189,9 @@
 
     def context_menu(self, point):
         """
-        Build the Right Click Context menu and set state depending on
-        the type of theme.
+        Build the Right Click Context menu and set state depending on the type of theme.
+
+        :param point: The position of the mouse so the correct item can be found.
         """
         item = self.theme_list_widget.itemAt(point)
         if item is None:
@@ -200,8 +225,9 @@
 
     def change_global_from_screen(self, index=-1):
         """
-        Change the global theme when a theme is double clicked upon in the
-        Theme Manager list
+        Change the global theme when a theme is double clicked upon in the Theme Manager list.
+
+        :param index:
         """
         selected_row = self.theme_list_widget.currentRow()
         for count in range(0, self.theme_list_widget.count()):
@@ -221,8 +247,9 @@
 
     def on_add_theme(self, field=None):
         """
-        Loads a new theme with the default settings and then launches the theme
-        editing form for the user to make their customisations.
+        Loads a new theme with the default settings and then launches the theme editing form for the user to make
+        their customisations.
+        :param field:
         """
         theme = ThemeXML()
         theme.set_default_header_footer()
@@ -233,6 +260,7 @@
     def on_rename_theme(self, field=None):
         """
         Renames an existing theme to a new name
+        :param field:
         """
         if self._validate_theme_action(translate('OpenLP.ThemeManager', 'You must select a theme to rename.'),
                                        translate('OpenLP.ThemeManager', 'Rename Confirmation'),
@@ -257,6 +285,7 @@
     def on_copy_theme(self, field=None):
         """
         Copies an existing theme to a new name
+        :param field:
         """
         item = self.theme_list_widget.currentItem()
         old_theme_name = item.data(QtCore.Qt.UserRole)
@@ -271,6 +300,9 @@
     def clone_theme_data(self, theme_data, new_theme_name):
         """
         Takes a theme and makes a new copy of it as well as saving it.
+
+        :param theme_data: The theme to be used
+        :param new_theme_name: The new theme name to save the data to
         """
         save_to = None
         save_from = None
@@ -286,6 +318,7 @@
         """
         Loads the settings for the theme that is to be edited and launches the
         theme editing form so the user can make their changes.
+        :param field:
         """
         if check_item_selected(self.theme_list_widget,
                                translate('OpenLP.ThemeManager', 'You must select a theme to edit.')):
@@ -301,11 +334,12 @@
 
     def on_delete_theme(self, field=None):
         """
-        Delete a theme
+        Delete a theme triggered by the UI.
+        :param field:
         """
         if self._validate_theme_action(translate('OpenLP.ThemeManager', 'You must select a theme to delete.'),
-                                                 translate('OpenLP.ThemeManager', 'Delete Confirmation'),
-                                                 translate('OpenLP.ThemeManager', 'Delete %s theme?')):
+                                       translate('OpenLP.ThemeManager', 'Delete Confirmation'),
+                                       translate('OpenLP.ThemeManager', 'Delete %s theme?')):
             item = self.theme_list_widget.currentItem()
             theme = item.text()
             row = self.theme_list_widget.row(item)
@@ -320,8 +354,7 @@
         """
         Delete a theme.
 
-        ``theme``
-            The theme to delete.
+        :param theme: The theme to delete.
         """
         self.theme_list.remove(theme)
         thumb = '%s.png' % theme
@@ -337,6 +370,7 @@
     def on_export_theme(self, field=None):
         """
         Export the theme in a zip file
+        :param field:
         """
         item = self.theme_list_widget.currentItem()
         if item is None:
@@ -378,6 +412,7 @@
         """
         Opens a file dialog to select the theme file(s) to import before attempting to extract OpenLP themes from
         those files. This process will load both OpenLP version 1 and version 2 themes.
+        :param field:
         """
         files = FileDialog.getOpenFileNames(self,
                                             translate('OpenLP.ThemeManager', 'Select Theme Import File'),
@@ -462,8 +497,8 @@
         """
         Returns a theme object from an XML file
 
-        ``theme_name``
-            Name of the theme to load from file
+        :param theme_name: Name of the theme to load from file
+        :return The theme object.
         """
         self.log_debug('get theme data for theme %s' % theme_name)
         xml_file = os.path.join(self.path, str(theme_name), str(theme_name) + '.xml')
@@ -472,11 +507,13 @@
             self.log_debug('No theme data - using default theme')
             return ThemeXML()
         else:
-            return self._create_theme_from_Xml(xml, self.path)
+            return self._create_theme_from_xml(xml, self.path)
 
     def over_write_message_box(self, theme_name):
         """
         Display a warning box to the user that a theme already exists
+        :param theme_name: Name of the theme.
+        :return Confirm if the theme is to be overeritten.
         """
         ret = QtGui.QMessageBox.question(self, translate('OpenLP.ThemeManager', 'Theme Already Exists'),
                                          translate('OpenLP.ThemeManager',
@@ -489,11 +526,12 @@
 
     def unzip_theme(self, file_name, directory):
         """
-        Unzip the theme, remove the preview file if stored
-        Generate a new preview file. Check the XML theme version and upgrade if
-        necessary.
+        Unzip the theme, remove the preview file if stored. Generate a new preview file. Check the XML theme version
+        and upgrade if necessary.
+        :param file_name:
+        :param directory:
         """
-        self.log_debug('Unzipping theme %s', file_name)
+        self.log_debug('Unzipping theme %s' % file_name)
         file_name = str(file_name)
         theme_zip = None
         out_file = None
@@ -548,8 +586,8 @@
             if not abort_import:
                 # As all files are closed, we can create the Theme.
                 if file_xml:
-                    theme = self._create_theme_from_Xml(file_xml, self.path)
-                    self.generate_and_save_image(directory, theme_name, theme)
+                    theme = self._create_theme_from_xml(file_xml, self.path)
+                    self.generate_and_save_image(theme_name, theme)
                 # Only show the error message, when IOError was not raised (in
                 # this case the error message has already been shown).
                 elif theme_zip is not None:
@@ -562,8 +600,8 @@
         """
         Check if theme already exists and displays error message
 
-        ``theme_name``
-            Name of the Theme to test
+        :param theme_name:  Name of the Theme to test
+        :return True or False if theme exists
         """
         theme_dir = os.path.join(self.path, theme_name)
         if os.path.exists(theme_dir):
@@ -575,7 +613,11 @@
 
     def save_theme(self, theme, image_from, image_to):
         """
-        Called by thememaintenance Dialog to save the theme and to trigger the reload of the theme list
+        Called by theme maintenance Dialog to save the theme and to trigger the reload of the theme list
+
+        :param theme: The theme data object.
+        :param image_from: Where the theme image is currently located.
+        :param image_to: Where the Theme Image is to be saved to
         """
         self._write_theme(theme, image_from, image_to)
         if theme.background_type == BackgroundType.to_string(BackgroundType.Image):
@@ -587,6 +629,10 @@
     def _write_theme(self, theme, image_from, image_to):
         """
         Writes the theme to the disk and handles the background image if necessary
+
+        :param theme: The theme data object.
+        :param image_from: Where the theme image is currently located.
+        :param image_to: Where the Theme Image is to be saved to
         """
         name = theme.theme_name
         theme_pretty_xml = theme.extract_formatted_xml()
@@ -611,11 +657,14 @@
             except IOError as xxx_todo_changeme:
                 shutil.Error = xxx_todo_changeme
                 self.log_exception('Failed to save theme image')
-        self.generate_and_save_image(self.path, name, theme)
+        self.generate_and_save_image(name, theme)
 
-    def generate_and_save_image(self, directory, name, theme):
+    def generate_and_save_image(self, name, theme):
         """
         Generate and save a preview image
+
+        :param name: The name of the theme.
+        :param theme: The theme data object.
         """
         frame = self.generate_image(theme)
         sample_path_name = os.path.join(self.path, name + '.png')
@@ -632,7 +681,7 @@
         self.main_window.display_progress_bar(len(self.theme_list))
         for theme in self.theme_list:
             self.main_window.increment_progress_bar()
-            self.generate_and_save_image(self.path, theme, self.get_theme_data(theme))
+            self.generate_and_save_image(theme, self.get_theme_data(theme))
         self.main_window.finished_progress_bar()
         self.load_themes()
 
@@ -640,11 +689,8 @@
         """
         Call the renderer to build a Sample Image
 
-        ``theme_data``
-            The theme to generated a preview for.
-
-        ``force_page``
-            Flag to tell message lines per page need to be generated.
+        :param theme_data: The theme to generated a preview for.
+        :param force_page: Flag to tell message lines per page need to be generated.
         """
         return self.renderer.generate_preview(theme_data, force_page)
 
@@ -652,26 +698,33 @@
         """
         Return an image representing the look of the theme
 
-        ``theme``
-            The theme to return the image for
+        :param theme: The theme to return the image for.
         """
         return os.path.join(self.path, theme + '.png')
 
-    def _create_theme_from_Xml(self, theme_xml, path):
+    def _create_theme_from_xml(self, theme_xml, image_path):
         """
         Return a theme object using information parsed from XML
 
-        ``theme_xml``
-            The XML data to load into the theme
+        :param theme_xml: The Theme data object.
+        :param image_path: Where the theme image is stored
+        :return Theme data.
         """
         theme = ThemeXML()
         theme.parse(theme_xml)
-        theme.extend_image_filename(path)
+        theme.extend_image_filename(image_path)
         return theme
 
     def _validate_theme_action(self, select_text, confirm_title, confirm_text, test_plugin=True, confirm=True):
         """
         Check to see if theme has been selected and the destructive action is allowed.
+
+        :param select_text: Text for message box if no item selected.
+        :param confirm_title: Confirm message title to be displayed.
+        :param confirm_text: Confirm message text to be displayed.
+        :param test_plugin: Do we check the plugins for theme usage.
+        :param confirm: Do we display a confirm box before run checks.
+        :return True or False depending on the validity.
         """
         self.global_theme = Settings().value(self.settings_section + '/global theme')
         if check_item_selected(self.theme_list_widget, select_text):

=== removed file 'openlp/core/ui/thememanagerhelper.py'
--- openlp/core/ui/thememanagerhelper.py	2013-12-28 21:33:38 +0000
+++ openlp/core/ui/thememanagerhelper.py	1970-01-01 00:00:00 +0000
@@ -1,50 +0,0 @@
-# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
-
-###############################################################################
-# OpenLP - Open Source Lyrics Projection                                      #
-# --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2014 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan      #
-# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
-# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
-# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #
-# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith,             #
-# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock,              #
-# Frode Woldsund, Martin Zibricky, Patrick Zimmermann                         #
-# --------------------------------------------------------------------------- #
-# This program is free software; you can redistribute it and/or modify it     #
-# under the terms of the GNU General Public License as published by the Free  #
-# Software Foundation; version 2 of the License.                              #
-#                                                                             #
-# This program is distributed in the hope that it will be useful, but WITHOUT #
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
-# more details.                                                               #
-#                                                                             #
-# You should have received a copy of the GNU General Public License along     #
-# with this program; if not, write to the Free Software Foundation, Inc., 59  #
-# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
-###############################################################################
-"""
-The Theme Controller helps manages adding, deleteing and modifying of themes.
-"""
-import os
-
-from openlp.core.common import AppLocation, check_directory_exists
-
-
-class ThemeManagerHelper(object):
-    """
-    Manages the non ui theme functions.
-    """
-    def build_theme_path(self):
-        """
-        Set up the theme path variables
-        """
-        self.log_debug('build theme path called')
-        self.path = AppLocation.get_section_data_path(self.settings_section)
-        check_directory_exists(self.path)
-        self.thumb_path = os.path.join(self.path, 'thumbnails')
-        check_directory_exists(self.thumb_path)
-        self.theme_form.path = self.path
\ No newline at end of file

=== modified file 'openlp/plugins/alerts/alertsplugin.py'
--- openlp/plugins/alerts/alertsplugin.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/alerts/alertsplugin.py	2014-01-01 11:50:01 +0000
@@ -127,9 +127,15 @@
 
 
 class AlertsPlugin(Plugin):
+    """
+    The Alerts Plugin Class
+    """
     log.info('Alerts Plugin loaded')
 
     def __init__(self):
+        """
+        Class __init__ method
+        """
         super(AlertsPlugin, self).__init__('alerts', __default_settings__, settings_tab_class=AlertsTab)
         self.weight = -3
         self.icon_path = ':/plugins/plugin_alerts.png'
@@ -142,17 +148,20 @@
         """
         Give the alerts plugin the opportunity to add items to the **Tools** menu.
 
-        ``tools_menu``
-            The actual **Tools** menu item, so that your actions can use it as their parent.
+        :param tools_menu: The actual **Tools** menu item, so that your actions can use it as their parent.
         """
         log.info('add tools menu')
         self.tools_alert_item = create_action(tools_menu, 'toolsAlertItem',
-            text=translate('AlertsPlugin', '&Alert'), icon=':/plugins/plugin_alerts.png',
-            statustip=translate('AlertsPlugin', 'Show an alert message.'),
-            visible=False, can_shortcuts=True, triggers=self.on_alerts_trigger)
+                                              text=translate('AlertsPlugin', '&Alert'),
+                                              icon=':/plugins/plugin_alerts.png',
+                                              statustip=translate('AlertsPlugin', 'Show an alert message.'),
+                                              visible=False, can_shortcuts=True, triggers=self.on_alerts_trigger)
         self.main_window.tools_menu.addAction(self.tools_alert_item)
 
     def initialise(self):
+        """
+        Initialise plugin
+        """
         log.info('Alerts Initialising')
         super(AlertsPlugin, self).initialise()
         self.tools_alert_item.setVisible(True)
@@ -171,16 +180,27 @@
         action_list.remove_action(self.tools_alert_item, 'Tools')
 
     def toggle_alerts_state(self):
+        """
+        Switch the alerts state
+        """
         self.alerts_active = not self.alerts_active
         Settings().setValue(self.settings_section + '/active', self.alerts_active)
 
     def on_alerts_trigger(self):
+        """
+        Start of the Alerts dialog triggered from the main menu.
+        """
         self.alert_form.load_list()
         self.alert_form.exec_()
 
     def about(self):
+        """
+        Plugin Alerts about method
+
+        :return: text
+        """
         about_text = translate('AlertsPlugin', '<strong>Alerts Plugin</strong>'
-            '<br />The alert plugin controls the displaying of nursery alerts on the display screen.')
+                               '<br />The alert plugin controls the displaying of alerts on the display screen.')
         return about_text
 
     def set_plugin_text_strings(self):
@@ -209,7 +229,7 @@
         """
         align = VerticalType.Names[self.settings_tab.location]
         return CSS % (align, self.settings_tab.font_face, self.settings_tab.font_size, self.settings_tab.font_color,
-            self.settings_tab.background_color)
+                      self.settings_tab.background_color)
 
     def get_display_html(self):
         """
@@ -219,12 +239,11 @@
 
     def refresh_css(self, frame):
         """
-        Trigger an update of the CSS in the maindisplay.
+        Trigger an update of the CSS in the main display.
 
-        ``frame``
-            The Web frame holding the page.
+        :param frame: The Web frame holding the page.
         """
         align = VerticalType.Names[self.settings_tab.location]
         frame.evaluateJavaScript('update_css("%s", "%s", "%s", "%s", "%s")' %
-            (align, self.settings_tab.font_face, self.settings_tab.font_size,
-            self.settings_tab.font_color, self.settings_tab.background_color))
+                                 (align, self.settings_tab.font_face, self.settings_tab.font_size,
+                                 self.settings_tab.font_color, self.settings_tab.background_color))

=== modified file 'openlp/plugins/alerts/forms/alertdialog.py'
--- openlp/plugins/alerts/forms/alertdialog.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/alerts/forms/alertdialog.py	2014-01-01 11:50:01 +0000
@@ -36,6 +36,11 @@
 
 class Ui_AlertDialog(object):
     def setupUi(self, alert_dialog):
+        """
+        Setup the Alert UI dialog
+
+        :param alert_dialog: The dialog
+        """
         alert_dialog.setObjectName('alert_dialog')
         alert_dialog.resize(400, 300)
         alert_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png'))
@@ -72,20 +77,25 @@
         self.save_button.setObjectName('save_button')
         self.manage_button_layout.addWidget(self.save_button)
         self.delete_button = create_button(alert_dialog, 'delete_button', role='delete', enabled=False,
-            click=alert_dialog.on_delete_button_clicked)
+                                           click=alert_dialog.on_delete_button_clicked)
         self.manage_button_layout.addWidget(self.delete_button)
         self.manage_button_layout.addStretch()
         self.alert_dialog_layout.addLayout(self.manage_button_layout, 1, 1)
         display_icon = build_icon(':/general/general_live.png')
         self.display_button = create_button(alert_dialog, 'display_button', icon=display_icon, enabled=False)
         self.display_close_button = create_button(alert_dialog, 'display_close_button', icon=display_icon,
-            enabled=False)
+                                                  enabled=False)
         self.button_box = create_button_box(alert_dialog, 'button_box', ['close'],
-            [self.display_button, self.display_close_button])
+                                            [self.display_button, self.display_close_button])
         self.alert_dialog_layout.addWidget(self.button_box, 2, 0, 1, 2)
         self.retranslateUi(alert_dialog)
 
     def retranslateUi(self, alert_dialog):
+        """
+        Retranslate the UI strings
+
+        :param alert_dialog: The dialog
+        """
         alert_dialog.setWindowTitle(translate('AlertsPlugin.AlertForm', 'Alert Message'))
         self.alert_entry_label.setText(translate('AlertsPlugin.AlertForm', 'Alert &text:'))
         self.alert_parameter.setText(translate('AlertsPlugin.AlertForm', '&Parameter:'))

=== modified file 'openlp/plugins/alerts/forms/alertform.py'
--- openlp/plugins/alerts/forms/alertform.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/alerts/forms/alertform.py	2014-01-01 11:50:01 +0000
@@ -113,9 +113,10 @@
         """
         if not self.alert_text_edit.text():
             QtGui.QMessageBox.information(self,
-                translate('AlertsPlugin.AlertForm', 'New Alert'),
-                translate('AlertsPlugin.AlertForm', 'You haven\'t specified any text for your alert. \n'
-                    'Please type in some text before clicking New.'))
+                                          translate('AlertsPlugin.AlertForm', 'New Alert'),
+                                          translate('AlertsPlugin.AlertForm',
+                                                    'You haven\'t specified any text for your alert. \n'
+                                                    'Please type in some text before clicking New.'))
         else:
             alert = AlertItem()
             alert.text = self.alert_text_edit.text()
@@ -153,10 +154,10 @@
         List item has been double clicked to display it.
         """
         item = self.alert_list_widget.selectedIndexes()[0]
-        bitem = self.alert_list_widget.item(item.row())
-        self.trigger_alert(bitem.text())
-        self.alert_text_edit.setText(bitem.text())
-        self.item_id = bitem.data(QtCore.Qt.UserRole)
+        list_item = self.alert_list_widget.item(item.row())
+        self.trigger_alert(list_item.text())
+        self.alert_text_edit.setText(list_item.text())
+        self.item_id = list_item.data(QtCore.Qt.UserRole)
         self.save_button.setEnabled(False)
 
     def on_single_click(self):
@@ -164,9 +165,9 @@
         List item has been single clicked to add it to the edit field so it can be changed.
         """
         item = self.alert_list_widget.selectedIndexes()[0]
-        bitem = self.alert_list_widget.item(item.row())
-        self.alert_text_edit.setText(bitem.text())
-        self.item_id = bitem.data(QtCore.Qt.UserRole)
+        list_item = self.alert_list_widget.item(item.row())
+        self.alert_text_edit.setText(list_item.text())
+        self.item_id = list_item.data(QtCore.Qt.UserRole)
         # If the alert does not contain '<>' we clear the ParameterEdit field.
         if self.alert_text_edit.text().find('<>') == -1:
             self.parameter_edit.setText('')
@@ -176,26 +177,30 @@
         """
         Prepares the alert text for displaying.
 
-        ``text``
-            The alert text (unicode).
+        :param text: The alert text.
         """
         if not text:
             return False
         # We found '<>' in the alert text, but the ParameterEdit field is empty.
-        if text.find('<>') != -1 and not self.parameter_edit.text() and QtGui.QMessageBox.question(self,
-                translate('AlertsPlugin.AlertForm', 'No Parameter Found'),
-                translate('AlertsPlugin.AlertForm', 'You have not entered a parameter to be replaced.\n'
-                    'Do you want to continue anyway?'),
-                QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
+        if text.find('<>') != -1 and not self.parameter_edit.text() and \
+            QtGui.QMessageBox.question(self,
+                                       translate('AlertsPlugin.AlertForm', 'No Parameter Found'),
+                                       translate('AlertsPlugin.AlertForm',
+                                                 'You have not entered a parameter to be replaced.\n'
+                                                 'Do you want to continue anyway?'),
+                                       QtGui.QMessageBox.StandardButtons(
+                                           QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
             self.parameter_edit.setFocus()
             return False
         # The ParameterEdit field is not empty, but we have not found '<>'
         # in the alert text.
-        elif text.find('<>') == -1 and self.parameter_edit.text() and QtGui.QMessageBox.question(self,
-                translate('AlertsPlugin.AlertForm', 'No Placeholder Found'),
-                translate('AlertsPlugin.AlertForm', 'The alert text does not contain \'<>\'.\n'
-                    'Do you want to continue anyway?'),
-                QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
+        elif text.find('<>') == -1 and self.parameter_edit.text() and \
+            QtGui.QMessageBox.question(self,
+                                       translate('AlertsPlugin.AlertForm', 'No Placeholder Found'),
+                                       translate('AlertsPlugin.AlertForm', 'The alert text does not contain \'<>\'.\n'
+                                                 'Do you want to continue anyway?'),
+                                       QtGui.QMessageBox.StandardButtons(
+                                           QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
             self.parameter_edit.setFocus()
             return False
         text = text.replace('<>', self.parameter_edit.text())
@@ -207,8 +212,7 @@
         Called when the *alert_list_widget*'s current row has been changed. This enables or disables buttons which
         require an item to act on.
 
-        ``row``
-            The row (int). If there is no current row, the value is -1.
+        :param row: The row (int). If there is no current row, the value is -1.
         """
         if row == -1:
             self.display_button.setEnabled(False)

=== modified file 'openlp/plugins/alerts/lib/alertsmanager.py'
--- openlp/plugins/alerts/lib/alertsmanager.py	2013-12-28 21:33:38 +0000
+++ openlp/plugins/alerts/lib/alertsmanager.py	2014-01-01 11:50:01 +0000
@@ -59,6 +59,8 @@
     def alert_text(self, message):
         """
         Called via a alerts_text event. Message is single element array containing text.
+
+        :param message: The message text to be displayed
         """
         if message:
             self.display_alert(message[0])
@@ -67,8 +69,7 @@
         """
         Called from the Alert Tab to display an alert.
 
-        ``text``
-            display text
+        :param text: The text to display
         """
         log.debug('display alert called %s' % text)
         if text:
@@ -98,8 +99,7 @@
         """
         Time has finished so if our time then request the next Alert if there is one and reset the timer.
 
-        ``event``
-            the QT event that has been triggered.
+        :param event: the QT event that has been triggered.
         """
         log.debug('timer event')
         if event.timerId() == self.timer_id:

=== modified file 'openlp/plugins/alerts/lib/alertstab.py'
--- openlp/plugins/alerts/lib/alertstab.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/alerts/lib/alertstab.py	2014-01-01 11:50:01 +0000
@@ -114,6 +114,9 @@
         self.font_preview.setText(UiStrings().OLPV2x)
 
     def on_background_color_button_clicked(self):
+        """
+        The background color has been changed.
+        """
         new_color = QtGui.QColorDialog.getColor(QtGui.QColor(self.background_color), self)
         if new_color.isValid():
             self.background_color = new_color.name()
@@ -121,9 +124,15 @@
             self.update_display()
 
     def on_font_combo_box_clicked(self):
+        """
+        The Font Combo was changed.
+        """
         self.update_display()
 
     def on_font_color_button_clicked(self):
+        """
+        The Font Color button has clicked.
+        """
         new_color = QtGui.QColorDialog.getColor(QtGui.QColor(self.font_color), self)
         if new_color.isValid():
             self.font_color = new_color.name()
@@ -131,14 +140,24 @@
             self.update_display()
 
     def on_timeout_spin_box_changed(self):
+        """
+        The Time out spin box has changed.
+
+        """
         self.timeout = self.timeout_spin_box.value()
         self.changed = True
 
     def on_font_size_spin_box_changed(self):
+        """
+        The font size spin box has changed.
+        """
         self.font_size = self.font_size_spin_box.value()
         self.update_display()
 
     def load(self):
+        """
+        Load the settings into the UI.
+        """
         settings = Settings()
         settings.beginGroup(self.settings_section)
         self.timeout = settings.value('timeout')
@@ -160,6 +179,9 @@
         self.changed = False
 
     def save(self):
+        """
+        Save the changes on exit of the Settings dialog.
+        """
         settings = Settings()
         settings.beginGroup(self.settings_section)
         # Check value has changed as no event handles this field
@@ -179,6 +201,9 @@
         self.changed = False
 
     def update_display(self):
+        """
+        Update the preview display after changes have been made,
+        """
         font = QtGui.QFont()
         font.setFamily(self.font_combo_box.currentFont().family())
         font.setBold(True)

=== modified file 'openlp/plugins/alerts/lib/db.py'
--- openlp/plugins/alerts/lib/db.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/alerts/lib/db.py	2014-01-01 11:50:01 +0000
@@ -47,14 +47,14 @@
     """
     Setup the alerts database connection and initialise the database schema
 
-    ``url``
+    :param url:
         The database to setup
     """
     session, metadata = init_db(url)
 
     alerts_table = Table('alerts', metadata,
-        Column('id', types.Integer(), primary_key=True),
-        Column('text', types.UnicodeText, nullable=False))
+                         Column('id', types.Integer(), primary_key=True),
+                         Column('text', types.UnicodeText, nullable=False))
 
     mapper(AlertItem, alerts_table)
 

=== modified file 'openlp/plugins/custom/forms/editcustomdialog.py'
--- openlp/plugins/custom/forms/editcustomdialog.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/custom/forms/editcustomdialog.py	2014-01-01 11:50:01 +0000
@@ -36,6 +36,10 @@
 
 class Ui_CustomEditDialog(object):
     def setupUi(self, custom_edit_dialog):
+        """
+        Build the Edit Dialog UI
+        :param custom_edit_dialog: The Dialog
+        """
         custom_edit_dialog.setObjectName('custom_edit_dialog')
         custom_edit_dialog.resize(450, 350)
         custom_edit_dialog.setWindowIcon(build_icon(':/icon/openlp-logo-16x16.png'))
@@ -100,7 +104,7 @@
         self.dialog_layout.addLayout(self.bottom_form_layout)
         self.preview_button = QtGui.QPushButton()
         self.button_box = create_button_box(custom_edit_dialog, 'button_box', ['cancel', 'save'],
-            [self.preview_button])
+                                            [self.preview_button])
         self.dialog_layout.addWidget(self.button_box)
         self.retranslateUi(custom_edit_dialog)
 

=== modified file 'openlp/plugins/custom/forms/editcustomform.py'
--- openlp/plugins/custom/forms/editcustomform.py	2013-12-28 21:33:38 +0000
+++ openlp/plugins/custom/forms/editcustomform.py	2014-01-01 11:50:01 +0000
@@ -70,8 +70,7 @@
         """
         Load a list of themes into the themes combo box.
 
-        ``theme_list``
-            The list of themes to load.
+        :param theme_list: The list of themes to load.
         """
         self.theme_combo_box.clear()
         self.theme_combo_box.addItem('')
@@ -81,12 +80,8 @@
         """
         Called when editing or creating a new custom.
 
-        ``id``
-            The custom's id. If zero, then a new custom is created.
-
-        ``preview``
-            States whether the custom is edited while being previewed in the
-            preview panel.
+        :param id: The custom's id. If zero, then a new custom is created.
+        :param preview: States whether the custom is edited while being previewed in the preview panel.
         """
         self.slide_list_view.clear()
         if id == 0:
@@ -195,11 +190,8 @@
         """
         Updates the slide list after editing slides.
 
-        ``slides``
-            A list of all slides which have been edited.
-
-        ``edit_all``
-            Indicates if all slides or only one slide has been edited.
+        :param slides: A list of all slides which have been edited.
+        :param edit_all:  Indicates if all slides or only one slide has been edited.
         """
         if edit_all:
             self.slide_list_view.clear()
@@ -229,8 +221,7 @@
         Called when the *slide_list_view*'s current row has been changed. This
         enables or disables buttons which require an slide to act on.
 
-        ``row``
-            The row (int). If there is no current row, the value is -1.
+        :param row: The row (int). If there is no current row, the value is -1.
         """
         if row == -1:
             self.delete_button.setEnabled(False)

=== modified file 'openlp/plugins/custom/forms/editcustomslideform.py'
--- openlp/plugins/custom/forms/editcustomslideform.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/custom/forms/editcustomslideform.py	2014-01-01 11:50:01 +0000
@@ -57,8 +57,7 @@
         """
         Set the text for slide_text_edit.
 
-        ``text``
-            The text (unicode).
+        :param text: The text (unicode).
         """
         self.slide_text_edit.clear()
         if text:
@@ -87,7 +86,9 @@
 
     def insert_single_line_text_at_cursor(self, text):
         """
-        Adds ``text`` in a single line at the cursor position.
+        Adds a single line at the cursor position.
+
+        :param text: The text to be inserted
         """
         full_text = self.slide_text_edit.toPlainText()
         position = self.slide_text_edit.textCursor().position()

=== modified file 'openlp/plugins/custom/lib/customtab.py'
--- openlp/plugins/custom/lib/customtab.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/custom/lib/customtab.py	2014-01-01 11:50:01 +0000
@@ -72,6 +72,8 @@
     def on_display_footer_check_box_changed(self, check_state):
         """
         Toggle the setting for displaying the footer.
+
+        :param check_state: The current check box state
         """
         self.display_footer = False
         # we have a set value convert to True/False
@@ -79,9 +81,18 @@
             self.display_footer = True
 
     def on_add_from_service_check_box_changed(self, check_state):
+        """
+        Allows service items to create Custom items.
+
+        :param check_state: The current check box state
+        """
         self.update_load = (check_state == QtCore.Qt.Checked)
 
     def load(self):
+        """
+
+        Load the settings into the dialog
+        """
         settings = Settings()
         settings.beginGroup(self.settings_section)
         self.display_footer = settings.value('display footer')
@@ -91,6 +102,9 @@
         settings.endGroup()
 
     def save(self):
+        """
+        Save the Dialog settings
+        """
         settings = Settings()
         settings.beginGroup(self.settings_section)
         settings.setValue('display footer', self.display_footer)

=== modified file 'openlp/plugins/custom/lib/customxmlhandler.py'
--- openlp/plugins/custom/lib/customxmlhandler.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/custom/lib/customxmlhandler.py	2014-01-01 11:50:01 +0000
@@ -50,6 +50,7 @@
 
 log = logging.getLogger(__name__)
 
+
 #TODO: These classes need to be refactored into a single class.
 class CustomXMLBuilder(object):
     """
@@ -89,15 +90,11 @@
         """
         Add a verse to the ``<lyrics>`` tag.
 
-        ``verse_type``
-            A string denoting the type of verse. Possible values are "Chorus",
-            "Verse", "Bridge", and "Custom".
-
-        ``number``
-            An integer denoting the number of the item, for example: verse 1.
-
-        ``content``
-            The actual text of the verse to be stored.
+        :param verse_type: A string denoting the type of verse. Possible values are "Chorus", "Verse", "Bridge",
+        and "Custom".
+        :param number:  An integer denoting the number of the item, for example: verse 1.
+        :param content: The actual text of the verse to be stored.
+
         """
         verse = self.custom_xml.createElement('verse')
         verse.setAttribute('type', verse_type)

=== modified file 'openlp/plugins/custom/lib/db.py'
--- openlp/plugins/custom/lib/db.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/custom/lib/db.py	2014-01-01 11:50:01 +0000
@@ -71,7 +71,7 @@
                                Column('text', types.UnicodeText, nullable=False),
                                Column('credits', types.UnicodeText),
                                Column('theme_name', types.Unicode(128))
-    )
+                               )
 
     mapper(CustomSlide, custom_slide_table)
 

=== modified file 'openlp/plugins/custom/lib/mediaitem.py'
--- openlp/plugins/custom/lib/mediaitem.py	2013-12-28 21:33:38 +0000
+++ openlp/plugins/custom/lib/mediaitem.py	2014-01-01 11:50:01 +0000
@@ -73,6 +73,9 @@
         self.remote_custom = -1
 
     def add_end_header_bar(self):
+        """
+        Add the Custom End Head bar and register events and functions
+        """
         self.toolbar.addSeparator()
         self.add_search_to_toolbar()
         # Signals and slots
@@ -91,10 +94,17 @@
         self.add_custom_from_service = Settings().value(self.settings_section + '/add custom from service')
 
     def retranslateUi(self):
+        """
+
+
+        """
         self.search_text_label.setText('%s:' % UiStrings().Search)
         self.search_text_button.setText(UiStrings().Search)
 
     def initialise(self):
+        """
+        Initialise the UI so it can provide Searches
+        """
         self.search_text_edit.set_search_types([(CustomSearch.Titles, ':/songs/song_search_title.png',
                                                 translate('SongsPlugin.MediaItem', 'Titles'),
                                                 translate('SongsPlugin.MediaItem', 'Search Titles...')),
@@ -107,6 +117,11 @@
 
     def load_list(self, custom_slides, target_group=None):
         # Sort out what custom we want to select after loading the list.
+        """
+
+        :param custom_slides:
+        :param target_group:
+        """
         self.save_auto_select_id()
         self.list_view.clear()
         custom_slides.sort()
@@ -123,6 +138,9 @@
         # active trigger it and clean up so it will not update again.
 
     def on_new_click(self):
+        """
+        Handle the New item event
+        """
         self.edit_custom_form.load_custom(0)
         self.edit_custom_form.exec_()
         self.on_clear_text_button_click()
@@ -132,6 +150,9 @@
         """
         Called by ServiceManager or SlideController by event passing the custom Id in the payload along with an
         indicator to say which type of display is required.
+
+        :param custom_id: The id of the item to be edited
+        :param preview: Do we need to update the Preview after the edit. (Default False)
         """
         custom_id = int(custom_id)
         valid = self.plugin.db_manager.get_object(CustomSlide, custom_id)
@@ -184,12 +205,20 @@
             self.on_search_text_button_clicked()
 
     def on_focus(self):
+        """
+        Set the focus
+        """
         self.search_text_edit.setFocus()
 
     def generate_slide_data(self, service_item, item=None, xml_version=False,
                             remote=False, context=ServiceItemContext.Service):
         """
         Generate the slide data. Needs to be implemented by the plugin.
+        :param service_item: To be updated
+        :param item: The custom database item to be used
+        :param xml_version: No used
+        :param remote: Is this triggered by the Preview Controller or Service Manager.
+        :param context: Why is this item required to be build (Default Service).
         """
         item_id = self._get_id_of_item_to_generate(item, self.remote_custom)
         service_item.add_capability(ItemCapabilities.CanEdit)
@@ -243,6 +272,8 @@
         """
         If search as type enabled invoke the search on each key press. If the Title is being searched do not start until
         2 characters have been entered.
+
+        :param text: The search text
         """
         search_length = 2
         if len(text) > search_length:
@@ -253,6 +284,8 @@
     def service_load(self, item):
         """
         Triggered by a custom item being loaded by the service manager.
+
+        :param item: The service Item from the service to load found in the database.
         """
         log.debug('service_load')
         if self.plugin.status != PluginStatus.Active:
@@ -271,6 +304,8 @@
     def create_from_service_item(self, item):
         """
         Create a custom slide from a text service item
+
+        :param item:  the service item to be converted to a Custom item
         """
         custom = CustomSlide()
         custom.title = item.title
@@ -303,6 +338,9 @@
     def search(self, string, show_error):
         """
         Search the database for a given item.
+
+        :param string: The search string
+        :param show_error: The error string to be show.
         """
         search = '%' + string.lower() + '%'
         search_results = self.plugin.db_manager.get_all_objects(CustomSlide,

=== modified file 'openlp/plugins/media/lib/mediaitem.py'
--- openlp/plugins/media/lib/mediaitem.py	2013-12-28 21:33:38 +0000
+++ openlp/plugins/media/lib/mediaitem.py	2014-01-01 11:50:01 +0000
@@ -94,7 +94,7 @@
         self.reset_action.setToolTip(UiStrings().ResetLiveBG)
         self.automatic = UiStrings().Automatic
         self.display_type_label.setText(translate('MediaPlugin.MediaItem', 'Use Player:'))
-        self.rebuild_players()
+        #self.rebuild_players()
 
     def required_icons(self):
         """
@@ -112,9 +112,9 @@
     def add_end_header_bar(self):
         # Replace backgrounds do not work at present so remove functionality.
         self.replace_action = self.toolbar.add_toolbar_action('replace_action', icon=':/slides/slide_blank.png',
-            triggers=self.onReplaceClick)
+                                                              triggers=self.on_replace_click)
         self.reset_action = self.toolbar.add_toolbar_action('reset_action', icon=':/system/system_close.png',
-            visible=False, triggers=self.onResetClick)
+                                                            visible=False, triggers=self.on_reset_click)
         self.media_widget = QtGui.QWidget(self)
         self.media_widget.setObjectName('media_widget')
         self.display_layout = QtGui.QFormLayout(self.media_widget)
@@ -128,16 +128,16 @@
         self.display_layout.addRow(self.display_type_label, self.display_type_combo_box)
         # Add the Media widget to the page layout.
         self.page_layout.addWidget(self.media_widget)
-        self.display_type_combo_box.currentIndexChanged.connect(self.overridePlayerChanged)
+        self.display_type_combo_box.currentIndexChanged.connect(self.override_player_changed)
 
-    def overridePlayerChanged(self, index):
+    def override_player_changed(self, index):
         player = get_media_players()[0]
         if index == 0:
             set_media_players(player)
         else:
             set_media_players(player, player[index-1])
 
-    def onResetClick(self):
+    def on_reset_click(self):
         """
         Called to reset the Live background with the media selected,
         """
@@ -150,12 +150,13 @@
         """
         self.reset_action.setVisible(False)
 
-    def onReplaceClick(self):
+    def on_replace_click(self):
         """
         Called to replace Live background with the media selected.
         """
         if check_item_selected(self.list_view,
-                translate('MediaPlugin.MediaItem', 'You must select a media file to replace the background with.')):
+                               translate('MediaPlugin.MediaItem', 
+                                         'You must select a media file to replace the background with.')):
             item = self.list_view.currentItem()
             filename = item.data(QtCore.Qt.UserRole)
             if os.path.exists(filename):
@@ -168,14 +169,16 @@
                     self.reset_action.setVisible(True)
                 else:
                     critical_error_message_box(UiStrings().LiveBGError,
-                        translate('MediaPlugin.MediaItem', 'There was no display item to amend.'))
+                                               translate('MediaPlugin.MediaItem', 
+                                                         'There was no display item to amend.'))
             else:
                 critical_error_message_box(UiStrings().LiveBGError,
-                    translate('MediaPlugin.MediaItem',
-                    'There was a problem replacing your background, the media file "%s" no longer exists.') % filename)
+                                           translate('MediaPlugin.MediaItem',
+                                                     'There was a problem replacing your background, '
+                                                     'the media file "%s" no longer exists.') % filename)
 
     def generate_slide_data(self, service_item, item=None, xml_version=False, remote=False,
-            context=ServiceItemContext.Live):
+                            context=ServiceItemContext.Live):
         """
         Generate the slide data. Needs to be implemented by the plugin.
         """
@@ -211,16 +214,16 @@
     def initialise(self):
         self.list_view.clear()
         self.list_view.setIconSize(QtCore.QSize(88, 50))
-        self.servicePath = os.path.join(AppLocation.get_section_data_path(self.settings_section), 'thumbnails')
-        check_directory_exists(self.servicePath)
+        self.service_path = os.path.join(AppLocation.get_section_data_path(self.settings_section), 'thumbnails')
+        check_directory_exists(self.service_path)
         self.load_list(Settings().value(self.settings_section + '/media files'))
-        self.populateDisplayTypes()
+        self.populate_display_types()
 
     def rebuild_players(self):
         """
         Rebuild the tab in the media manager when changes are made in the settings.
         """
-        self.populateDisplayTypes()
+        self.populate_display_types()
         self.on_new_file_masks = translate('MediaPlugin.MediaItem', 'Videos (%s);;Audio (%s);;%s (*)') % (
             ' '.join(self.media_controller.video_extensions_list),
             ' '.join(self.media_controller.audio_extensions_list), UiStrings().AllFiles)
@@ -228,25 +231,25 @@
     def display_setup(self):
         self.media_controller.setup_display(self.display_controller.preview_display, False)
 
-    def populateDisplayTypes(self):
+    def populate_display_types(self):
         """
         Load the combobox with the enabled media players,  allowing user to select a specific player if settings allow.
         """
-        # block signals to avoid unnecessary overridePlayerChanged Signals while combo box creation
+        # block signals to avoid unnecessary override_player_changed Signals while combo box creation
         self.display_type_combo_box.blockSignals(True)
         self.display_type_combo_box.clear()
-        usedPlayers, overridePlayer = get_media_players()
+        used_players, override_player = get_media_players()
         media_players = self.media_controller.media_players
-        currentIndex = 0
-        for player in usedPlayers:
+        current_index = 0
+        for player in used_players:
             # load the drop down selection
             self.display_type_combo_box.addItem(media_players[player].original_name)
-            if overridePlayer == player:
-                currentIndex = len(self.display_type_combo_box)
+            if override_player == player:
+                current_index = len(self.display_type_combo_box)
         if self.display_type_combo_box.count() > 1:
             self.display_type_combo_box.insertItem(0, self.automatic)
-            self.display_type_combo_box.setCurrentIndex(currentIndex)
-        if overridePlayer:
+            self.display_type_combo_box.setCurrentIndex(current_index)
+        if override_player:
             self.media_widget.show()
         else:
             self.media_widget.hide()
@@ -257,7 +260,7 @@
         Remove a media item from the list.
         """
         if check_item_selected(self.list_view,
-                translate('MediaPlugin.MediaItem', 'You must select a media file to delete.')):
+                               translate('MediaPlugin.MediaItem', 'You must select a media file to delete.')):
             row_list = [item.row() for item in self.list_view.selectedIndexes()]
             row_list.sort(reverse=True)
             for row in row_list:
@@ -266,25 +269,25 @@
 
     def load_list(self, media, target_group=None):
         # Sort the media by its filename considering language specific characters.
-        media.sort(key=lambda filename: get_locale_key(os.path.split(str(filename))[1]))
+        media.sort(key=lambda file_name: get_locale_key(os.path.split(str(file_name))[1]))
         for track in media:
             track_info = QtCore.QFileInfo(track)
             if not os.path.exists(track):
-                filename = os.path.split(str(track))[1]
-                item_name = QtGui.QListWidgetItem(filename)
+                file_name = os.path.split(str(track))[1]
+                item_name = QtGui.QListWidgetItem(file_name)
                 item_name.setIcon(ERROR_ICON)
                 item_name.setData(QtCore.Qt.UserRole, track)
             elif track_info.isFile():
-                filename = os.path.split(str(track))[1]
-                item_name = QtGui.QListWidgetItem(filename)
-                if '*.%s' % (filename.split('.')[-1].lower()) in self.media_controller.audio_extensions_list:
+                file_name = os.path.split(str(track))[1]
+                item_name = QtGui.QListWidgetItem(file_name)
+                if '*.%s' % (file_name.split('.')[-1].lower()) in self.media_controller.audio_extensions_list:
                     item_name.setIcon(AUDIO_ICON)
                 else:
                     item_name.setIcon(VIDEO_ICON)
                 item_name.setData(QtCore.Qt.UserRole, track)
             else:
-                filename = os.path.split(str(track))[1]
-                item_name = QtGui.QListWidgetItem(filename)
+                file_name = os.path.split(str(track))[1]
+                item_name = QtGui.QListWidgetItem(file_name)
                 item_name.setIcon(build_icon(DVD_ICON))
                 item_name.setData(QtCore.Qt.UserRole, track)
             item_name.setToolTip(track)
@@ -302,7 +305,7 @@
         media = [x for x in media if os.path.splitext(x)[1] in extension]
         return media
 
-    def search(self, string, showError):
+    def search(self, string, show_error):
         files = Settings().value(self.settings_section + '/media files')
         results = []
         string = string.lower()

=== modified file 'tests/functional/openlp_core_common/test_init.py'
--- tests/functional/openlp_core_common/test_init.py	2013-12-13 19:44:17 +0000
+++ tests/functional/openlp_core_common/test_init.py	2014-01-01 11:50:01 +0000
@@ -4,8 +4,8 @@
 ###############################################################################
 # OpenLP - Open Source Lyrics Projection                                      #
 # --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2013 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan      #
+# Copyright (c) 2008-2014 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan      #
 # Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
 # Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
 # Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #

=== modified file 'tests/functional/openlp_core_lib/test_pluginmanager.py'
--- tests/functional/openlp_core_lib/test_pluginmanager.py	2013-12-28 21:33:38 +0000
+++ tests/functional/openlp_core_lib/test_pluginmanager.py	2014-01-01 11:50:01 +0000
@@ -71,7 +71,7 @@
 
         # THEN: The create_media_manager_item() method should have been called
         self.assertEqual(0, mocked_plugin.create_media_manager_item.call_count,
-            'The create_media_manager_item() method should not have been called.')
+                         'The create_media_manager_item() method should not have been called.')
 
     def hook_media_manager_with_active_plugin_test(self):
         """
@@ -104,7 +104,7 @@
 
         # THEN: The hook_settings_tabs() method should have been called
         self.assertEqual(0, mocked_plugin.create_media_manager_item.call_count,
-            'The create_media_manager_item() method should not have been called.')
+                         'The create_media_manager_item() method should not have been called.')
 
     def hook_settings_tabs_with_disabled_plugin_and_mocked_form_test(self):
         """
@@ -124,9 +124,9 @@
 
         # THEN: The create_settings_tab() method should not have been called, but the plugins lists should be the same
         self.assertEqual(0, mocked_plugin.create_settings_tab.call_count,
-            'The create_media_manager_item() method should not have been called.')
+                         'The create_media_manager_item() method should not have been called.')
         self.assertEqual(mocked_settings_form.plugin_manager.plugins, plugin_manager.plugins,
-            'The plugins on the settings form should be the same as the plugins in the plugin manager')
+                         'The plugins on the settings form should be the same as the plugins in the plugin manager')
 
     def hook_settings_tabs_with_active_plugin_and_mocked_form_test(self):
         """
@@ -146,9 +146,9 @@
 
         # THEN: The create_media_manager_item() method should have been called with the mocked settings form
         self.assertEqual(1, mocked_plugin.create_settings_tab.call_count,
-            'The create_media_manager_item() method should have been called once.')
+                         'The create_media_manager_item() method should have been called once.')
         self.assertEqual(plugin_manager.plugins, mocked_settings_form.plugin_manager.plugins,
-            'The plugins on the settings form should be the same as the plugins in the plugin manager')
+                         'The plugins on the settings form should be the same as the plugins in the plugin manager')
 
     def hook_settings_tabs_with_active_plugin_and_no_form_test(self):
         """
@@ -181,7 +181,7 @@
 
         # THEN: The create_media_manager_item() method should have been called
         self.assertEqual(0, mocked_plugin.add_import_menu_item.call_count,
-            'The add_import_menu_item() method should not have been called.')
+                         'The add_import_menu_item() method should not have been called.')
 
     def hook_import_menu_with_active_plugin_test(self):
         """
@@ -214,7 +214,7 @@
 
         # THEN: The add_export_menu_Item() method should not have been called
         self.assertEqual(0, mocked_plugin.add_export_menu_Item.call_count,
-            'The add_export_menu_Item() method should not have been called.')
+                         'The add_export_menu_Item() method should not have been called.')
 
     def hook_export_menu_with_active_plugin_test(self):
         """
@@ -248,7 +248,7 @@
 
         # THEN: The upgrade_settings() method should not have been called
         self.assertEqual(0, mocked_plugin.upgrade_settings.call_count,
-            'The upgrade_settings() method should not have been called.')
+                         'The upgrade_settings() method should not have been called.')
 
     def hook_upgrade_plugin_settings_with_active_plugin_test(self):
         """
@@ -282,7 +282,7 @@
 
         # THEN: The add_tools_menu_item() method should have been called
         self.assertEqual(0, mocked_plugin.add_tools_menu_item.call_count,
-            'The add_tools_menu_item() method should not have been called.')
+                         'The add_tools_menu_item() method should not have been called.')
 
     def hook_tools_menu_with_active_plugin_test(self):
         """

=== modified file 'tests/functional/openlp_core_lib/test_screen.py'
--- tests/functional/openlp_core_lib/test_screen.py	2013-12-28 21:33:38 +0000
+++ tests/functional/openlp_core_lib/test_screen.py	2014-01-01 11:50:01 +0000
@@ -84,4 +84,4 @@
         new_screen_count = len(self.screens.screen_list)
         self.assertEqual(old_screen_count + 1, new_screen_count, 'The new_screens list should be bigger')
         self.assertEqual(SCREEN, self.screens.screen_list.pop(),
-            'The 2nd screen should be identical to the first screen')
+                         'The 2nd screen should be identical to the first screen')

=== modified file 'tests/functional/openlp_core_lib/test_serviceitem.py'
--- tests/functional/openlp_core_lib/test_serviceitem.py	2013-12-28 21:33:38 +0000
+++ tests/functional/openlp_core_lib/test_serviceitem.py	2014-01-01 11:50:01 +0000
@@ -99,9 +99,9 @@
         # THEN: The frames should also be valid
         self.assertEqual('Test Custom', service_item.get_display_title(), 'The title should be "Test Custom"')
         self.assertEqual(VERSE[:-1], service_item.get_frames()[0]['text'],
-            'The returned text matches the input, except the last line feed')
+                         'The returned text matches the input, except the last line feed')
         self.assertEqual(VERSE.split('\n', 1)[0], service_item.get_rendered_frame(1),
-            'The first line has been returned')
+                         'The first line has been returned')
         self.assertEqual('Slide 1', service_item.get_frame_title(0), '"Slide 1" has been returned as the title')
         self.assertEqual('Slide 2', service_item.get_frame_title(1), '"Slide 2" has been returned as the title')
         self.assertEqual('', service_item.get_frame_title(2), 'Blank has been returned as the title of slide 3')
@@ -127,24 +127,24 @@
         # THEN: We should get back a valid service item
         self.assertTrue(service_item.is_valid, 'The new service item should be valid')
         self.assertEqual(test_file, service_item.get_rendered_frame(0),
-            'The first frame should match the path to the image')
+                         'The first frame should match the path to the image')
         self.assertEqual(frame_array, service_item.get_frames()[0],
-            'The return should match frame array1')
+                         'The return should match frame array1')
         self.assertEqual(test_file, service_item.get_frame_path(0),
-            'The frame path should match the full path to the image')
+                         'The frame path should match the full path to the image')
         self.assertEqual(image_name, service_item.get_frame_title(0),
-            'The frame title should match the image name')
+                         'The frame title should match the image name')
         self.assertEqual(image_name, service_item.get_display_title(),
-            'The display title should match the first image name')
+                         'The display title should match the first image name')
         self.assertTrue(service_item.is_image(), 'This service item should be of an "image" type')
         self.assertTrue(service_item.is_capable(ItemCapabilities.CanMaintain),
-            'This service item should be able to be Maintained')
+                        'This service item should be able to be Maintained')
         self.assertTrue(service_item.is_capable(ItemCapabilities.CanPreview),
-            'This service item should be able to be be Previewed')
+                        'This service item should be able to be be Previewed')
         self.assertTrue(service_item.is_capable(ItemCapabilities.CanLoop),
-            'This service item should be able to be run in a can be made to Loop')
+                        'This service item should be able to be run in a can be made to Loop')
         self.assertTrue(service_item.is_capable(ItemCapabilities.CanAppend),
-            'This service item should be able to have new items added to it')
+                        'This service item should be able to have new items added to it')
 
     def service_item_load_image_from_local_service_test(self):
         """
@@ -180,27 +180,27 @@
         self.assertTrue(service_item.is_valid, 'The first service item should be valid')
         self.assertTrue(service_item2.is_valid, 'The second service item should be valid')
         self.assertEqual(test_file1, service_item.get_rendered_frame(0),
-            'The first frame should match the path to the image')
+                         'The first frame should match the path to the image')
         self.assertEqual(test_file2, service_item2.get_rendered_frame(0),
-            'The Second frame should match the path to the image')
+                         'The Second frame should match the path to the image')
         self.assertEqual(frame_array1, service_item.get_frames()[0], 'The return should match the frame array1')
         self.assertEqual(frame_array2, service_item2.get_frames()[0], 'The return should match the frame array2')
         self.assertEqual(test_file1, service_item.get_frame_path(0),
-            'The frame path should match the full path to the image')
+                         'The frame path should match the full path to the image')
         self.assertEqual(test_file2, service_item2.get_frame_path(0),
-            'The frame path should match the full path to the image')
+                         'The frame path should match the full path to the image')
         self.assertEqual(image_name1, service_item.get_frame_title(0),
-            'The 1st frame title should match the image name')
+                         'The 1st frame title should match the image name')
         self.assertEqual(image_name2, service_item2.get_frame_title(0),
-            'The 2nd frame title should match the image name')
+                         'The 2nd frame title should match the image name')
         self.assertEqual(service_item.name, service_item.title.lower(),
-            'The plugin name should match the display title, as there are > 1 Images')
+                         'The plugin name should match the display title, as there are > 1 Images')
         self.assertTrue(service_item.is_image(), 'This service item should be of an "image" type')
         self.assertTrue(service_item.is_capable(ItemCapabilities.CanMaintain),
-            'This service item should be able to be Maintained')
+                        'This service item should be able to be Maintained')
         self.assertTrue(service_item.is_capable(ItemCapabilities.CanPreview),
-            'This service item should be able to be be Previewed')
+                        'This service item should be able to be be Previewed')
         self.assertTrue(service_item.is_capable(ItemCapabilities.CanLoop),
-            'This service item should be able to be run in a can be made to Loop')
+                        'This service item should be able to be run in a can be made to Loop')
         self.assertTrue(service_item.is_capable(ItemCapabilities.CanAppend),
-            'This service item should be able to have new items added to it')
+                        'This service item should be able to have new items added to it')

=== modified file 'tests/interfaces/openlp_core_ui/test_servicemanager.py'
--- tests/interfaces/openlp_core_ui/test_servicemanager.py	2013-12-13 17:44:05 +0000
+++ tests/interfaces/openlp_core_ui/test_servicemanager.py	2014-01-01 11:50:01 +0000
@@ -4,10 +4,10 @@
 
 from unittest import TestCase
 
-from PyQt4 import QtGui
+from PyQt4 import QtGui, QtTest, QtCore
 
 from openlp.core.common import Registry
-from openlp.core.lib import ScreenList, ServiceItem
+from openlp.core.lib import ScreenList, ServiceItem, ItemCapabilities
 from openlp.core.ui.mainwindow import MainWindow
 from tests.interfaces import MagicMock, patch
 
@@ -35,52 +35,287 @@
 
     def basic_service_manager_test(self):
         """
-        Test the Service Manager display functionality
+        Test the Service Manager UI Functionality
         """
         # GIVEN: A New Service Manager instance
 
-        # WHEN I have an empty display
+        # WHEN I have set up the display
+        self.service_manager.setup_ui(self.service_manager)
         # THEN the count of items should be zero
         self.assertEqual(self.service_manager.service_manager_list.topLevelItemCount(), 0,
                          'The service manager list should be empty ')
 
-    def context_menu_test(self):
-        """
-        Test the context_menu() method.
-        """
-        # GIVEN: A service item added
-        with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
-                patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
-                patch('PyQt4.QtGui.QMenu.exec_'):
-            mocked_item = MagicMock()
-            mocked_item.parent.return_value = None
-            mocked_item_at_method.return_value = mocked_item
-            # We want 1 to be returned for the position
-            mocked_item.data.return_value = 1
-            # A service item without capabilities.
-            service_item = ServiceItem()
-            self.service_manager.service_items = [{'service_item': service_item}]
-            q_point = None
-            # Mocked actions.
-            self.service_manager.edit_action.setVisible = MagicMock()
-            self.service_manager.create_custom_action.setVisible = MagicMock()
-            self.service_manager.maintain_action.setVisible = MagicMock()
-            self.service_manager.notes_action.setVisible = MagicMock()
-            self.service_manager.time_action.setVisible = MagicMock()
-            self.service_manager.auto_start_action.setVisible = MagicMock()
-
-            # WHEN: Show the context menu.
-            self.service_manager.context_menu(q_point)
-
-            # THEN: The following actions should be not visible.
-            self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
-                'The action should be set invisible.'
-            self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
-                'The action should be set invisible.'
-            self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
-                'The action should be set invisible.'
-            self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
-            self.service_manager.time_action.setVisible.assert_called_once_with(False), \
-                'The action should be set invisible.'
-            self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
-                'The action should be set invisible.'
+    def default_context_menu_test(self):
+        """
+        Test the context_menu() method with a default service item
+        """
+        # GIVEN: A service item added
+        self.service_manager.setup_ui(self.service_manager)
+        with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
+                patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
+                patch('PyQt4.QtGui.QMenu.exec_'):
+            mocked_item = MagicMock()
+            mocked_item.parent.return_value = None
+            mocked_item_at_method.return_value = mocked_item
+            # We want 1 to be returned for the position
+            mocked_item.data.return_value = 1
+            # A service item without capabilities.
+            service_item = ServiceItem()
+            self.service_manager.service_items = [{'service_item': service_item}]
+            q_point = None
+            # Mocked actions.
+            self.service_manager.edit_action.setVisible = MagicMock()
+            self.service_manager.create_custom_action.setVisible = MagicMock()
+            self.service_manager.maintain_action.setVisible = MagicMock()
+            self.service_manager.notes_action.setVisible = MagicMock()
+            self.service_manager.time_action.setVisible = MagicMock()
+            self.service_manager.auto_start_action.setVisible = MagicMock()
+
+            # WHEN: Show the context menu.
+            self.service_manager.context_menu(q_point)
+
+            # THEN: The following actions should be not visible.
+            self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
+            self.service_manager.time_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+
+    def edit_context_menu_test(self):
+        """
+        Test the context_menu() method with a edit service item
+        """
+        # GIVEN: A service item added
+        self.service_manager.setup_ui(self.service_manager)
+        with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
+                patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
+                patch('PyQt4.QtGui.QMenu.exec_'):
+            mocked_item = MagicMock()
+            mocked_item.parent.return_value = None
+            mocked_item_at_method.return_value = mocked_item
+            # We want 1 to be returned for the position
+            mocked_item.data.return_value = 1
+            # A service item without capabilities.
+            service_item = ServiceItem()
+            service_item.add_capability(ItemCapabilities.CanEdit)
+            service_item.edit_id = 1
+            self.service_manager.service_items = [{'service_item': service_item}]
+            q_point = None
+            # Mocked actions.
+            self.service_manager.edit_action.setVisible = MagicMock()
+            self.service_manager.create_custom_action.setVisible = MagicMock()
+            self.service_manager.maintain_action.setVisible = MagicMock()
+            self.service_manager.notes_action.setVisible = MagicMock()
+            self.service_manager.time_action.setVisible = MagicMock()
+            self.service_manager.auto_start_action.setVisible = MagicMock()
+
+            # WHEN: Show the context menu.
+            self.service_manager.context_menu(q_point)
+
+            # THEN: The following actions should be not visible.
+            self.service_manager.edit_action.setVisible.assert_called_with(True), \
+                'The action should be set visible.'
+            self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
+            self.service_manager.time_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+
+    def maintain_context_menu_test(self):
+        """
+        Test the context_menu() method with a maintain
+        """
+        # GIVEN: A service item added
+        self.service_manager.setup_ui(self.service_manager)
+        with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
+                patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
+                patch('PyQt4.QtGui.QMenu.exec_'):
+            mocked_item = MagicMock()
+            mocked_item.parent.return_value = None
+            mocked_item_at_method.return_value = mocked_item
+            # We want 1 to be returned for the position
+            mocked_item.data.return_value = 1
+            # A service item without capabilities.
+            service_item = ServiceItem()
+            service_item.add_capability(ItemCapabilities.CanMaintain)
+            self.service_manager.service_items = [{'service_item': service_item}]
+            q_point = None
+            # Mocked actions.
+            self.service_manager.edit_action.setVisible = MagicMock()
+            self.service_manager.create_custom_action.setVisible = MagicMock()
+            self.service_manager.maintain_action.setVisible = MagicMock()
+            self.service_manager.notes_action.setVisible = MagicMock()
+            self.service_manager.time_action.setVisible = MagicMock()
+            self.service_manager.auto_start_action.setVisible = MagicMock()
+
+            # WHEN: Show the context menu.
+            self.service_manager.context_menu(q_point)
+
+            # THEN: The following actions should be not visible.
+            self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.maintain_action.setVisible.assert_called_with(True), \
+                'The action should be set visible.'
+            self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
+            self.service_manager.time_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+
+    def loopy_context_menu_test(self):
+        """
+        Test the context_menu() method with a loop
+        """
+        # GIVEN: A service item added
+        self.service_manager.setup_ui(self.service_manager)
+        with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
+                patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
+                patch('PyQt4.QtGui.QMenu.exec_'):
+            mocked_item = MagicMock()
+            mocked_item.parent.return_value = None
+            mocked_item_at_method.return_value = mocked_item
+            # We want 1 to be returned for the position
+            mocked_item.data.return_value = 1
+            # A service item without capabilities.
+            service_item = ServiceItem()
+            service_item.add_capability(ItemCapabilities.CanLoop)
+            service_item._raw_frames.append("One")
+            service_item._raw_frames.append("Two")
+            self.service_manager.service_items = [{'service_item': service_item}]
+            q_point = None
+            # Mocked actions.
+            self.service_manager.edit_action.setVisible = MagicMock()
+            self.service_manager.create_custom_action.setVisible = MagicMock()
+            self.service_manager.maintain_action.setVisible = MagicMock()
+            self.service_manager.notes_action.setVisible = MagicMock()
+            self.service_manager.time_action.setVisible = MagicMock()
+            self.service_manager.auto_start_action.setVisible = MagicMock()
+
+            # WHEN: Show the context menu.
+            self.service_manager.context_menu(q_point)
+
+            # THEN: The following actions should be not visible.
+            self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
+            self.service_manager.time_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+
+    def start_time_context_menu_test(self):
+        """
+        Test the context_menu() method with a start time
+        """
+        # GIVEN: A service item added
+        self.service_manager.setup_ui(self.service_manager)
+        with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
+                patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
+                patch('PyQt4.QtGui.QMenu.exec_'):
+            mocked_item = MagicMock()
+            mocked_item.parent.return_value = None
+            mocked_item_at_method.return_value = mocked_item
+            # We want 1 to be returned for the position
+            mocked_item.data.return_value = 1
+            # A service item without capabilities.
+            service_item = ServiceItem()
+            service_item.add_capability(ItemCapabilities.HasVariableStartTime)
+            self.service_manager.service_items = [{'service_item': service_item}]
+            q_point = None
+            # Mocked actions.
+            self.service_manager.edit_action.setVisible = MagicMock()
+            self.service_manager.create_custom_action.setVisible = MagicMock()
+            self.service_manager.maintain_action.setVisible = MagicMock()
+            self.service_manager.notes_action.setVisible = MagicMock()
+            self.service_manager.time_action.setVisible = MagicMock()
+            self.service_manager.auto_start_action.setVisible = MagicMock()
+
+            # WHEN: Show the context menu.
+            self.service_manager.context_menu(q_point)
+
+            # THEN: The following actions should be not visible.
+            self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
+            self.service_manager.time_action.setVisible.assert_called_with(True), \
+                'The action should be set visible.'
+            self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+
+    def auto_start_context_menu_test(self):
+        """
+        Test the context_menu() method with can auto start
+        """
+        # GIVEN: A service item added
+        self.service_manager.setup_ui(self.service_manager)
+        with patch('PyQt4.QtGui.QTreeWidget.itemAt') as mocked_item_at_method, \
+                patch('PyQt4.QtGui.QWidget.mapToGlobal'), \
+                patch('PyQt4.QtGui.QMenu.exec_'):
+            mocked_item = MagicMock()
+            mocked_item.parent.return_value = None
+            mocked_item_at_method.return_value = mocked_item
+            # We want 1 to be returned for the position
+            mocked_item.data.return_value = 1
+            # A service item without capabilities.
+            service_item = ServiceItem()
+            service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
+            self.service_manager.service_items = [{'service_item': service_item}]
+            q_point = None
+            # Mocked actions.
+            self.service_manager.edit_action.setVisible = MagicMock()
+            self.service_manager.create_custom_action.setVisible = MagicMock()
+            self.service_manager.maintain_action.setVisible = MagicMock()
+            self.service_manager.notes_action.setVisible = MagicMock()
+            self.service_manager.time_action.setVisible = MagicMock()
+            self.service_manager.auto_start_action.setVisible = MagicMock()
+
+            # WHEN: Show the context menu.
+            self.service_manager.context_menu(q_point)
+
+            # THEN: The following actions should be not visible.
+            self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
+            self.service_manager.time_action.setVisible.assert_called_once_with(False), \
+                'The action should be set invisible.'
+            self.service_manager.auto_start_action.setVisible.assert_called_with(True), \
+                'The action should be set visible.'
+
+    def click_on_new_service_test(self):
+        """
+        Test the on_new_service event handler is called by the UI
+        """
+        # GIVEN: An initial form
+        mocked_event = MagicMock()
+        self.service_manager.on_new_service_clicked = mocked_event
+        self.service_manager.setup_ui(self.service_manager)
+
+        # WHEN displaying the UI and pressing cancel
+        new_service = self.service_manager.toolbar.actions['newService']
+        new_service.trigger()
+
+        assert mocked_event.call_count == 1, 'The on_new_service_clicked method should have been called once'

=== renamed file 'tests/interfaces/openlp_core_ui/test_thememanagerhelper.py' => 'tests/interfaces/openlp_core_ui/test_thememanager.py'
--- tests/interfaces/openlp_core_ui/test_thememanagerhelper.py	2013-12-24 08:56:50 +0000
+++ tests/interfaces/openlp_core_ui/test_thememanager.py	2014-01-01 11:50:01 +0000
@@ -27,20 +27,20 @@
 # Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
 ###############################################################################
 """
-Interface tests to test the thememanagerhelper class and related methods.
+Interface tests to test the themeManager class and related methods.
 """
 import os
 from unittest import TestCase
 from tempfile import mkstemp
 
-from openlp.core.common import Settings
-from openlp.core.ui import ThemeManagerHelper
+from openlp.core.common import Registry, Settings
+from openlp.core.ui import ThemeManager
 from tests.functional import patch, MagicMock
 
 
-class TestThemeManagerHelper(TestCase):
+class TestThemeManager(TestCase):
     """
-    Test the functions in the ThemeManagerHelp[er module
+    Test the functions in the ThemeManager module
     """
     def setUp(self):
         """
@@ -48,8 +48,8 @@
         """
         fd, self.ini_file = mkstemp('.ini')
         Settings().set_filename(self.ini_file)
-        self.helper = ThemeManagerHelper()
-        self.helper.settings_section = "themes"
+        Registry.create()
+        self.theme_manager = ThemeManager()
 
     def tearDown(self):
         """
@@ -58,29 +58,29 @@
         os.unlink(self.ini_file)
         os.unlink(Settings().fileName())
 
-    def test_initialise(self):
+    def initialise_test(self):
         """
-        Test the thememanagerhelper initialise - basic test
+        Test the thememanager initialise - basic test
         """
         # GIVEN: A new a call to initialise
+        self.theme_manager.build_theme_path = MagicMock()
+        self.theme_manager.load_first_time_themes = MagicMock()
         Settings().setValue('themes/global theme', 'my_theme')
-        self.helper.build_theme_path = MagicMock()
-        self.helper.load_first_time_themes = MagicMock()
 
         # WHEN: the initialistion is run
-        self.helper.initialise()
+        self.theme_manager.bootstrap_initialise()
 
         # THEN:
-        self.assertEqual(1, self.helper.build_theme_path.call_count,
+        self.assertEqual(1, self.theme_manager.build_theme_path.call_count,
                          'The function build_theme_path should have been called')
-        self.assertEqual(1, self.helper.load_first_time_themes.call_count,
+        self.assertEqual(1, self.theme_manager.load_first_time_themes.call_count,
                          'The function load_first_time_themes should have been called only once')
-        self.assertEqual(self.helper.global_theme, 'my_theme',
+        self.assertEqual(self.theme_manager.global_theme, 'my_theme',
                          'The global theme should have been set to my_theme')
 
-    def test_build_theme_path(self):
+    def build_theme_path_test(self):
         """
-        Test the thememanagerhelper build_theme_path - basic test
+        Test the thememanager build_theme_path - basic test
         """
         # GIVEN: A new a call to initialise
         with patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists:
@@ -88,12 +88,28 @@
             mocked_check_directory_exists.return_value = True
         Settings().setValue('themes/global theme', 'my_theme')
 
-        self.helper.theme_form = MagicMock()
-        #self.helper.load_first_time_themes = MagicMock()
+        self.theme_manager.theme_form = MagicMock()
+        self.theme_manager.load_first_time_themes = MagicMock()
 
         # WHEN: the build_theme_path is run
-        self.helper.build_theme_path()
-
-        # THEN:
-        self.assertEqual(self.helper.path, self.helper.theme_form.path,
-                         'The theme path and the main path should be the same value')
\ No newline at end of file
+        self.theme_manager.build_theme_path()
+
+        #  THEN:
+        assert self.theme_manager.thumb_path.startswith(self.theme_manager.path) is True, \
+            'The thumb path and the main path should start with the same value'
+
+    def click_on_new_theme_test(self):
+        """
+        Test the on_add_theme event handler is called by the UI
+        """
+        # GIVEN: An initial form
+        Settings().setValue('themes/global theme', 'my_theme')
+        mocked_event = MagicMock()
+        self.theme_manager.on_add_theme = mocked_event
+        self.theme_manager.setup_ui(self.theme_manager)
+
+        # WHEN displaying the UI and pressing cancel
+        new_theme = self.theme_manager.toolbar.actions['newTheme']
+        new_theme.trigger()
+
+        assert mocked_event.call_count == 1, 'The on_add_theme method should have been called once'


Follow ups