← 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:
  Raoul Snyman (raoul-snyman)

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

Move Registry from lib to common

Move de_hump to common from theme

add logging mixin and clean up pluginmanager to use this.

add Registry minix and clean up slide controller.

Convert and cleaned up ThemeManager

Fixed Slidecontroller action bug.
-- 
https://code.launchpad.net/~trb143/openlp/refactor1/+merge/200136
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/__init__.py'
--- openlp/core/__init__.py	2013-12-24 08:56:50 +0000
+++ openlp/core/__init__.py	2013-12-28 21:35:49 +0000
@@ -43,8 +43,8 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import AppLocation, Settings, UiStrings, check_directory_exists
-from openlp.core.lib import ScreenList, Registry
+from openlp.core.common import Registry, AppLocation, Settings, UiStrings, check_directory_exists
+from openlp.core.lib import ScreenList
 from openlp.core.resources import qInitResources
 from openlp.core.ui.mainwindow import MainWindow
 from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm
@@ -162,7 +162,8 @@
         self.shared_memory = QtCore.QSharedMemory('OpenLP')
         if self.shared_memory.attach():
             status = QtGui.QMessageBox.critical(None, UiStrings().Error, UiStrings().OpenLPStart,
-                QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No))
+                                                QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
+                                                                                  QtGui.QMessageBox.No))
             if status == QtGui.QMessageBox.No:
                 return True
             return False
@@ -248,16 +249,16 @@
     usage = 'Usage: %prog [options] [qt-options]'
     parser = OptionParser(usage=usage)
     parser.add_option('-e', '--no-error-form', dest='no_error_form', action='store_true',
-        help='Disable the error notification form.')
+                      help='Disable the error notification form.')
     parser.add_option('-l', '--log-level', dest='loglevel', default='warning', metavar='LEVEL',
-        help='Set logging to LEVEL level. Valid values are "debug", "info", "warning".')
+                      help='Set logging to LEVEL level. Valid values are "debug", "info", "warning".')
     parser.add_option('-p', '--portable', dest='portable', action='store_true',
-        help='Specify if this should be run as a portable app, off a USB flash drive (not implemented).')
+                      help='Specify if this should be run as a portable app, off a USB flash drive (not implemented).')
     parser.add_option('-d', '--dev-version', dest='dev_version', action='store_true',
-        help='Ignore the version file and pull the version directly from Bazaar')
+                      help='Ignore the version file and pull the version directly from Bazaar')
     parser.add_option('-s', '--style', dest='style', help='Set the Qt4 style (passed directly to Qt4).')
     # Parse command line options and deal with them.
-    # Use args supplied programatically if possible.
+    # Use args supplied pragmatically if possible.
     (options, args) = parser.parse_args(args) if args else parser.parse_args()
     qt_args = []
     if options.loglevel.lower() in ['d', 'debug']:
@@ -326,4 +327,3 @@
     if not options.no_error_form:
         sys.excepthook = application.hook_exception
     sys.exit(application.run(qt_args))
-

=== modified file 'openlp/core/common/__init__.py'
--- openlp/core/common/__init__.py	2013-12-24 08:56:50 +0000
+++ openlp/core/common/__init__.py	2013-12-28 21:35:49 +0000
@@ -30,15 +30,32 @@
 The :mod:`common` module contains most of the components and libraries that make
 OpenLP work.
 """
+import re
 import os
 import logging
 import sys
+import traceback
 
 from PyQt4 import QtCore
 
 log = logging.getLogger(__name__)
 
 
+FIRST_CAMEL_REGEX = re.compile('(.)([A-Z][a-z]+)')
+SECOND_CAMEL_REGEX = re.compile('([a-z0-9])([A-Z])')
+
+
+def trace_error_handler(logger):
+    """
+    Log the calling path of an exception
+
+    'logger'
+    Logger to use so traceback is logged to correct class
+    """
+    for tb in traceback.extract_stack():
+        logger.error('Called by ' + tb[3] + ' at line ' + str(tb[1]) + ' in ' + tb[0])
+
+
 def check_directory_exists(directory, do_not_log=False):
     """
     Check a theme directory exists and if not create it
@@ -103,6 +120,17 @@
     Wrap = 2
     Next = 3
 
+
+def de_hump(name):
+    """
+    Change any Camel Case string to python string
+    """
+    sub_name = FIRST_CAMEL_REGEX.sub(r'\1_\2', name)
+    return SECOND_CAMEL_REGEX.sub(r'\1_\2', sub_name).lower()
+
+from .openlpmixin import OpenLPMixin
+from .registry import Registry
+from .registrymixin import RegistryMixin
 from .uistrings import UiStrings
 from .settings import Settings
 from .applocation import AppLocation

=== added file 'openlp/core/common/openlpmixin.py'
--- openlp/core/common/openlpmixin.py	1970-01-01 00:00:00 +0000
+++ openlp/core/common/openlpmixin.py	2013-12-28 21:35:49 +0000
@@ -0,0 +1,93 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2013 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2013 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                          #
+###############################################################################
+"""
+Provide Error Handling and login Services
+"""
+import logging
+import inspect
+
+from openlp.core.common import trace_error_handler
+DO_NOT_TRACE_EVENTS = ['timerEvent', 'paintEvent']
+
+
+class OpenLPMixin(object):
+    """
+    Base Calling object for OpenLP classes.
+    """
+    def __init__(self, parent):
+        try:
+            super(OpenLPMixin, self).__init__(parent)
+        except TypeError:
+            super(OpenLPMixin, self).__init__()
+        self.logger = logging.getLogger("%s.%s" % (self.__module__, self.__class__.__name__))
+        if self.logger.getEffectiveLevel() == logging.DEBUG:
+            for name, m in inspect.getmembers(self, inspect.ismethod):
+                if name not in DO_NOT_TRACE_EVENTS:
+                    if not name.startswith("_") and not name.startswith("log"):
+                        setattr(self, name, self.logging_wrapper(m, self))
+
+    def logging_wrapper(self, func, parent):
+        """
+        Code to added debug wrapper to work on called functions within a decorated class.
+        """
+        def wrapped(*args, **kwargs):
+            parent.logger.debug("Entering %s" % func.__name__)
+            try:
+                return func(*args, **kwargs)
+            except Exception as e:
+                if parent.logger.getEffectiveLevel() <= logging.ERROR:
+                    parent.logger.error('Exception in %s : %s' % (func.__name__, e))
+                raise e
+        return wrapped
+
+    def log_debug(self, message):
+        """
+        Common log debug handler which prints the calling path
+        """
+        self.logger.debug(message)
+
+    def log_info(self, message):
+        """
+        Common log info handler which prints the calling path
+        """
+        self.logger.info(message)
+
+    def log_error(self, message):
+        """
+        Common log error handler which prints the calling path
+        """
+        trace_error_handler(self.logger)
+        self.logger.error(message)
+
+    def log_exception(self, message):
+        """
+        Common log exception handler which prints the calling path
+        """
+        trace_error_handler(self.logger)
+        self.logger.exception(message)
\ No newline at end of file

=== added file 'openlp/core/common/registry.py'
--- openlp/core/common/registry.py	1970-01-01 00:00:00 +0000
+++ openlp/core/common/registry.py	2013-12-28 21:35:49 +0000
@@ -0,0 +1,174 @@
+# -*- 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                          #
+###############################################################################
+"""
+Provide Registry Services
+"""
+import logging
+import sys
+
+from openlp.core.common import trace_error_handler
+
+log = logging.getLogger(__name__)
+
+
+class Registry(object):
+    """
+    This is the Component Registry.  It is a singleton object and is used to provide a look up service for common
+    objects.
+    """
+    log.info('Registry loaded')
+    __instance__ = None
+
+    def __new__(cls):
+        """
+        Re-implement the __new__ method to make sure we create a true singleton.
+        """
+        if not cls.__instance__:
+            cls.__instance__ = object.__new__(cls)
+        return cls.__instance__
+
+    @classmethod
+    def create(cls):
+        """
+        The constructor for the component registry providing a single registry of objects.
+        """
+        log.info('Registry Initialising')
+        registry = cls()
+        registry.service_list = {}
+        registry.functions_list = {}
+        registry.running_under_test = False
+        # Allow the tests to remove Registry entries but not the live system
+        if 'nose' in sys.argv[0]:
+            registry.running_under_test = True
+        return registry
+
+    def get(self, key):
+        """
+        Extracts the registry value from the list based on the key passed in
+
+        ``key``
+            The service to be retrieved.
+        """
+        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)
+
+    def register(self, key, reference):
+        """
+        Registers a component against a key.
+
+        ``key``
+            The service to be created this is usually a major class like "renderer" or "main_window" .
+
+        ``reference``
+            The service address to be saved.
+        """
+        if key in self.service_list:
+            trace_error_handler(log)
+            log.error('Duplicate service exception %s' % key)
+            raise KeyError('Duplicate service exception %s' % key)
+        else:
+            self.service_list[key] = reference
+
+    def remove(self, key):
+        """
+        Removes the registry value from the list based on the key passed in (Only valid and active for testing
+        framework).
+
+        ``key``
+            The service to be deleted.
+        """
+        if key in self.service_list:
+            del self.service_list[key]
+
+    def register_function(self, event, function):
+        """
+        Register an event and associated function to be called
+
+        ``event``
+            The function description like "live_display_hide" where a number of places in the code
+            will/may need to respond to a single action and the caller does not need to understand or know about the
+            recipients.
+
+        ``function``
+            The function to be called when the event happens.
+        """
+        if event in self.functions_list:
+            self.functions_list[event].append(function)
+        else:
+            self.functions_list[event] = [function]
+
+    def remove_function(self, event, function):
+        """
+        Remove an event and associated handler
+
+        ``event``
+            The function description..
+
+        ``function``
+            The function to be called when the event happens.
+        """
+        if self.running_under_test is False:
+            trace_error_handler(log)
+            log.error('Invalid Method call for key %s' % event)
+            raise KeyError('Invalid Method call for key %s' % event)
+        if event in self.functions_list:
+            self.functions_list[event].remove(function)
+
+    def execute(self, event, *args, **kwargs):
+        """
+        Execute all the handlers associated with the event and return an array of results.
+
+        ``event``
+            The function to be processed
+
+        ``*args``
+            Parameters to be passed to the function.
+
+        ``*kwargs``
+            Parameters to be passed to the function.
+        """
+        results = []
+        if event in self.functions_list:
+            for function in self.functions_list[event]:
+                try:
+                    result = function(*args, **kwargs)
+                    if result:
+                        results.append(result)
+                except TypeError:
+                    # Who has called me can help in debugging
+                    trace_error_handler(log)
+                    log.exception('Exception for function %s', function)
+        else:
+            trace_error_handler(log)
+            log.error("Event %s not called by not registered" % event)
+        return results

=== added file 'openlp/core/common/registrymixin.py'
--- openlp/core/common/registrymixin.py	1970-01-01 00:00:00 +0000
+++ openlp/core/common/registrymixin.py	2013-12-28 21:35:49 +0000
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2013 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2013 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                          #
+###############################################################################
+"""
+Provide Registry Services
+"""
+from openlp.core.common import Registry, de_hump
+
+
+class RegistryMixin(object):
+    """
+    This adds registry components to classes to use at run time.
+    """
+    def __init__(self, parent):
+        """
+        Register the class and bootstrap hooks.
+        """
+        try:
+            super(RegistryMixin, self).__init__(parent)
+        except TypeError:
+            super(RegistryMixin, self).__init__()
+        Registry().register(de_hump(self.__class__.__name__), self)
+        Registry().register_function('bootstrap_initialise', self.bootstrap_initialise)
+        Registry().register_function('bootstrap_post_set_up', self.bootstrap_post_set_up)
+
+    def bootstrap_initialise(self):
+        """
+        Dummy method to be overridden
+        """
+        pass
+
+    def bootstrap_post_set_up(self):
+        """
+        Dummy method to be overridden
+        """
+        pass

=== modified file 'openlp/core/lib/__init__.py'
--- openlp/core/lib/__init__.py	2013-12-24 08:56:50 +0000
+++ openlp/core/lib/__init__.py	2013-12-28 21:35:49 +0000
@@ -329,7 +329,6 @@
         return translate('OpenLP.core.lib', '%s, %s', 'Locale list separator: start') % (string_list[0], merged)
 
 
-from .registry import Registry
 from .filedialog import FileDialog
 from .screen import ScreenList
 from .listwidgetwithdnd import ListWidgetWithDnD

=== modified file 'openlp/core/lib/imagemanager.py'
--- openlp/core/lib/imagemanager.py	2013-12-24 08:56:50 +0000
+++ openlp/core/lib/imagemanager.py	2013-12-28 21:35:49 +0000
@@ -39,7 +39,8 @@
 
 from PyQt4 import QtCore
 
-from openlp.core.lib import Registry, ScreenList, resize_image, image_to_byte
+from openlp.core.common import Registry
+from openlp.core.lib import ScreenList, resize_image, image_to_byte
 
 log = logging.getLogger(__name__)
 

=== modified file 'openlp/core/lib/listwidgetwithdnd.py'
--- openlp/core/lib/listwidgetwithdnd.py	2013-12-24 08:56:50 +0000
+++ openlp/core/lib/listwidgetwithdnd.py	2013-12-28 21:35:49 +0000
@@ -33,7 +33,7 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 
 
 class ListWidgetWithDnD(QtGui.QListWidget):

=== modified file 'openlp/core/lib/mediamanageritem.py'
--- openlp/core/lib/mediamanageritem.py	2013-12-24 08:56:50 +0000
+++ openlp/core/lib/mediamanageritem.py	2013-12-28 21:35:49 +0000
@@ -35,9 +35,9 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import Settings, UiStrings, translate
+from openlp.core.common import Registry, Settings, UiStrings, translate
 from openlp.core.lib import FileDialog, OpenLPToolbar, ServiceItem, StringContent, ListWidgetWithDnD, \
-    ServiceItemContext, Registry
+    ServiceItemContext
 from openlp.core.lib.searchedit import SearchEdit
 from openlp.core.lib.ui import create_widget_action, critical_error_message_box
 

=== modified file 'openlp/core/lib/plugin.py'
--- openlp/core/lib/plugin.py	2013-12-24 08:56:50 +0000
+++ openlp/core/lib/plugin.py	2013-12-28 21:35:49 +0000
@@ -34,8 +34,7 @@
 
 from PyQt4 import QtCore
 
-from openlp.core.common import Settings, UiStrings
-from openlp.core.lib import Registry
+from openlp.core.common import Registry, Settings, UiStrings
 from openlp.core.utils import get_application_version
 
 log = logging.getLogger(__name__)

=== modified file 'openlp/core/lib/pluginmanager.py'
--- openlp/core/lib/pluginmanager.py	2013-12-24 08:56:50 +0000
+++ openlp/core/lib/pluginmanager.py	2013-12-28 21:35:49 +0000
@@ -31,68 +31,56 @@
 """
 import os
 import sys
-import logging
 import imp
 
-from openlp.core.lib import Plugin, PluginStatus, Registry
-from openlp.core.common import AppLocation
-
-log = logging.getLogger(__name__)
-
-
-class PluginManager(object):
+from openlp.core.lib import Plugin, PluginStatus
+from openlp.core.common import AppLocation, Registry, OpenLPMixin, RegistryMixin
+
+
+class PluginManager(RegistryMixin, OpenLPMixin):
     """
     This is the Plugin manager, which loads all the plugins,
     and executes all the hooks, as and when necessary.
     """
-    log.info('Plugin manager loaded')
-
-    def __init__(self):
+    def __init__(self, parent=None):
         """
         The constructor for the plugin manager. Passes the controllers on to
         the plugins for them to interact with via their ServiceItems.
         """
-        log.info('Plugin manager Initialising')
-        Registry().register('plugin_manager', self)
-        Registry().register_function('bootstrap_initialise', self.bootstrap_initialise)
+        super(PluginManager, self).__init__(parent)
+        self.log_info('Plugin manager Initialising')
         self.base_path = os.path.abspath(AppLocation.get_directory(AppLocation.PluginsDir))
-        log.debug('Base path %s ', self.base_path)
+        self.log_debug('Base path %s ' % self.base_path)
         self.plugins = []
-        log.info('Plugin manager Initialised')
+        self.log_info('Plugin manager Initialised')
 
     def bootstrap_initialise(self):
         """
         Bootstrap all the plugin manager functions
         """
-        log.info('bootstrap_initialise')
         self.find_plugins()
         # hook methods have to happen after find_plugins. Find plugins needs
         # the controllers hence the hooks have moved from setupUI() to here
         # Find and insert settings tabs
-        log.info('hook settings')
         self.hook_settings_tabs()
         # Find and insert media manager items
-        log.info('hook media')
         self.hook_media_manager()
         # Call the hook method to pull in import menus.
-        log.info('hook menus')
         self.hook_import_menu()
         # Call the hook method to pull in export menus.
         self.hook_export_menu()
         # Call the hook method to pull in tools menus.
         self.hook_tools_menu()
         # Call the initialise method to setup plugins.
-        log.info('initialise plugins')
         self.initialise_plugins()
 
     def find_plugins(self):
         """
         Scan a directory for objects inheriting from the ``Plugin`` class.
         """
-        log.info('Finding plugins')
         start_depth = len(os.path.abspath(self.base_path).split(os.sep))
         present_plugin_dir = os.path.join(self.base_path, 'presentations')
-        log.debug('finding plugins in %s at depth %d', str(self.base_path), start_depth)
+        self.log_debug('finding plugins in %s at depth %d' % (self.base_path, start_depth))
         for root, dirs, files in os.walk(self.base_path):
             if sys.platform == 'darwin' and root.startswith(present_plugin_dir):
                 # TODO Presentation plugin is not yet working on Mac OS X.
@@ -108,7 +96,7 @@
                         break
                     module_name = name[:-3]
                     # import the modules
-                    log.debug('Importing %s from %s. Depth %d', module_name, root, this_depth)
+                    self.log_debug('Importing %s from %s. Depth %d' % (module_name, root, this_depth))
                     try:
                         # Use the "imp" library to try to get around a problem with the PyUNO library which
                         # monkey-patches the __import__ function to do some magic. This causes issues with our tests.
@@ -117,20 +105,21 @@
                         # Then load the module (do the actual import) using the details from find_module()
                         imp.load_module(module_name, fp, path_name, description)
                     except ImportError as e:
-                        log.exception('Failed to import module %s on path %s: %s', module_name, path, e.args[0])
+                        self.log_exception('Failed to import module %s on path %s: %s'
+                                           % (module_name, path, e.args[0]))
         plugin_classes = Plugin.__subclasses__()
         plugin_objects = []
         for p in plugin_classes:
             try:
                 plugin = p()
-                log.debug('Loaded plugin %s', str(p))
+                self.log_debug('Loaded plugin %s' % str(p))
                 plugin_objects.append(plugin)
             except TypeError:
-                log.exception('Failed to load plugin %s', str(p))
+                self.log_exception('Failed to load plugin %s' % str(p))
         plugins_list = sorted(plugin_objects, key=lambda plugin: plugin.weight)
         for plugin in plugins_list:
             if plugin.check_pre_conditions():
-                log.debug('Plugin %s active', str(plugin.name))
+                self.log_debug('Plugin %s active' % str(plugin.name))
                 plugin.set_status()
             else:
                 plugin.status = PluginStatus.Disabled
@@ -199,24 +188,21 @@
         Loop through all the plugins and give them an opportunity to
         initialise themselves.
         """
-        log.info('Initialise Plugins - Started')
         for plugin in self.plugins:
-            log.info('initialising plugins %s in a %s state' % (plugin.name, plugin.is_active()))
+            self.log_info('initialising plugins %s in a %s state' % (plugin.name, plugin.is_active()))
             if plugin.is_active():
                 plugin.initialise()
-                log.info('Initialisation Complete for %s ' % plugin.name)
-        log.info('Initialise Plugins - Finished')
+                self.log_info('Initialisation Complete for %s ' % plugin.name)
 
     def finalise_plugins(self):
         """
         Loop through all the plugins and give them an opportunity to
         clean themselves up
         """
-        log.info('finalising plugins')
         for plugin in self.plugins:
             if plugin.is_active():
                 plugin.finalise()
-                log.info('Finalisation Complete for %s ' % plugin.name)
+                self.log_info('Finalisation Complete for %s ' % plugin.name)
 
     def get_plugin_by_name(self, name):
         """
@@ -231,7 +217,6 @@
         """
         Loop through all the plugins and give them an opportunity to handle a new service
         """
-        log.info('plugins - new service created')
         for plugin in self.plugins:
             if plugin.is_active():
                 plugin.new_service_created()
@@ -255,4 +240,3 @@
         return self._main_window
 
     main_window = property(_get_main_window)
-

=== removed file 'openlp/core/lib/registry.py'
--- openlp/core/lib/registry.py	2013-12-24 08:56:50 +0000
+++ openlp/core/lib/registry.py	1970-01-01 00:00:00 +0000
@@ -1,167 +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                          #
-###############################################################################
-"""
-Provide Registry Services
-"""
-import logging
-import sys
-
-log = logging.getLogger(__name__)
-
-
-class Registry(object):
-    """
-    This is the Component Registry.  It is a singleton object and is used to provide a look up service for common
-    objects.
-    """
-    log.info('Registry loaded')
-    __instance__ = None
-
-    def __new__(cls):
-        """
-        Re-implement the __new__ method to make sure we create a true singleton.
-        """
-        if not cls.__instance__:
-            cls.__instance__ = object.__new__(cls)
-        return cls.__instance__
-
-    @classmethod
-    def create(cls):
-        """
-        The constructor for the component registry providing a single registry of objects.
-        """
-        log.info('Registry Initialising')
-        registry = cls()
-        registry.service_list = {}
-        registry.functions_list = {}
-        registry.running_under_test = False
-        # Allow the tests to remove Registry entries but not the live system
-        if 'nose' in sys.argv[0]:
-            registry.running_under_test = True
-        return registry
-
-    def get(self, key):
-        """
-        Extracts the registry value from the list based on the key passed in
-
-        ``key``
-            The service to be retrieved.
-        """
-        if key in self.service_list:
-            return self.service_list[key]
-        else:
-            log.error('Service %s not found in list' % key)
-            raise KeyError('Service %s not found in list' % key)
-
-    def register(self, key, reference):
-        """
-        Registers a component against a key.
-
-        ``key``
-            The service to be created this is usually a major class like "renderer" or "main_window" .
-
-        ``reference``
-            The service address to be saved.
-        """
-        if key in self.service_list:
-            log.error('Duplicate service exception %s' % key)
-            raise KeyError('Duplicate service exception %s' % key)
-        else:
-            self.service_list[key] = reference
-
-    def remove(self, key):
-        """
-        Removes the registry value from the list based on the key passed in (Only valid and active for testing
-        framework).
-
-        ``key``
-            The service to be deleted.
-        """
-        if key in self.service_list:
-            del self.service_list[key]
-
-    def register_function(self, event, function):
-        """
-        Register an event and associated function to be called
-
-        ``event``
-            The function description like "live_display_hide" where a number of places in the code
-            will/may need to respond to a single action and the caller does not need to understand or know about the
-            recipients.
-
-        ``function``
-            The function to be called when the event happens.
-        """
-        if event in self.functions_list:
-            self.functions_list[event].append(function)
-        else:
-            self.functions_list[event] = [function]
-
-    def remove_function(self, event, function):
-        """
-        Remove an event and associated handler
-
-        ``event``
-            The function description..
-
-        ``function``
-            The function to be called when the event happens.
-        """
-        if self.running_under_test is False:
-            log.error('Invalid Method call for key %s' % event)
-            raise KeyError('Invalid Method call for key %s' % event)
-        if event in self.functions_list:
-            self.functions_list[event].remove(function)
-
-    def execute(self, event, *args, **kwargs):
-        """
-        Execute all the handlers associated with the event and return an array of results.
-
-        ``event``
-            The function to be processed
-
-        ``*args``
-            Parameters to be passed to the function.
-
-        ``*kwargs``
-            Parameters to be passed to the function.
-        """
-        results = []
-        if event in self.functions_list:
-            for function in self.functions_list[event]:
-                try:
-                    result = function(*args, **kwargs)
-                    if result:
-                        results.append(result)
-                except TypeError:
-                    # Who has called me can help in debugging
-                    import inspect
-                    log.debug(inspect.currentframe().f_back.f_locals)
-                    log.exception('Exception for function %s', function)
-        return results

=== modified file 'openlp/core/lib/renderer.py'
--- openlp/core/lib/renderer.py	2013-12-26 08:56:53 +0000
+++ openlp/core/lib/renderer.py	2013-12-28 21:35:49 +0000
@@ -31,9 +31,9 @@
 
 from PyQt4 import QtGui, QtCore, QtWebKit
 
-from openlp.core.common import Settings
-from openlp.core.lib import FormattingTags, ImageSource, ItemCapabilities, Registry, ScreenList, \
-    ServiceItem, expand_tags, build_lyrics_format_css, build_lyrics_outline_css
+from openlp.core.common import Registry, Settings
+from openlp.core.lib import FormattingTags, ImageSource, ItemCapabilities, ScreenList, ServiceItem, expand_tags, \
+    build_lyrics_format_css, build_lyrics_outline_css
 from openlp.core.common import ThemeLevel
 from openlp.core.ui import MainDisplay
 

=== modified file 'openlp/core/lib/screen.py'
--- openlp/core/lib/screen.py	2013-12-24 08:56:50 +0000
+++ openlp/core/lib/screen.py	2013-12-28 21:35:49 +0000
@@ -36,8 +36,7 @@
 
 from PyQt4 import QtCore
 
-from openlp.core.common import Settings, translate
-from openlp.core.lib import Registry
+from openlp.core.common import Registry, Settings, translate
 
 log = logging.getLogger(__name__)
 

=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py	2013-12-26 08:56:53 +0000
+++ openlp/core/lib/serviceitem.py	2013-12-28 21:35:49 +0000
@@ -39,8 +39,8 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.common import Settings, translate
-from openlp.core.lib import ImageSource, Registry, build_icon, clean_tags, expand_tags
+from openlp.core.common import Registry, Settings, translate
+from openlp.core.lib import ImageSource, build_icon, clean_tags, expand_tags
 
 log = logging.getLogger(__name__)
 

=== modified file 'openlp/core/lib/settingstab.py'
--- openlp/core/lib/settingstab.py	2013-12-24 08:56:50 +0000
+++ openlp/core/lib/settingstab.py	2013-12-28 21:35:49 +0000
@@ -35,7 +35,7 @@
 from PyQt4 import QtGui
 
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 
 
 class SettingsTab(QtGui.QWidget):

=== modified file 'openlp/core/lib/theme.py'
--- openlp/core/lib/theme.py	2013-12-24 08:56:50 +0000
+++ openlp/core/lib/theme.py	2013-12-28 21:35:49 +0000
@@ -36,7 +36,7 @@
 
 from xml.dom.minidom import Document
 from lxml import etree, objectify
-from openlp.core.common import AppLocation
+from openlp.core.common import AppLocation, de_hump
 
 from openlp.core.lib import str_to_bool, ScreenList, get_text_file_string
 
@@ -157,9 +157,6 @@
     """
     A class to encapsulate the Theme XML.
     """
-    FIRST_CAMEL_REGEX = re.compile('(.)([A-Z][a-z]+)')
-    SECOND_CAMEL_REGEX = re.compile('([a-z0-9])([A-Z])')
-
     def __init__(self):
         """
         Initialise the theme object.
@@ -532,7 +529,7 @@
         reject, master, element, value = self._translate_tags(master, element, value)
         if reject:
             return
-        field = self._de_hump(element)
+        field = de_hump(element)
         tag = master + '_' + field
         if field in BOOLEAN_LIST:
             setattr(self, tag, str_to_bool(value))
@@ -557,13 +554,6 @@
                 theme_strings.append('%30s: %s' % (key, getattr(self, key)))
         return '\n'.join(theme_strings)
 
-    def _de_hump(self, name):
-        """
-        Change Camel Case string to python string
-        """
-        sub_name = ThemeXML.FIRST_CAMEL_REGEX.sub(r'\1_\2', name)
-        return ThemeXML.SECOND_CAMEL_REGEX.sub(r'\1_\2', sub_name).lower()
-
     def _build_xml_from_attrs(self):
         """
         Build the XML from the varables in the object

=== modified file 'openlp/core/lib/treewidgetwithdnd.py'
--- openlp/core/lib/treewidgetwithdnd.py	2013-12-24 08:56:50 +0000
+++ openlp/core/lib/treewidgetwithdnd.py	2013-12-28 21:35:49 +0000
@@ -33,7 +33,7 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 
 
 class TreeWidgetWithDnD(QtGui.QTreeWidget):

=== modified file 'openlp/core/lib/ui.py'
--- openlp/core/lib/ui.py	2013-12-24 08:56:50 +0000
+++ openlp/core/lib/ui.py	2013-12-28 21:35:49 +0000
@@ -33,8 +33,8 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import UiStrings, translate
-from openlp.core.lib import Registry, build_icon
+from openlp.core.common import Registry, UiStrings, translate
+from openlp.core.lib import build_icon
 from openlp.core.utils.actions import ActionList
 
 

=== modified file 'openlp/core/ui/__init__.py'
--- openlp/core/ui/__init__.py	2013-12-24 08:56:50 +0000
+++ openlp/core/ui/__init__.py	2013-12-28 21:35:49 +0000
@@ -86,7 +86,7 @@
 from .maindisplay import MainDisplay, Display
 from .servicenoteform import ServiceNoteForm
 from .serviceitemeditform import ServiceItemEditForm
-from .slidecontroller import SlideController, DisplayController
+from .slidecontroller import SlideController, DisplayController, PreviewController, LiveController
 from .splashscreen import SplashScreen
 from .generaltab import GeneralTab
 from .themestab import ThemesTab

=== modified file 'openlp/core/ui/exceptionform.py'
--- openlp/core/ui/exceptionform.py	2013-12-24 11:32:38 +0000
+++ openlp/core/ui/exceptionform.py	2013-12-28 21:35:49 +0000
@@ -38,7 +38,7 @@
 import sqlalchemy
 from lxml import etree
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 
 from PyQt4 import Qt, QtCore, QtGui, QtWebKit
 

=== modified file 'openlp/core/ui/filerenameform.py'
--- openlp/core/ui/filerenameform.py	2013-12-24 08:56:50 +0000
+++ openlp/core/ui/filerenameform.py	2013-12-28 21:35:49 +0000
@@ -34,8 +34,7 @@
 
 from .filerenamedialog import Ui_FileRenameDialog
 
-from openlp.core.common import translate
-from openlp.core.lib import Registry
+from openlp.core.common import Registry, translate
 
 
 class FileRenameForm(QtGui.QDialog, Ui_FileRenameDialog):

=== modified file 'openlp/core/ui/firsttimeform.py'
--- openlp/core/ui/firsttimeform.py	2013-12-24 15:55:01 +0000
+++ openlp/core/ui/firsttimeform.py	2013-12-28 21:35:49 +0000
@@ -41,8 +41,8 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import AppLocation, Settings, check_directory_exists, translate
-from openlp.core.lib import PluginStatus, Registry, build_icon
+from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, translate
+from openlp.core.lib import PluginStatus, build_icon
 from openlp.core.utils import get_web_page
 from .firsttimewizard import Ui_FirstTimeWizard, FirstTimePage
 

=== modified file 'openlp/core/ui/generaltab.py'
--- openlp/core/ui/generaltab.py	2013-12-24 15:55:01 +0000
+++ openlp/core/ui/generaltab.py	2013-12-28 21:35:49 +0000
@@ -33,8 +33,8 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import Settings, UiStrings, translate
-from openlp.core.lib import Registry, SettingsTab, ScreenList
+from openlp.core.common import Registry, Settings, UiStrings, translate
+from openlp.core.lib import SettingsTab, ScreenList
 
 log = logging.getLogger(__name__)
 

=== modified file 'openlp/core/ui/listpreviewwidget.py'
--- openlp/core/ui/listpreviewwidget.py	2013-12-24 15:55:01 +0000
+++ openlp/core/ui/listpreviewwidget.py	2013-12-28 21:35:49 +0000
@@ -33,7 +33,8 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.lib import ImageSource, Registry, ServiceItem
+from openlp.core.common import Registry
+from openlp.core.lib import ImageSource, ServiceItem
 
 
 class ListPreviewWidget(QtGui.QTableWidget):

=== modified file 'openlp/core/ui/maindisplay.py'
--- openlp/core/ui/maindisplay.py	2013-12-26 08:56:53 +0000
+++ openlp/core/ui/maindisplay.py	2013-12-28 21:35:49 +0000
@@ -44,8 +44,8 @@
 from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL
 from PyQt4.phonon import Phonon
 
-from openlp.core.common import Settings, translate
-from openlp.core.lib import ServiceItem, ImageSource, Registry, build_html, expand_tags, image_to_byte
+from openlp.core.common import Registry, Settings, translate
+from openlp.core.lib import ServiceItem, ImageSource, build_html, expand_tags, image_to_byte
 from openlp.core.lib.theme import BackgroundType
 
 from openlp.core.lib import ScreenList

=== modified file 'openlp/core/ui/mainwindow.py'
--- openlp/core/ui/mainwindow.py	2013-12-24 08:56:50 +0000
+++ openlp/core/ui/mainwindow.py	2013-12-28 21:35:49 +0000
@@ -41,13 +41,13 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.lib import Renderer, OpenLPDockWidget, PluginManager, ImageManager, PluginStatus, Registry, \
-    ScreenList, build_icon
+from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, translate
+from openlp.core.lib import Renderer, OpenLPDockWidget, PluginManager, ImageManager, PluginStatus, ScreenList, \
+    build_icon
 from openlp.core.lib.ui import UiStrings, create_action
-from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, ThemeManager, SlideController, PluginForm, \
-    MediaDockManager, ShortcutListForm, FormattingTagForm
+from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, ThemeManager, LiveController, PluginForm, \
+    MediaDockManager, ShortcutListForm, FormattingTagForm, PreviewController
 
-from openlp.core.common import AppLocation, Settings, check_directory_exists, translate
 from openlp.core.ui.media import MediaController
 from openlp.core.utils import LanguageManager, add_actions, get_application_version
 from openlp.core.utils.actions import ActionList, CategoryOrder
@@ -96,23 +96,21 @@
         # Set up the main container, which contains all the other form widgets.
         self.main_content = QtGui.QWidget(main_window)
         self.main_content.setObjectName('main_content')
-        self.main_contentLayout = QtGui.QHBoxLayout(self.main_content)
-        self.main_contentLayout.setSpacing(0)
-        self.main_contentLayout.setMargin(0)
-        self.main_contentLayout.setObjectName('main_contentLayout')
+        self.main_content_layout = QtGui.QHBoxLayout(self.main_content)
+        self.main_content_layout.setSpacing(0)
+        self.main_content_layout.setMargin(0)
+        self.main_content_layout.setObjectName('main_content_layout')
         main_window.setCentralWidget(self.main_content)
         self.control_splitter = QtGui.QSplitter(self.main_content)
         self.control_splitter.setOrientation(QtCore.Qt.Horizontal)
         self.control_splitter.setObjectName('control_splitter')
-        self.main_contentLayout.addWidget(self.control_splitter)
+        self.main_content_layout.addWidget(self.control_splitter)
         # Create slide controllers
-        self.preview_controller = SlideController(self)
-        self.live_controller = SlideController(self, True)
+        self.preview_controller = PreviewController(self)
+        self.live_controller = LiveController(self)
         preview_visible = Settings().value('user interface/preview panel')
-        self.preview_controller.panel.setVisible(preview_visible)
         live_visible = Settings().value('user interface/live panel')
         panel_locked = Settings().value('user interface/lock panel')
-        self.live_controller.panel.setVisible(live_visible)
         # Create menu
         self.menu_bar = QtGui.QMenuBar(main_window)
         self.menu_bar.setObjectName('menu_bar')
@@ -127,8 +125,8 @@
         # View Menu
         self.view_menu = QtGui.QMenu(self.menu_bar)
         self.view_menu.setObjectName('viewMenu')
-        self.view_modeMenu = QtGui.QMenu(self.view_menu)
-        self.view_modeMenu.setObjectName('viewModeMenu')
+        self.view_mode_menu = QtGui.QMenu(self.view_menu)
+        self.view_mode_menu.setObjectName('viewModeMenu')
         # Tools Menu
         self.tools_menu = QtGui.QMenu(self.menu_bar)
         self.tools_menu.setObjectName('tools_menu')
@@ -155,7 +153,7 @@
         self.status_bar.addPermanentWidget(self.default_theme_label)
         # Create the MediaManager
         self.media_manager_dock = OpenLPDockWidget(main_window, 'media_manager_dock',
-            ':/system/system_mediamanager.png')
+                                                   ':/system/system_mediamanager.png')
         self.media_manager_dock.setStyleSheet(MEDIA_MANAGER_STYLE)
         # Create the media toolbox
         self.media_tool_box = QtGui.QToolBox(self.media_manager_dock)
@@ -164,13 +162,13 @@
         main_window.addDockWidget(QtCore.Qt.LeftDockWidgetArea, self.media_manager_dock)
         # Create the service manager
         self.service_manager_dock = OpenLPDockWidget(main_window, 'service_manager_dock',
-            ':/system/system_servicemanager.png')
+                                                     ':/system/system_servicemanager.png')
         self.service_manager_contents = ServiceManager(self.service_manager_dock)
         self.service_manager_dock.setWidget(self.service_manager_contents)
         main_window.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.service_manager_dock)
         # Create the theme manager
         self.theme_manager_dock = OpenLPDockWidget(main_window, 'theme_manager_dock',
-            ':/system/system_thememanager.png')
+                                                   ':/system/system_thememanager.png')
         self.theme_manager_contents = ThemeManager(self.theme_manager_dock)
         self.theme_manager_contents.setObjectName('theme_manager_contents')
         self.theme_manager_dock.setWidget(self.theme_manager_contents)
@@ -178,98 +176,86 @@
         # Create the menu items
         action_list = ActionList.get_instance()
         action_list.add_category(UiStrings().File, CategoryOrder.standard_menu)
-        self.file_new_item = create_action(main_window, 'fileNewItem',
-            icon=':/general/general_new.png',
-            can_shortcuts=True,
-            category=UiStrings().File,
-            triggers=self.service_manager_contents.on_new_service_clicked)
-        self.file_open_item = create_action(main_window, 'fileOpenItem',
-            icon=':/general/general_open.png',
-            can_shortcuts=True,
-            category=UiStrings().File,
-            triggers=self.service_manager_contents.on_load_service_clicked)
-        self.file_save_item = create_action(main_window, 'fileSaveItem',
-            icon=':/general/general_save.png',
-            can_shortcuts=True,
-            category=UiStrings().File,
-            triggers=self.service_manager_contents.save_file)
-        self.file_save_as_item = create_action(main_window, 'fileSaveAsItem',
-            can_shortcuts=True,
-            category=UiStrings().File,
-            triggers=self.service_manager_contents.save_file_as)
-        self.print_service_order_item = create_action(main_window,
-            'printServiceItem', can_shortcuts=True,
-            category=UiStrings().File,
-            triggers=self.service_manager_contents.print_service_order)
-        self.file_exit_item = create_action(main_window, 'fileExitItem',
-            icon=':/system/system_exit.png',
-            can_shortcuts=True,
-            category=UiStrings().File, triggers=main_window.close)
+        self.file_new_item = create_action(main_window, 'fileNewItem', icon=':/general/general_new.png',
+                                           can_shortcuts=True, category=UiStrings().File,
+                                           triggers=self.service_manager_contents.on_new_service_clicked)
+        self.file_open_item = create_action(main_window, 'fileOpenItem', icon=':/general/general_open.png',
+                                            can_shortcuts=True, category=UiStrings().File,
+                                            triggers=self.service_manager_contents.on_load_service_clicked)
+        self.file_save_item = create_action(main_window, 'fileSaveItem', icon=':/general/general_save.png',
+                                            can_shortcuts=True, category=UiStrings().File,
+                                            triggers=self.service_manager_contents.save_file)
+        self.file_save_as_item = create_action(main_window, 'fileSaveAsItem', can_shortcuts=True,
+                                               category=UiStrings().File,
+                                               triggers=self.service_manager_contents.save_file_as)
+        self.print_service_order_item = create_action(main_window, 'printServiceItem', can_shortcuts=True,
+                                                      category=UiStrings().File,
+                                                      triggers=self.service_manager_contents.print_service_order)
+        self.file_exit_item = create_action(main_window, 'fileExitItem', icon=':/system/system_exit.png',
+                                            can_shortcuts=True,
+                                            category=UiStrings().File, triggers=main_window.close)
         # Give QT Extra Hint that this is the Exit Menu Item
         self.file_exit_item.setMenuRole(QtGui.QAction.QuitRole)
         action_list.add_category(UiStrings().Import, CategoryOrder.standard_menu)
-        self.import_theme_item = create_action(
-            main_window, 'importThemeItem', category=UiStrings().Import, can_shortcuts=True)
+        self.import_theme_item = create_action(main_window, 'importThemeItem', category=UiStrings().Import,
+                                               can_shortcuts=True)
         self.import_language_item = create_action(main_window, 'importLanguageItem')
         action_list.add_category(UiStrings().Export, CategoryOrder.standard_menu)
-        self.export_theme_item = create_action(
-            main_window, 'exportThemeItem', category=UiStrings().Export, can_shortcuts=True)
+        self.export_theme_item = create_action(main_window, 'exportThemeItem', category=UiStrings().Export,
+                                               can_shortcuts=True)
         self.export_language_item = create_action(main_window, 'exportLanguageItem')
         action_list.add_category(UiStrings().View, CategoryOrder.standard_menu)
-        self.view_media_manager_item = create_action(main_window,
-            'viewMediaManagerItem',
-            icon=':/system/system_mediamanager.png',
-            checked=self.media_manager_dock.isVisible(),
-            can_shortcuts=True,
-            category=UiStrings().View, triggers=self.toggle_media_manager)
-        self.view_theme_manager_item = create_action(main_window,
-            'viewThemeManagerItem', can_shortcuts=True,
-            icon=':/system/system_thememanager.png',
-            checked=self.theme_manager_dock.isVisible(),
-            category=UiStrings().View, triggers=self.toggle_theme_manager)
-        self.view_service_manager_item = create_action(main_window,
-            'viewServiceManagerItem', can_shortcuts=True,
-            icon=':/system/system_servicemanager.png',
-            checked=self.service_manager_dock.isVisible(),
-            category=UiStrings().View, triggers=self.toggle_service_manager)
-        self.view_preview_panel = create_action(main_window, 'viewPreviewPanel',
-            can_shortcuts=True, checked=preview_visible,
-            category=UiStrings().View, triggers=self.set_preview_panel_visibility)
-        self.view_live_panel = create_action(main_window, 'viewLivePanel',
-            can_shortcuts=True, checked=live_visible,
-            category=UiStrings().View, triggers=self.set_live_panel_visibility)
-        self.lock_panel = create_action(main_window, 'lockPanel',
-            can_shortcuts=True, checked=panel_locked,
-            category=UiStrings().View,
-            triggers=self.set_lock_panel)
+        self.view_media_manager_item = create_action(main_window, 'viewMediaManagerItem',
+                                                     icon=':/system/system_mediamanager.png',
+                                                     checked=self.media_manager_dock.isVisible(),
+                                                     can_shortcuts=True,
+                                                     category=UiStrings().View, triggers=self.toggle_media_manager)
+        self.view_theme_manager_item = create_action(main_window, 'viewThemeManagerItem', can_shortcuts=True,
+                                                     icon=':/system/system_thememanager.png',
+                                                     checked=self.theme_manager_dock.isVisible(),
+                                                     category=UiStrings().View, triggers=self.toggle_theme_manager)
+        self.view_service_manager_item = create_action(main_window, 'viewServiceManagerItem', can_shortcuts=True,
+                                                       icon=':/system/system_servicemanager.png',
+                                                       checked=self.service_manager_dock.isVisible(),
+                                                       category=UiStrings().View, triggers=self.toggle_service_manager)
+        self.view_preview_panel = create_action(main_window, 'viewPreviewPanel', can_shortcuts=True,
+                                                checked=preview_visible, category=UiStrings().View,
+                                                triggers=self.set_preview_panel_visibility)
+        self.view_live_panel = create_action(main_window, 'viewLivePanel', can_shortcuts=True, checked=live_visible,
+                                             category=UiStrings().View, triggers=self.set_live_panel_visibility)
+        self.lock_panel = create_action(main_window, 'lockPanel', can_shortcuts=True, checked=panel_locked,
+                                        category=UiStrings().View, triggers=self.set_lock_panel)
         action_list.add_category(UiStrings().ViewMode, CategoryOrder.standard_menu)
-        self.mode_default_Item = create_action(
+        self.mode_default_item = create_action(
             main_window, 'modeDefaultItem', checked=False, category=UiStrings().ViewMode, can_shortcuts=True)
-        self.mode_setup_item = create_action(
-            main_window, 'modeSetupItem', checked=False, category=UiStrings().ViewMode, can_shortcuts=True)
-        self.mode_live_item = create_action(
-            main_window, 'modeLiveItem', checked=True, category=UiStrings().ViewMode, can_shortcuts=True)
+        self.mode_setup_item = create_action(main_window, 'modeSetupItem', checked=False, category=UiStrings().ViewMode,
+                                             can_shortcuts=True)
+        self.mode_live_item = create_action(main_window, 'modeLiveItem', checked=True, category=UiStrings().ViewMode, 
+                                            can_shortcuts=True)
         self.mode_group = QtGui.QActionGroup(main_window)
-        self.mode_group.addAction(self.mode_default_Item)
+        self.mode_group.addAction(self.mode_default_item)
         self.mode_group.addAction(self.mode_setup_item)
         self.mode_group.addAction(self.mode_live_item)
-        self.mode_default_Item.setChecked(True)
+        self.mode_default_item.setChecked(True)
         action_list.add_category(UiStrings().Tools, CategoryOrder.standard_menu)
         self.tools_add_tool_item = create_action(main_window,
-            'toolsAddToolItem', icon=':/tools/tools_add.png', category=UiStrings().Tools, can_shortcuts=True)
+                                                 'toolsAddToolItem', icon=':/tools/tools_add.png', 
+                                                 category=UiStrings().Tools, can_shortcuts=True)
         self.tools_open_data_folder = create_action(main_window,
-            'toolsOpenDataFolder', icon=':/general/general_open.png', category=UiStrings().Tools, can_shortcuts=True)
+                                                    'toolsOpenDataFolder', icon=':/general/general_open.png', 
+                                                    category=UiStrings().Tools, can_shortcuts=True)
         self.tools_first_time_wizard = create_action(main_window,
-            'toolsFirstTimeWizard', icon=':/general/general_revert.png',
-            category=UiStrings().Tools, can_shortcuts=True)
+                                                     'toolsFirstTimeWizard', icon=':/general/general_revert.png',
+                                                     category=UiStrings().Tools, can_shortcuts=True)
         self.update_theme_images = create_action(main_window,
-            'updateThemeImages', category=UiStrings().Tools, can_shortcuts=True)
+                                                 'updateThemeImages', category=UiStrings().Tools, can_shortcuts=True)
         action_list.add_category(UiStrings().Settings, CategoryOrder.standard_menu)
-        self.settingsPluginListItem = create_action(main_window,
-            'settingsPluginListItem',
-            icon=':/system/settings_plugin_list.png',
-            can_shortcuts=True,
-            category=UiStrings().Settings, triggers=self.on_plugin_item_clicked)
+        self.settings_plugin_list_item = create_action(main_window,
+                                                       'settingsPluginListItem',
+                                                       icon=':/system/settings_plugin_list.png',
+                                                       can_shortcuts=True,
+                                                       category=UiStrings().Settings,
+                                                       triggers=self.on_plugin_item_clicked)
         # i18n Language Items
         self.auto_language_item = create_action(main_window, 'autoLanguageItem', checked=LanguageManager.auto_language)
         self.language_group = QtGui.QActionGroup(main_window)
@@ -282,73 +268,77 @@
             language_item = create_action(main_window, key, checked=qm_list[key] == saved_language)
             add_actions(self.language_group, [language_item])
         self.settings_shortcuts_item = create_action(main_window, 'settingsShortcutsItem',
-            icon=':/system/system_configure_shortcuts.png', category=UiStrings().Settings, can_shortcuts=True)
+                                                     icon=':/system/system_configure_shortcuts.png', 
+                                                     category=UiStrings().Settings, can_shortcuts=True)
         # Formatting Tags were also known as display tags.
         self.formatting_tag_item = create_action(main_window, 'displayTagItem',
-            icon=':/system/tag_editor.png', category=UiStrings().Settings, can_shortcuts=True)
+                                                 icon=':/system/tag_editor.png', category=UiStrings().Settings, 
+                                                 can_shortcuts=True)
         self.settings_configure_item = create_action(main_window, 'settingsConfigureItem',
-            icon=':/system/system_settings.png', can_shortcuts=True, category=UiStrings().Settings)
+                                                     icon=':/system/system_settings.png', can_shortcuts=True, 
+                                                     category=UiStrings().Settings)
         # Give QT Extra Hint that this is the Preferences Menu Item
         self.settings_configure_item.setMenuRole(QtGui.QAction.PreferencesRole)
-        self.settings_import_item = create_action(
-            main_window, 'settingsImportItem', category=UiStrings().Import, can_shortcuts=True)
-        self.settings_export_item = create_action(
-            main_window, 'settingsExportItem', category=UiStrings().Export, can_shortcuts=True)
+        self.settings_import_item = create_action(main_window, 'settingsImportItem', 
+                                                  category=UiStrings().Import, can_shortcuts=True)
+        self.settings_export_item = create_action(main_window, 'settingsExportItem', 
+                                                  category=UiStrings().Export, can_shortcuts=True)
         action_list.add_category(UiStrings().Help, CategoryOrder.standard_menu)
         self.about_item = create_action(main_window, 'aboutItem', icon=':/system/system_about.png',
-            can_shortcuts=True, category=UiStrings().Help, triggers=self.on_about_item_clicked)
+                                        can_shortcuts=True, category=UiStrings().Help, 
+                                        triggers=self.on_about_item_clicked)
         # Give QT Extra Hint that this is an About Menu Item
         self.about_item.setMenuRole(QtGui.QAction.AboutRole)
         if os.name == 'nt':
-            self.localHelpFile = os.path.join(
-                AppLocation.get_directory(AppLocation.AppDir), 'OpenLP.chm')
-            self.offlineHelpItem = create_action(main_window, 'offlineHelpItem',
-                icon=':/system/system_help_contents.png',
-                can_shortcuts=True,
-                category=UiStrings().Help, triggers=self.on_offline_help_clicked)
+            self.local_help_file = os.path.join(AppLocation.get_directory(AppLocation.AppDir), 'OpenLP.chm')
+            self.offline_help_item = create_action(main_window, 'offline_help_item',
+                                                   icon=':/system/system_help_contents.png',
+                                                   can_shortcuts=True,
+                                                   category=UiStrings().Help, triggers=self.on_offline_help_clicked)
         self.on_line_help_item = create_action(main_window, 'onlineHelpItem',
-            icon=':/system/system_online_help.png',
-            can_shortcuts=True,
-            category=UiStrings().Help, triggers=self.on_online_help_clicked)
+                                               icon=':/system/system_online_help.png',
+                                               can_shortcuts=True,
+                                               category=UiStrings().Help, triggers=self.on_online_help_clicked)
         self.web_site_item = create_action(main_window, 'webSiteItem', can_shortcuts=True, category=UiStrings().Help)
-        # Shortcuts not connected to buttons or menu entires.
+        # Shortcuts not connected to buttons or menu entries.
         self.search_shortcut_action = create_action(main_window,
-            'searchShortcut', can_shortcuts=True, category=translate('OpenLP.MainWindow', 'General'),
-            triggers=self.on_search_shortcut_triggered)
+                                                    'searchShortcut', can_shortcuts=True, 
+                                                    category=translate('OpenLP.MainWindow', 'General'),
+                                                    triggers=self.on_search_shortcut_triggered)
         add_actions(self.file_import_menu, (self.settings_import_item, None, self.import_theme_item,
-            self.import_language_item))
+                    self.import_language_item))
         add_actions(self.file_export_menu, (self.settings_export_item, None, self.export_theme_item,
-            self.export_language_item))
+                    self.export_language_item))
         add_actions(self.file_menu, (self.file_new_item, self.file_open_item,
-            self.file_save_item, self.file_save_as_item, self.recent_files_menu.menuAction(), None,
-            self.file_import_menu.menuAction(), self.file_export_menu.menuAction(), None, self.print_service_order_item,
-            self.file_exit_item))
-        add_actions(self.view_modeMenu, (self.mode_default_Item, self.mode_setup_item, self.mode_live_item))
-        add_actions(self.view_menu, (self.view_modeMenu.menuAction(), None, self.view_media_manager_item,
-            self.view_service_manager_item, self.view_theme_manager_item, None, self.view_preview_panel,
-            self.view_live_panel, None, self.lock_panel))
+                    self.file_save_item, self.file_save_as_item, self.recent_files_menu.menuAction(), None,
+                    self.file_import_menu.menuAction(), self.file_export_menu.menuAction(), None, self.print_service_order_item,
+                    self.file_exit_item))
+        add_actions(self.view_mode_menu, (self.mode_default_item, self.mode_setup_item, self.mode_live_item))
+        add_actions(self.view_menu, (self.view_mode_menu.menuAction(), None, self.view_media_manager_item,
+                    self.view_service_manager_item, self.view_theme_manager_item, None, self.view_preview_panel,
+                    self.view_live_panel, None, self.lock_panel))
         # i18n add Language Actions
         add_actions(self.settings_language_menu, (self.auto_language_item, None))
         add_actions(self.settings_language_menu, self.language_group.actions())
         # Order things differently in OS X so that Preferences menu item in the
         # app menu is correct (this gets picked up automatically by Qt).
         if sys.platform == 'darwin':
-            add_actions(self.settings_menu, (self.settingsPluginListItem, self.settings_language_menu.menuAction(),
-                None, self.settings_configure_item, self.settings_shortcuts_item, self.formatting_tag_item))
+            add_actions(self.settings_menu, (self.settings_plugin_list_item, self.settings_language_menu.menuAction(),
+                        None, self.settings_configure_item, self.settings_shortcuts_item, self.formatting_tag_item))
         else:
-            add_actions(self.settings_menu, (self.settingsPluginListItem, self.settings_language_menu.menuAction(),
-                None, self.formatting_tag_item, self.settings_shortcuts_item, self.settings_configure_item))
+            add_actions(self.settings_menu, (self.settings_plugin_list_item, self.settings_language_menu.menuAction(),
+                        None, self.formatting_tag_item, self.settings_shortcuts_item, self.settings_configure_item))
         add_actions(self.tools_menu, (self.tools_add_tool_item, None))
         add_actions(self.tools_menu, (self.tools_open_data_folder, None))
         add_actions(self.tools_menu, (self.tools_first_time_wizard, None))
         add_actions(self.tools_menu, [self.update_theme_images])
         if os.name == 'nt':
-            add_actions(self.help_menu, (self.offlineHelpItem, self.on_line_help_item, None, self.web_site_item,
-                self.about_item))
+            add_actions(self.help_menu, (self.offline_help_item, self.on_line_help_item, None, self.web_site_item,
+                        self.about_item))
         else:
             add_actions(self.help_menu, (self.on_line_help_item, None, self.web_site_item, self.about_item))
         add_actions(self.menu_bar, (self.file_menu.menuAction(), self.view_menu.menuAction(),
-            self.tools_menu.menuAction(), self.settings_menu.menuAction(), self.help_menu.menuAction()))
+                    self.tools_menu.menuAction(), self.settings_menu.menuAction(), self.help_menu.menuAction()))
         add_actions(self, [self.search_shortcut_action])
         # Initialise the translation
         self.retranslateUi(main_window)
@@ -360,7 +350,7 @@
         self.import_language_item.setVisible(False)
         self.export_language_item.setVisible(False)
         self.set_lock_panel(panel_locked)
-        self.settingsImported = False
+        self.settings_imported = False
 
     def retranslateUi(self, main_window):
         """
@@ -372,7 +362,7 @@
         self.file_export_menu.setTitle(translate('OpenLP.MainWindow', '&Export'))
         self.recent_files_menu.setTitle(translate('OpenLP.MainWindow', '&Recent Files'))
         self.view_menu.setTitle(translate('OpenLP.MainWindow', '&View'))
-        self.view_modeMenu.setTitle(translate('OpenLP.MainWindow', 'M&ode'))
+        self.view_mode_menu.setTitle(translate('OpenLP.MainWindow', 'M&ode'))
         self.tools_menu.setTitle(translate('OpenLP.MainWindow', '&Tools'))
         self.settings_menu.setTitle(translate('OpenLP.MainWindow', '&Settings'))
         self.settings_language_menu.setTitle(translate('OpenLP.MainWindow', '&Language'))
@@ -392,7 +382,7 @@
         self.file_save_as_item.setText(translate('OpenLP.MainWindow', 'Save &As...'))
         self.file_save_as_item.setToolTip(translate('OpenLP.MainWindow', 'Save Service As'))
         self.file_save_as_item.setStatusTip(translate('OpenLP.MainWindow',
-            'Save the current service under a new name.'))
+                                            'Save the current service under a new name.'))
         self.print_service_order_item.setText(UiStrings().PrintService)
         self.print_service_order_item.setStatusTip(translate('OpenLP.MainWindow', 'Print the current service.'))
         self.file_exit_item.setText(translate('OpenLP.MainWindow', 'E&xit'))
@@ -405,23 +395,24 @@
         self.formatting_tag_item.setText(translate('OpenLP.MainWindow', 'Configure &Formatting Tags...'))
         self.settings_configure_item.setText(translate('OpenLP.MainWindow', '&Configure OpenLP...'))
         self.settings_export_item.setStatusTip(translate('OpenLP.MainWindow',
-            'Export OpenLP settings to a specified *.config file'))
+                                               'Export OpenLP settings to a specified *.config file'))
         self.settings_export_item.setText(translate('OpenLP.MainWindow', 'Settings'))
         self.settings_import_item.setStatusTip(translate('OpenLP.MainWindow',
-            'Import OpenLP settings from a specified *.config file previously exported on this or another machine'))
+                                               'Import OpenLP settings from a specified *.config file previously '
+                                               'exported on this or another machine'))
         self.settings_import_item.setText(translate('OpenLP.MainWindow', 'Settings'))
         self.view_media_manager_item.setText(translate('OpenLP.MainWindow', '&Media Manager'))
         self.view_media_manager_item.setToolTip(translate('OpenLP.MainWindow', 'Toggle Media Manager'))
         self.view_media_manager_item.setStatusTip(translate('OpenLP.MainWindow',
-            'Toggle the visibility of the media manager.'))
+                                                  'Toggle the visibility of the media manager.'))
         self.view_theme_manager_item.setText(translate('OpenLP.MainWindow', '&Theme Manager'))
         self.view_theme_manager_item.setToolTip(translate('OpenLP.MainWindow', 'Toggle Theme Manager'))
         self.view_theme_manager_item.setStatusTip(translate('OpenLP.MainWindow',
-            'Toggle the visibility of the theme manager.'))
+                                                  'Toggle the visibility of the theme manager.'))
         self.view_service_manager_item.setText(translate('OpenLP.MainWindow', '&Service Manager'))
         self.view_service_manager_item.setToolTip(translate('OpenLP.MainWindow', 'Toggle Service Manager'))
         self.view_service_manager_item.setStatusTip(translate('OpenLP.MainWindow',
-            'Toggle the visibility of the service manager.'))
+                                                    'Toggle the visibility of the service manager.'))
         self.view_preview_panel.setText(translate('OpenLP.MainWindow', '&Preview Panel'))
         self.view_preview_panel.setToolTip(translate('OpenLP.MainWindow', 'Toggle Preview Panel'))
         self.view_preview_panel.setStatusTip(
@@ -431,12 +422,12 @@
         self.lock_panel.setText(translate('OpenLP.MainWindow', 'L&ock Panels'))
         self.lock_panel.setStatusTip(translate('OpenLP.MainWindow', 'Prevent the panels being moved.'))
         self.view_live_panel.setStatusTip(translate('OpenLP.MainWindow', 'Toggle the visibility of the live panel.'))
-        self.settingsPluginListItem.setText(translate('OpenLP.MainWindow', '&Plugin List'))
-        self.settingsPluginListItem.setStatusTip(translate('OpenLP.MainWindow', 'List the Plugins'))
+        self.settings_plugin_list_item.setText(translate('OpenLP.MainWindow', '&Plugin List'))
+        self.settings_plugin_list_item.setStatusTip(translate('OpenLP.MainWindow', 'List the Plugins'))
         self.about_item.setText(translate('OpenLP.MainWindow', '&About'))
         self.about_item.setStatusTip(translate('OpenLP.MainWindow', 'More information about OpenLP'))
         if os.name == 'nt':
-            self.offlineHelpItem.setText(translate('OpenLP.MainWindow', '&User Guide'))
+            self.offline_help_item.setText(translate('OpenLP.MainWindow', '&User Guide'))
         self.on_line_help_item.setText(translate('OpenLP.MainWindow', '&Online Help'))
         self.search_shortcut_action.setText(UiStrings().Search)
         self.search_shortcut_action.setToolTip(
@@ -449,18 +440,18 @@
         self.auto_language_item.setStatusTip(translate('OpenLP.MainWindow', 'Use the system language, if available.'))
         self.tools_add_tool_item.setText(translate('OpenLP.MainWindow', 'Add &Tool...'))
         self.tools_add_tool_item.setStatusTip(translate('OpenLP.MainWindow',
-            'Add an application to the list of tools.'))
+                                                        'Add an application to the list of tools.'))
         self.tools_open_data_folder.setText(translate('OpenLP.MainWindow', 'Open &Data Folder...'))
         self.tools_open_data_folder.setStatusTip(translate('OpenLP.MainWindow',
-            'Open the folder where songs, bibles and other data resides.'))
+                                                 'Open the folder where songs, bibles and other data resides.'))
         self.tools_first_time_wizard.setText(translate('OpenLP.MainWindow', 'Re-run First Time Wizard'))
         self.tools_first_time_wizard.setStatusTip(translate('OpenLP.MainWindow',
-            'Re-run the First Time Wizard, importing songs, Bibles and themes.'))
+                                                  'Re-run the First Time Wizard, importing songs, Bibles and themes.'))
         self.update_theme_images.setText(translate('OpenLP.MainWindow', 'Update Theme Images'))
         self.update_theme_images.setStatusTip(translate('OpenLP.MainWindow',
-            'Update the preview images for all themes.'))
-        self.mode_default_Item.setText(translate('OpenLP.MainWindow', '&Default'))
-        self.mode_default_Item.setStatusTip(translate('OpenLP.MainWindow', 'Set the view mode back to the default.'))
+                                                        'Update the preview images for all themes.'))
+        self.mode_default_item.setText(translate('OpenLP.MainWindow', '&Default'))
+        self.mode_default_item.setStatusTip(translate('OpenLP.MainWindow', 'Set the view mode back to the default.'))
         self.mode_setup_item.setText(translate('OpenLP.MainWindow', '&Setup'))
         self.mode_setup_item.setStatusTip(translate('OpenLP.MainWindow', 'Set the view mode to Setup.'))
         self.mode_live_item.setText(translate('OpenLP.MainWindow', '&Live'))
@@ -504,14 +495,14 @@
         self.formatting_tag_form = FormattingTagForm(self)
         self.shortcut_form = ShortcutListForm(self)
         # Set up the path with plugins
-        self.plugin_manager = PluginManager()
+        self.plugin_manager = PluginManager(self)
         self.image_manager = ImageManager()
+        self.renderer = Renderer()
         # Set up the interface
         self.setupUi(self)
         # Define the media Dock Manager
         self.media_dock_manager = MediaDockManager(self.media_tool_box)
         # Load settings after setupUi so default UI sizes are overwritten
-        self.load_settings()
         # Once settings are loaded update the menu with the recent files.
         self.update_recent_files_menu()
         self.plugin_form = PluginForm(self)
@@ -523,7 +514,7 @@
         self.export_theme_item.triggered.connect(self.theme_manager_contents.on_export_theme)
         self.web_site_item.triggered.connect(self.on_help_web_site_clicked)
         self.tools_open_data_folder.triggered.connect(self.on_tools_open_data_folder_clicked)
-        self.tools_first_time_wizard.triggered.connect(self.on_first_time_wzard_clicked)
+        self.tools_first_time_wizard.triggered.connect(self.on_first_time_wizard_clicked)
         self.update_theme_images.triggered.connect(self.on_update_theme_images)
         self.formatting_tag_item.triggered.connect(self.on_formatting_tag_item_clicked)
         self.settings_configure_item.triggered.connect(self.on_settings_configure_iem_clicked)
@@ -532,7 +523,7 @@
         self.settings_export_item.triggered.connect(self.on_settings_export_item_clicked)
         # i18n set signals for languages
         self.language_group.triggered.connect(LanguageManager.set_language)
-        self.mode_default_Item.triggered.connect(self.on_mode_default_item_clicked)
+        self.mode_default_item.triggered.connect(self.on_mode_default_item_clicked)
         self.mode_setup_item.triggered.connect(self.on_mode_setup_item_clicked)
         self.mode_live_item.triggered.connect(self.on_mode_live_item_clicked)
         # Media Manager
@@ -542,11 +533,19 @@
         Registry().register_function('theme_update_global', self.default_theme_changed)
         Registry().register_function('openlp_version_check', self.version_notice)
         Registry().register_function('config_screen_changed', self.screen_changed)
-        Registry().register_function('bootstrap_post_set_up', self.restore_current_media_manager_item)
-        self.renderer = Renderer()
+        Registry().register_function('bootstrap_post_set_up', self.bootstrap_post_set_up)
         # Reset the cursor
         self.application.set_normal_cursor()
 
+    def bootstrap_post_set_up(self):
+        """
+        process the bootstrap post setup request
+        """
+        self.preview_controller.panel.setVisible(Settings().value('user interface/preview panel'))
+        self.live_controller.panel.setVisible(Settings().value('user interface/live panel'))
+        self.load_settings()
+        self.restore_current_media_manager_item()
+
     def restore_current_media_manager_item(self):
         """
         Called on start up to restore the last active media plugin.
@@ -559,7 +558,7 @@
 
     def on_search_shortcut_triggered(self):
         """
-        Called when the search shotcut has been pressed.
+        Called when the search shortcut has been pressed.
         """
         # Make sure the media_dock is visible.
         if not self.media_manager_dock.isVisible():
@@ -583,7 +582,8 @@
         """
         log.debug('version_notice')
         version_text = translate('OpenLP.MainWindow', 'Version %s of OpenLP is now available for download (you are '
-            'currently running version %s). \n\nYou can download the latest version from http://openlp.org/.')
+                                 'currently running version %s). \n\nYou can download the latest version from '
+                                 'http://openlp.org/.')
         self.version_text = version_text % (version, get_application_version()['full'])
 
     def show(self):
@@ -607,12 +607,12 @@
         self.timer_version_id = self.startTimer(1000)
         view_mode = Settings().value('%s/view mode' % self.general_settings_section)
         if view_mode == 'default':
-            self.mode_default_Item.setChecked(True)
+            self.mode_default_item.setChecked(True)
         elif view_mode == 'setup':
-            self.setViewMode(True, True, False, True, False)
+            self.set_view_mode(True, True, False, True, False)
             self.mode_setup_item.setChecked(True)
         elif view_mode == 'live':
-            self.setViewMode(False, True, False, False, True)
+            self.set_view_mode(False, True, False, False, True)
             self.mode_live_item.setChecked(True)
 
     def app_startup(self):
@@ -638,21 +638,21 @@
         temp_dir = os.path.join(str(gettempdir()), 'openlp')
         shutil.rmtree(temp_dir, True)
 
-    def on_first_time_wzard_clicked(self):
+    def on_first_time_wizard_clicked(self):
         """
-        Re-run the first time wizard.  Prompts the user for run confirmation
-        If wizard is run, songs, bibles and themes are imported.  The default
-        theme is changed (if necessary).  The plugins in pluginmanager are
+        Re-run the first time wizard.  Prompts the user for run confirmation.If wizard is run, songs, bibles and
+        themes are imported.  The default theme is changed (if necessary).  The plugins in pluginmanager are
         set active/in-active to match the selection in the wizard.
         """
         answer = QtGui.QMessageBox.warning(self,
-            translate('OpenLP.MainWindow', 'Re-run First Time Wizard?'),
-            translate('OpenLP.MainWindow', 'Are you sure you want to re-run the First Time Wizard?\n\n'
-                'Re-running this wizard may make changes to your current '
-                'OpenLP configuration and possibly add songs to your '
-                'existing songs list and change your default theme.'),
-            QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
-                QtGui.QMessageBox.No)
+                                           translate('OpenLP.MainWindow', 'Re-run First Time Wizard?'),
+                                           translate('OpenLP.MainWindow', 'Are you sure you want to re-run the First '
+                                                     'Time Wizard?\n\nRe-running this wizard may make changes to your '
+                                                     'current OpenLP configuration and possibly add songs to your '
+                                                     '#existing songs list and change your default theme.'),
+                                           QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | 
+                                                                             QtGui.QMessageBox.No),
+                                           QtGui.QMessageBox.No)
         if answer == QtGui.QMessageBox.No:
             return
         screens = ScreenList()
@@ -663,15 +663,15 @@
         self.application.set_busy_cursor()
         self.first_time()
         for plugin in self.plugin_manager.plugins:
-            self.activePlugin = plugin
-            oldStatus = self.activePlugin.status
-            self.activePlugin.set_status()
-            if oldStatus != self.activePlugin.status:
-                if self.activePlugin.status == PluginStatus.Active:
-                    self.activePlugin.toggle_status(PluginStatus.Active)
-                    self.activePlugin.app_startup()
+            self.active_plugin = plugin
+            old_status = self.active_plugin.status
+            self.active_plugin.set_status()
+            if old_status != self.active_plugin.status:
+                if self.active_plugin.status == PluginStatus.Active:
+                    self.active_plugin.toggle_status(PluginStatus.Active)
+                    self.active_plugin.app_startup()
                 else:
-                    self.activePlugin.toggle_status(PluginStatus.Inactive)
+                    self.active_plugin.toggle_status(PluginStatus.Inactive)
         # Set global theme and
         Registry().execute('theme_update_global', self.theme_manager_contents.global_theme)
         self.theme_manager_contents.load_first_time_themes()
@@ -688,7 +688,7 @@
         if settings.value('%s/screen blank' % self.general_settings_section):
             if settings.value('%s/blank warning' % self.general_settings_section):
                 QtGui.QMessageBox.question(self, translate('OpenLP.MainWindow', 'OpenLP Main Display Blanked'),
-                    translate('OpenLP.MainWindow', 'The Main Display has been blanked out'))
+                                           translate('OpenLP.MainWindow', 'The Main Display has been blanked out'))
 
     def error_message(self, title, message):
         """
@@ -743,7 +743,7 @@
         """
         Load the local OpenLP help file
         """
-        os.startfile(self.localHelpFile)
+        os.startfile(self.local_help_file)
 
     def on_online_help_clicked(self):
         """
@@ -809,15 +809,21 @@
         Import settings from an export INI file
         """
         answer = QtGui.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'Import settings?'),
-            translate('OpenLP.MainWindow', 'Are you sure you want to import settings?\n\n'
-                'Importing settings will make permanent changes to your current OpenLP configuration.\n\n'
-                'Importing incorrect settings may cause erratic behaviour or OpenLP to terminate abnormally.'),
-            QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
-                QtGui.QMessageBox.No)
+                                            translate('OpenLP.MainWindow', 'Are you sure you want to import '
+                                                                           'settings?\n\n Importing settings will '
+                                                                           'make permanent changes to your current '
+                                                                           'OpenLP configuration.\n\n Importing '
+                                                                           'incorrect settings may cause erratic '
+                                                                           'behaviour or OpenLP to terminate '
+                                                                           'abnormally.'),
+                                            QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
+                                                                              QtGui.QMessageBox.No),
+                                            QtGui.QMessageBox.No)
         if answer == QtGui.QMessageBox.No:
             return
         import_file_name = QtGui.QFileDialog.getOpenFileName(self, translate('OpenLP.MainWindow', 'Open File'), '',
-            translate('OpenLP.MainWindow', 'OpenLP Export Settings Files (*.conf)'))
+                                                             translate('OpenLP.MainWindow', 'OpenLP Export Settings '
+                                                                                            'Files (*.conf)'))
         if not import_file_name:
             return
         setting_sections = []
@@ -850,9 +856,10 @@
         # load what we can from it, and just silently ignore anything we don't recognise.
         if import_settings.value('SettingsImport/type') != 'OpenLP_settings_export':
             QtGui.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'Import settings'),
-                translate('OpenLP.MainWindow', 'The file you have selected does not appear to be a valid OpenLP '
-                    'settings file.\n\nProcessing has terminated and no changes have been made.'),
-                QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
+                                       translate('OpenLP.MainWindow', 'The file you have selected does not appear to '
+                                                 'be a valid OpenLP settings file.\n\nProcessing has terminated and '
+                                                 'no changes have been made.'),
+                                       QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
             return
         import_keys = import_settings.allKeys()
         for section_key in import_keys:
@@ -885,10 +892,10 @@
         # We must do an immediate restart or current configuration will overwrite what was just imported when
         # application terminates normally.   We need to exit without saving configuration.
         QtGui.QMessageBox.information(self, translate('OpenLP.MainWindow', 'Import settings'),
-            translate('OpenLP.MainWindow', 'OpenLP will now close.  Imported settings will '
-                'be applied the next time you start OpenLP.'),
-            QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
-        self.settingsImported = True
+                                      translate('OpenLP.MainWindow', 'OpenLP will now close.  Imported settings will '
+                                      'be applied the next time you start OpenLP.'),
+                                      QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
+        self.settings_imported = True
         self.clean_up()
         QtCore.QCoreApplication.exit()
 
@@ -897,8 +904,10 @@
         Export settings to a .conf file in INI format
         """
         export_file_name = QtGui.QFileDialog.getSaveFileName(self,
-            translate('OpenLP.MainWindow', 'Export Settings File'), '',
-            translate('OpenLP.MainWindow', 'OpenLP Export Settings File (*.conf)'))
+                                                             translate('OpenLP.MainWindow', 'Export Settings File'),
+                                                             '',
+                                                             translate('OpenLP.MainWindow', 'OpenLP Export Settings '
+                                                                                            'File (*.conf)'))
         if not export_file_name:
             return
             # Make sure it's a .conf file.
@@ -967,21 +976,21 @@
         """
         Put OpenLP into "Default" view mode.
         """
-        self.setViewMode(True, True, True, True, True, 'default')
+        self.set_view_mode(True, True, True, True, True, 'default')
 
     def on_mode_setup_item_clicked(self):
         """
         Put OpenLP into "Setup" view mode.
         """
-        self.setViewMode(True, True, False, True, False, 'setup')
+        self.set_view_mode(True, True, False, True, False, 'setup')
 
     def on_mode_live_item_clicked(self):
         """
         Put OpenLP into "Live" view mode.
         """
-        self.setViewMode(False, True, False, False, True, 'live')
+        self.set_view_mode(False, True, False, False, True, 'live')
 
-    def setViewMode(self, media=True, service=True, theme=True, preview=True, live=True, mode=''):
+    def set_view_mode(self, media=True, service=True, theme=True, preview=True, live=True, mode=''):
         """
         Set OpenLP to a different view mode.
         """
@@ -1018,7 +1027,7 @@
             event.ignore()
             return
         # If we just did a settings import, close without saving changes.
-        if self.settingsImported:
+        if self.settings_imported:
             self.clean_up(False)
             event.accept()
         if self.service_manager_contents.is_modified():
@@ -1037,9 +1046,11 @@
         else:
             if Settings().value('advanced/enable exit confirmation'):
                 ret = QtGui.QMessageBox.question(self, translate('OpenLP.MainWindow', 'Close OpenLP'),
-                        translate('OpenLP.MainWindow', 'Are you sure you want to close OpenLP?'),
-                    QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
-                    QtGui.QMessageBox.Yes)
+                                                 translate('OpenLP.MainWindow', 'Are you sure you want to close '
+                                                                                'OpenLP?'),
+                                                 QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui
+                                                 .QMessageBox.No),
+                                                 QtGui.QMessageBox.Yes)
                 if ret == QtGui.QMessageBox.Yes:
                     self.clean_up()
                     event.accept()
@@ -1108,7 +1119,7 @@
         Update the default theme indicator in the status bar
         """
         self.default_theme_label.setText(translate('OpenLP.MainWindow', 'Default Theme: %s') %
-            Settings().value('themes/global theme'))
+                                         Settings().value('themes/global theme'))
 
     def toggle_media_manager(self):
         """
@@ -1197,8 +1208,8 @@
         self.live_controller.splitter.restoreState(settings.value('live splitter geometry'))
         self.preview_controller.splitter.restoreState(settings.value('preview splitter geometry'))
         self.control_splitter.restoreState(settings.value('main window splitter geometry'))
-        #This needs to be called after restoreState(), because saveState() also saves the "Collapsible" property
-        #which was True (by default) < OpenLP 2.1.
+        # This needs to be called after restoreState(), because saveState() also saves the "Collapsible" property
+        # which was True (by default) < OpenLP 2.1.
         self.control_splitter.setChildrenCollapsible(False)
         settings.endGroup()
 
@@ -1207,7 +1218,7 @@
         Save the main window settings.
         """
         # Exit if we just did a settings import.
-        if self.settingsImported:
+        if self.settings_imported:
             return
         log.debug('Saving QSettings')
         settings = Settings()
@@ -1228,22 +1239,23 @@
         Updates the recent file menu with the latest list of service files accessed.
         """
         recent_file_count = Settings().value('advanced/recent file count')
-        existing_recent_files = [recentFile for recentFile in self.recent_files
-            if os.path.isfile(str(recentFile))]
+        existing_recent_files = [recentFile for recentFile in self.recent_files if os.path.isfile(str(recentFile))]
         recent_files_to_display = existing_recent_files[0:recent_file_count]
         self.recent_files_menu.clear()
         for file_id, filename in enumerate(recent_files_to_display):
             log.debug('Recent file name: %s', filename)
             action = create_action(self, '',
-                text='&%d %s' % (file_id + 1, os.path.splitext(os.path.basename(
-                str(filename)))[0]), data=filename,
-                triggers=self.service_manager_contents.on_recent_service_clicked)
+                                   text='&%d %s' % (file_id + 1,
+                                   os.path.splitext(os.path.basename(str(filename)))[0]), data=filename,
+                                   triggers=self.service_manager_contents.on_recent_service_clicked)
             self.recent_files_menu.addAction(action)
         clear_recent_files_action = create_action(self, '',
-            text=translate('OpenLP.MainWindow', 'Clear List', 'Clear List of recent files'),
-            statustip=translate('OpenLP.MainWindow', 'Clear the list of recent files.'),
-            enabled=bool(self.recent_files),
-            triggers=self.clear_recent_file_menu)
+                                                  text=translate('OpenLP.MainWindow', 'Clear List', 'Clear List of '
+                                                                                                   'recent files'),
+                                                  statustip=translate('OpenLP.MainWindow', 'Clear the list of recent '
+                                                                                          'files.'),
+                                                  enabled=bool(self.recent_files),
+                                                  triggers=self.clear_recent_file_menu)
         add_actions(self.recent_files_menu, (None, clear_recent_files_action))
         clear_recent_files_action.setEnabled(bool(self.recent_files))
 
@@ -1312,7 +1324,7 @@
             # Has the thread passed some data to be displayed so display it and stop all waiting
             if hasattr(self, 'version_text'):
                 QtGui.QMessageBox.question(self, translate('OpenLP.MainWindow', 'OpenLP Version Updated'),
-                    self.version_text)
+                                           self.version_text)
             else:
                 # the thread has not confirmed it is running or it has not yet sent any data so lets keep waiting
                 if not hasattr(self, 'version_update_running') or self.version_update_running:
@@ -1344,16 +1356,16 @@
             try:
                 self.showStatusMessage(
                     translate('OpenLP.MainWindow', 'Copying OpenLP data to new data directory location - %s '
-                    '- Please wait for copy to finish').replace('%s', self.new_data_path))
+                              '- Please wait for copy to finish').replace('%s', self.new_data_path))
                 dir_util.copy_tree(old_data_path, self.new_data_path)
-                log.info('Copy sucessful')
+                log.info('Copy successful')
             except (IOError, os.error, DistutilsFileError) as why:
                 self.application.set_normal_cursor()
                 log.exception('Data copy failed %s' % str(why))
                 QtGui.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'New Data Directory Error'),
-                    translate('OpenLP.MainWindow',
-                        'OpenLP Data directory copy failed\n\n%s').replace('%s', str(why)),
-                    QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
+                                           translate('OpenLP.MainWindow',
+                                           'OpenLP Data directory copy failed\n\n%s').replace('%s', str(why)),
+                                           QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
                 return False
         else:
             log.info('No data copy requested')

=== modified file 'openlp/core/ui/media/mediacontroller.py'
--- openlp/core/ui/media/mediacontroller.py	2013-12-24 08:56:50 +0000
+++ openlp/core/ui/media/mediacontroller.py	2013-12-28 21:35:49 +0000
@@ -35,8 +35,8 @@
 import datetime
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import Settings, UiStrings, translate
-from openlp.core.lib import OpenLPToolbar, Registry
+from openlp.core.common import Registry, 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
 from openlp.core.ui.media.mediaplayer import MediaPlayer

=== modified file 'openlp/core/ui/media/mediaplayer.py'
--- openlp/core/ui/media/mediaplayer.py	2013-12-24 08:56:50 +0000
+++ openlp/core/ui/media/mediaplayer.py	2013-12-28 21:35:49 +0000
@@ -31,7 +31,7 @@
 """
 import os
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 from openlp.core.ui.media import MediaState
 
 

=== modified file 'openlp/core/ui/media/playertab.py'
--- openlp/core/ui/media/playertab.py	2013-12-24 08:56:50 +0000
+++ openlp/core/ui/media/playertab.py	2013-12-28 21:35:49 +0000
@@ -31,8 +31,8 @@
 """
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import Settings, UiStrings, translate
-from openlp.core.lib import Registry, SettingsTab
+from openlp.core.common import Registry, Settings, UiStrings, translate
+from openlp.core.lib import SettingsTab
 from openlp.core.lib.ui import create_button
 from openlp.core.ui.media import get_media_players, set_media_players
 

=== modified file 'openlp/core/ui/pluginform.py'
--- openlp/core/ui/pluginform.py	2013-12-24 20:45:29 +0000
+++ openlp/core/ui/pluginform.py	2013-12-28 21:35:49 +0000
@@ -34,8 +34,8 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.common import translate
-from openlp.core.lib import PluginStatus, Registry
+from openlp.core.common import Registry, translate
+from openlp.core.lib import PluginStatus
 from .plugindialog import Ui_PluginViewDialog
 
 log = logging.getLogger(__name__)

=== modified file 'openlp/core/ui/printserviceform.py'
--- openlp/core/ui/printserviceform.py	2013-12-24 15:55:01 +0000
+++ openlp/core/ui/printserviceform.py	2013-12-28 21:35:49 +0000
@@ -35,8 +35,8 @@
 from PyQt4 import QtCore, QtGui
 from lxml import html
 
-from openlp.core.common import Settings, UiStrings, translate
-from openlp.core.lib import Registry, get_text_file_string
+from openlp.core.common import Registry, Settings, UiStrings, translate
+from openlp.core.lib import get_text_file_string
 from openlp.core.ui.printservicedialog import Ui_PrintServiceDialog, ZoomSize
 from openlp.core.common import AppLocation
 

=== modified file 'openlp/core/ui/serviceitemeditform.py'
--- openlp/core/ui/serviceitemeditform.py	2013-12-24 08:56:50 +0000
+++ openlp/core/ui/serviceitemeditform.py	2013-12-28 21:35:49 +0000
@@ -30,7 +30,7 @@
 The service item edit dialog
 """
 from PyQt4 import QtGui
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 
 from .serviceitemeditdialog import Ui_ServiceItemEditDialog
 

=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py	2013-12-26 20:19:47 +0000
+++ openlp/core/ui/servicemanager.py	2013-12-28 21:35:49 +0000
@@ -42,8 +42,8 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import AppLocation, Settings, ThemeLevel, check_directory_exists, UiStrings, translate
-from openlp.core.lib import OpenLPToolbar, ServiceItem, ItemCapabilities, PluginStatus, Registry, build_icon
+from openlp.core.common import Registry, AppLocation, Settings, ThemeLevel, 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
 from openlp.core.ui.printserviceform import PrintServiceForm
@@ -741,7 +741,7 @@
                 Settings().setValue('servicemanager/last file', file_name)
             else:
                 critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File is not a valid service.'))
-                log.exception('File contains no service data')
+                log.error('File contains no service data')
         except (IOError, NameError, zipfile.BadZipfile):
             log.exception('Problem loading service file %s' % file_name)
             critical_error_message_box(message=translate('OpenLP.ServiceManager',

=== modified file 'openlp/core/ui/servicenoteform.py'
--- openlp/core/ui/servicenoteform.py	2013-12-24 08:56:50 +0000
+++ openlp/core/ui/servicenoteform.py	2013-12-28 21:35:49 +0000
@@ -31,8 +31,8 @@
 """
 from PyQt4 import QtGui
 
-from openlp.core.common import translate
-from openlp.core.lib import SpellTextEdit, Registry
+from openlp.core.common import Registry, translate
+from openlp.core.lib import SpellTextEdit
 from openlp.core.lib.ui import create_button_box
 
 

=== modified file 'openlp/core/ui/settingsform.py'
--- openlp/core/ui/settingsform.py	2013-12-24 15:55:01 +0000
+++ openlp/core/ui/settingsform.py	2013-12-28 21:35:49 +0000
@@ -33,7 +33,8 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.lib import PluginStatus, Registry, build_icon
+from openlp.core.common import Registry
+from openlp.core.lib import PluginStatus, build_icon
 from openlp.core.ui import AdvancedTab, GeneralTab, ThemesTab
 from openlp.core.ui.media import PlayerTab
 from .settingsdialog import Ui_SettingsDialog

=== modified file 'openlp/core/ui/shortcutlistform.py'
--- openlp/core/ui/shortcutlistform.py	2013-12-24 20:45:29 +0000
+++ openlp/core/ui/shortcutlistform.py	2013-12-28 21:35:49 +0000
@@ -33,8 +33,7 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.lib import Registry
-from openlp.core.common import Settings, translate
+from openlp.core.common import Registry, Settings, translate
 from openlp.core.utils.actions import ActionList
 from .shortcutlistdialog import Ui_ShortcutListDialog
 

=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py	2013-12-24 08:56:50 +0000
+++ openlp/core/ui/slidecontroller.py	2013-12-28 21:35:49 +0000
@@ -31,24 +31,21 @@
 """
 
 import os
-import logging
 import copy
 from collections import deque
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import Settings, SlideLimits, UiStrings, translate
-from openlp.core.lib import OpenLPToolbar, ItemCapabilities, ServiceItem, ImageSource, ServiceItemAction, Registry, \
+from openlp.core.common import Registry, Settings, SlideLimits, UiStrings, translate, RegistryMixin, OpenLPMixin
+from openlp.core.lib import OpenLPToolbar, ItemCapabilities, ServiceItem, ImageSource, ServiceItemAction, \
     ScreenList, build_icon, build_html
 from openlp.core.ui import HideMode, MainDisplay, Display, DisplayControllerType
 from openlp.core.lib.ui import create_action
 from openlp.core.utils.actions import ActionList, CategoryOrder
 from openlp.core.ui.listpreviewwidget import ListPreviewWidget
 
-log = logging.getLogger(__name__)
-
 # Threshold which has to be trespassed to toggle.
-HIDE_MENU_THRESHOLD  = 27
+HIDE_MENU_THRESHOLD = 27
 AUDIO_TIME_LABEL_STYLESHEET = 'background-color: palette(background); ' \
     'border-top-color: palette(shadow); ' \
     'border-left-color: palette(shadow); ' \
@@ -57,17 +54,35 @@
     'border-radius: 3px; border-style: inset; ' \
     'border-width: 1; font-family: monospace; margin: 2px;'
 
+NARROW_MENU = [
+    'hide_menu'
+]
+LOOP_LIST = [
+    'play_slides_menu',
+    'loop_separator',
+    'delay_spin_box'
+]
+AUDIO_LIST = [
+    'audioPauseItem',
+    'audio_time_label'
+]
+WIDE_MENU = [
+    'blank_screen_button',
+    'theme_screen_button',
+    'desktop_screen_button'
+]
+
 
 class DisplayController(QtGui.QWidget):
     """
     Controller is a general display controller widget.
     """
-    def __init__(self, parent, is_live=False):
+    def __init__(self, parent):
         """
         Set up the general Controller.
         """
         super(DisplayController, self).__init__(parent)
-        self.is_live = is_live
+        self.is_live = False
         self.display = None
         self.controller_type = DisplayControllerType.Plugin
 
@@ -87,43 +102,33 @@
     SlideController is the slide controller widget. This widget is what the
     user uses to control the displaying of verses/slides/etc on the screen.
     """
-    def __init__(self, parent, is_live=False):
+    def __init__(self, parent):
         """
         Set up the Slide Controller.
         """
-        super(SlideController, self).__init__(parent, is_live)
-        Registry().register_function('bootstrap_post_set_up', self.screen_size_changed)
+        super(SlideController, self).__init__(parent)
+
+    def post_set_up(self):
+        """
+        Call by bootstrap functions
+        """
+        self.initialise()
+        self.screen_size_changed()
+
+    def initialise(self):
         self.screens = ScreenList()
         try:
             self.ratio = self.screens.current['size'].width() / self.screens.current['size'].height()
         except ZeroDivisionError:
             self.ratio = 1
-        self.loop_list = [
-            'play_slides_menu',
-            'loop_separator',
-            'delay_spin_box'
-        ]
-        # audioPauseItem is also in Settings so any changes need to be paired
-        self.audio_list = [
-            'audioPauseItem',
-            'audio_time_label'
-        ]
-        self.wide_menu = [
-            'blank_screen_button',
-            'theme_screen_button',
-            'desktop_screen_button'
-        ]
-        self.narrow_menu = [
-            'hide_menu'
-        ]
         self.timer_id = 0
         self.song_edit = False
         self.selected_row = 0
         self.service_item = None
         self.slide_limits = None
         self.update_slide_limits()
-        self.panel = QtGui.QWidget(parent.control_splitter)
-        self.slideList = {}
+        self.panel = QtGui.QWidget(self.main_window.control_splitter)
+        self.slide_list = {}
         self.slide_count = 0
         self.slide_image = None
         # Layout for holding panel
@@ -132,21 +137,6 @@
         self.panel_layout.setMargin(0)
         # Type label for the top of the slide controller
         self.type_label = QtGui.QLabel(self.panel)
-        if self.is_live:
-            Registry().register('live_controller', self)
-            self.type_label.setText(UiStrings().Live)
-            self.split = 1
-            self.type_prefix = 'live'
-            self.keypress_queue = deque()
-            self.keypress_loop = False
-            self.category = UiStrings().LiveToolbar
-            ActionList.get_instance().add_category(str(self.category), CategoryOrder.standard_toolbar)
-        else:
-            Registry().register('preview_controller', self)
-            self.type_label.setText(UiStrings().Preview)
-            self.split = 0
-            self.type_prefix = 'preview'
-            self.category = None
         self.type_label.setStyleSheet('font-weight: bold; font-size: 12pt;')
         self.type_label.setAlignment(QtCore.Qt.AlignCenter)
         self.panel_layout.addWidget(self.type_label)
@@ -172,17 +162,19 @@
         size_toolbar_policy.setHeightForWidth(self.toolbar.sizePolicy().hasHeightForWidth())
         self.toolbar.setSizePolicy(size_toolbar_policy)
         self.previous_item = create_action(self, 'previousItem_' + self.type_prefix,
-            text=translate('OpenLP.SlideController', 'Previous Slide'), icon=':/slides/slide_previous.png',
-            tooltip=translate('OpenLP.SlideController', 'Move to previous.'),
-            can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut,
-            category=self.category, triggers=self.on_slide_selected_previous)
+                                           text=translate('OpenLP.SlideController', 'Previous Slide'), 
+                                           icon=':/slides/slide_previous.png',
+                                           tooltip=translate('OpenLP.SlideController', 'Move to previous.'),
+                                           can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut,
+                                           category=self.category, triggers=self.on_slide_selected_previous)
         self.toolbar.addAction(self.previous_item)
-        self.nextItem = create_action(self, 'nextItem_' + self.type_prefix,
-            text=translate('OpenLP.SlideController', 'Next Slide'), icon=':/slides/slide_next.png',
-            tooltip=translate('OpenLP.SlideController', 'Move to next.'),
-            can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut,
-            category=self.category, triggers=self.on_slide_selected_next_action)
-        self.toolbar.addAction(self.nextItem)
+        self.next_item = create_action(self, 'nextItem_' + self.type_prefix,
+                                       text=translate('OpenLP.SlideController', 'Next Slide'), 
+                                       icon=':/slides/slide_next.png',
+                                       tooltip=translate('OpenLP.SlideController', 'Move to next.'),
+                                       can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut,
+                                       category=self.category, triggers=self.on_slide_selected_next_action)
+        self.toolbar.addAction(self.next_item)
         self.toolbar.addSeparator()
         self.controller_type = DisplayControllerType.Preview
         if self.is_live:
@@ -195,16 +187,20 @@
             self.hide_menu.setMenu(QtGui.QMenu(translate('OpenLP.SlideController', 'Hide'), self.toolbar))
             self.toolbar.add_toolbar_widget(self.hide_menu)
             self.blank_screen = create_action(self, 'blankScreen',
-                text=translate('OpenLP.SlideController', 'Blank Screen'), icon=':/slides/slide_blank.png',
-                checked=False, can_shortcuts=True, category=self.category, triggers=self.on_blank_display)
+                                              text=translate('OpenLP.SlideController', 'Blank Screen'), 
+                                              icon=':/slides/slide_blank.png',
+                                              checked=False, can_shortcuts=True, category=self.category, 
+                                              triggers=self.on_blank_display)
             self.theme_screen = create_action(self, 'themeScreen',
-                text=translate('OpenLP.SlideController', 'Blank to Theme'), icon=':/slides/slide_theme.png',
-                checked=False, can_shortcuts=True, category=self.category,
-                triggers=self.on_theme_display)
+                                              text=translate('OpenLP.SlideController', 'Blank to Theme'), 
+                                              icon=':/slides/slide_theme.png',
+                                              checked=False, can_shortcuts=True, category=self.category,
+                                              triggers=self.on_theme_display)
             self.desktop_screen = create_action(self, 'desktopScreen',
-                text=translate('OpenLP.SlideController', 'Show Desktop'), icon=':/slides/slide_desktop.png',
-                checked=False, can_shortcuts=True, category=self.category,
-                triggers=self.on_hide_display)
+                                                text=translate('OpenLP.SlideController', 'Show Desktop'), 
+                                                icon=':/slides/slide_desktop.png',
+                                                checked=False, can_shortcuts=True, category=self.category,
+                                                triggers=self.on_hide_display)
             self.hide_menu.setDefaultAction(self.blank_screen)
             self.hide_menu.menu().addAction(self.blank_screen)
             self.hide_menu.menu().addAction(self.theme_screen)
@@ -231,11 +227,11 @@
             self.play_slides_menu.setMenu(QtGui.QMenu(translate('OpenLP.SlideController', 'Play Slides'), self.toolbar))
             self.toolbar.add_toolbar_widget(self.play_slides_menu)
             self.play_slides_loop = create_action(self, 'playSlidesLoop', text=UiStrings().PlaySlidesInLoop,
-                icon=':/media/media_time.png', checked=False, can_shortcuts=True,
-                category=self.category, triggers=self.on_play_slides_loop)
+                                                  icon=':/media/media_time.png', checked=False, can_shortcuts=True,
+                                                  category=self.category, triggers=self.on_play_slides_loop)
             self.play_slides_once = create_action(self, 'playSlidesOnce', text=UiStrings().PlaySlidesToEnd,
-                icon=':/media/media_time.png', checked=False, can_shortcuts=True,
-                category=self.category, triggers=self.on_play_slides_once)
+                                                  icon=':/media/media_time.png', checked=False, can_shortcuts=True,
+                                                  category=self.category, triggers=self.on_play_slides_once)
             if Settings().value(self.main_window.advanced_settings_section + '/slide limits') == SlideLimits.Wrap:
                 self.play_slides_menu.setDefaultAction(self.play_slides_loop)
             else:
@@ -251,12 +247,15 @@
             self.toolbar.add_toolbar_widget(self.delay_spin_box)
         else:
             self.toolbar.add_toolbar_action('goLive', icon=':/general/general_live.png',
-                tooltip=translate('OpenLP.SlideController', 'Move to live.'), triggers=self.on_go_live)
+                                            tooltip=translate('OpenLP.SlideController', 'Move to live.'), 
+                                            triggers=self.on_go_live)
             self.toolbar.add_toolbar_action('addToService', icon=':/general/general_add.png',
-                tooltip=translate('OpenLP.SlideController', 'Add to Service.'), triggers=self.on_preview_add_to_service)
+                                            tooltip=translate('OpenLP.SlideController', 'Add to Service.'), 
+                                            triggers=self.on_preview_add_to_service)
             self.toolbar.addSeparator()
             self.toolbar.add_toolbar_action('editSong', icon=':/general/general_edit.png',
-                tooltip=translate('OpenLP.SlideController', 'Edit and reload song preview.'), triggers=self.on_edit_song)
+                                            tooltip=translate('OpenLP.SlideController', 'Edit and reload song preview.')
+                                            , triggers=self.on_edit_song)
         self.controller_layout.addWidget(self.toolbar)
         # Build the Media Toolbar
         self.media_controller.register_controller(self)
@@ -278,27 +277,29 @@
             self.audio_menu = QtGui.QMenu(translate('OpenLP.SlideController', 'Background Audio'), self.toolbar)
             self.audio_pause_item.setMenu(self.audio_menu)
             self.audio_pause_item.setParent(self.toolbar)
-            self.toolbar.widgetForAction(self.audio_pause_item).setPopupMode(
-                QtGui.QToolButton.MenuButtonPopup)
-            self.nextTrackItem = create_action(self, 'nextTrackItem', text=UiStrings().NextTrack,
-                icon=':/slides/media_playback_next.png',
-                tooltip=translate('OpenLP.SlideController', 'Go to next audio track.'),
-                category=self.category, can_shortcuts=True, triggers=self.on_next_track_clicked)
-            self.audio_menu.addAction(self.nextTrackItem)
-            self.trackMenu = self.audio_menu.addMenu(translate('OpenLP.SlideController', 'Tracks'))
+            self.toolbar.widgetForAction(self.audio_pause_item).setPopupMode(QtGui.QToolButton.MenuButtonPopup)
+            self.next_track_item = create_action(self, 'nextTrackItem', text=UiStrings().NextTrack,
+                                                 icon=':/slides/media_playback_next.png',
+                                                 tooltip=translate('OpenLP.SlideController',
+                                                                   'Go to next audio track.'),
+                                                 category=self.category,
+                                                 can_shortcuts=True,
+                                                 triggers=self.on_next_track_clicked)
+            self.audio_menu.addAction(self.next_track_item)
+            self.track_menu = self.audio_menu.addMenu(translate('OpenLP.SlideController', 'Tracks'))
             self.audio_time_label = QtGui.QLabel(' 00:00 ', self.toolbar)
             self.audio_time_label.setAlignment(QtCore.Qt.AlignCenter | QtCore.Qt.AlignHCenter)
             self.audio_time_label.setStyleSheet(AUDIO_TIME_LABEL_STYLESHEET)
             self.audio_time_label.setObjectName('audio_time_label')
             self.toolbar.add_toolbar_widget(self.audio_time_label)
-            self.toolbar.set_widget_visible(self.audio_list, False)
+            self.toolbar.set_widget_visible(AUDIO_LIST, False)
             self.toolbar.set_widget_visible(['song_menu'], False)
         # Screen preview area
         self.preview_frame = QtGui.QFrame(self.splitter)
         self.preview_frame.setGeometry(QtCore.QRect(0, 0, 300, 300 * self.ratio))
         self.preview_frame.setMinimumHeight(100)
         self.preview_frame.setSizePolicy(QtGui.QSizePolicy(QtGui.QSizePolicy.Ignored, QtGui.QSizePolicy.Ignored,
-            QtGui.QSizePolicy.Label))
+                                         QtGui.QSizePolicy.Label))
         self.preview_frame.setFrameShape(QtGui.QFrame.StyledPanel)
         self.preview_frame.setFrameShadow(QtGui.QFrame.Sunken)
         self.preview_frame.setObjectName('preview_frame')
@@ -331,9 +332,9 @@
         self.grid.addLayout(self.slide_layout, 0, 0, 1, 1)
         if self.is_live:
             self.current_shortcut = ''
-            self.shortcutTimer = QtCore.QTimer()
-            self.shortcutTimer.setObjectName('shortcutTimer')
-            self.shortcutTimer.setSingleShot(True)
+            self.shortcut_timer = QtCore.QTimer()
+            self.shortcut_timer.setObjectName('shortcut_timer')
+            self.shortcut_timer.setSingleShot(True)
             shortcuts = [
                 {'key': 'V', 'configurable': True, 'text': translate('OpenLP.SlideController', 'Go to "Verse"')},
                 {'key': 'C', 'configurable': True, 'text': translate('OpenLP.SlideController', 'Go to "Chorus"')},
@@ -345,51 +346,49 @@
                 {'key': 'O', 'configurable': True, 'text': translate('OpenLP.SlideController', 'Go to "Other"')}
             ]
             shortcuts.extend([{'key': str(number)} for number in range(10)])
-            self.controller.addActions([create_action(self,
-                'shortcutAction_%s' % s['key'], text=s.get('text'),
-                can_shortcuts=True,
-                context=QtCore.Qt.WidgetWithChildrenShortcut,
-                category=self.category if s.get('configurable') else None,
-                triggers=self._slide_shortcut_activated) for s in shortcuts])
-            self.shortcutTimer.timeout.connect(self._slide_shortcut_activated)
+            self.controller.addActions([create_action(self, 'shortcutAction_%s' % s['key'], 
+                                                      text=s.get('text'),
+                                                      can_shortcuts=True,
+                                                      context=QtCore.Qt.WidgetWithChildrenShortcut,
+                                                      category=self.category if s.get('configurable') else None,
+                                                      triggers=self._slide_shortcut_activated) for s in shortcuts])
+            self.shortcut_timer.timeout.connect(self._slide_shortcut_activated)
         # Signals
-        self.preview_widget.clicked.connect(self.on_slide_selected)
+        self.preview_widget.itemSelectionChanged.connect(self.on_slide_selected)
         if self.is_live:
             # Need to use event as called across threads and UI is updated
             QtCore.QObject.connect(self, QtCore.SIGNAL('slidecontroller_toggle_display'), self.toggle_display)
             Registry().register_function('slidecontroller_live_spin_delay', self.receive_spin_delay)
-            self.toolbar.set_widget_visible(self.loop_list, False)
-            self.toolbar.set_widget_visible(self.wide_menu, False)
+            self.toolbar.set_widget_visible(LOOP_LIST, False)
+            self.toolbar.set_widget_visible(WIDE_MENU, False)
         else:
             self.preview_widget.doubleClicked.connect(self.on_preview_add_to_service)
             self.toolbar.set_widget_visible(['editSong'], False)
         if self.is_live:
-            self.set_live_hotkeys(self)
+            self.set_live_hot_keys(self)
             self.__add_actions_to_widget(self.controller)
         else:
-            self.controller.addActions([self.nextItem, self.previous_item])
+            self.controller.addActions([self.next_item, self.previous_item])
         Registry().register_function('slidecontroller_%s_stop_loop' % self.type_prefix, self.on_stop_loop)
         Registry().register_function('slidecontroller_%s_change' % self.type_prefix, self.on_slide_change)
         Registry().register_function('slidecontroller_%s_blank' % self.type_prefix, self.on_slide_blank)
         Registry().register_function('slidecontroller_%s_unblank' % self.type_prefix, self.on_slide_unblank)
         Registry().register_function('slidecontroller_update_slide_limits', self.update_slide_limits)
         QtCore.QObject.connect(self, QtCore.SIGNAL('slidecontroller_%s_set' % self.type_prefix),
-            self.on_slide_selected_index)
+                               self.on_slide_selected_index)
         QtCore.QObject.connect(self, QtCore.SIGNAL('slidecontroller_%s_next' % self.type_prefix),
-            self.on_slide_selected_next)
+                               self.on_slide_selected_next)
         QtCore.QObject.connect(self, QtCore.SIGNAL('slidecontroller_%s_previous' % self.type_prefix),
-            self.on_slide_selected_previous)
+                               self.on_slide_selected_previous)
 
     def _slide_shortcut_activated(self):
         """
-        Called, when a shortcut has been activated to jump to a chorus, verse,
-        etc.
+        Called, when a shortcut has been activated to jump to a chorus, verse, etc.
 
-        **Note**: This implementation is based on shortcuts. But it rather works
-        like "key sequenes". You have to press one key after the other and
-        **not** at the same time.
-        For example to jump to "V3" you have to press "V" and afterwards but
-        within a time frame of 350ms you have to press "3".
+        **Note**: This implementation is based on shortcuts. But it rather works like "key sequenes". You have to 
+        press one key after the other and **not** at the same time.
+        For example to jump to "V3" you have to press "V" and afterwards but within a time frame of 350ms 
+        you have to press "3".
         """
         try:
             from openlp.plugins.songs.lib import VerseType
@@ -420,43 +419,45 @@
             self.current_shortcut += verse_type
         elif verse_type:
             self.current_shortcut = verse_type
-        keys = list(self.slideList.keys())
-        matches = [match for match in keys
-            if match.startswith(self.current_shortcut)]
+        keys = list(self.slide_list.keys())
+        matches = [match for match in keys if match.startswith(self.current_shortcut)]
         if len(matches) == 1:
-            self.shortcutTimer.stop()
+            self.shortcut_timer.stop()
             self.current_shortcut = ''
-            self.preview_widget.change_slide(self.slideList[matches[0]])
+            self.preview_widget.change_slide(self.slide_list[matches[0]])
             self.slide_selected()
-        elif sender_name != 'shortcutTimer':
+        elif sender_name != 'shortcut_timer':
             # Start the time as we did not have any match.
-            self.shortcutTimer.start(350)
+            self.shortcut_timer.start(350)
         else:
             # The timer timed out.
             if self.current_shortcut in keys:
                 # We had more than one match for example "V1" and "V10", but
                 # "V1" was the slide we wanted to go.
-                self.preview_widget.change_slide(self.slideList[self.current_shortcut])
+                self.preview_widget.change_slide(self.slide_list[self.current_shortcut])
                 self.slide_selected()
            # Reset the shortcut.
             self.current_shortcut = ''
 
-    def set_live_hotkeys(self, parent=None):
+    def set_live_hot_keys(self, parent=None):
         """
         Set the live hotkeys
         """
-        self.previousService = create_action(parent, 'previousService',
-            text=translate('OpenLP.SlideController', 'Previous Service'),
-            can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut, category=self.category,
-            triggers=self.service_previous)
-        self.nextService = create_action(parent, 'nextService',
-            text=translate('OpenLP.SlideController', 'Next Service'),
-            can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut, category=self.category,
-            triggers=self.service_next)
-        self.escapeItem = create_action(parent, 'escapeItem',
-            text=translate('OpenLP.SlideController', 'Escape Item'),
-            can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut, category=self.category,
-            triggers=self.live_escape)
+        self.previous_service = create_action(parent, 'previousService',
+                                              text=translate('OpenLP.SlideController', 'Previous Service'),
+                                              can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut, 
+                                              category=self.category,
+                                              triggers=self.service_previous)
+        self.next_service = create_action(parent, 'nextService',
+                                          text=translate('OpenLP.SlideController', 'Next Service'),
+                                          can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut, 
+                                          category=self.category,
+                                          triggers=self.service_next)
+        self.escape_item = create_action(parent, 'escapeItem',
+                                         text=translate('OpenLP.SlideController', 'Escape Item'),
+                                         can_shortcuts=True, context=QtCore.Qt.WidgetWithChildrenShortcut, 
+                                         category=self.category,
+                                         triggers=self.live_escape)
 
     def live_escape(self):
         """
@@ -502,10 +503,10 @@
         if self.keypress_queue:
             while len(self.keypress_queue) and not self.keypress_loop:
                 self.keypress_loop = True
-                keypressCommand = self.keypress_queue.popleft()
-                if keypressCommand == ServiceItemAction.Previous:
+                keypress_command = self.keypress_queue.popleft()
+                if keypress_command == ServiceItemAction.Previous:
                     self.service_manager.previous_item()
-                elif keypressCommand == ServiceItemAction.PreviousLastSlide:
+                elif keypress_command == ServiceItemAction.PreviousLastSlide:
                     # Go to the last slide of the previous item
                     self.service_manager.previous_item(last_slide=True)
                 else:
@@ -535,7 +536,7 @@
         self.preview_display.setup()
         service_item = ServiceItem()
         self.preview_display.web_view.setHtml(build_html(service_item, self.preview_display.screen, None, self.is_live,
-            plugins=self.plugin_manager.plugins))
+                                              plugins=self.plugin_manager.plugins))
         self.media_controller.setup_display(self.preview_display, True)
         if self.service_item:
             self.refresh_service_item()
@@ -545,9 +546,9 @@
         Add actions to the widget specified by `widget`
         """
         widget.addActions([
-            self.previous_item, self.nextItem,
-            self.previousService, self.nextService,
-            self.escapeItem])
+            self.previous_item, self.next_item,
+            self.previous_service, self.next_service,
+            self.escape_item])
 
     def preview_size_changed(self):
         """
@@ -579,19 +580,19 @@
             used_space = self.toolbar.size().width() + self.hide_menu.size().width()
             # Add the threshold to prevent flickering.
             if width > used_space + HIDE_MENU_THRESHOLD and self.hide_menu.isVisible():
-                self.toolbar.set_widget_visible(self.narrow_menu, False)
-                self.toolbar.set_widget_visible(self.wide_menu)
+                self.toolbar.set_widget_visible(NARROW_MENU, False)
+                self.toolbar.set_widget_visible(WIDE_MENU)
             # Take away a threshold to prevent flickering.
             elif width < used_space - HIDE_MENU_THRESHOLD and not self.hide_menu.isVisible():
-                self.toolbar.set_widget_visible(self.wide_menu, False)
-                self.toolbar.set_widget_visible(self.narrow_menu)
+                self.toolbar.set_widget_visible(WIDE_MENU, False)
+                self.toolbar.set_widget_visible(NARROW_MENU)
 
     def on_song_bar_handler(self):
         """
         Some song handler
         """
         request = self.sender().text()
-        slide_no = self.slideList[request]
+        slide_no = self.slide_list[request]
         width = self.main_window.control_splitter.sizes()[self.split]
         self.preview_widget.replace_service_item(self.service_item, width, slide_no)
         self.slide_selected()
@@ -626,7 +627,7 @@
         self.toolbar.hide()
         self.mediabar.hide()
         self.song_menu.hide()
-        self.toolbar.set_widget_visible(self.loop_list, False)
+        self.toolbar.set_widget_visible(LOOP_LIST, False)
         self.toolbar.set_widget_visible(['song_menu'], False)
         # Reset the button
         self.play_slides_once.setChecked(False)
@@ -634,14 +635,14 @@
         self.play_slides_loop.setChecked(False)
         self.play_slides_loop.setIcon(build_icon(':/media/media_time.png'))
         if item.is_text():
-            if Settings().value(self.main_window.songs_settings_section + '/display songbar') and self.slideList:
+            if Settings().value(self.main_window.songs_settings_section + '/display songbar') and self.slide_list:
                 self.toolbar.set_widget_visible(['song_menu'], True)
         if item.is_capable(ItemCapabilities.CanLoop) and len(item.get_frames()) > 1:
-            self.toolbar.set_widget_visible(self.loop_list)
+            self.toolbar.set_widget_visible(LOOP_LIST)
         if item.is_media():
             self.mediabar.show()
         self.previous_item.setVisible(not item.is_media())
-        self.nextItem.setVisible(not item.is_media())
+        self.next_item.setVisible(not item.is_media())
         # Work-around for OS X, hide and then show the toolbar
         # See bug #791050
         self.toolbar.show()
@@ -660,7 +661,7 @@
         elif item.is_media():
             self.mediabar.show()
         self.previous_item.setVisible(not item.is_media())
-        self.nextItem.setVisible(not item.is_media())
+        self.next_item.setVisible(not item.is_media())
         # Work-around for OS X, hide and then show the toolbar
         # See bug #791050
         self.toolbar.show()
@@ -669,7 +670,6 @@
         """
         Method to update the service item if the screen has changed
         """
-        log.debug('refresh_service_item live = %s' % self.is_live)
         if self.service_item.is_text() or self.service_item.is_image():
             item = self.service_item
             item.render()
@@ -680,7 +680,6 @@
         Method to install the service item into the controller
         Called by plugins
         """
-        log.debug('add_service_item live = %s' % self.is_live)
         item.render()
         slide_no = 0
         if self.song_edit:
@@ -700,18 +699,17 @@
         Method to install the service item into the controller and request the correct toolbar for the plugin. Called by
         :class:`~openlp.core.ui.ServiceManager`
         """
-        log.debug('add_service_manager_item live = %s' % self.is_live)
         # If no valid slide number is specified we take the first one, but we remember the initial value to see if we
         # should reload the song or not
-        slidenum = slide_no
+        slide_num = slide_no
         if slide_no == -1:
-            slidenum = 0
+            slide_num = 0
         # If service item is the same as the current one, only change slide
         if slide_no >= 0 and item == self.service_item:
-            self.preview_widget.change_slide(slidenum)
+            self.preview_widget.change_slide(slide_num)
             self.slide_selected()
         else:
-            self._process_item(item, slidenum)
+            self._process_item(item, slide_num)
             if self.is_live and item.auto_play_slides_loop and item.timed_slide_interval > 0:
                 self.play_slides_loop.setChecked(item.auto_play_slides_loop)
                 self.delay_spin_box.setValue(int(item.timed_slide_interval))
@@ -721,11 +719,10 @@
                 self.delay_spin_box.setValue(int(item.timed_slide_interval))
                 self.on_play_slides_once()
 
-    def _process_item(self, service_item, slideno):
+    def _process_item(self, service_item, slide_no):
         """
         Loads a ServiceItem into the system from ServiceManager. Display the slide number passed.
         """
-        log.debug('processManagerItem live = %s' % self.is_live)
         self.on_stop_loop()
         old_item = self.service_item
         # take a copy not a link to the servicemanager copy.
@@ -733,8 +730,8 @@
         if old_item and self.is_live and old_item.is_capable(ItemCapabilities.ProvidesOwnDisplay):
             self._reset_blank()
         Registry().execute(
-            '%s_start' % service_item.name.lower(), [service_item, self.is_live, self.hide_mode(), slideno])
-        self.slideList = {}
+            '%s_start' % service_item.name.lower(), [service_item, self.is_live, self.hide_mode(), slide_no])
+        self.slide_list = {}
         if self.is_live:
             self.song_menu.menu().clear()
             self.display.audio_player.reset()
@@ -742,15 +739,15 @@
             self.audio_pause_item.setChecked(False)
             # If the current item has background audio
             if self.service_item.is_capable(ItemCapabilities.HasBackgroundAudio):
-                log.debug('Starting to play...')
+                self.log_debug('Starting to play...')
                 self.display.audio_player.add_to_playlist(self.service_item.background_audio)
-                self.trackMenu.clear()
+                self.track_menu.clear()
                 for counter in range(len(self.service_item.background_audio)):
-                    action = self.trackMenu.addAction(os.path.basename(self.service_item.background_audio[counter]))
+                    action = self.track_menu.addAction(os.path.basename(self.service_item.background_audio[counter]))
                     action.setData(counter)
                     action.triggered.connect(self.on_track_triggered)
-                self.display.audio_player.repeat = Settings().value(
-                    self.main_window.general_settings_section + '/audio repeat list')
+                self.display.audio_player.repeat = \
+                    Settings().value(self.main_window.general_settings_section + '/audio repeat list')
                 if Settings().value(self.main_window.general_settings_section + '/audio start paused'):
                     self.audio_pause_item.setChecked(True)
                     self.display.audio_player.pause()
@@ -767,21 +764,21 @@
                     verse_def = '%s%s' % (verse_def[0], verse_def[1:])
                     two_line_def = '%s\n%s' % (verse_def[0], verse_def[1:])
                     row = two_line_def
-                    if verse_def not in self.slideList:
-                        self.slideList[verse_def] = framenumber
+                    if verse_def not in self.slide_list:
+                        self.slide_list[verse_def] = framenumber
                         if self.is_live:
                             self.song_menu.menu().addAction(verse_def, self.on_song_bar_handler)
                 else:
                     row += 1
-                    self.slideList[str(row)] = row - 1
+                    self.slide_list[str(row)] = row - 1
             else:
                 row += 1
-                self.slideList[str(row)] = row - 1
+                self.slide_list[str(row)] = row - 1
                 # If current slide set background to image
-                if not self.service_item.is_command() and framenumber == slideno:
-                    self.service_item.bg_image_bytes = self.image_manager.get_image_bytes(frame['path'],
-                        ImageSource.ImagePlugin)
-        self.preview_widget.replace_service_item(self.service_item, width, slideno)
+                if not self.service_item.is_command() and framenumber == slide_no:
+                    self.service_item.bg_image_bytes = \
+                        self.image_manager.get_image_bytes(frame['path'], ImageSource.ImagePlugin)
+        self.preview_widget.replace_service_item(self.service_item, width, slide_no)
         self.enable_tool_bar(service_item)
         # Pass to display for viewing.
         # Postpone image build, we need to do this later to avoid the theme
@@ -821,7 +818,6 @@
         """
         Allow the main display to blank the main display at startup time
         """
-        log.debug('main_display_set_background live = %s' % self.is_live)
         display_type = Settings().value(self.main_window.general_settings_section + '/screen blank')
         if self.screens.which_screen(self.window()) != self.screens.which_screen(self.display):
             # Order done to handle initial conversion
@@ -854,7 +850,7 @@
         """
         if checked is None:
             checked = self.blank_screen.isChecked()
-        log.debug('on_blank_display %s' % checked)
+        self.log_debug('on_blank_display %s' % checked)
         self.hide_menu.setDefaultAction(self.blank_screen)
         self.blank_screen.setChecked(checked)
         self.theme_screen.setChecked(False)
@@ -873,7 +869,7 @@
         """
         if checked is None:
             checked = self.theme_screen.isChecked()
-        log.debug('on_theme_display %s' % checked)
+        self.log_debug('on_theme_display %s' % checked)
         self.hide_menu.setDefaultAction(self.theme_screen)
         self.blank_screen.setChecked(False)
         self.theme_screen.setChecked(checked)
@@ -892,7 +888,7 @@
         """
         if checked is None:
             checked = self.desktop_screen.isChecked()
-        log.debug('on_hide_display %s' % checked)
+        self.log_debug('on_hide_display %s' % checked)
         self.hide_menu.setDefaultAction(self.desktop_screen)
         self.blank_screen.setChecked(False)
         self.theme_screen.setChecked(False)
@@ -910,13 +906,13 @@
         Blank/Hide the display screen within a plugin if required.
         """
         hide_mode = self.hide_mode()
-        log.debug('blank_plugin %s ', hide_mode)
+        self.log_debug('blank_plugin %s ' % hide_mode)
         if self.service_item is not None:
             if hide_mode:
                 if not self.service_item.is_command():
                     Registry().execute('live_display_hide', hide_mode)
-                Registry().execute('%s_blank' % self.service_item.name.lower(),
-                    [self.service_item, self.is_live, hide_mode])
+                Registry().execute('%s_blank' %
+                                   self.service_item.name.lower(), [self.service_item, self.is_live, hide_mode])
             else:
                 if not self.service_item.is_command():
                     Registry().execute('live_display_show')
@@ -931,7 +927,7 @@
         """
         Tell the plugin to hide the display screen.
         """
-        log.debug('hide_plugin %s ', hide)
+        self.log_debug('hide_plugin %s ' % hide)
         if self.service_item is not None:
             if hide:
                 Registry().execute('live_display_hide', HideMode.Screen)
@@ -946,9 +942,10 @@
             else:
                 Registry().execute('live_display_show')
 
-    def on_slide_selected(self):
+    def on_slide_selected(self, field=None):
         """
         Slide selected in controller
+        Note for some reason a dummy field is required.  Nothing is passed!
         """
         self.slide_selected()
 
@@ -962,8 +959,8 @@
         if -1 < row < self.preview_widget.slide_count():
             if self.service_item.is_command():
                 if self.is_live and not start:
-                    Registry().execute('%s_slide' % self.service_item.name.lower(),
-                        [self.service_item, self.is_live, row])
+                    Registry().execute('%s_slide' %
+                                       self.service_item.name.lower(), [self.service_item, self.is_live, row])
             else:
                 to_display = self.service_item.get_rendered_frame(row)
                 if self.service_item.is_text():
@@ -993,7 +990,7 @@
         """
         This updates the preview frame, for example after changing a slide or using *Blank to Theme*.
         """
-        log.debug('update_preview %s ' % self.screens.current['primary'])
+        self.log_debug('update_preview %s ' % self.screens.current['primary'])
         if not self.screens.current['primary'] and self.service_item and \
                 self.service_item.is_capable(ItemCapabilities.ProvidesOwnDisplay):
             # Grab now, but try again in a couple of seconds if slide change is slow
@@ -1048,7 +1045,7 @@
             self.preview_widget.change_slide(row)
             self.slide_selected()
 
-    def on_slide_selected_previous(self):
+    def on_slide_selected_previous(self, field=None):
         """
         Go to the previous slide.
         """
@@ -1104,7 +1101,7 @@
             checked = self.play_slides_loop.isChecked()
         else:
             self.play_slides_loop.setChecked(checked)
-        log.debug('on_play_slides_loop %s' % checked)
+        self.log_debug('on_play_slides_loop %s' % checked)
         if checked:
             self.play_slides_loop.setIcon(build_icon(':/media/media_stop.png'))
             self.play_slides_loop.setText(UiStrings().StopPlaySlidesInLoop)
@@ -1125,7 +1122,7 @@
             checked = self.play_slides_once.isChecked()
         else:
             self.play_slides_once.setChecked(checked)
-        log.debug('on_play_slides_once %s' % checked)
+        self.log_debug('on_play_slides_once %s' % checked)
         if checked:
             self.play_slides_once.setIcon(build_icon(':/media/media_stop.png'))
             self.play_slides_once.setText(UiStrings().StopPlaySlidesToEnd)
@@ -1142,7 +1139,7 @@
         """
         Set the visibility of the audio stuff
         """
-        self.toolbar.set_widget_visible(self.audio_list, visible)
+        self.toolbar.set_widget_visible(AUDIO_LIST, visible)
 
     def set_audio_pause_clicked(self, checked):
         """
@@ -1206,7 +1203,6 @@
         """
         Respond to the arrival of a media service item
         """
-        log.debug('SlideController on_media_start')
         self.media_controller.video(self.controller_type, item, self.hide_mode())
         if not self.is_live:
             self.preview_display.show()
@@ -1216,7 +1212,6 @@
         """
         Respond to a request to close the Video
         """
-        log.debug('SlideController on_media_close')
         self.media_controller.media_reset(self)
         self.preview_display.hide()
         self.slide_preview.show()
@@ -1333,3 +1328,46 @@
 
     main_window = property(_get_main_window)
 
+
+class PreviewController(RegistryMixin, OpenLPMixin, SlideController):
+    """
+    Set up the Live Controller.
+    """
+    def __init__(self, parent):
+        """
+        Set up the general Controller.
+        """
+        super(PreviewController, self).__init__(parent)
+        self.split = 0
+        self.type_prefix = 'preview'
+        self.category = None
+
+    def bootstrap_post_set_up(self):
+        """
+        process the bootstrap post setup request
+        """
+        self.post_set_up()
+
+
+class LiveController(RegistryMixin, OpenLPMixin, SlideController):
+    """
+    Set up the Live Controller.
+    """
+    def __init__(self, parent):
+        """
+        Set up the general Controller.
+        """
+        super(LiveController, self).__init__(parent)
+        self.is_live = True
+        self.split = 1
+        self.type_prefix = 'live'
+        self.keypress_queue = deque()
+        self.keypress_loop = False
+        self.category = UiStrings().LiveToolbar
+        ActionList.get_instance().add_category(str(self.category), CategoryOrder.standard_toolbar)
+
+    def bootstrap_post_set_up(self):
+        """
+        process the bootstrap post setup request
+        """
+        self.post_set_up()
\ No newline at end of file

=== modified file 'openlp/core/ui/starttimeform.py'
--- openlp/core/ui/starttimeform.py	2013-12-24 15:55:01 +0000
+++ openlp/core/ui/starttimeform.py	2013-12-28 21:35:49 +0000
@@ -33,8 +33,7 @@
 
 from .starttimedialog import Ui_StartTimeDialog
 
-from openlp.core.common import UiStrings, translate
-from openlp.core.lib import Registry
+from openlp.core.common import Registry, UiStrings, translate
 from openlp.core.lib.ui import critical_error_message_box
 
 

=== modified file 'openlp/core/ui/themeform.py'
--- openlp/core/ui/themeform.py	2013-12-26 17:36:00 +0000
+++ openlp/core/ui/themeform.py	2013-12-28 21:35:49 +0000
@@ -34,8 +34,7 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import UiStrings, translate
-from openlp.core.lib import Registry
+from openlp.core.common import Registry, UiStrings, translate
 from openlp.core.lib.theme import BackgroundType, BackgroundGradientType
 from openlp.core.lib.ui import critical_error_message_box
 from openlp.core.ui import ThemeLayoutForm

=== modified file 'openlp/core/ui/thememanager.py'
--- openlp/core/ui/thememanager.py	2013-12-24 08:56:50 +0000
+++ openlp/core/ui/thememanager.py	2013-12-28 21:35:49 +0000
@@ -32,23 +32,21 @@
 import os
 import zipfile
 import shutil
-import logging
 
 from xml.etree.ElementTree import ElementTree, XML
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import AppLocation, Settings, check_directory_exists, UiStrings, translate
-from openlp.core.lib import FileDialog, ImageSource, OpenLPToolbar, Registry, get_text_file_string, build_icon, \
+from openlp.core.common import Registry, AppLocation, Settings, OpenLPMixin, RegistryMixin, check_directory_exists, \
+    UiStrings, translate
+from openlp.core.lib import FileDialog, ImageSource, OpenLPToolbar, get_text_file_string, build_icon, \
     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.utils import delete_file, get_locale_key, get_filesystem_encoding
 
-log = logging.getLogger(__name__)
-
-
-class ThemeManager(QtGui.QWidget, ThemeManagerHelper):
+
+class ThemeManager(RegistryMixin, OpenLPMixin, QtGui.QWidget, ThemeManagerHelper):
     """
     Manages the orders of Theme.
     """
@@ -57,9 +55,6 @@
         Constructor
         """
         super(ThemeManager, self).__init__(parent)
-        Registry().register('theme_manager', self)
-        Registry().register_function('bootstrap_initialise', self.initialise)
-        Registry().register_function('bootstrap_post_set_up', self._push_themes)
         self.settings_section = 'themes'
         self.theme_form = ThemeForm(self)
         self.file_rename_form = FileRenameForm()
@@ -71,30 +66,32 @@
         self.toolbar = OpenLPToolbar(self)
         self.toolbar.setObjectName('toolbar')
         self.toolbar.add_toolbar_action('newTheme',
-            text=UiStrings().NewTheme, icon=':/themes/theme_new.png',
-            tooltip=translate('OpenLP.ThemeManager', 'Create a new theme.'),
-            triggers=self.on_add_theme)
+                                        text=UiStrings().NewTheme, icon=':/themes/theme_new.png',
+                                        tooltip=translate('OpenLP.ThemeManager', 'Create a new theme.'),
+                                        triggers=self.on_add_theme)
         self.toolbar.add_toolbar_action('editTheme',
-            text=translate('OpenLP.ThemeManager', 'Edit Theme'),
-            icon=':/themes/theme_edit.png',
-            tooltip=translate('OpenLP.ThemeManager', 'Edit a theme.'),
-            triggers=self.on_edit_theme)
+                                        text=translate('OpenLP.ThemeManager', 'Edit Theme'),
+                                        icon=':/themes/theme_edit.png',
+                                        tooltip=translate('OpenLP.ThemeManager', 'Edit a theme.'),
+                                        triggers=self.on_edit_theme)
         self.delete_toolbar_action = self.toolbar.add_toolbar_action('delete_theme',
-            text=translate('OpenLP.ThemeManager', 'Delete Theme'),
-            icon=':/general/general_delete.png',
-            tooltip=translate('OpenLP.ThemeManager', 'Delete a theme.'),
-            triggers=self.on_delete_theme)
+                                                                     text=translate('OpenLP.ThemeManager',
+                                                                                    'Delete Theme'),
+                                                                     icon=':/general/general_delete.png',
+                                                                     tooltip=translate('OpenLP.ThemeManager',
+                                                                                       'Delete a theme.'),
+                                                                     triggers=self.on_delete_theme)
         self.toolbar.addSeparator()
         self.toolbar.add_toolbar_action('importTheme',
-            text=translate('OpenLP.ThemeManager', 'Import Theme'),
-            icon=':/general/general_import.png',
-            tooltip=translate('OpenLP.ThemeManager', 'Import a theme.'),
-            triggers=self.on_import_theme)
+                                        text=translate('OpenLP.ThemeManager', 'Import Theme'),
+                                        icon=':/general/general_import.png',
+                                        tooltip=translate('OpenLP.ThemeManager', 'Import a theme.'),
+                                        triggers=self.on_import_theme)
         self.toolbar.add_toolbar_action('exportTheme',
-            text=translate('OpenLP.ThemeManager', 'Export Theme'),
-            icon=':/general/general_export.png',
-            tooltip=translate('OpenLP.ThemeManager', 'Export a theme.'),
-            triggers=self.on_export_theme)
+                                        text=translate('OpenLP.ThemeManager', 'Export Theme'),
+                                        icon=':/general/general_export.png',
+                                        tooltip=translate('OpenLP.ThemeManager', 'Export a theme.'),
+                                        triggers=self.on_export_theme)
         self.layout.addWidget(self.toolbar)
         self.theme_widget = QtGui.QWidgetAction(self.toolbar)
         self.theme_widget.setObjectName('theme_widget')
@@ -109,25 +106,25 @@
         # build the context menu
         self.menu = QtGui.QMenu()
         self.edit_action = create_widget_action(self.menu,
-            text=translate('OpenLP.ThemeManager', '&Edit Theme'),
-            icon=':/themes/theme_edit.png', triggers=self.on_edit_theme)
+                                                text=translate('OpenLP.ThemeManager', '&Edit Theme'),
+                                                icon=':/themes/theme_edit.png', triggers=self.on_edit_theme)
         self.copy_action = create_widget_action(self.menu,
-            text=translate('OpenLP.ThemeManager', '&Copy Theme'),
-            icon=':/themes/theme_edit.png', triggers=self.on_copy_theme)
+                                                text=translate('OpenLP.ThemeManager', '&Copy Theme'),
+                                                icon=':/themes/theme_edit.png', triggers=self.on_copy_theme)
         self.rename_action = create_widget_action(self.menu,
-            text=translate('OpenLP.ThemeManager', '&Rename Theme'),
-            icon=':/themes/theme_edit.png', triggers=self.on_rename_theme)
+                                                  text=translate('OpenLP.ThemeManager', '&Rename Theme'),
+                                                  icon=':/themes/theme_edit.png', triggers=self.on_rename_theme)
         self.delete_action = create_widget_action(self.menu,
-            text=translate('OpenLP.ThemeManager', '&Delete Theme'),
-            icon=':/general/general_delete.png', triggers=self.on_delete_theme)
+                                                  text=translate('OpenLP.ThemeManager', '&Delete Theme'),
+                                                  icon=':/general/general_delete.png', triggers=self.on_delete_theme)
         self.menu.addSeparator()
         self.global_action = create_widget_action(self.menu,
-            text=translate('OpenLP.ThemeManager', 'Set As &Global Default'),
-            icon=':/general/general_export.png',
-            triggers=self.change_global_from_screen)
-        self.exportAction = create_widget_action(self.menu,
-            text=translate('OpenLP.ThemeManager', '&Export Theme'),
-            icon=':/general/general_export.png', triggers=self.on_export_theme)
+                                                  text=translate('OpenLP.ThemeManager', 'Set As &Global Default'),
+                                                  icon=':/general/general_export.png',
+                                                  triggers=self.change_global_from_screen)
+        self.export_action = create_widget_action(self.menu,
+                                                  text=translate('OpenLP.ThemeManager', '&Export Theme'),
+                                                  icon=':/general/general_export.png', triggers=self.on_export_theme)
         # Signals
         self.theme_list_widget.doubleClicked.connect(self.change_global_from_screen)
         self.theme_list_widget.currentItemChanged.connect(self.check_list_state)
@@ -136,9 +133,25 @@
         self.theme_list = []
         self.old_background_image = None
 
-    def check_list_state(self, item):
+    def bootstrap_initialise(self):
+        """
+        process the bootstrap initialise setup request
+        """
+        self.log_debug('initialise called')
+        self.global_theme = Settings().value(self.settings_section + '/global theme')
+        self.build_theme_path()
+        self.load_first_time_themes()
+
+    def bootstrap_post_set_up(self):
+        """
+        process the bootstrap post setup request
+        """
+        self._push_themes()
+
+    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!
         """
         if item is None:
             return
@@ -171,7 +184,7 @@
         Change the global theme when it is changed through the Themes settings tab
         """
         self.global_theme = Settings().value(self.settings_section + '/global theme')
-        log.debug('change_global_from_tab %s', self.global_theme)
+        self.log_debug('change_global_from_tab %s' % self.global_theme)
         for count in range(0, self.theme_list_widget.count()):
             # reset the old name
             item = self.theme_list_widget.item(count)
@@ -190,7 +203,6 @@
         Change the global theme when a theme is double clicked upon in the
         Theme Manager list
         """
-        log.debug('change_global_from_screen %s', index)
         selected_row = self.theme_list_widget.currentRow()
         for count in range(0, self.theme_list_widget.count()):
             item = self.theme_list_widget.item(count)
@@ -207,7 +219,7 @@
                 Registry().execute('theme_update_global')
                 self._push_themes()
 
-    def on_add_theme(self):
+    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.
@@ -218,13 +230,13 @@
         self.theme_form.exec_()
         self.load_themes()
 
-    def on_rename_theme(self):
+    def on_rename_theme(self, field=None):
         """
         Renames an existing theme to a new name
         """
         if self._validate_theme_action(translate('OpenLP.ThemeManager', 'You must select a theme to rename.'),
-                translate('OpenLP.ThemeManager', 'Rename Confirmation'),
-                translate('OpenLP.ThemeManager', 'Rename %s theme?'), False, False):
+                                       translate('OpenLP.ThemeManager', 'Rename Confirmation'),
+                                       translate('OpenLP.ThemeManager', 'Rename %s theme?'), False, False):
             item = self.theme_list_widget.currentItem()
             old_theme_name = item.data(QtCore.Qt.UserRole)
             self.file_rename_form.file_name_edit.setText(old_theme_name)
@@ -242,14 +254,14 @@
                     self.renderer.update_theme(new_theme_name, old_theme_name)
                     self.load_themes()
 
-    def on_copy_theme(self):
+    def on_copy_theme(self, field=None):
         """
         Copies an existing theme to a new name
         """
         item = self.theme_list_widget.currentItem()
         old_theme_name = item.data(QtCore.Qt.UserRole)
         self.file_rename_form.file_name_edit.setText(translate('OpenLP.ThemeManager',
-            'Copy of %s', 'Copy of <theme name>') % old_theme_name)
+                                                     'Copy of %s', 'Copy of <theme name>') % old_theme_name)
         if self.file_rename_form.exec_(True):
             new_theme_name = self.file_rename_form.file_name_edit.text()
             if self.check_if_theme_exists(new_theme_name):
@@ -260,7 +272,6 @@
         """
         Takes a theme and makes a new copy of it as well as saving it.
         """
-        log.debug('clone_theme_data')
         save_to = None
         save_from = None
         if theme_data.background_type == 'image':
@@ -271,13 +282,13 @@
         self.save_theme(theme_data, save_from, save_to)
         self.load_themes()
 
-    def on_edit_theme(self):
+    def on_edit_theme(self, field=None):
         """
         Loads the settings for the theme that is to be edited and launches the
         theme editing form so the user can make their changes.
         """
         if check_item_selected(self.theme_list_widget,
-                translate('OpenLP.ThemeManager', 'You must select a theme to edit.')):
+                               translate('OpenLP.ThemeManager', 'You must select a theme to edit.')):
             item = self.theme_list_widget.currentItem()
             theme = self.get_theme_data(item.data(QtCore.Qt.UserRole))
             if theme.background_type == 'image':
@@ -288,13 +299,13 @@
             self.renderer.update_theme(theme.theme_name)
             self.load_themes()
 
-    def on_delete_theme(self):
+    def on_delete_theme(self, field=None):
         """
         Delete a theme
         """
         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)
@@ -321,9 +332,9 @@
             shutil.rmtree(os.path.join(self.path, theme).encode(encoding))
         except OSError as os_error:
             shutil.Error = os_error
-            log.exception('Error deleting theme %s', theme)
+            self.log_exception('Error deleting theme %s', theme)
 
-    def on_export_theme(self):
+    def on_export_theme(self, field=None):
         """
         Export the theme in a zip file
         """
@@ -333,8 +344,9 @@
             return
         theme = item.data(QtCore.Qt.UserRole)
         path = QtGui.QFileDialog.getExistingDirectory(self,
-            translate('OpenLP.ThemeManager', 'Save Theme - (%s)') % theme,
-            Settings().value(self.settings_section + '/last directory export'))
+                                                      translate('OpenLP.ThemeManager', 'Save Theme - (%s)') % theme,
+                                                      Settings().value(self.settings_section +
+                                                                       '/last directory export'))
         self.application.set_busy_cursor()
         if path:
             Settings().setValue(self.settings_section + '/last directory export', path)
@@ -349,27 +361,29 @@
                             os.path.join(source, name).encode('utf-8'), os.path.join(theme, name).encode('utf-8')
                         )
                 QtGui.QMessageBox.information(self,
-                    translate('OpenLP.ThemeManager', 'Theme Exported'),
-                    translate('OpenLP.ThemeManager', 'Your theme has been successfully exported.'))
+                                              translate('OpenLP.ThemeManager', 'Theme Exported'),
+                                              translate('OpenLP.ThemeManager',
+                                                        'Your theme has been successfully exported.'))
             except (IOError, OSError):
-                log.exception('Export Theme Failed')
+                self.log_exception('Export Theme Failed')
                 critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'),
-                    translate('OpenLP.ThemeManager', 'Your theme could not be exported due to an error.'))
+                                           translate('OpenLP.ThemeManager',
+                                                     'Your theme could not be exported due to an error.'))
             finally:
                 if theme_zip:
                     theme_zip.close()
         self.application.set_normal_cursor()
 
-    def on_import_theme(self):
+    def on_import_theme(self, field=None):
         """
         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.
         """
         files = FileDialog.getOpenFileNames(self,
-            translate('OpenLP.ThemeManager', 'Select Theme Import File'),
-            Settings().value(self.settings_section + '/last directory import'),
-            translate('OpenLP.ThemeManager', 'OpenLP Themes (*.theme *.otz)'))
-        log.info('New Themes %s', str(files))
+                                            translate('OpenLP.ThemeManager', 'Select Theme Import File'),
+                                            Settings().value(self.settings_section + '/last directory import'),
+                                            translate('OpenLP.ThemeManager', 'OpenLP Themes (*.theme *.otz)'))
+        self.log_info('New Themes %s' % str(files))
         if not files:
             return
         self.application.set_busy_cursor()
@@ -383,7 +397,6 @@
         """
         Imports any themes on start up and makes sure there is at least one theme
         """
-        log.debug('load_first_time_themes called')
         self.application.set_busy_cursor()
         files = AppLocation.get_files(self.settings_section, '.otz')
         for theme_file in files:
@@ -406,7 +419,6 @@
         events for the plugins.
         The plugins will call back in to get the real list if they want it.
         """
-        log.debug('Load themes from dir')
         self.theme_list = []
         self.theme_list_widget.clear()
         files = AppLocation.get_files(self.settings_section, '.png')
@@ -444,7 +456,6 @@
         """
         Return the list of loaded themes
         """
-        log.debug('get themes')
         return self.theme_list
 
     def get_theme_data(self, theme_name):
@@ -454,11 +465,11 @@
         ``theme_name``
             Name of the theme to load from file
         """
-        log.debug('get theme data for theme %s', theme_name)
+        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')
         xml = get_text_file_string(xml_file)
         if not xml:
-            log.debug('No theme data - using default theme')
+            self.log_debug('No theme data - using default theme')
             return ThemeXML()
         else:
             return self._create_theme_from_Xml(xml, self.path)
@@ -468,10 +479,12 @@
         Display a warning box to the user that a theme already exists
         """
         ret = QtGui.QMessageBox.question(self, translate('OpenLP.ThemeManager', 'Theme Already Exists'),
-            translate('OpenLP.ThemeManager',
-                'Theme %s already exists. Do you want to replace it?').replace('%s', theme_name),
-            QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
-            QtGui.QMessageBox.No)
+                                         translate('OpenLP.ThemeManager',
+                                         'Theme %s already exists. Do you want to replace it?')
+                                         .replace('%s', theme_name),
+                                         QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
+                                                                           QtGui.QMessageBox.No),
+                                         QtGui.QMessageBox.No)
         return ret == QtGui.QMessageBox.Yes
 
     def unzip_theme(self, file_name, directory):
@@ -480,7 +493,7 @@
         Generate a new preview file. Check the XML theme version and upgrade if
         necessary.
         """
-        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
@@ -490,7 +503,7 @@
             theme_zip = zipfile.ZipFile(file_name)
             xml_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.xml']
             if len(xml_file) != 1:
-                log.exception('Theme contains "%s" XML files' % len(xml_file))
+                self.log_error('Theme contains "%s" XML files' % len(xml_file))
                 raise Exception('validation')
             xml_tree = ElementTree(element=XML(theme_zip.read(xml_file[0]))).getroot()
             theme_name = xml_tree.find('name').text.strip()
@@ -518,12 +531,12 @@
                     out_file.write(theme_zip.read(name))
                 out_file.close()
         except (IOError, zipfile.BadZipfile):
-            log.exception('Importing theme from zip failed %s' % file_name)
+            self.log_exception('Importing theme from zip failed %s' % file_name)
             raise Exception('validation')
         except Exception as info:
             if str(info) == 'validation':
-                critical_error_message_box(translate('OpenLP.ThemeManager',
-                    'Validation Error'), translate('OpenLP.ThemeManager', 'File is not a valid theme.'))
+                critical_error_message_box(translate('OpenLP.ThemeManager', 'Validation Error'),
+                                           translate('OpenLP.ThemeManager', 'File is not a valid theme.'))
             else:
                 raise
         finally:
@@ -543,7 +556,7 @@
                     critical_error_message_box(
                         translate('OpenLP.ThemeManager', 'Validation Error'),
                         translate('OpenLP.ThemeManager', 'File is not a valid theme.'))
-                    log.exception('Theme file does not contain XML data %s' % file_name)
+                    self.log_error('Theme file does not contain XML data %s' % file_name)
 
     def check_if_theme_exists(self, theme_name):
         """
@@ -562,23 +575,21 @@
 
     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 thememaintenance Dialog to save the theme and to trigger the reload of the theme list
         """
         self._write_theme(theme, image_from, image_to)
         if theme.background_type == BackgroundType.to_string(BackgroundType.Image):
             self.image_manager.update_image_border(theme.background_filename,
-                ImageSource.Theme, QtGui.QColor(theme.background_border_color))
+                                                   ImageSource.Theme,
+                                                   QtGui.QColor(theme.background_border_color))
             self.image_manager.process_updates()
 
     def _write_theme(self, theme, image_from, image_to):
         """
-        Writes the theme to the disk and handles the background image if
-        necessary
+        Writes the theme to the disk and handles the background image if necessary
         """
         name = theme.theme_name
         theme_pretty_xml = theme.extract_formatted_xml()
-        log.debug('save_theme %s %s', name, theme_pretty_xml.decode('utf-8'))
         theme_dir = os.path.join(self.path, name)
         check_directory_exists(theme_dir)
         theme_file = os.path.join(theme_dir, name + '.xml')
@@ -589,7 +600,7 @@
             out_file = open(theme_file, 'w')
             out_file.write(theme_pretty_xml.decode('UTF-8'))
         except IOError:
-            log.exception('Saving theme to file failed')
+            self.log_exception('Saving theme to file failed')
         finally:
             if out_file:
                 out_file.close()
@@ -599,14 +610,13 @@
                 shutil.copyfile(str(image_from).encode(encoding), str(image_to).encode(encoding))
             except IOError as xxx_todo_changeme:
                 shutil.Error = xxx_todo_changeme
-                log.exception('Failed to save theme image')
+                self.log_exception('Failed to save theme image')
         self.generate_and_save_image(self.path, name, theme)
 
     def generate_and_save_image(self, directory, name, theme):
         """
         Generate and save a preview image
         """
-        log.debug('generate_and_save_image %s %s', directory, name)
         frame = self.generate_image(theme)
         sample_path_name = os.path.join(self.path, name + '.png')
         if os.path.exists(sample_path_name):
@@ -614,13 +624,11 @@
         frame.save(sample_path_name, 'png')
         thumb = os.path.join(self.thumb_path, '%s.png' % name)
         create_thumb(sample_path_name, thumb, False)
-        log.debug('Theme image written to %s', sample_path_name)
 
     def update_preview_images(self):
         """
         Called to update the themes' preview images.
         """
-        log.debug('update_preview_images')
         self.main_window.display_progress_bar(len(self.theme_list))
         for theme in self.theme_list:
             self.main_window.increment_progress_bar()
@@ -638,7 +646,6 @@
         ``force_page``
             Flag to tell message lines per page need to be generated.
         """
-        log.debug('generate_image \n%s ', theme_data)
         return self.renderer.generate_preview(theme_data, force_page)
 
     def get_preview_image(self, theme):
@@ -648,9 +655,7 @@
         ``theme``
             The theme to return the image for
         """
-        log.debug('get_preview_image %s ', theme)
-        image = os.path.join(self.path, theme + '.png')
-        return image
+        return os.path.join(self.path, theme + '.png')
 
     def _create_theme_from_Xml(self, theme_xml, path):
         """
@@ -666,8 +671,7 @@
 
     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.
+        Check to see if theme has been selected and the destructive action is allowed.
         """
         self.global_theme = Settings().value(self.settings_section + '/global theme')
         if check_item_selected(self.theme_list_widget, select_text):
@@ -676,8 +680,9 @@
             # confirm deletion
             if confirm:
                 answer = QtGui.QMessageBox.question(self, confirm_title, confirm_text % theme,
-                    QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
-                    QtGui.QMessageBox.No)
+                                                    QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
+                                                                                      QtGui.QMessageBox.No),
+                                                    QtGui.QMessageBox.No)
                 if answer == QtGui.QMessageBox.No:
                     return False
             # should be the same unless default
@@ -690,8 +695,9 @@
                 for plugin in self.plugin_manager.plugins:
                     if plugin.uses_theme(theme):
                         critical_error_message_box(translate('OpenLP.ThemeManager', 'Validation Error'),
-                            translate('OpenLP.ThemeManager', 'Theme %s is used in the %s plugin.') %
-                                (theme, plugin.name))
+                                                   translate('OpenLP.ThemeManager',
+                                                             'Theme %s is used in the %s plugin.')
+                                                   % (theme, plugin.name))
                         return False
             return True
         return False

=== modified file 'openlp/core/ui/thememanagerhelper.py'
--- openlp/core/ui/thememanagerhelper.py	2013-12-24 08:56:50 +0000
+++ openlp/core/ui/thememanagerhelper.py	2013-12-28 21:35:49 +0000
@@ -29,32 +29,20 @@
 """
 The Theme Controller helps manages adding, deleteing and modifying of themes.
 """
-import logging
 import os
 
-from openlp.core.common import AppLocation, Settings, check_directory_exists
-
-log = logging.getLogger(__name__)
+from openlp.core.common import AppLocation, check_directory_exists
 
 
 class ThemeManagerHelper(object):
     """
     Manages the non ui theme functions.
     """
-    def initialise(self):
-        """
-        Setup the manager
-        """
-        log.debug('initialise called')
-        self.global_theme = Settings().value(self.settings_section + '/global theme')
-        self.build_theme_path()
-        self.load_first_time_themes()
-
     def build_theme_path(self):
         """
         Set up the theme path variables
         """
-        log.debug('build theme path called')
+        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')

=== modified file 'openlp/core/ui/themestab.py'
--- openlp/core/ui/themestab.py	2013-12-26 17:36:00 +0000
+++ openlp/core/ui/themestab.py	2013-12-28 21:35:49 +0000
@@ -33,8 +33,8 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import Settings, ThemeLevel, UiStrings, translate
-from openlp.core.lib import Registry, SettingsTab
+from openlp.core.common import Registry, Settings, ThemeLevel, UiStrings, translate
+from openlp.core.lib import SettingsTab
 from openlp.core.lib.ui import find_and_set_in_combo_box
 
 

=== modified file 'openlp/core/ui/wizard.py'
--- openlp/core/ui/wizard.py	2013-12-24 08:56:50 +0000
+++ openlp/core/ui/wizard.py	2013-12-28 21:35:49 +0000
@@ -34,8 +34,8 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.common import Settings, UiStrings, translate
-from openlp.core.lib import Registry, build_icon
+from openlp.core.common import Registry, Settings, UiStrings, translate
+from openlp.core.lib import build_icon
 from openlp.core.lib.ui import add_welcome_page
 
 log = logging.getLogger(__name__)

=== modified file 'openlp/core/utils/__init__.py'
--- openlp/core/utils/__init__.py	2013-12-24 11:32:56 +0000
+++ openlp/core/utils/__init__.py	2013-12-28 21:35:49 +0000
@@ -44,8 +44,7 @@
 
 from PyQt4 import QtGui, QtCore
 
-from openlp.core.common import AppLocation, Settings
-from openlp.core.lib import Registry
+from openlp.core.common import Registry, AppLocation, Settings
 
 
 if sys.platform != 'win32' and sys.platform != 'darwin':

=== modified file 'openlp/plugins/alerts/lib/alertsmanager.py'
--- openlp/plugins/alerts/lib/alertsmanager.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/alerts/lib/alertsmanager.py	2013-12-28 21:35:49 +0000
@@ -35,8 +35,7 @@
 
 from PyQt4 import QtCore
 
-from openlp.core.common import translate
-from openlp.core.lib import Registry
+from openlp.core.common import Registry, translate
 
 
 log = logging.getLogger(__name__)

=== modified file 'openlp/plugins/bibles/forms/bibleupgradeform.py'
--- openlp/plugins/bibles/forms/bibleupgradeform.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/bibles/forms/bibleupgradeform.py	2013-12-28 21:35:49 +0000
@@ -36,8 +36,7 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import AppLocation, UiStrings, Settings, check_directory_exists, translate
-from openlp.core.lib import Registry
+from openlp.core.common import Registry, AppLocation, UiStrings, Settings, check_directory_exists, translate
 from openlp.core.lib.ui import critical_error_message_box
 from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
 from openlp.core.utils import delete_file

=== modified file 'openlp/plugins/bibles/forms/editbibleform.py'
--- openlp/plugins/bibles/forms/editbibleform.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/bibles/forms/editbibleform.py	2013-12-28 21:35:49 +0000
@@ -33,8 +33,7 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.common import UiStrings, translate
-from openlp.core.lib import Registry
+from openlp.core.common import Registry, UiStrings, translate
 from openlp.core.lib.ui import critical_error_message_box
 from .editbibledialog import Ui_EditBibleDialog
 from openlp.plugins.bibles.lib import BibleStrings

=== modified file 'openlp/plugins/bibles/lib/biblestab.py'
--- openlp/plugins/bibles/lib/biblestab.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/bibles/lib/biblestab.py	2013-12-28 21:35:49 +0000
@@ -31,8 +31,8 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import Settings, UiStrings, translate
-from openlp.core.lib import Registry, SettingsTab
+from openlp.core.common import Registry, Settings, UiStrings, translate
+from openlp.core.lib import SettingsTab
 from openlp.core.lib.ui import find_and_set_in_combo_box
 from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, update_reference_separators, \
     get_reference_separator, LanguageSelection

=== modified file 'openlp/plugins/bibles/lib/csvbible.py'
--- openlp/plugins/bibles/lib/csvbible.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/bibles/lib/csvbible.py	2013-12-28 21:35:49 +0000
@@ -93,7 +93,7 @@
         success = True
         language_id = self.get_language(bible_name)
         if not language_id:
-            log.exception('Importing books from "%s" failed' % self.filename)
+            log.error('Importing books from "%s" failed' % self.filename)
             return False
         books_file = None
         book_list = {}
@@ -112,7 +112,7 @@
                     str(line[2], details['encoding']))
                 book_ref_id = self.get_book_ref_id_by_name(str(line[2], details['encoding']), 67, language_id)
                 if not book_ref_id:
-                    log.exception('Importing books from "%s" failed' % self.booksfile)
+                    log.error('Importing books from "%s" failed' % self.booksfile)
                     return False
                 book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
                 self.create_book(str(line[2], details['encoding']), book_ref_id, book_details['testament_id'])

=== modified file 'openlp/plugins/bibles/lib/db.py'
--- openlp/plugins/bibles/lib/db.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/bibles/lib/db.py	2013-12-28 21:35:49 +0000
@@ -38,8 +38,7 @@
 from sqlalchemy.orm import class_mapper, mapper, relation
 from sqlalchemy.orm.exc import UnmappedClassError
 
-from openlp.core.common import AppLocation, translate
-from openlp.core.lib import Registry
+from openlp.core.common import Registry, AppLocation, translate
 from openlp.core.lib.db import BaseModel, init_db, Manager
 from openlp.core.lib.ui import critical_error_message_box
 from openlp.core.utils import clean_filename

=== modified file 'openlp/plugins/bibles/lib/http.py'
--- openlp/plugins/bibles/lib/http.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/bibles/lib/http.py	2013-12-28 21:35:49 +0000
@@ -38,8 +38,7 @@
 
 from bs4 import BeautifulSoup, NavigableString, Tag
 
-from openlp.core.common import translate
-from openlp.core.lib import Registry
+from openlp.core.common import Registry, translate
 from openlp.core.lib.ui import critical_error_message_box
 from openlp.core.utils import get_web_page
 from openlp.plugins.bibles.lib import SearchResults
@@ -553,7 +552,7 @@
             handler = BSExtract(self.proxy_server)
         books = handler.get_books_from_http(self.download_name)
         if not books:
-            log.exception('Importing books from %s - download name: "%s" '\
+            log.error('Importing books from %s - download name: "%s" '\
                 'failed' % (self.download_source, self.download_name))
             return False
         self.wizard.progress_bar.setMaximum(len(books) + 2)
@@ -565,7 +564,7 @@
         else:
             language_id = self.get_language(bible_name)
         if not language_id:
-            log.exception('Importing books from %s failed' % self.filename)
+            log.error('Importing books from %s failed' % self.filename)
             return False
         for book in books:
             if self.stop_import_flag:
@@ -574,7 +573,7 @@
                 'BiblesPlugin.HTTPBible', 'Importing %s...', 'Importing <book name>...') % book)
             book_ref_id = self.get_book_ref_id_by_name(book, len(books), language_id)
             if not book_ref_id:
-                log.exception('Importing books from %s - download name: "%s" '\
+                log.error('Importing books from %s - download name: "%s" '\
                     'failed' % (self.download_source, self.download_name))
                 return False
             book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)

=== modified file 'openlp/plugins/bibles/lib/manager.py'
--- openlp/plugins/bibles/lib/manager.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/bibles/lib/manager.py	2013-12-28 21:35:49 +0000
@@ -30,8 +30,7 @@
 import logging
 import os
 
-from openlp.core.common import AppLocation, Settings, translate
-from openlp.core.lib import Registry
+from openlp.core.common import Registry, AppLocation, Settings, translate
 from openlp.core.utils import delete_file
 from openlp.plugins.bibles.lib import parse_reference, get_reference_separator, LanguageSelection
 from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta

=== modified file 'openlp/plugins/bibles/lib/mediaitem.py'
--- openlp/plugins/bibles/lib/mediaitem.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/bibles/lib/mediaitem.py	2013-12-28 21:35:49 +0000
@@ -31,8 +31,8 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import Settings, UiStrings, translate
-from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, ServiceItemContext, create_separated_list
+from openlp.core.common import Registry, Settings, UiStrings, translate
+from openlp.core.lib import MediaManagerItem, ItemCapabilities, ServiceItemContext, create_separated_list
 from openlp.core.lib.searchedit import SearchEdit
 from openlp.core.lib.ui import set_case_insensitive_completer, create_horizontal_adjusting_combo_box, \
     critical_error_message_box, find_and_set_in_combo_box, build_icon

=== modified file 'openlp/plugins/bibles/lib/opensong.py'
--- openlp/plugins/bibles/lib/opensong.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/bibles/lib/opensong.py	2013-12-28 21:35:49 +0000
@@ -84,14 +84,14 @@
             bible = opensong.getroot()
             language_id = self.get_language(bible_name)
             if not language_id:
-                log.exception('Importing books from "%s" failed' % self.filename)
+                log.error('Importing books from "%s" failed' % self.filename)
                 return False
             for book in bible.b:
                 if self.stop_import_flag:
                     break
                 book_ref_id = self.get_book_ref_id_by_name(str(book.attrib['n']), len(bible.b), language_id)
                 if not book_ref_id:
-                    log.exception('Importing books from "%s" failed' % self.filename)
+                    log.error('Importing books from "%s" failed' % self.filename)
                     return False
                 book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
                 db_book = self.create_book(str(book.attrib['n']), book_ref_id, book_details['testament_id'])

=== modified file 'openlp/plugins/bibles/lib/osis.py'
--- openlp/plugins/bibles/lib/osis.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/bibles/lib/osis.py	2013-12-28 21:35:49 +0000
@@ -131,7 +131,7 @@
                     if not language_id:
                         language_id = self.get_language(bible_name)
                         if not language_id:
-                            log.exception('Importing books from "%s" failed' % self.filename)
+                            log.error('Importing books from "%s" failed' % self.filename)
                             return False
                     match_count += 1
                     book = str(match.group(1))
@@ -140,7 +140,7 @@
                     verse_text = match.group(4)
                     book_ref_id = self.get_book_ref_id_by_name(book, book_count, language_id)
                     if not book_ref_id:
-                        log.exception('Importing books from "%s" failed' % self.filename)
+                        log.error('Importing books from "%s" failed' % self.filename)
                         return False
                     book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
                     if not db_book or db_book.name != book_details['name']:

=== modified file 'openlp/plugins/custom/forms/editcustomform.py'
--- openlp/plugins/custom/forms/editcustomform.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/custom/forms/editcustomform.py	2013-12-28 21:35:49 +0000
@@ -31,7 +31,7 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.lib import Registry, translate
+from openlp.core.common import Registry, translate
 from openlp.core.lib.ui import critical_error_message_box, find_and_set_in_combo_box
 from openlp.plugins.custom.lib import CustomXMLBuilder, CustomXMLParser
 from openlp.plugins.custom.lib.db import CustomSlide

=== modified file 'openlp/plugins/custom/lib/mediaitem.py'
--- openlp/plugins/custom/lib/mediaitem.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/custom/lib/mediaitem.py	2013-12-28 21:35:49 +0000
@@ -32,9 +32,9 @@
 from PyQt4 import QtCore, QtGui
 from sqlalchemy.sql import or_, func, and_
 
-from openlp.core.common import Settings, UiStrings, translate
-from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, ServiceItemContext, PluginStatus, \
-    check_item_selected
+from openlp.core.common import Registry, Settings, UiStrings, translate
+from openlp.core.lib import MediaManagerItem, ItemCapabilities, ServiceItemContext, PluginStatus,\
+   check_item_selected
 from openlp.plugins.custom.forms.editcustomform import EditCustomForm
 from openlp.plugins.custom.lib import CustomXMLParser, CustomXMLBuilder
 from openlp.plugins.custom.lib.db import CustomSlide

=== modified file 'openlp/plugins/images/imageplugin.py'
--- openlp/plugins/images/imageplugin.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/images/imageplugin.py	2013-12-28 21:35:49 +0000
@@ -31,8 +31,8 @@
 
 import logging
 
-from openlp.core.common import Settings, translate
-from openlp.core.lib import Plugin, StringContent, Registry, ImageSource, build_icon
+from openlp.core.common import Registry, Settings, translate
+from openlp.core.lib import Plugin, StringContent, ImageSource, build_icon
 from openlp.core.lib.db import Manager
 from openlp.plugins.images.lib import ImageMediaItem, ImageTab
 from openlp.plugins.images.lib.db import init_schema

=== modified file 'openlp/plugins/images/lib/mediaitem.py'
--- openlp/plugins/images/lib/mediaitem.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/images/lib/mediaitem.py	2013-12-28 21:35:49 +0000
@@ -32,9 +32,9 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import AppLocation, Settings, UiStrings, check_directory_exists, translate
-from openlp.core.lib import ItemCapabilities, MediaManagerItem, Registry, ServiceItemContext, \
-    StringContent, TreeWidgetWithDnD, build_icon, check_item_selected, create_thumb, validate_thumb
+from openlp.core.common import Registry, AppLocation, Settings, UiStrings, check_directory_exists, translate
+from openlp.core.lib import ItemCapabilities, MediaManagerItem, ServiceItemContext, StringContent, TreeWidgetWithDnD,\
+    build_icon, check_item_selected, create_thumb, validate_thumb
 from openlp.core.lib.ui import create_widget_action, critical_error_message_box
 from openlp.core.utils import delete_file, get_locale_key, get_images_filter
 from openlp.plugins.images.forms import AddGroupForm, ChooseGroupForm

=== modified file 'openlp/plugins/media/lib/mediaitem.py'
--- openlp/plugins/media/lib/mediaitem.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/media/lib/mediaitem.py	2013-12-28 21:35:49 +0000
@@ -32,8 +32,8 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import AppLocation, Settings, check_directory_exists, UiStrings, translate
-from openlp.core.lib import ItemCapabilities, MediaManagerItem,MediaType, Registry, ServiceItem, ServiceItemContext, \
+from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, UiStrings, translate
+from openlp.core.lib import ItemCapabilities, MediaManagerItem,MediaType, ServiceItem, ServiceItemContext, \
     build_icon, check_item_selected
 from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box
 from openlp.core.ui import DisplayController, Display, DisplayControllerType

=== modified file 'openlp/plugins/media/mediaplugin.py'
--- openlp/plugins/media/mediaplugin.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/media/mediaplugin.py	2013-12-28 21:35:49 +0000
@@ -31,8 +31,8 @@
 
 from PyQt4 import QtCore
 
-from openlp.core.common import translate
-from openlp.core.lib import Plugin, Registry, StringContent, build_icon
+from openlp.core.common import Registry, translate
+from openlp.core.lib import Plugin, StringContent, build_icon
 from openlp.plugins.media.lib import MediaMediaItem, MediaTab
 
 

=== modified file 'openlp/plugins/presentations/lib/mediaitem.py'
--- openlp/plugins/presentations/lib/mediaitem.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/presentations/lib/mediaitem.py	2013-12-28 21:35:49 +0000
@@ -32,8 +32,8 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import Settings, UiStrings, translate
-from openlp.core.lib import MediaManagerItem, Registry, ItemCapabilities, ServiceItemContext,\
+from openlp.core.common import Registry, Settings, UiStrings, translate
+from openlp.core.lib import MediaManagerItem, ItemCapabilities, ServiceItemContext,\
     build_icon, check_item_selected, create_thumb, validate_thumb
 from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box
 from openlp.core.utils import get_locale_key

=== modified file 'openlp/plugins/presentations/lib/messagelistener.py'
--- openlp/plugins/presentations/lib/messagelistener.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/presentations/lib/messagelistener.py	2013-12-28 21:35:49 +0000
@@ -31,7 +31,7 @@
 
 from PyQt4 import QtCore
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 from openlp.core.ui import HideMode
 
 log = logging.getLogger(__name__)

=== modified file 'openlp/plugins/presentations/lib/presentationcontroller.py'
--- openlp/plugins/presentations/lib/presentationcontroller.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/presentations/lib/presentationcontroller.py	2013-12-28 21:35:49 +0000
@@ -33,8 +33,8 @@
 
 from PyQt4 import QtCore
 
-from openlp.core.common import AppLocation, Settings, check_directory_exists
-from openlp.core.lib import Registry, create_thumb, validate_thumb
+from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists
+from openlp.core.lib import create_thumb, validate_thumb
 
 log = logging.getLogger(__name__)
 

=== modified file 'openlp/plugins/remotes/lib/httprouter.py'
--- openlp/plugins/remotes/lib/httprouter.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/remotes/lib/httprouter.py	2013-12-28 21:35:49 +0000
@@ -124,8 +124,8 @@
 from mako.template import Template
 from PyQt4 import QtCore
 
-from openlp.core.common import AppLocation, Settings, translate
-from openlp.core.lib import Registry, PluginStatus, StringContent, image_to_byte
+from openlp.core.common import Registry, AppLocation, Settings, translate
+from openlp.core.lib import PluginStatus, StringContent, image_to_byte
 
 log = logging.getLogger(__name__)
 FILE_TYPES = {

=== modified file 'openlp/plugins/songs/forms/duplicatesongremovalform.py'
--- openlp/plugins/songs/forms/duplicatesongremovalform.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/songs/forms/duplicatesongremovalform.py	2013-12-28 21:35:49 +0000
@@ -35,7 +35,7 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.lib import Registry, translate
+from openlp.core.common import Registry, translate
 from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
 from openlp.plugins.songs.lib import delete_song
 from openlp.plugins.songs.lib.db import Song, MediaFile

=== modified file 'openlp/plugins/songs/forms/editsongform.py'
--- openlp/plugins/songs/forms/editsongform.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/songs/forms/editsongform.py	2013-12-28 21:35:49 +0000
@@ -38,8 +38,8 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import AppLocation, UiStrings, check_directory_exists, translate
-from openlp.core.lib import FileDialog, Registry, PluginStatus, MediaType, create_separated_list
+from openlp.core.common import Registry, AppLocation, UiStrings, check_directory_exists, translate
+from openlp.core.lib import FileDialog, PluginStatus, MediaType, create_separated_list
 from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box
 from openlp.plugins.songs.lib import VerseType, clean_song
 from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile

=== modified file 'openlp/plugins/songs/forms/songexportform.py'
--- openlp/plugins/songs/forms/songexportform.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/songs/forms/songexportform.py	2013-12-28 21:35:49 +0000
@@ -34,8 +34,8 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import UiStrings, translate
-from openlp.core.lib import Registry,  create_separated_list, build_icon
+from openlp.core.common import Registry, UiStrings, translate
+from openlp.core.lib import create_separated_list, build_icon
 from openlp.core.lib.ui import critical_error_message_box
 from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
 from openlp.plugins.songs.lib.db import Song

=== modified file 'openlp/plugins/songs/forms/songimportform.py'
--- openlp/plugins/songs/forms/songimportform.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/songs/forms/songimportform.py	2013-12-28 21:35:49 +0000
@@ -35,9 +35,8 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import UiStrings, translate
-from openlp.core.common import Settings
-from openlp.core.lib import FileDialog, Registry
+from openlp.core.common import Registry, Settings, UiStrings, translate
+from openlp.core.lib import FileDialog
 from openlp.core.lib.ui import critical_error_message_box
 from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
 from openlp.plugins.songs.lib.importer import SongFormat, SongFormatSelect

=== modified file 'openlp/plugins/songs/forms/songmaintenanceform.py'
--- openlp/plugins/songs/forms/songmaintenanceform.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/songs/forms/songmaintenanceform.py	2013-12-28 21:35:49 +0000
@@ -32,8 +32,7 @@
 from PyQt4 import QtGui, QtCore
 from sqlalchemy.sql import and_
 
-from openlp.core.common import UiStrings, translate
-from openlp.core.lib import Registry
+from openlp.core.common import Registry, UiStrings, translate
 from openlp.core.lib.ui import critical_error_message_box
 from openlp.plugins.songs.forms.authorsform import AuthorsForm
 from openlp.plugins.songs.forms.topicsform import TopicsForm

=== modified file 'openlp/plugins/songs/lib/mediaitem.py'
--- openlp/plugins/songs/lib/mediaitem.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/songs/lib/mediaitem.py	2013-12-28 21:35:49 +0000
@@ -35,8 +35,8 @@
 from PyQt4 import QtCore, QtGui
 from sqlalchemy.sql import or_
 
-from openlp.core.common import AppLocation, Settings, check_directory_exists, UiStrings, translate
-from openlp.core.lib import Registry, MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, \
+from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, UiStrings, translate
+from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, \
      check_item_selected, create_separated_list
 from openlp.core.lib.ui import create_widget_action
 from openlp.plugins.songs.forms.editsongform import EditSongForm

=== modified file 'openlp/plugins/songs/lib/oooimport.py'
--- openlp/plugins/songs/lib/oooimport.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/songs/lib/oooimport.py	2013-12-28 21:35:49 +0000
@@ -127,7 +127,7 @@
                     manager = uno_instance.ServiceManager
                     self.desktop = manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance)
                     return
-            raise
+            raise Exception('Unable to start LibreOffice')
 
     def startOooProcess(self):
         try:

=== modified file 'openlp/plugins/songs/lib/openlyricsexport.py'
--- openlp/plugins/songs/lib/openlyricsexport.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/songs/lib/openlyricsexport.py	2013-12-28 21:35:49 +0000
@@ -35,8 +35,7 @@
 
 from lxml import etree
 
-from openlp.core.common import check_directory_exists, translate
-from openlp.core.lib import Registry
+from openlp.core.common import Registry, check_directory_exists, translate
 from openlp.core.utils import clean_filename
 from openlp.plugins.songs.lib.xml import OpenLyrics
 

=== modified file 'openlp/plugins/songs/lib/songimport.py'
--- openlp/plugins/songs/lib/songimport.py	2013-12-24 09:24:32 +0000
+++ openlp/plugins/songs/lib/songimport.py	2013-12-28 21:35:49 +0000
@@ -34,8 +34,7 @@
 
 from PyQt4 import QtCore
 
-from openlp.core.common import AppLocation, check_directory_exists, translate
-from openlp.core.lib import Registry
+from openlp.core.common import Registry, AppLocation, check_directory_exists, translate
 from openlp.core.ui.wizard import WizardStrings
 from openlp.plugins.songs.lib import clean_song, VerseType
 from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile

=== modified file 'openlp/plugins/songusage/forms/songusagedeleteform.py'
--- openlp/plugins/songusage/forms/songusagedeleteform.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/songusage/forms/songusagedeleteform.py	2013-12-28 21:35:49 +0000
@@ -29,8 +29,7 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.common import translate
-from openlp.core.lib import Registry
+from openlp.core.common import Registry, translate
 from openlp.plugins.songusage.lib.db import SongUsageItem
 from .songusagedeletedialog import Ui_SongUsageDeleteDialog
 

=== modified file 'openlp/plugins/songusage/forms/songusagedetailform.py'
--- openlp/plugins/songusage/forms/songusagedetailform.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/songusage/forms/songusagedetailform.py	2013-12-28 21:35:49 +0000
@@ -33,8 +33,7 @@
 from PyQt4 import QtGui
 from sqlalchemy.sql import and_
 
-from openlp.core.common import Settings, check_directory_exists, translate
-from openlp.core.lib import Registry
+from openlp.core.common import Registry, Settings, check_directory_exists, translate
 from openlp.plugins.songusage.lib.db import SongUsageItem
 from .songusagedetaildialog import Ui_SongUsageDetailDialog
 

=== modified file 'openlp/plugins/songusage/songusageplugin.py'
--- openlp/plugins/songusage/songusageplugin.py	2013-12-24 08:56:50 +0000
+++ openlp/plugins/songusage/songusageplugin.py	2013-12-28 21:35:49 +0000
@@ -32,8 +32,8 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import Settings, translate
-from openlp.core.lib import Plugin, Registry, StringContent, build_icon
+from openlp.core.common import Registry, Settings, translate
+from openlp.core.lib import Plugin, StringContent, build_icon
 from openlp.core.lib.db import Manager
 from openlp.core.lib.ui import create_action
 from openlp.core.utils.actions import ActionList

=== added file 'tests/functional/openlp_core_common/test_init.py'
--- tests/functional/openlp_core_common/test_init.py	1970-01-01 00:00:00 +0000
+++ tests/functional/openlp_core_common/test_init.py	2013-12-28 21:35:49 +0000
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2013 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2013 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                          #
+###############################################################################
+"""
+Functional tests to test the AppLocation class and related methods.
+"""
+
+from unittest import TestCase
+
+from openlp.core.common import de_hump
+
+
+class TestInitFunctions(TestCase):
+    """
+    A test suite to test out various functions in the __init__ class.
+    """
+    def de_hump_conversion_test(self):
+        """
+        Test the de_hump function with a class name
+        """
+        # GIVEN: a Class name in Camel Case
+        string = "MyClass"
+
+        # WHEN: we call de_hump
+        new_string = de_hump(string)
+
+        # THEN: the new string should be converted to python format
+        self.assertTrue(new_string == "my_class", 'The class name should have been converted')
+
+    def de_hump_static_test(self):
+        """
+        Test the de_hump function with a python string
+        """
+        # GIVEN: a Class name in Camel Case
+        string = "my_class"
+
+        # WHEN: we call de_hump
+        new_string = de_hump(string)
+
+        # THEN: the new string should be converted to python format
+        self.assertTrue(new_string == "my_class", 'The class name should have been preserved')

=== added file 'tests/functional/openlp_core_common/test_registry.py'
--- tests/functional/openlp_core_common/test_registry.py	1970-01-01 00:00:00 +0000
+++ tests/functional/openlp_core_common/test_registry.py	2013-12-28 21:35:49 +0000
@@ -0,0 +1,112 @@
+# -*- 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                          #
+###############################################################################
+"""
+Package to test the openlp.core.lib package.
+"""
+import os
+from unittest import TestCase
+
+from openlp.core.common import Registry
+from tests.functional import MagicMock
+
+TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../', '..', 'resources'))
+
+
+class TestRegistry(TestCase):
+
+    def registry_service_test(self):
+        """
+        Test the registry creation and its usage
+        """
+        # GIVEN: A new registry
+        Registry.create()
+
+        # WHEN: I add a component it should save it
+        mock_1 = MagicMock()
+        Registry().register('test1', mock_1)
+
+        # THEN: we should be able retrieve the saved component
+        assert Registry().get('test1') == mock_1, 'The saved service can be retrieved and matches'
+
+        # WHEN: I add a component for the second time I am mad.
+        # THEN  and I will get an exception
+        with self.assertRaises(KeyError) as context:
+            Registry().register('test1', mock_1)
+        self.assertEqual(context.exception.args[0], 'Duplicate service exception test1',
+            'KeyError exception should have been thrown for duplicate service')
+
+        # WHEN I try to get back a non existent component
+        # THEN I will get an exception
+        with self.assertRaises(KeyError) as context:
+            temp = Registry().get('test2')
+        self.assertEqual(context.exception.args[0], 'Service test2 not found in list',
+            'KeyError exception should have been thrown for missing service')
+
+        # WHEN I try to replace a component I should be allowed (testing only)
+        Registry().remove('test1')
+        # THEN I will get an exception
+        with self.assertRaises(KeyError) as context:
+            temp = Registry().get('test1')
+        self.assertEqual(context.exception.args[0], 'Service test1 not found in list',
+            'KeyError exception should have been thrown for deleted service')
+
+    def registry_function_test(self):
+        """
+        Test the registry function creation and their usages
+        """
+        # GIVEN: An existing registry register a function
+        Registry.create()
+        Registry().register_function('test1', self.dummy_function_1)
+
+        # WHEN: I execute the function
+        return_value = Registry().execute('test1')
+
+        # THEN: I expect then function to have been called and a return given
+        self.assertEqual(return_value[0], 'function_1', 'A return value is provided and matches')
+
+        # WHEN: I execute the a function with the same reference and execute the function
+        Registry().register_function('test1', self.dummy_function_1)
+        return_value = Registry().execute('test1')
+
+        # THEN: I expect then function to have been called and a return given
+        self.assertEqual(return_value, ['function_1', 'function_1'], 'A return value list is provided and matches')
+
+        # WHEN: I execute the a 2nd function with the different reference and execute the function
+        Registry().register_function('test2', self.dummy_function_2)
+        return_value = Registry().execute('test2')
+
+        # THEN: I expect then function to have been called and a return given
+        self.assertEqual(return_value[0], 'function_2', 'A return value is provided and matches')
+
+    def dummy_function_1(self):
+        return "function_1"
+
+    def dummy_function_2(self):
+        return "function_2"
+

=== modified file 'tests/functional/openlp_core_lib/test_image_manager.py'
--- tests/functional/openlp_core_lib/test_image_manager.py	2013-12-24 08:56:50 +0000
+++ tests/functional/openlp_core_lib/test_image_manager.py	2013-12-28 21:35:49 +0000
@@ -34,7 +34,8 @@
 from unittest import TestCase
 from PyQt4 import QtGui
 
-from openlp.core.lib import Registry, ImageManager, ScreenList
+from openlp.core.common import Registry
+from openlp.core.lib import ImageManager, ScreenList
 
 TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources'))
 

=== modified file 'tests/functional/openlp_core_lib/test_pluginmanager.py'
--- tests/functional/openlp_core_lib/test_pluginmanager.py	2013-12-24 08:56:50 +0000
+++ tests/functional/openlp_core_lib/test_pluginmanager.py	2013-12-28 21:35:49 +0000
@@ -31,9 +31,9 @@
 """
 from unittest import TestCase
 
-from openlp.core.common import Settings
+from openlp.core.common import Registry, Settings
 from openlp.core.lib.pluginmanager import PluginManager
-from openlp.core.lib import Registry, PluginStatus
+from openlp.core.lib import PluginStatus
 from tests.functional import MagicMock
 
 

=== removed file 'tests/functional/openlp_core_lib/test_registry.py'
--- tests/functional/openlp_core_lib/test_registry.py	2013-12-24 08:56:50 +0000
+++ tests/functional/openlp_core_lib/test_registry.py	1970-01-01 00:00:00 +0000
@@ -1,112 +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                          #
-###############################################################################
-"""
-Package to test the openlp.core.lib package.
-"""
-import os
-from unittest import TestCase
-
-from openlp.core.lib import Registry
-from tests.functional import MagicMock
-
-TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources'))
-
-
-class TestRegistry(TestCase):
-
-    def registry_service_test(self):
-        """
-        Test the registry creation and its usage
-        """
-        # GIVEN: A new registry
-        Registry.create()
-
-        # WHEN: I add a component it should save it
-        mock_1 = MagicMock()
-        Registry().register('test1', mock_1)
-
-        # THEN: we should be able retrieve the saved component
-        assert Registry().get('test1') == mock_1, 'The saved service can be retrieved and matches'
-
-        # WHEN: I add a component for the second time I am mad.
-        # THEN  and I will get an exception
-        with self.assertRaises(KeyError) as context:
-            Registry().register('test1', mock_1)
-        self.assertEqual(context.exception.args[0], 'Duplicate service exception test1',
-            'KeyError exception should have been thrown for duplicate service')
-
-        # WHEN I try to get back a non existent component
-        # THEN I will get an exception
-        with self.assertRaises(KeyError) as context:
-            temp = Registry().get('test2')
-        self.assertEqual(context.exception.args[0], 'Service test2 not found in list',
-            'KeyError exception should have been thrown for missing service')
-
-        # WHEN I try to replace a component I should be allowed (testing only)
-        Registry().remove('test1')
-        # THEN I will get an exception
-        with self.assertRaises(KeyError) as context:
-            temp = Registry().get('test1')
-        self.assertEqual(context.exception.args[0], 'Service test1 not found in list',
-            'KeyError exception should have been thrown for deleted service')
-
-    def registry_function_test(self):
-        """
-        Test the registry function creation and their usages
-        """
-        # GIVEN: An existing registry register a function
-        Registry.create()
-        Registry().register_function('test1', self.dummy_function_1)
-
-        # WHEN: I execute the function
-        return_value = Registry().execute('test1')
-
-        # THEN: I expect then function to have been called and a return given
-        self.assertEqual(return_value[0], 'function_1', 'A return value is provided and matches')
-
-        # WHEN: I execute the a function with the same reference and execute the function
-        Registry().register_function('test1', self.dummy_function_1)
-        return_value = Registry().execute('test1')
-
-        # THEN: I expect then function to have been called and a return given
-        self.assertEqual(return_value, ['function_1', 'function_1'], 'A return value list is provided and matches')
-
-        # WHEN: I execute the a 2nd function with the different reference and execute the function
-        Registry().register_function('test2', self.dummy_function_2)
-        return_value = Registry().execute('test2')
-
-        # THEN: I expect then function to have been called and a return given
-        self.assertEqual(return_value[0], 'function_2', 'A return value is provided and matches')
-
-    def dummy_function_1(self):
-        return "function_1"
-
-    def dummy_function_2(self):
-        return "function_2"
-

=== modified file 'tests/functional/openlp_core_lib/test_screen.py'
--- tests/functional/openlp_core_lib/test_screen.py	2013-12-24 08:56:50 +0000
+++ tests/functional/openlp_core_lib/test_screen.py	2013-12-28 21:35:49 +0000
@@ -33,7 +33,8 @@
 
 from PyQt4 import QtGui, QtCore
 
-from openlp.core.lib import Registry, ScreenList
+from openlp.core.common import Registry
+from openlp.core.lib import ScreenList
 from tests.functional import MagicMock
 
 SCREEN = {

=== modified file 'tests/functional/openlp_core_lib/test_serviceitem.py'
--- tests/functional/openlp_core_lib/test_serviceitem.py	2013-12-24 08:56:50 +0000
+++ tests/functional/openlp_core_lib/test_serviceitem.py	2013-12-28 21:35:49 +0000
@@ -36,7 +36,8 @@
 from tests.functional import MagicMock, patch
 from tests.utils import assert_length, convert_file_service_item
 
-from openlp.core.lib import ItemCapabilities, ServiceItem, Registry
+from openlp.core.common import Registry
+from openlp.core.lib import ItemCapabilities, ServiceItem
 
 
 VERSE = 'The Lord said to {r}Noah{/r}: \n'\

=== modified file 'tests/functional/openlp_plugins/images/test_lib.py'
--- tests/functional/openlp_plugins/images/test_lib.py	2013-12-24 08:56:50 +0000
+++ tests/functional/openlp_plugins/images/test_lib.py	2013-12-28 21:35:49 +0000
@@ -31,7 +31,7 @@
 """
 from unittest import TestCase
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups
 from openlp.plugins.images.lib.mediaitem import ImageMediaItem
 from tests.functional import MagicMock, patch

=== modified file 'tests/functional/openlp_plugins/presentations/test_mediaitem.py'
--- tests/functional/openlp_plugins/presentations/test_mediaitem.py	2013-12-24 08:56:50 +0000
+++ tests/functional/openlp_plugins/presentations/test_mediaitem.py	2013-12-28 21:35:49 +0000
@@ -33,7 +33,7 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 from openlp.plugins.presentations.lib.mediaitem import PresentationMediaItem
 from tests.functional import patch, MagicMock
 

=== modified file 'tests/functional/openlp_plugins/songs/test_mediaitem.py'
--- tests/functional/openlp_plugins/songs/test_mediaitem.py	2013-10-13 20:36:42 +0000
+++ tests/functional/openlp_plugins/songs/test_mediaitem.py	2013-12-28 21:35:49 +0000
@@ -7,8 +7,8 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.common import Settings
-from openlp.core.lib import Registry, ServiceItem
+from openlp.core.common import Registry, Settings
+from openlp.core.lib import ServiceItem
 from openlp.plugins.songs.lib.mediaitem import SongMediaItem
 from tests.functional import patch, MagicMock
 

=== modified file 'tests/interfaces/openlp_core_lib/test_pluginmanager.py'
--- tests/interfaces/openlp_core_lib/test_pluginmanager.py	2013-10-14 04:55:20 +0000
+++ tests/interfaces/openlp_core_lib/test_pluginmanager.py	2013-12-28 21:35:49 +0000
@@ -9,9 +9,8 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.common import Settings
+from openlp.core.common import Registry, Settings
 from openlp.core.lib.pluginmanager import PluginManager
-from openlp.core.lib import Registry
 from tests.interfaces import MagicMock
 
 

=== modified file 'tests/interfaces/openlp_core_ui/test_filerenamedialog.py'
--- tests/interfaces/openlp_core_ui/test_filerenamedialog.py	2013-10-04 21:45:38 +0000
+++ tests/interfaces/openlp_core_ui/test_filerenamedialog.py	2013-12-28 21:35:49 +0000
@@ -5,7 +5,7 @@
 
 from PyQt4 import QtGui, QtTest
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 from openlp.core.ui import filerenameform
 from tests.interfaces import MagicMock, patch
 

=== modified file 'tests/interfaces/openlp_core_ui/test_listpreviewwidget.py'
--- tests/interfaces/openlp_core_ui/test_listpreviewwidget.py	2013-10-04 21:45:38 +0000
+++ tests/interfaces/openlp_core_ui/test_listpreviewwidget.py	2013-12-28 21:35:49 +0000
@@ -6,7 +6,8 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.lib import Registry, ServiceItem
+from openlp.core.common import Registry
+from openlp.core.lib import ServiceItem
 from openlp.core.ui import listpreviewwidget
 from tests.interfaces import MagicMock, patch
 from tests.utils.osdinteraction import read_service_from_file

=== modified file 'tests/interfaces/openlp_core_ui/test_mainwindow.py'
--- tests/interfaces/openlp_core_ui/test_mainwindow.py	2013-10-04 21:45:38 +0000
+++ tests/interfaces/openlp_core_ui/test_mainwindow.py	2013-12-28 21:35:49 +0000
@@ -5,7 +5,7 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 from openlp.core.ui.mainwindow import MainWindow
 from tests.interfaces import MagicMock, patch
 

=== modified file 'tests/interfaces/openlp_core_ui/test_servicemanager.py'
--- tests/interfaces/openlp_core_ui/test_servicemanager.py	2013-10-04 21:45:38 +0000
+++ tests/interfaces/openlp_core_ui/test_servicemanager.py	2013-12-28 21:35:49 +0000
@@ -6,7 +6,8 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.lib import Registry, ScreenList, ServiceItem
+from openlp.core.common import Registry
+from openlp.core.lib import ScreenList, ServiceItem
 from openlp.core.ui.mainwindow import MainWindow
 from tests.interfaces import MagicMock, patch
 

=== modified file 'tests/interfaces/openlp_core_ui/test_servicenotedialog.py'
--- tests/interfaces/openlp_core_ui/test_servicenotedialog.py	2013-10-04 21:45:38 +0000
+++ tests/interfaces/openlp_core_ui/test_servicenotedialog.py	2013-12-28 21:35:49 +0000
@@ -5,7 +5,7 @@
 
 from PyQt4 import QtCore, QtGui, QtTest
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 from openlp.core.ui import servicenoteform
 from tests.interfaces import patch
 

=== modified file 'tests/interfaces/openlp_core_ui/test_settings_form.py'
--- tests/interfaces/openlp_core_ui/test_settings_form.py	2013-10-04 21:45:38 +0000
+++ tests/interfaces/openlp_core_ui/test_settings_form.py	2013-12-28 21:35:49 +0000
@@ -5,8 +5,9 @@
 
 from PyQt4 import QtCore, QtTest, QtGui
 
+from openlp.core.common import Registry
 from openlp.core.ui import settingsform
-from openlp.core.lib import Registry, ScreenList
+from openlp.core.lib import ScreenList
 from tests.interfaces import MagicMock, patch
 
 

=== modified file 'tests/interfaces/openlp_core_ui/test_starttimedialog.py'
--- tests/interfaces/openlp_core_ui/test_starttimedialog.py	2013-10-04 21:45:38 +0000
+++ tests/interfaces/openlp_core_ui/test_starttimedialog.py	2013-12-28 21:35:49 +0000
@@ -5,7 +5,7 @@
 
 from PyQt4 import QtCore, QtGui, QtTest
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 from openlp.core.ui import starttimeform
 from tests.interfaces import MagicMock, patch
 

=== modified file 'tests/interfaces/openlp_plugins/bibles/test_lib_http.py'
--- tests/interfaces/openlp_plugins/bibles/test_lib_http.py	2013-10-04 21:45:38 +0000
+++ tests/interfaces/openlp_plugins/bibles/test_lib_http.py	2013-12-28 21:35:49 +0000
@@ -3,7 +3,7 @@
 """
 from unittest import TestCase
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 from openlp.plugins.bibles.lib.http import BGExtract, CWExtract
 from tests.interfaces import MagicMock
 

=== modified file 'tests/interfaces/openlp_plugins/custom/forms/test_customform.py'
--- tests/interfaces/openlp_plugins/custom/forms/test_customform.py	2013-10-04 21:45:38 +0000
+++ tests/interfaces/openlp_plugins/custom/forms/test_customform.py	2013-12-28 21:35:49 +0000
@@ -5,7 +5,7 @@
 
 from PyQt4 import QtGui, QtTest, QtCore
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 # Import needed due to import problems.
 from openlp.plugins.custom.lib.mediaitem import CustomMediaItem
 from openlp.plugins.custom.forms.editcustomform import EditCustomForm

=== modified file 'tests/interfaces/openlp_plugins/custom/forms/test_customslideform.py'
--- tests/interfaces/openlp_plugins/custom/forms/test_customslideform.py	2013-10-04 21:45:38 +0000
+++ tests/interfaces/openlp_plugins/custom/forms/test_customslideform.py	2013-12-28 21:35:49 +0000
@@ -5,7 +5,7 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 from openlp.plugins.custom.forms.editcustomslideform import EditCustomSlideForm
 from tests.interfaces import MagicMock, patch
 

=== modified file 'tests/interfaces/openlp_plugins/songs/forms/test_authorsform.py'
--- tests/interfaces/openlp_plugins/songs/forms/test_authorsform.py	2013-08-31 18:17:38 +0000
+++ tests/interfaces/openlp_plugins/songs/forms/test_authorsform.py	2013-12-28 21:35:49 +0000
@@ -5,7 +5,7 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 from openlp.plugins.songs.forms.authorsform import AuthorsForm
 
 

=== modified file 'tests/interfaces/openlp_plugins/songs/forms/test_editsongform.py'
--- tests/interfaces/openlp_plugins/songs/forms/test_editsongform.py	2013-10-21 07:38:18 +0000
+++ tests/interfaces/openlp_plugins/songs/forms/test_editsongform.py	2013-12-28 21:35:49 +0000
@@ -5,7 +5,7 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 from openlp.plugins.songs.forms.editsongform import EditSongForm
 from tests.interfaces import MagicMock
 

=== modified file 'tests/interfaces/openlp_plugins/songs/forms/test_editverseform.py'
--- tests/interfaces/openlp_plugins/songs/forms/test_editverseform.py	2013-08-31 18:17:38 +0000
+++ tests/interfaces/openlp_plugins/songs/forms/test_editverseform.py	2013-12-28 21:35:49 +0000
@@ -5,7 +5,7 @@
 
 from PyQt4 import QtCore, QtGui, QtTest
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 from openlp.plugins.songs.forms.editverseform import EditVerseForm
 
 

=== modified file 'tests/interfaces/openlp_plugins/songs/forms/test_topicsform.py'
--- tests/interfaces/openlp_plugins/songs/forms/test_topicsform.py	2013-08-31 18:17:38 +0000
+++ tests/interfaces/openlp_plugins/songs/forms/test_topicsform.py	2013-12-28 21:35:49 +0000
@@ -5,7 +5,7 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.lib import Registry
+from openlp.core.common import Registry
 from openlp.plugins.songs.forms.topicsform import TopicsForm
 
 


Follow ups