← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~crichter/openlp/i18n into lp:openlp

 

rimach has proposed merging lp:~crichter/openlp/i18n into lp:openlp.

Requested reviews:
  Tim Bentley (trb143)


I added some code for:
- translating the MediaManagerDock and all the content (Icons, context menues)
- translating the PluginList
-- 
The attached diff has been truncated due to its size.
https://code.launchpad.net/~crichter/openlp/i18n/+merge/35125
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/lib/__init__.py'
--- openlp/core/lib/__init__.py	2010-09-04 13:35:15 +0000
+++ openlp/core/lib/__init__.py	2010-09-10 15:56:22 +0000
@@ -1,336 +1,336 @@
-# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
-
-###############################################################################
-# OpenLP - Open Source Lyrics Projection                                      #
-# --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2010 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
-# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
-# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
-# Carsten Tinggaard, Frode Woldsund                                           #
-# --------------------------------------------------------------------------- #
-# This program is free software; you can redistribute it and/or modify it     #
-# under the terms of the GNU General Public License as published by the Free  #
-# Software Foundation; version 2 of the License.                              #
-#                                                                             #
-# This program is distributed in the hope that it will be useful, but WITHOUT #
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
-# more details.                                                               #
-#                                                                             #
-# You should have received a copy of the GNU General Public License along     #
-# with this program; if not, write to the Free Software Foundation, Inc., 59  #
-# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
-###############################################################################
-"""
-The :mod:`lib` module contains most of the components and libraries that make
-OpenLP work.
-"""
-import logging
-import os.path
-import types
-
-from PyQt4 import QtCore, QtGui
-
-log = logging.getLogger(__name__)
-
-# TODO make external and configurable in alpha 4 via a settings dialog
-html_expands = []
-
-html_expands.append({u'desc':u'Red', u'start tag':u'{r}', \
-                          u'start html':u'<span style="-webkit-text-fill-color:red">', \
-                          u'end tag':u'{/r}', u'end html':u'</span>', \
-                          u'protected':False})
-html_expands.append({u'desc':u'Black', u'start tag':u'{b}', \
-                          u'start html':u'<span style="-webkit-text-fill-color:black">', \
-                          u'end tag':u'{/b}', u'end html':u'</span>', \
-                          u'protected':False})
-html_expands.append({u'desc':u'Blue', u'start tag':u'{bl}', \
-                          u'start html':u'<span style="-webkit-text-fill-color:blue">', \
-                          u'end tag':u'{/bl}', u'end html':u'</span>', \
-                          u'protected':False})
-html_expands.append({u'desc':u'Yellow', u'start tag':u'{y}', \
-                          u'start html':u'<span style="-webkit-text-fill-color:yellow">', \
-                          u'end tag':u'{/y}', u'end html':u'</span>', \
-                          u'protected':False})
-html_expands.append({u'desc':u'Green', u'start tag':u'{g}', \
-                          u'start html':u'<span style="-webkit-text-fill-color:green">', \
-                          u'end tag':u'{/g}', u'end html':u'</span>', \
-                          u'protected':False})
-html_expands.append({u'desc':u'Pink', u'start tag':u'{pk}', \
-                          u'start html':u'<span style="-webkit-text-fill-color:#CC33CC">', \
-                          u'end tag':u'{/pk}', u'end html':u'</span>', \
-                          u'protected':False})
-html_expands.append({u'desc':u'Orange', u'start tag':u'{o}', \
-                          u'start html':u'<span style="-webkit-text-fill-color:#CC0033">', \
-                          u'end tag':u'{/o}', u'end html':u'</span>', \
-                          u'protected':False})
-html_expands.append({u'desc':u'Purple', u'start tag':u'{pp}', \
-                          u'start html':u'<span style="-webkit-text-fill-color:#9900FF">', \
-                          u'end tag':u'{/pp}', u'end html':u'</span>', \
-                          u'protected':False})
-html_expands.append({u'desc':u'White', u'start tag':u'{w}', \
-                          u'start html':u'<span style="-webkit-text-fill-color:white">', \
-                          u'end tag':u'{/w}', u'end html':u'</span>', \
-                          u'protected':False})
-html_expands.append({u'desc':u'Superscript', u'start tag':u'{su}', \
-                          u'start html':u'<sup>', \
-                          u'end tag':u'{/su}', u'end html':u'</sup>', \
-                          u'protected':True})
-html_expands.append({u'desc':u'Subscript', u'start tag':u'{sb}', \
-                          u'start html':u'<sub>', \
-                          u'end tag':u'{/sb}', u'end html':u'</sub>', \
-                          u'protected':True})
-html_expands.append({u'desc':u'Paragraph', u'start tag':u'{p}', \
-                          u'start html':u'<p>', \
-                          u'end tag':u'{/p}', u'end html':u'</p>', \
-                          u'protected':True})
-html_expands.append({u'desc':u'Bold', u'start tag':u'{st}', \
-                          u'start html':u'<strong>', \
-                          u'end tag':u'{/st}', \
-                          u'end html':u'</strong>', \
-                          u'protected':True})
-html_expands.append({u'desc':u'Italics', u'start tag':u'{it}', \
-                          u'start html':u'<em>', \
-                          u'end tag':u'{/it}', u'end html':u'</em>', \
-                          u'protected':True})
-
-def translate(context, text, comment=None):
-    """
-    A special shortcut method to wrap around the Qt4 translation functions.
-    This abstracts the translation procedure so that we can change it if at a
-    later date if necessary, without having to redo the whole of OpenLP.
-
-    ``context``
-        The translation context, used to give each string a context or a
-        namespace.
-
-    ``text``
-        The text to put into the translation tables for translation.
-
-    ``comment``
-        An identifying string for when the same text is used in different roles
-        within the same context.
-    """
-    return QtCore.QCoreApplication.translate(context, text, comment)
-
-def get_text_file_string(text_file):
-    """
-    Open a file and return its content as unicode string.  If the supplied file
-    name is not a file then the function returns False.  If there is an error
-    loading the file or the content can't be decoded then the function will
-    return None.
-
-    ``textfile``
-        The name of the file.
-    """
-    if not os.path.isfile(text_file):
-        return False
-    file_handle = None
-    content_string = None
-    try:
-        file_handle = open(text_file, u'r')
-        content = file_handle.read()
-        content_string = content.decode(u'utf-8')
-    except (IOError, UnicodeError):
-        log.exception(u'Failed to open text file %s' % text_file)
-    finally:
-        if file_handle:
-            file_handle.close()
-    return content_string
-
-def str_to_bool(stringvalue):
-    """
-    Convert a string version of a boolean into a real boolean.
-
-    ``stringvalue``
-        The string value to examine and convert to a boolean type.
-    """
-    if isinstance(stringvalue, bool):
-        return stringvalue
-    return unicode(stringvalue).strip().lower() in (u'true', u'yes', u'y')
-
-def build_icon(icon):
-    """
-    Build a QIcon instance from an existing QIcon, a resource location, or a
-    physical file location. If the icon is a QIcon instance, that icon is
-    simply returned. If not, it builds a QIcon instance from the resource or
-    file name.
-
-    ``icon``
-        The icon to build. This can be a QIcon, a resource string in the form
-        ``:/resource/file.png``, or a file location like ``/path/to/file.png``.
-    """
-    button_icon = QtGui.QIcon()
-    if isinstance(icon, QtGui.QIcon):
-        button_icon = icon
-    elif isinstance(icon, basestring):
-        if icon.startswith(u':/'):
-            button_icon.addPixmap(QtGui.QPixmap(icon), QtGui.QIcon.Normal,
-                QtGui.QIcon.Off)
-        else:
-            button_icon.addPixmap(QtGui.QPixmap.fromImage(QtGui.QImage(icon)),
-                QtGui.QIcon.Normal, QtGui.QIcon.Off)
-    elif isinstance(icon, QtGui.QImage):
-        button_icon.addPixmap(QtGui.QPixmap.fromImage(icon),
-            QtGui.QIcon.Normal, QtGui.QIcon.Off)
-    return button_icon
-
-def context_menu_action(base, icon, text, slot):
-    """
-    Utility method to help build context menus for plugins
-
-    ``base``
-        The parent menu to add this menu item to
-
-    ``icon``
-        An icon for this action
-
-    ``text``
-        The text to display for this action
-
-    ``slot``
-        The code to run when this action is triggered
-    """
-    action = QtGui.QAction(text, base)
-    if icon:
-        action.setIcon(build_icon(icon))
-    QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), slot)
-    return action
-
-def context_menu(base, icon, text):
-    """
-    Utility method to help build context menus for plugins
-
-    ``base``
-        The parent object to add this menu to
-
-    ``icon``
-        An icon for this menu
-
-    ``text``
-        The text to display for this menu
-    """
-    action = QtGui.QMenu(text, base)
-    action.setIcon(build_icon(icon))
-    return action
-
-def context_menu_separator(base):
-    """
-    Add a separator to a context menu
-
-    ``base``
-        The menu object to add the separator to
-    """
-    action = QtGui.QAction(u'', base)
-    action.setSeparator(True)
-    return action
-
-def image_to_byte(image):
-    """
-    Resize an image to fit on the current screen for the web and returns
-    it as a byte stream.
-
-    ``image``
-        The image to converted.
-    """
-    byte_array = QtCore.QByteArray()
-    # use buffer to store pixmap into byteArray
-    buffie = QtCore.QBuffer(byte_array)
-    buffie.open(QtCore.QIODevice.WriteOnly)
-    if isinstance(image, QtGui.QImage):
-        pixmap = QtGui.QPixmap.fromImage(image)
-    else:
-        pixmap = QtGui.QPixmap(image)
-    pixmap.save(buffie, "PNG")
-    # convert to base64 encoding so does not get missed!
-    return byte_array.toBase64()
-
-def resize_image(image, width, height, background=QtCore.Qt.black):
-    """
-    Resize an image to fit on the current screen.
-
-    ``image``
-        The image to resize.
-
-    ``width``
-        The new image width.
-
-    ``height``
-        The new image height.
-
-     ``background``
-        The background colour defaults to black.
-
-    """
-    preview = QtGui.QImage(image)
-    if not preview.isNull():
-        # Only resize if different size
-        if preview.width() == width and preview.height == height:
-            return preview
-        preview = preview.scaled(width, height, QtCore.Qt.KeepAspectRatio,
-            QtCore.Qt.SmoothTransformation)
-    realw = preview.width()
-    realh = preview.height()
-    # and move it to the centre of the preview space
-    new_image = QtGui.QImage(width, height,
-        QtGui.QImage.Format_ARGB32_Premultiplied)
-    new_image.fill(background)
-    painter = QtGui.QPainter(new_image)
-    painter.drawImage((width - realw) / 2, (height - realh) / 2, preview)
-    return new_image
-
-def check_item_selected(list_widget, message):
-    """
-    Check if a list item is selected so an action may be performed on it
-
-    ``list_widget``
-        The list to check for selected items
-
-    ``message``
-        The message to give the user if no item is selected
-    """
-    if not list_widget.selectedIndexes():
-        QtGui.QMessageBox.information(list_widget.parent(),
-            translate('OpenLP.MediaManagerItem', 'No Items Selected'), message)
-        return False
-    return True
-
-def clean_tags(text):
-    """
-    Remove Tags from text for display
-    """
-    text = text.replace(u'<br>', u'\n')
-    for tag in html_expands:
-        text = text.replace(tag[u'start tag'], u'')
-        text = text.replace(tag[u'end tag'], u'')
-    return text
-
-def expand_tags(text):
-    """
-    Expand tags HTML for display
-    """
-    for tag in html_expands:
-        text = text.replace(tag[u'start tag'], tag[u'start html'])
-        text = text.replace(tag[u'end tag'], tag[u'end html'])
-    return text
-
-from spelltextedit import SpellTextEdit
-from eventreceiver import Receiver
-from settingsmanager import SettingsManager
-from plugin import PluginStatus, Plugin
-from pluginmanager import PluginManager
-from settingstab import SettingsTab
-from serviceitem import ServiceItem
-from serviceitem import ServiceItemType
-from serviceitem import ItemCapabilities
-from htmlbuilder import build_html, build_lyrics_format_css, \
-    build_lyrics_outline_css
-from toolbar import OpenLPToolbar
-from dockwidget import OpenLPDockWidget
-from theme import ThemeLevel, ThemeXML
-from renderer import Renderer
-from rendermanager import RenderManager
-from mediamanageritem import MediaManagerItem
-from baselistwithdnd import BaseListWithDnD
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
+# Carsten Tinggaard, Frode Woldsund                                           #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it     #
+# under the terms of the GNU General Public License as published by the Free  #
+# Software Foundation; version 2 of the License.                              #
+#                                                                             #
+# This program is distributed in the hope that it will be useful, but WITHOUT #
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
+# more details.                                                               #
+#                                                                             #
+# You should have received a copy of the GNU General Public License along     #
+# with this program; if not, write to the Free Software Foundation, Inc., 59  #
+# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
+###############################################################################
+"""
+The :mod:`lib` module contains most of the components and libraries that make
+OpenLP work.
+"""
+import logging
+import os.path
+import types
+
+from PyQt4 import QtCore, QtGui
+
+log = logging.getLogger(__name__)
+
+# TODO make external and configurable in alpha 4 via a settings dialog
+html_expands = []
+
+html_expands.append({u'desc':u'Red', u'start tag':u'{r}', \
+                          u'start html':u'<span style="-webkit-text-fill-color:red">', \
+                          u'end tag':u'{/r}', u'end html':u'</span>', \
+                          u'protected':False})
+html_expands.append({u'desc':u'Black', u'start tag':u'{b}', \
+                          u'start html':u'<span style="-webkit-text-fill-color:black">', \
+                          u'end tag':u'{/b}', u'end html':u'</span>', \
+                          u'protected':False})
+html_expands.append({u'desc':u'Blue', u'start tag':u'{bl}', \
+                          u'start html':u'<span style="-webkit-text-fill-color:blue">', \
+                          u'end tag':u'{/bl}', u'end html':u'</span>', \
+                          u'protected':False})
+html_expands.append({u'desc':u'Yellow', u'start tag':u'{y}', \
+                          u'start html':u'<span style="-webkit-text-fill-color:yellow">', \
+                          u'end tag':u'{/y}', u'end html':u'</span>', \
+                          u'protected':False})
+html_expands.append({u'desc':u'Green', u'start tag':u'{g}', \
+                          u'start html':u'<span style="-webkit-text-fill-color:green">', \
+                          u'end tag':u'{/g}', u'end html':u'</span>', \
+                          u'protected':False})
+html_expands.append({u'desc':u'Pink', u'start tag':u'{pk}', \
+                          u'start html':u'<span style="-webkit-text-fill-color:#CC33CC">', \
+                          u'end tag':u'{/pk}', u'end html':u'</span>', \
+                          u'protected':False})
+html_expands.append({u'desc':u'Orange', u'start tag':u'{o}', \
+                          u'start html':u'<span style="-webkit-text-fill-color:#CC0033">', \
+                          u'end tag':u'{/o}', u'end html':u'</span>', \
+                          u'protected':False})
+html_expands.append({u'desc':u'Purple', u'start tag':u'{pp}', \
+                          u'start html':u'<span style="-webkit-text-fill-color:#9900FF">', \
+                          u'end tag':u'{/pp}', u'end html':u'</span>', \
+                          u'protected':False})
+html_expands.append({u'desc':u'White', u'start tag':u'{w}', \
+                          u'start html':u'<span style="-webkit-text-fill-color:white">', \
+                          u'end tag':u'{/w}', u'end html':u'</span>', \
+                          u'protected':False})
+html_expands.append({u'desc':u'Superscript', u'start tag':u'{su}', \
+                          u'start html':u'<sup>', \
+                          u'end tag':u'{/su}', u'end html':u'</sup>', \
+                          u'protected':True})
+html_expands.append({u'desc':u'Subscript', u'start tag':u'{sb}', \
+                          u'start html':u'<sub>', \
+                          u'end tag':u'{/sb}', u'end html':u'</sub>', \
+                          u'protected':True})
+html_expands.append({u'desc':u'Paragraph', u'start tag':u'{p}', \
+                          u'start html':u'<p>', \
+                          u'end tag':u'{/p}', u'end html':u'</p>', \
+                          u'protected':True})
+html_expands.append({u'desc':u'Bold', u'start tag':u'{st}', \
+                          u'start html':u'<strong>', \
+                          u'end tag':u'{/st}', \
+                          u'end html':u'</strong>', \
+                          u'protected':True})
+html_expands.append({u'desc':u'Italics', u'start tag':u'{it}', \
+                          u'start html':u'<em>', \
+                          u'end tag':u'{/it}', u'end html':u'</em>', \
+                          u'protected':True})
+
+def translate(context, text, comment=None):
+    """
+    A special shortcut method to wrap around the Qt4 translation functions.
+    This abstracts the translation procedure so that we can change it if at a
+    later date if necessary, without having to redo the whole of OpenLP.
+
+    ``context``
+        The translation context, used to give each string a context or a
+        namespace.
+
+    ``text``
+        The text to put into the translation tables for translation.
+
+    ``comment``
+        An identifying string for when the same text is used in different roles
+        within the same context.
+    """
+    return QtCore.QCoreApplication.translate(context, text, comment)
+
+def get_text_file_string(text_file):
+    """
+    Open a file and return its content as unicode string.  If the supplied file
+    name is not a file then the function returns False.  If there is an error
+    loading the file or the content can't be decoded then the function will
+    return None.
+
+    ``textfile``
+        The name of the file.
+    """
+    if not os.path.isfile(text_file):
+        return False
+    file_handle = None
+    content_string = None
+    try:
+        file_handle = open(text_file, u'r')
+        content = file_handle.read()
+        content_string = content.decode(u'utf-8')
+    except (IOError, UnicodeError):
+        log.exception(u'Failed to open text file %s' % text_file)
+    finally:
+        if file_handle:
+            file_handle.close()
+    return content_string
+
+def str_to_bool(stringvalue):
+    """
+    Convert a string version of a boolean into a real boolean.
+
+    ``stringvalue``
+        The string value to examine and convert to a boolean type.
+    """
+    if isinstance(stringvalue, bool):
+        return stringvalue
+    return unicode(stringvalue).strip().lower() in (u'true', u'yes', u'y')
+
+def build_icon(icon):
+    """
+    Build a QIcon instance from an existing QIcon, a resource location, or a
+    physical file location. If the icon is a QIcon instance, that icon is
+    simply returned. If not, it builds a QIcon instance from the resource or
+    file name.
+
+    ``icon``
+        The icon to build. This can be a QIcon, a resource string in the form
+        ``:/resource/file.png``, or a file location like ``/path/to/file.png``.
+    """
+    button_icon = QtGui.QIcon()
+    if isinstance(icon, QtGui.QIcon):
+        button_icon = icon
+    elif isinstance(icon, basestring):
+        if icon.startswith(u':/'):
+            button_icon.addPixmap(QtGui.QPixmap(icon), QtGui.QIcon.Normal,
+                QtGui.QIcon.Off)
+        else:
+            button_icon.addPixmap(QtGui.QPixmap.fromImage(QtGui.QImage(icon)),
+                QtGui.QIcon.Normal, QtGui.QIcon.Off)
+    elif isinstance(icon, QtGui.QImage):
+        button_icon.addPixmap(QtGui.QPixmap.fromImage(icon),
+            QtGui.QIcon.Normal, QtGui.QIcon.Off)
+    return button_icon
+
+def context_menu_action(base, icon, text, slot):
+    """
+    Utility method to help build context menus for plugins
+
+    ``base``
+        The parent menu to add this menu item to
+
+    ``icon``
+        An icon for this action
+
+    ``text``
+        The text to display for this action
+
+    ``slot``
+        The code to run when this action is triggered
+    """
+    action = QtGui.QAction(text, base)
+    if icon:
+        action.setIcon(build_icon(icon))
+    QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), slot)
+    return action
+
+def context_menu(base, icon, text):
+    """
+    Utility method to help build context menus for plugins
+
+    ``base``
+        The parent object to add this menu to
+
+    ``icon``
+        An icon for this menu
+
+    ``text``
+        The text to display for this menu
+    """
+    action = QtGui.QMenu(text, base)
+    action.setIcon(build_icon(icon))
+    return action
+
+def context_menu_separator(base):
+    """
+    Add a separator to a context menu
+
+    ``base``
+        The menu object to add the separator to
+    """
+    action = QtGui.QAction(u'', base)
+    action.setSeparator(True)
+    return action
+
+def image_to_byte(image):
+    """
+    Resize an image to fit on the current screen for the web and returns
+    it as a byte stream.
+
+    ``image``
+        The image to converted.
+    """
+    byte_array = QtCore.QByteArray()
+    # use buffer to store pixmap into byteArray
+    buffie = QtCore.QBuffer(byte_array)
+    buffie.open(QtCore.QIODevice.WriteOnly)
+    if isinstance(image, QtGui.QImage):
+        pixmap = QtGui.QPixmap.fromImage(image)
+    else:
+        pixmap = QtGui.QPixmap(image)
+    pixmap.save(buffie, "PNG")
+    # convert to base64 encoding so does not get missed!
+    return byte_array.toBase64()
+
+def resize_image(image, width, height, background=QtCore.Qt.black):
+    """
+    Resize an image to fit on the current screen.
+
+    ``image``
+        The image to resize.
+
+    ``width``
+        The new image width.
+
+    ``height``
+        The new image height.
+
+     ``background``
+        The background colour defaults to black.
+
+    """
+    preview = QtGui.QImage(image)
+    if not preview.isNull():
+        # Only resize if different size
+        if preview.width() == width and preview.height == height:
+            return preview
+        preview = preview.scaled(width, height, QtCore.Qt.KeepAspectRatio,
+            QtCore.Qt.SmoothTransformation)
+    realw = preview.width()
+    realh = preview.height()
+    # and move it to the centre of the preview space
+    new_image = QtGui.QImage(width, height,
+        QtGui.QImage.Format_ARGB32_Premultiplied)
+    new_image.fill(background)
+    painter = QtGui.QPainter(new_image)
+    painter.drawImage((width - realw) / 2, (height - realh) / 2, preview)
+    return new_image
+
+def check_item_selected(list_widget, message):
+    """
+    Check if a list item is selected so an action may be performed on it
+
+    ``list_widget``
+        The list to check for selected items
+
+    ``message``
+        The message to give the user if no item is selected
+    """
+    if not list_widget.selectedIndexes():
+        QtGui.QMessageBox.information(list_widget.parent(),
+            translate('OpenLP.MediaManagerItem', 'No Items Selected'), message)
+        return False
+    return True
+
+def clean_tags(text):
+    """
+    Remove Tags from text for display
+    """
+    text = text.replace(u'<br>', u'\n')
+    for tag in html_expands:
+        text = text.replace(tag[u'start tag'], u'')
+        text = text.replace(tag[u'end tag'], u'')
+    return text
+
+def expand_tags(text):
+    """
+    Expand tags HTML for display
+    """
+    for tag in html_expands:
+        text = text.replace(tag[u'start tag'], tag[u'start html'])
+        text = text.replace(tag[u'end tag'], tag[u'end html'])
+    return text
+
+from spelltextedit import SpellTextEdit
+from eventreceiver import Receiver
+from settingsmanager import SettingsManager
+from plugin import PluginStatus, StringType, Plugin
+from pluginmanager import PluginManager
+from settingstab import SettingsTab
+from serviceitem import ServiceItem
+from serviceitem import ServiceItemType
+from serviceitem import ItemCapabilities
+from htmlbuilder import build_html, build_lyrics_format_css, \
+    build_lyrics_outline_css
+from toolbar import OpenLPToolbar
+from dockwidget import OpenLPDockWidget
+from theme import ThemeLevel, ThemeXML
+from renderer import Renderer
+from rendermanager import RenderManager
+from mediamanageritem import MediaManagerItem
+from baselistwithdnd import BaseListWithDnD

=== modified file 'openlp/core/lib/mediamanageritem.py'
--- openlp/core/lib/mediamanageritem.py	2010-07-26 15:48:54 +0000
+++ openlp/core/lib/mediamanageritem.py	2010-09-10 15:56:22 +0000
@@ -1,542 +1,531 @@
-# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
-
-###############################################################################
-# OpenLP - Open Source Lyrics Projection                                      #
-# --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2010 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
-# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
-# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
-# Carsten Tinggaard, Frode Woldsund                                           #
-# --------------------------------------------------------------------------- #
-# 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                          #
-###############################################################################
-"""
-Provides the generic functions for interfacing plugins with the Media Manager.
-"""
-import logging
-import os
-
-from PyQt4 import QtCore, QtGui
-
-from openlp.core.lib import context_menu_action, context_menu_separator, \
-    SettingsManager, OpenLPToolbar, ServiceItem, build_icon, translate
-
-log = logging.getLogger(__name__)
-
-class MediaManagerItem(QtGui.QWidget):
-    """
-    MediaManagerItem is a helper widget for plugins.
-
-    None of the following *need* to be used, feel free to override
-    them completely in your plugin's implementation. Alternatively,
-    call them from your plugin before or after you've done extra
-    things that you need to.
-
-    **Constructor Parameters**
-
-    ``parent``
-        The parent widget. Usually this will be the *Media Manager*
-        itself. This needs to be a class descended from ``QWidget``.
-
-    ``icon``
-        Either a ``QIcon``, a resource path, or a file name. This is
-        the icon which is displayed in the *Media Manager*.
-
-    ``title``
-        The title visible on the item in the *Media Manager*.
-
-    **Member Variables**
-
-    When creating a descendant class from this class for your plugin,
-    the following member variables should be set.
-
-    ``self.PluginNameShort``
-        The shortened (usually singular) name for the plugin e.g. *'Song'*
-        for the Songs plugin.
-
-    ``self.pluginNameVisible``
-        The user visible name for a plugin which should use a suitable
-        translation function.
-
-     ``self.OnNewPrompt``
-        Defaults to *'Select Image(s)'*.
-
-     ``self.OnNewFileMasks``
-        Defaults to *'Images (*.jpg *jpeg *.gif *.png *.bmp)'*. This
-        assumes that the new action is to load a file. If not, you
-        need to override the ``OnNew`` method.
-
-     ``self.ListViewWithDnD_class``
-        This must be a **class**, not an object, descended from
-        ``openlp.core.lib.BaseListWithDnD`` that is not used in any
-        other part of OpenLP.
-
-     ``self.PreviewFunction``
-        This must be a method which returns a QImage to represent the
-        item (usually a preview). No scaling is required, that is
-        performed automatically by OpenLP when necessary. If this
-        method is not defined, a default will be used (treat the
-        filename as an image).
-    """
-    log.info(u'Media Item loaded')
-
-    def __init__(self, parent=None, icon=None, title=None):
-        """
-        Constructor to create the media manager item.
-        """
-        QtGui.QWidget.__init__(self)
-        self.parent = parent
-        self.settingsSection = title.lower()
-        if isinstance(icon, QtGui.QIcon):
-            self.icon = icon
-        elif isinstance(icon, basestring):
-            self.icon.addPixmap(QtGui.QPixmap.fromImage(QtGui.QImage(icon)),
-                QtGui.QIcon.Normal, QtGui.QIcon.Off)
-        else:
-            self.icon = None
-        if title:
-            self.title = title
-        self.toolbar = None
-        self.remoteTriggered = None
-        self.serviceItemIconName = None
-        self.singleServiceItem = True
-        self.pageLayout = QtGui.QVBoxLayout(self)
-        self.pageLayout.setSpacing(0)
-        self.pageLayout.setContentsMargins(4, 0, 4, 0)
-        self.requiredIcons()
-        self.setupUi()
-        self.retranslateUi()
-
-    def requiredIcons(self):
-        """
-        This method is called to define the icons for the plugin.
-        It provides a default set and the plugin is able to override
-        the if required.
-        """
-        self.hasImportIcon = False
-        self.hasNewIcon = True
-        self.hasEditIcon = True
-        self.hasFileIcon = False
-        self.hasDeleteIcon = True
-        self.addToServiceItem = False
-
-    def retranslateUi(self):
-        """
-        This method is called automatically to provide OpenLP with the
-        opportunity to translate the ``MediaManagerItem`` to another
-        language.
-        """
-        pass
-
-    def addToolbar(self):
-        """
-        A method to help developers easily add a toolbar to the media
-        manager item.
-        """
-        if self.toolbar is None:
-            self.toolbar = OpenLPToolbar(self)
-            self.pageLayout.addWidget(self.toolbar)
-
-    def addToolbarButton(
-        self, title, tooltip, icon, slot=None, checkable=False):
-        """
-        A method to help developers easily add a button to the toolbar.
-
-        ``title``
-            The title of the button.
-
-        ``tooltip``
-            The tooltip to be displayed when the mouse hovers over the
-            button.
-
-        ``icon``
-            The icon of the button. This can be an instance of QIcon, or a
-            string cotaining either the absolute path to the image, or an
-            internal resource path starting with ':/'.
-
-        ``slot``
-            The method to call when the button is clicked.
-
-        ``objectname``
-            The name of the button.
-        """
-        # NB different order (when I broke this out, I didn't want to
-        # break compatability), but it makes sense for the icon to
-        # come before the tooltip (as you have to have an icon, but
-        # not neccesarily a tooltip)
-        self.toolbar.addToolbarButton(title, icon, tooltip, slot, checkable)
-
-    def addToolbarSeparator(self):
-        """
-        A very simple method to add a separator to the toolbar.
-        """
-        self.toolbar.addSeparator()
-
-    def setupUi(self):
-        """
-        This method sets up the interface on the button. Plugin
-        developers use this to add and create toolbars, and the rest
-        of the interface of the media manager item.
-        """
-        # Add a toolbar
-        self.addToolbar()
-        #Allow the plugin to define buttons at start of bar
-        self.addStartHeaderBar()
-        #Add the middle of the tool bar (pre defined)
-        self.addMiddleHeaderBar()
-        #Allow the plugin to define buttons at end of bar
-        self.addEndHeaderBar()
-        #Add the list view
-        self.addListViewToToolBar()
-
-    def addMiddleHeaderBar(self):
-        """
-        Create buttons for the media item toolbar
-        """
-        ## Import Button ##
-        if self.hasImportIcon:
-            self.addToolbarButton(
-                unicode(translate('OpenLP.MediaManagerItem', 'Import %s')) %
-                self.PluginNameShort,
-                unicode(translate('OpenLP.MediaManagerItem', 'Import a %s')) %
-                self.pluginNameVisible,
-                u':/general/general_import.png', self.onImportClick)
-        ## File Button ##
-        if self.hasFileIcon:
-            self.addToolbarButton(
-                unicode(translate('OpenLP.MediaManagerItem', 'Load %s')) %
-                self.PluginNameShort,
-                unicode(translate('OpenLP.MediaManagerItem', 'Load a new %s')) %
-                self.pluginNameVisible,
-                u':/general/general_open.png', self.onFileClick)
-        ## New Button ##
-        if self.hasNewIcon:
-            self.addToolbarButton(
-                unicode(translate('OpenLP.MediaManagerItem', 'New %s')) %
-                self.PluginNameShort,
-                unicode(translate('OpenLP.MediaManagerItem', 'Add a new %s')) %
-                self.pluginNameVisible,
-                u':/general/general_new.png', self.onNewClick)
-        ## Edit Button ##
-        if self.hasEditIcon:
-            self.addToolbarButton(
-                unicode(translate('OpenLP.MediaManagerItem', 'Edit %s')) %
-                self.PluginNameShort,
-                unicode(translate(
-                    'OpenLP.MediaManagerItem', 'Edit the selected %s')) %
-                self.pluginNameVisible,
-                u':/general/general_edit.png', self.onEditClick)
-        ## Delete Button ##
-        if self.hasDeleteIcon:
-            self.addToolbarButton(
-                unicode(translate('OpenLP.MediaManagerItem', 'Delete %s')) %
-                self.PluginNameShort,
-                translate('OpenLP.MediaManagerItem',
-                    'Delete the selected item'),
-                u':/general/general_delete.png', self.onDeleteClick)
-        ## Separator Line ##
-        self.addToolbarSeparator()
-        ## Preview ##
-        self.addToolbarButton(
-            unicode(translate('OpenLP.MediaManagerItem', 'Preview %s')) %
-            self.PluginNameShort,
-            translate('OpenLP.MediaManagerItem', 'Preview the selected item'),
-            u':/general/general_preview.png', self.onPreviewClick)
-        ## Live  Button ##
-        self.addToolbarButton(
-            u'Go Live',
-            translate('OpenLP.MediaManagerItem', 'Send the selected item live'),
-            u':/general/general_live.png', self.onLiveClick)
-        ## Add to service Button ##
-        self.addToolbarButton(
-            unicode(translate('OpenLP.MediaManagerItem', 'Add %s to Service')) %
-            self.PluginNameShort,
-            translate('OpenLP.MediaManagerItem',
-                'Add the selected item(s) to the service'),
-            u':/general/general_add.png', self.onAddClick)
-
-    def addListViewToToolBar(self):
-        """
-        Creates the main widget for listing items the media item is tracking
-        """
-        #Add the List widget
-        self.listView = self.ListViewWithDnD_class(self)
-        self.listView.uniformItemSizes = True
-        self.listView.setGeometry(QtCore.QRect(10, 100, 256, 591))
-        self.listView.setSpacing(1)
-        self.listView.setSelectionMode(
-            QtGui.QAbstractItemView.ExtendedSelection)
-        self.listView.setAlternatingRowColors(True)
-        self.listView.setDragEnabled(True)
-        self.listView.setObjectName(u'%sListView' % self.PluginNameShort)
-        #Add to pageLayout
-        self.pageLayout.addWidget(self.listView)
-        #define and add the context menu
-        self.listView.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
-        if self.hasEditIcon:
-            self.listView.addAction(
-                context_menu_action(
-                    self.listView, u':/general/general_edit.png',
-                    unicode(translate('OpenLP.MediaManagerItem', '&Edit %s')) %
-                    self.pluginNameVisible,
-                    self.onEditClick))
-            self.listView.addAction(context_menu_separator(self.listView))
-        if self.hasDeleteIcon:
-            self.listView.addAction(
-                context_menu_action(
-                    self.listView, u':/general/general_delete.png',
-                    unicode(translate('OpenLP.MediaManagerItem',
-                        '&Delete %s')) %
-                    self.pluginNameVisible,
-                    self.onDeleteClick))
-            self.listView.addAction(context_menu_separator(self.listView))
-        self.listView.addAction(
-            context_menu_action(
-                self.listView, u':/general/general_preview.png',
-                unicode(translate('OpenLP.MediaManagerItem', '&Preview %s')) %
-                self.pluginNameVisible,
-                self.onPreviewClick))
-        self.listView.addAction(
-            context_menu_action(
-                self.listView, u':/general/general_live.png',
-                translate('OpenLP.MediaManagerItem', '&Show Live'),
-                self.onLiveClick))
-        self.listView.addAction(
-            context_menu_action(
-                self.listView, u':/general/general_add.png',
-                translate('OpenLP.MediaManagerItem', '&Add to Service'),
-                self.onAddClick))
-        if self.addToServiceItem:
-            self.listView.addAction(
-                context_menu_action(
-                    self.listView, u':/general/general_add.png',
-                    translate('OpenLP.MediaManagerItem',
-                        '&Add to selected Service Item'),
-                    self.onAddEditClick))
-        if QtCore.QSettings().value(u'advanced/double click live',
-            QtCore.QVariant(False)).toBool():
-            QtCore.QObject.connect(self.listView,
-                QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
-                self.onLiveClick)
-        else:
-            QtCore.QObject.connect(self.listView,
-                QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
-                self.onPreviewClick)
-
-    def initialise(self):
-        """
-        Implement this method in your descendent media manager item to
-        do any UI or other initialisation. This method is called automatically.
-        """
-        pass
-
-    def addStartHeaderBar(self):
-        """
-        Slot at start of toolbar for plugin to addwidgets
-        """
-        pass
-
-    def addEndHeaderBar(self):
-        """
-        Slot at end of toolbar for plugin to add widgets
-        """
-        pass
-
-    def onFileClick(self):
-        """
-        Add a file to the list widget to make it available for showing
-        """
-        files = QtGui.QFileDialog.getOpenFileNames(
-            self, self.OnNewPrompt,
-            SettingsManager.get_last_dir(self.settingsSection),
-            self.OnNewFileMasks)
-        log.info(u'New files(s) %s', unicode(files))
-        if files:
-            self.loadList(files)
-            lastDir = os.path.split(unicode(files[0]))[0]
-            SettingsManager.set_last_dir(self.settingsSection, lastDir)
-            SettingsManager.set_list(self.settingsSection,
-                self.settingsSection, self.getFileList())
-
-    def getFileList(self):
-        """
-        Return the current list of files
-        """
-        count = 0
-        filelist = []
-        while count < self.listView.count():
-            bitem = self.listView.item(count)
-            filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
-            filelist.append(filename)
-            count += 1
-        return filelist
-
-    def validate(self, file, thumb):
-        """
-        Validates to see if the file still exists or thumbnail is up to date
-        """
-        if not os.path.exists(file):
-            return False
-        if os.path.exists(thumb):
-            filedate = os.stat(file).st_mtime
-            thumbdate = os.stat(thumb).st_mtime
-            #if file updated rebuild icon
-            if filedate > thumbdate:
-                self.iconFromFile(file, thumb)
-        else:
-            self.iconFromFile(file, thumb)
-        return True
-
-    def iconFromFile(self, file, thumb):
-        """
-        Create a thumbnail icon from a given file
-
-        ``file``
-            The file to create the icon from
-
-        ``thumb``
-            The filename to save the thumbnail to
-        """
-        icon = build_icon(unicode(file))
-        pixmap = icon.pixmap(QtCore.QSize(88, 50))
-        ext = os.path.splitext(thumb)[1].lower()
-        pixmap.save(thumb, ext[1:])
-        return icon
-
-    def loadList(self, list):
-        raise NotImplementedError(u'MediaManagerItem.loadList needs to be '
-            u'defined by the plugin')
-
-    def onNewClick(self):
-        raise NotImplementedError(u'MediaManagerItem.onNewClick needs to be '
-            u'defined by the plugin')
-
-    def onEditClick(self):
-        raise NotImplementedError(u'MediaManagerItem.onEditClick needs to be '
-            u'defined by the plugin')
-
-    def onDeleteClick(self):
-        raise NotImplementedError(u'MediaManagerItem.onDeleteClick needs to '
-            u'be defined by the plugin')
-
-    def generateSlideData(self, service_item, item):
-        raise NotImplementedError(u'MediaManagerItem.generateSlideData needs '
-            u'to be defined by the plugin')
-
-    def onPreviewClick(self):
-        """
-        Preview an item by building a service item then adding that service
-        item to the preview slide controller.
-        """
-        if not self.listView.selectedIndexes() and not self.remoteTriggered:
-            QtGui.QMessageBox.information(self,
-                translate('OpenLP.MediaManagerItem', 'No Items Selected'),
-                translate('OpenLP.MediaManagerItem',
-                    'You must select one or more items to preview.'))
-        else:
-            log.debug(self.PluginNameShort + u' Preview requested')
-            service_item = self.buildServiceItem()
-            if service_item:
-                service_item.from_plugin = True
-                self.parent.previewController.addServiceItem(service_item)
-
-    def onLiveClick(self):
-        """
-        Send an item live by building a service item then adding that service
-        item to the live slide controller.
-        """
-        if not self.listView.selectedIndexes():
-            QtGui.QMessageBox.information(self,
-                translate('OpenLP.MediaManagerItem', 'No Items Selected'),
-                translate('OpenLP.MediaManagerItem',
-                    'You must select one or more items to send live.'))
-        else:
-            log.debug(self.PluginNameShort + u' Live requested')
-            service_item = self.buildServiceItem()
-            if service_item:
-                service_item.from_plugin = True
-                self.parent.liveController.addServiceItem(service_item)
-
-    def onAddClick(self):
-        """
-        Add a selected item to the current service
-        """
-        if not self.listView.selectedIndexes() and not self.remoteTriggered:
-            QtGui.QMessageBox.information(self,
-                translate('OpenLP.MediaManagerItem', 'No Items Selected'),
-                translate('OpenLP.MediaManagerItem',
-                    'You must select one or more items.'))
-        else:
-            #Is it posssible to process multiple list items to generate multiple
-            #service items?
-            if self.singleServiceItem or self.remoteTriggered:
-                log.debug(self.PluginNameShort + u' Add requested')
-                service_item = self.buildServiceItem()
-                if service_item:
-                    service_item.from_plugin = False
-                    self.parent.serviceManager.addServiceItem(service_item,
-                        replace=self.remoteTriggered)
-            else:
-                items = self.listView.selectedIndexes()
-                for item in items:
-                    service_item = self.buildServiceItem(item)
-                    if service_item:
-                        service_item.from_plugin = False
-                        self.parent.serviceManager.addServiceItem(service_item)
-
-    def onAddEditClick(self):
-        """
-        Add a selected item to an existing item in the current service.
-        """
-        if not self.listView.selectedIndexes() and not self.remoteTriggered:
-            QtGui.QMessageBox.information(self,
-                translate('OpenLP.MediaManagerItem', 'No items selected'),
-                translate('OpenLP.MediaManagerItem',
-                    'You must select one or more items'))
-        else:
-            log.debug(self.PluginNameShort + u' Add requested')
-            service_item = self.parent.serviceManager.getServiceItem()
-            if not service_item:
-                QtGui.QMessageBox.information(self,
-                    translate('OpenLP.MediaManagerItem',
-                        'No Service Item Selected'),
-                    translate('OpenLP.MediaManagerItem',
-                        'You must select an existing service item to add to.'))
-            elif self.title.lower() == service_item.name.lower():
-                self.generateSlideData(service_item)
-                self.parent.serviceManager.addServiceItem(service_item,
-                    replace=True)
-            else:
-                #Turn off the remote edit update message indicator
-                QtGui.QMessageBox.information(self,
-                    translate('OpenLP.MediaManagerItem',
-                        'Invalid Service Item'),
-                    unicode(translate('OpenLP.MediaManagerItem',
-                        'You must select a %s service item.')) % self.title)
-
-    def buildServiceItem(self, item=None):
-        """
-        Common method for generating a service item
-        """
-        service_item = ServiceItem(self.parent)
-        if self.serviceItemIconName:
-            service_item.add_icon(self.serviceItemIconName)
-        else:
-            service_item.add_icon(self.parent.icon_path)
-        if self.generateSlideData(service_item, item):
-            return service_item
-        else:
-            return None
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
+# Carsten Tinggaard, Frode Woldsund                                           #
+# --------------------------------------------------------------------------- #
+# 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                          #
+###############################################################################
+"""
+Provides the generic functions for interfacing plugins with the Media Manager.
+"""
+import logging
+import os
+
+from PyQt4 import QtCore, QtGui
+
+from openlp.core.lib import context_menu_action, context_menu_separator, \
+    SettingsManager, OpenLPToolbar, ServiceItem, StringType, build_icon, \
+    translate
+
+log = logging.getLogger(__name__)
+
+class MediaManagerItem(QtGui.QWidget):
+    """
+    MediaManagerItem is a helper widget for plugins.
+
+    None of the following *need* to be used, feel free to override
+    them completely in your plugin's implementation. Alternatively,
+    call them from your plugin before or after you've done extra
+    things that you need to.
+
+    **Constructor Parameters**
+
+    ``parent``
+        The parent widget. Usually this will be the *Media Manager*
+        itself. This needs to be a class descended from ``QWidget``.
+
+    ``icon``
+        Either a ``QIcon``, a resource path, or a file name. This is
+        the icon which is displayed in the *Media Manager*.
+
+    ``title``
+        The title visible on the item in the *Media Manager*.
+
+    **Member Variables**
+
+    When creating a descendant class from this class for your plugin,
+    the following member variables should be set.
+
+     ``self.OnNewPrompt``
+        Defaults to *'Select Image(s)'*.
+
+     ``self.OnNewFileMasks``
+        Defaults to *'Images (*.jpg *jpeg *.gif *.png *.bmp)'*. This
+        assumes that the new action is to load a file. If not, you
+        need to override the ``OnNew`` method.
+
+     ``self.ListViewWithDnD_class``
+        This must be a **class**, not an object, descended from
+        ``openlp.core.lib.BaseListWithDnD`` that is not used in any
+        other part of OpenLP.
+
+     ``self.PreviewFunction``
+        This must be a method which returns a QImage to represent the
+        item (usually a preview). No scaling is required, that is
+        performed automatically by OpenLP when necessary. If this
+        method is not defined, a default will be used (treat the
+        filename as an image).
+    """
+    log.info(u'Media Item loaded')
+
+    def __init__(self, parent=None, icon=None, title=None, plugin=None):
+        """
+        Constructor to create the media manager item.
+        """
+        QtGui.QWidget.__init__(self)
+        self.parent = parent
+        self.plugin = parent # rimach may changed
+        self.settingsSection = self.plugin.name_lower
+        if isinstance(icon, QtGui.QIcon):
+            self.icon = icon
+        elif isinstance(icon, basestring):
+            self.icon.addPixmap(QtGui.QPixmap.fromImage(QtGui.QImage(icon)),
+                QtGui.QIcon.Normal, QtGui.QIcon.Off)
+        else:
+            self.icon = None
+        if title:
+            nameString = self.plugin.getString(StringType.Name)
+            self.title = nameString[u'plural']
+        self.toolbar = None
+        self.remoteTriggered = None
+        self.serviceItemIconName = None
+        self.singleServiceItem = True
+        self.pageLayout = QtGui.QVBoxLayout(self)
+        self.pageLayout.setSpacing(0)
+        self.pageLayout.setContentsMargins(4, 0, 4, 0)
+        self.requiredIcons()
+        self.setupUi()
+        self.retranslateUi()
+
+    def requiredIcons(self):
+        """
+        This method is called to define the icons for the plugin.
+        It provides a default set and the plugin is able to override
+        the if required.
+        """
+        self.hasImportIcon = False
+        self.hasNewIcon = True
+        self.hasEditIcon = True
+        self.hasFileIcon = False
+        self.hasDeleteIcon = True
+        self.addToServiceItem = False
+
+    def retranslateUi(self):
+        """
+        This method is called automatically to provide OpenLP with the
+        opportunity to translate the ``MediaManagerItem`` to another
+        language.
+        """
+        pass
+
+    def addToolbar(self):
+        """
+        A method to help developers easily add a toolbar to the media
+        manager item.
+        """
+        if self.toolbar is None:
+            self.toolbar = OpenLPToolbar(self)
+            self.pageLayout.addWidget(self.toolbar)
+
+    def addToolbarButton(
+        self, title, tooltip, icon, slot=None, checkable=False):
+        """
+        A method to help developers easily add a button to the toolbar.
+
+        ``title``
+            The title of the button.
+
+        ``tooltip``
+            The tooltip to be displayed when the mouse hovers over the
+            button.
+
+        ``icon``
+            The icon of the button. This can be an instance of QIcon, or a
+            string cotaining either the absolute path to the image, or an
+            internal resource path starting with ':/'.
+
+        ``slot``
+            The method to call when the button is clicked.
+
+        ``objectname``
+            The name of the button.
+        """
+        # NB different order (when I broke this out, I didn't want to
+        # break compatability), but it makes sense for the icon to
+        # come before the tooltip (as you have to have an icon, but
+        # not neccesarily a tooltip)
+        self.toolbar.addToolbarButton(title, icon, tooltip, slot, checkable)
+
+    def addToolbarSeparator(self):
+        """
+        A very simple method to add a separator to the toolbar.
+        """
+        self.toolbar.addSeparator()
+
+    def setupUi(self):
+        """
+        This method sets up the interface on the button. Plugin
+        developers use this to add and create toolbars, and the rest
+        of the interface of the media manager item.
+        """
+        # Add a toolbar
+        self.addToolbar()
+        #Allow the plugin to define buttons at start of bar
+        self.addStartHeaderBar()
+        #Add the middle of the tool bar (pre defined)
+        self.addMiddleHeaderBar()
+        #Allow the plugin to define buttons at end of bar
+        self.addEndHeaderBar()
+        #Add the list view
+        self.addListViewToToolBar()
+
+    def addMiddleHeaderBar(self):
+        """
+        Create buttons for the media item toolbar
+        """
+        ## Import Button ##
+        if self.hasImportIcon:
+            importString = self.plugin.getString(StringType.Import)
+            self.addToolbarButton(
+                importString[u'title'],
+                importString[u'tooltip'],
+                u':/general/general_import.png', self.onImportClick)
+        ## Load Button ##
+        if self.hasFileIcon:
+            loadString = self.plugin.getString(StringType.Load)
+            self.addToolbarButton(
+                loadString[u'title'],
+                loadString[u'tooltip'],
+                u':/general/general_open.png', self.onFileClick)
+        ## New Button ## rimach
+        if self.hasNewIcon:
+            newString = self.plugin.getString(StringType.New)
+            self.addToolbarButton(
+                newString[u'title'],
+                newString[u'tooltip'],
+                u':/general/general_new.png', self.onNewClick)
+        ## Edit Button ##
+        if self.hasEditIcon:
+            editString = self.plugin.getString(StringType.Edit)
+            self.addToolbarButton(
+                editString[u'title'],
+                editString[u'tooltip'],
+                u':/general/general_edit.png', self.onEditClick)
+        ## Delete Button ##
+        if self.hasDeleteIcon:
+            deleteString = self.plugin.getString(StringType.Delete)
+            self.addToolbarButton(
+                deleteString[u'title'],
+                deleteString[u'tooltip'],
+                u':/general/general_delete.png', self.onDeleteClick)
+        ## Separator Line ##
+        self.addToolbarSeparator()
+        ## Preview ##
+        previewString = self.plugin.getString(StringType.Preview)
+        self.addToolbarButton(
+            previewString[u'title'],
+            previewString[u'tooltip'],
+            u':/general/general_preview.png', self.onPreviewClick)
+        ## Live  Button ##
+        liveString = self.plugin.getString(StringType.Live)
+        self.addToolbarButton(
+            liveString[u'title'],
+            liveString[u'tooltip'],
+            u':/general/general_live.png', self.onLiveClick)
+        ## Add to service Button ##
+        serviceString = self.plugin.getString(StringType.Service)
+        self.addToolbarButton(
+            serviceString[u'title'],
+            serviceString[u'tooltip'],
+            u':/general/general_add.png', self.onAddClick)
+
+    def addListViewToToolBar(self):
+        """
+        Creates the main widget for listing items the media item is tracking
+        """
+        #Add the List widget
+        self.listView = self.ListViewWithDnD_class(self)
+        self.listView.uniformItemSizes = True
+        self.listView.setGeometry(QtCore.QRect(10, 100, 256, 591))
+        self.listView.setSpacing(1)
+        self.listView.setSelectionMode(
+            QtGui.QAbstractItemView.ExtendedSelection)
+        self.listView.setAlternatingRowColors(True)
+        self.listView.setDragEnabled(True)
+        self.listView.setObjectName(u'%sListView' % self.parent.name)
+        #Add to pageLayout
+        self.pageLayout.addWidget(self.listView)
+        #define and add the context menu
+        self.listView.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
+        if self.hasEditIcon:
+            self.listView.addAction(
+                context_menu_action(
+                    self.listView, u':/general/general_edit.png',
+                    unicode(translate('OpenLP.MediaManagerItem', '&Edit %s')) %
+                    self.parent.name,
+                    self.onEditClick))
+            self.listView.addAction(context_menu_separator(self.listView))
+        if self.hasDeleteIcon:
+            self.listView.addAction(
+                context_menu_action(
+                    self.listView, u':/general/general_delete.png',
+                    unicode(translate('OpenLP.MediaManagerItem',
+                        '&Delete %s')) %
+                    self.parent.name,
+                    self.onDeleteClick))
+            self.listView.addAction(context_menu_separator(self.listView))
+        self.listView.addAction(
+            context_menu_action(
+                self.listView, u':/general/general_preview.png',
+                unicode(translate('OpenLP.MediaManagerItem', '&Preview %s')) %
+                self.parent.name,
+                self.onPreviewClick))
+        self.listView.addAction(
+            context_menu_action(
+                self.listView, u':/general/general_live.png',
+                translate('OpenLP.MediaManagerItem', '&Show Live'),
+                self.onLiveClick))
+        self.listView.addAction(
+            context_menu_action(
+                self.listView, u':/general/general_add.png',
+                translate('OpenLP.MediaManagerItem', '&Add to Service'),
+                self.onAddClick))
+        if self.addToServiceItem:
+            self.listView.addAction(
+                context_menu_action(
+                    self.listView, u':/general/general_add.png',
+                    translate('OpenLP.MediaManagerItem',
+                        '&Add to selected Service Item'),
+                    self.onAddEditClick))
+        if QtCore.QSettings().value(u'advanced/double click live',
+            QtCore.QVariant(False)).toBool():
+            QtCore.QObject.connect(self.listView,
+                QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
+                self.onLiveClick)
+        else:
+            QtCore.QObject.connect(self.listView,
+                QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
+                self.onPreviewClick)
+
+    def initialise(self):
+        """
+        Implement this method in your descendent media manager item to
+        do any UI or other initialisation. This method is called automatically.
+        """
+        pass
+
+    def addStartHeaderBar(self):
+        """
+        Slot at start of toolbar for plugin to addwidgets
+        """
+        pass
+
+    def addEndHeaderBar(self):
+        """
+        Slot at end of toolbar for plugin to add widgets
+        """
+        pass
+
+    def onFileClick(self):
+        """
+        Add a file to the list widget to make it available for showing
+        """
+        files = QtGui.QFileDialog.getOpenFileNames(
+            self, self.OnNewPrompt,
+            SettingsManager.get_last_dir(self.settingsSection),
+            self.OnNewFileMasks)
+        log.info(u'New files(s) %s', unicode(files))
+        if files:
+            self.loadList(files)
+            lastDir = os.path.split(unicode(files[0]))[0]
+            SettingsManager.set_last_dir(self.settingsSection, lastDir)
+            SettingsManager.set_list(self.settingsSection,
+                self.settingsSection, self.getFileList())
+
+    def getFileList(self):
+        """
+        Return the current list of files
+        """
+        count = 0
+        filelist = []
+        while count < self.listView.count():
+            bitem = self.listView.item(count)
+            filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
+            filelist.append(filename)
+            count += 1
+        return filelist
+
+    def validate(self, file, thumb):
+        """
+        Validates to see if the file still exists or thumbnail is up to date
+        """
+        if not os.path.exists(file):
+            return False
+        if os.path.exists(thumb):
+            filedate = os.stat(file).st_mtime
+            thumbdate = os.stat(thumb).st_mtime
+            #if file updated rebuild icon
+            if filedate > thumbdate:
+                self.iconFromFile(file, thumb)
+        else:
+            self.iconFromFile(file, thumb)
+        return True
+
+    def iconFromFile(self, file, thumb):
+        """
+        Create a thumbnail icon from a given file
+
+        ``file``
+            The file to create the icon from
+
+        ``thumb``
+            The filename to save the thumbnail to
+        """
+        icon = build_icon(unicode(file))
+        pixmap = icon.pixmap(QtCore.QSize(88, 50))
+        ext = os.path.splitext(thumb)[1].lower()
+        pixmap.save(thumb, ext[1:])
+        return icon
+
+    def loadList(self, list):
+        raise NotImplementedError(u'MediaManagerItem.loadList needs to be '
+            u'defined by the plugin')
+
+    def onNewClick(self):
+        raise NotImplementedError(u'MediaManagerItem.onNewClick needs to be '
+            u'defined by the plugin')
+
+    def onEditClick(self):
+        raise NotImplementedError(u'MediaManagerItem.onEditClick needs to be '
+            u'defined by the plugin')
+
+    def onDeleteClick(self):
+        raise NotImplementedError(u'MediaManagerItem.onDeleteClick needs to '
+            u'be defined by the plugin')
+
+    def generateSlideData(self, service_item, item):
+        raise NotImplementedError(u'MediaManagerItem.generateSlideData needs '
+            u'to be defined by the plugin')
+
+    def onPreviewClick(self):
+        """
+        Preview an item by building a service item then adding that service
+        item to the preview slide controller.
+        """
+        if not self.listView.selectedIndexes() and not self.remoteTriggered:
+            QtGui.QMessageBox.information(self,
+                translate('OpenLP.MediaManagerItem', 'No Items Selected'),
+                translate('OpenLP.MediaManagerItem',
+                    'You must select one or more items to preview.'))
+        else:
+            log.debug(self.parent.name + u' Preview requested')
+            service_item = self.buildServiceItem()
+            if service_item:
+                service_item.from_plugin = True
+                self.parent.previewController.addServiceItem(service_item)
+
+    def onLiveClick(self):
+        """
+        Send an item live by building a service item then adding that service
+        item to the live slide controller.
+        """
+        if not self.listView.selectedIndexes():
+            QtGui.QMessageBox.information(self,
+                translate('OpenLP.MediaManagerItem', 'No Items Selected'),
+                translate('OpenLP.MediaManagerItem',
+                    'You must select one or more items to send live.'))
+        else:
+            log.debug(self.parent.name + u' Live requested')
+            service_item = self.buildServiceItem()
+            if service_item:
+                service_item.from_plugin = True
+                self.parent.liveController.addServiceItem(service_item)
+
+    def onAddClick(self):
+        """
+        Add a selected item to the current service
+        """
+        if not self.listView.selectedIndexes() and not self.remoteTriggered:
+            QtGui.QMessageBox.information(self,
+                translate('OpenLP.MediaManagerItem', 'No Items Selected'),
+                translate('OpenLP.MediaManagerItem',
+                    'You must select one or more items.'))
+        else:
+            #Is it posssible to process multiple list items to generate multiple
+            #service items?
+            if self.singleServiceItem or self.remoteTriggered:
+                log.debug(self.parent.name + u' Add requested')
+                service_item = self.buildServiceItem()
+                if service_item:
+                    service_item.from_plugin = False
+                    self.parent.serviceManager.addServiceItem(service_item,
+                        replace=self.remoteTriggered)
+            else:
+                items = self.listView.selectedIndexes()
+                for item in items:
+                    service_item = self.buildServiceItem(item)
+                    if service_item:
+                        service_item.from_plugin = False
+                        self.parent.serviceManager.addServiceItem(service_item)
+
+    def onAddEditClick(self):
+        """
+        Add a selected item to an existing item in the current service.
+        """
+        if not self.listView.selectedIndexes() and not self.remoteTriggered:
+            QtGui.QMessageBox.information(self,
+                translate('OpenLP.MediaManagerItem', 'No items selected'),
+                translate('OpenLP.MediaManagerItem',
+                    'You must select one or more items'))
+        else:
+            log.debug(self.parent.name + u' Add requested')
+            service_item = self.parent.serviceManager.getServiceItem()
+            if not service_item:
+                QtGui.QMessageBox.information(self,
+                    translate('OpenLP.MediaManagerItem',
+                        'No Service Item Selected'),
+                    translate('OpenLP.MediaManagerItem',
+                        'You must select an existing service item to add to.'))
+            elif self.title.lower() == service_item.name.lower():
+                self.generateSlideData(service_item)
+                self.parent.serviceManager.addServiceItem(service_item,
+                    replace=True)
+            else:
+                #Turn off the remote edit update message indicator
+                QtGui.QMessageBox.information(self,
+                    translate('OpenLP.MediaManagerItem',
+                        'Invalid Service Item'),
+                    unicode(translate('OpenLP.MediaManagerItem',
+                        'You must select a %s service item.')) % self.title)
+
+    def buildServiceItem(self, item=None):
+        """
+        Common method for generating a service item
+        """
+        service_item = ServiceItem(self.parent)
+        if self.serviceItemIconName:
+            service_item.add_icon(self.serviceItemIconName)
+        else:
+            service_item.add_icon(self.parent.icon_path)
+        if self.generateSlideData(service_item, item):
+            return service_item
+        else:
+            return None

=== modified file 'openlp/core/lib/plugin.py'
--- openlp/core/lib/plugin.py	2010-08-01 08:30:42 +0000
+++ openlp/core/lib/plugin.py	2010-09-10 15:56:22 +0000
@@ -1,291 +1,319 @@
-# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
-
-###############################################################################
-# OpenLP - Open Source Lyrics Projection                                      #
-# --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2010 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
-# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
-# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
-# Carsten Tinggaard, Frode Woldsund                                           #
-# --------------------------------------------------------------------------- #
-# 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 the generic plugin functionality for OpenLP plugins.
-"""
-import logging
-
-from PyQt4 import QtCore
-
-from openlp.core.lib import Receiver
-
-log = logging.getLogger(__name__)
-
-class PluginStatus(object):
-    """
-    Defines the status of the plugin
-    """
-    Active = 1
-    Inactive = 0
-    Disabled = -1
-
-class Plugin(QtCore.QObject):
-    """
-    Base class for openlp plugins to inherit from.
-
-    **Basic Attributes**
-
-    ``name``
-        The name that should appear in the plugins list.
-
-    ``version``
-        The version number of this iteration of the plugin.
-
-    ``settingsSection``
-        The namespace to store settings for the plugin.
-
-    ``icon``
-        An instance of QIcon, which holds an icon for this plugin.
-
-    ``log``
-        A log object used to log debugging messages. This is pre-instantiated.
-
-    ``weight``
-        A numerical value used to order the plugins.
-
-    **Hook Functions**
-
-    ``checkPreConditions()``
-        Provides the Plugin with a handle to check if it can be loaded.
-
-    ``getMediaManagerItem()``
-        Returns an instance of MediaManagerItem to be used in the Media Manager.
-
-    ``addImportMenuItem(import_menu)``
-        Add an item to the Import menu.
-
-    ``addExportMenuItem(export_menu)``
-        Add an item to the Export menu.
-
-    ``getSettingsTab()``
-        Returns an instance of SettingsTabItem to be used in the Settings
-        dialog.
-
-    ``addToMenu(menubar)``
-        A method to add a menu item to anywhere in the menu, given the menu bar.
-
-    ``handle_event(event)``
-        A method use to handle events, given an Event object.
-
-    ``about()``
-        Used in the plugin manager, when a person clicks on the 'About' button.
-
-    """
-    log.info(u'loaded')
-
-    def __init__(self, name, version=None, plugin_helpers=None):
-        """
-        This is the constructor for the plugin object. This provides an easy
-        way for descendent plugins to populate common data. This method *must*
-        be overridden, like so::
-
-            class MyPlugin(Plugin):
-                def __init__(self):
-                    Plugin.__init(self, u'MyPlugin', u'0.1')
-
-        ``name``
-            Defaults to *None*. The name of the plugin.
-
-        ``version``
-            Defaults to *None*. The version of the plugin.
-
-        ``plugin_helpers``
-            Defaults to *None*. A list of helper objects.
-        """
-        QtCore.QObject.__init__(self)
-        self.name = name
-        if version:
-            self.version = version
-        self.settingsSection = self.name.lower()
-        self.icon = None
-        self.weight = 0
-        self.status = PluginStatus.Inactive
-        # Set up logging
-        self.log = logging.getLogger(self.name)
-        self.previewController = plugin_helpers[u'preview']
-        self.liveController = plugin_helpers[u'live']
-        self.renderManager = plugin_helpers[u'render']
-        self.serviceManager = plugin_helpers[u'service']
-        self.settingsForm = plugin_helpers[u'settings form']
-        self.mediadock = plugin_helpers[u'toolbox']
-        self.pluginManager = plugin_helpers[u'pluginmanager']
-        self.formparent = plugin_helpers[u'formparent']
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'%s_add_service_item' % self.name),
-            self.processAddServiceEvent)
-
-    def checkPreConditions(self):
-        """
-        Provides the Plugin with a handle to check if it can be loaded.
-        Failing Preconditions does not stop a settings Tab being created
-
-        Returns True or False.
-        """
-        return True
-
-    def setStatus(self):
-        """
-        Sets the status of the plugin
-        """
-        self.status = QtCore.QSettings().value(
-            self.settingsSection + u'/status',
-            QtCore.QVariant(PluginStatus.Inactive)).toInt()[0]
-
-    def toggleStatus(self, new_status):
-        """
-        Changes the status of the plugin and remembers it
-        """
-        self.status = new_status
-        QtCore.QSettings().setValue(
-            self.settingsSection + u'/status', QtCore.QVariant(self.status))
-
-    def isActive(self):
-        """
-        Indicates if the plugin is active
-
-        Returns True or False.
-        """
-        return self.status == PluginStatus.Active
-
-    def getMediaManagerItem(self):
-        """
-        Construct a MediaManagerItem object with all the buttons and things
-        you need, and return it for integration into openlp.org.
-        """
-        pass
-
-    def addImportMenuItem(self, importMenu):
-        """
-        Create a menu item and add it to the "Import" menu.
-
-        ``importMenu``
-            The Import menu.
-        """
-        pass
-
-    def addExportMenuItem(self, exportMenu):
-        """
-        Create a menu item and add it to the "Export" menu.
-
-        ``exportMenu``
-            The Export menu
-        """
-        pass
-
-    def addToolsMenuItem(self, toolsMenu):
-        """
-        Create a menu item and add it to the "Tools" menu.
-
-        ``toolsMenu``
-            The Tools menu
-        """
-        pass
-
-    def getSettingsTab(self):
-        """
-        Create a tab for the settings window.
-        """
-        pass
-
-    def addToMenu(self, menubar):
-        """
-        Add menu items to the menu, given the menubar.
-
-        ``menubar``
-            The application's menu bar.
-        """
-        pass
-
-    def processAddServiceEvent(self, replace=False):
-        """
-        Generic Drag and drop handler triggered from service_manager.
-        """
-        log.debug(u'processAddServiceEvent event called for plugin %s' %
-            self.name)
-        if replace:
-            self.mediaItem.onAddEditClick()
-        else:
-            self.mediaItem.onAddClick()
-
-    def about(self):
-        """
-        Show a dialog when the user clicks on the 'About' button in the plugin
-        manager.
-        """
-        raise NotImplementedError(
-            u'Plugin.about needs to be defined by the plugin')
-
-    def initialise(self):
-        """
-        Called by the plugin Manager to initialise anything it needs.
-        """
-        if self.mediaItem:
-            self.mediaItem.initialise()
-        self.insertToolboxItem()
-
-    def finalise(self):
-        """
-        Called by the plugin Manager to cleanup things.
-        """
-        self.removeToolboxItem()
-
-    def removeToolboxItem(self):
-        """
-        Called by the plugin to remove toolbar
-        """
-        if self.mediaItem:
-            self.mediadock.remove_dock(self.name)
-        if self.settings_tab:
-            self.settingsForm.removeTab(self.name)
-
-    def insertToolboxItem(self):
-        """
-        Called by plugin to replace toolbar
-        """
-        if self.mediaItem:
-            self.mediadock.insert_dock(self.mediaItem, self.icon, self.weight)
-        if self.settings_tab:
-            self.settingsForm.insertTab(self.settings_tab, self.weight)
-
-    def usesTheme(self, theme):
-        """
-        Called to find out if a plugin is currently using a theme.
-
-        Returns True if the theme is being used, otherwise returns False.
-        """
-        return False
-
-    def renameTheme(self, oldTheme, newTheme):
-        """
-        Renames a theme a plugin is using making the plugin use the new name.
-
-        ``oldTheme``
-            The name of the theme the plugin should stop using.
-
-        ``newTheme``
-            The new name the plugin should now use.
-        """
-        pass
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
+# Carsten Tinggaard, Frode Woldsund                                           #
+# --------------------------------------------------------------------------- #
+# 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 the generic plugin functionality for OpenLP plugins.
+"""
+import logging
+
+from PyQt4 import QtCore
+
+from openlp.core.lib import Receiver
+
+log = logging.getLogger(__name__)
+
+class PluginStatus(object):
+    """
+    Defines the status of the plugin
+    """
+    Active = 1
+    Inactive = 0
+    Disabled = -1
+
+class StringType(object):
+    Name = u'name'
+    Import = u'import'
+    Load = u'load'
+    New = u'new'
+    Edit = u'edit'
+    Delete = u'delete'
+    Preview = u'preview'
+    Live = u'live'
+    Service = u'service'
+
+class Plugin(QtCore.QObject):
+    """
+    Base class for openlp plugins to inherit from.
+
+    **Basic Attributes**
+
+    ``name``
+        The name that should appear in the plugins list.
+
+    ``version``
+        The version number of this iteration of the plugin.
+
+    ``settingsSection``
+        The namespace to store settings for the plugin.
+
+    ``icon``
+        An instance of QIcon, which holds an icon for this plugin.
+
+    ``log``
+        A log object used to log debugging messages. This is pre-instantiated.
+
+    ``weight``
+        A numerical value used to order the plugins.
+
+    **Hook Functions**
+
+    ``checkPreConditions()``
+        Provides the Plugin with a handle to check if it can be loaded.
+
+    ``getMediaManagerItem()``
+        Returns an instance of MediaManagerItem to be used in the Media Manager.
+
+    ``addImportMenuItem(import_menu)``
+        Add an item to the Import menu.
+
+    ``addExportMenuItem(export_menu)``
+        Add an item to the Export menu.
+
+    ``getSettingsTab()``
+        Returns an instance of SettingsTabItem to be used in the Settings
+        dialog.
+
+    ``addToMenu(menubar)``
+        A method to add a menu item to anywhere in the menu, given the menu bar.
+
+    ``handle_event(event)``
+        A method use to handle events, given an Event object.
+
+    ``about()``
+        Used in the plugin manager, when a person clicks on the 'About' button.
+
+    """
+    log.info(u'loaded')
+
+    def __init__(self, name, version=None, plugin_helpers=None):
+        """
+        This is the constructor for the plugin object. This provides an easy
+        way for descendent plugins to populate common data. This method *must*
+        be overridden, like so::
+
+            class MyPlugin(Plugin):
+                def __init__(self):
+                    Plugin.__init(self, u'MyPlugin', u'0.1')
+
+        ``name``
+            Defaults to *None*. The name of the plugin.
+
+        ``version``
+            Defaults to *None*. The version of the plugin.
+
+        ``plugin_helpers``
+            Defaults to *None*. A list of helper objects.
+        """
+        QtCore.QObject.__init__(self)
+        self.name = name
+        self.set_plugin_strings()
+        if version:
+            self.version = version
+        self.settingsSection = self.name.lower()
+        self.icon = None
+        self.weight = 0
+        self.status = PluginStatus.Inactive
+        # Set up logging
+        self.log = logging.getLogger(self.name)
+        self.previewController = plugin_helpers[u'preview']
+        self.liveController = plugin_helpers[u'live']
+        self.renderManager = plugin_helpers[u'render']
+        self.serviceManager = plugin_helpers[u'service']
+        self.settingsForm = plugin_helpers[u'settings form']
+        self.mediadock = plugin_helpers[u'toolbox']
+        self.pluginManager = plugin_helpers[u'pluginmanager']
+        self.formparent = plugin_helpers[u'formparent']
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'%s_add_service_item' % self.name),
+            self.processAddServiceEvent)
+
+    def checkPreConditions(self):
+        """
+        Provides the Plugin with a handle to check if it can be loaded.
+        Failing Preconditions does not stop a settings Tab being created
+
+        Returns True or False.
+        """
+        return True
+
+    def setStatus(self):
+        """
+        Sets the status of the plugin
+        """
+        self.status = QtCore.QSettings().value(
+            self.settingsSection + u'/status',
+            QtCore.QVariant(PluginStatus.Inactive)).toInt()[0]
+
+    def toggleStatus(self, new_status):
+        """
+        Changes the status of the plugin and remembers it
+        """
+        self.status = new_status
+        QtCore.QSettings().setValue(
+            self.settingsSection + u'/status', QtCore.QVariant(self.status))
+
+    def isActive(self):
+        """
+        Indicates if the plugin is active
+
+        Returns True or False.
+        """
+        return self.status == PluginStatus.Active
+
+    def getMediaManagerItem(self):
+        """
+        Construct a MediaManagerItem object with all the buttons and things
+        you need, and return it for integration into openlp.org.
+        """
+        pass
+
+    def addImportMenuItem(self, importMenu):
+        """
+        Create a menu item and add it to the "Import" menu.
+
+        ``importMenu``
+            The Import menu.
+        """
+        pass
+
+    def addExportMenuItem(self, exportMenu):
+        """
+        Create a menu item and add it to the "Export" menu.
+
+        ``exportMenu``
+            The Export menu
+        """
+        pass
+
+    def addToolsMenuItem(self, toolsMenu):
+        """
+        Create a menu item and add it to the "Tools" menu.
+
+        ``toolsMenu``
+            The Tools menu
+        """
+        pass
+
+    def getSettingsTab(self):
+        """
+        Create a tab for the settings window.
+        """
+        pass
+
+    def addToMenu(self, menubar):
+        """
+        Add menu items to the menu, given the menubar.
+
+        ``menubar``
+            The application's menu bar.
+        """
+        pass
+
+    def processAddServiceEvent(self, replace=False):
+        """
+        Generic Drag and drop handler triggered from service_manager.
+        """
+        log.debug(u'processAddServiceEvent event called for plugin %s' %
+            self.name)
+        if replace:
+            self.mediaItem.onAddEditClick()
+        else:
+            self.mediaItem.onAddClick()
+
+    def about(self):
+        """
+        Show a dialog when the user clicks on the 'About' button in the plugin
+        manager.
+        """
+        raise NotImplementedError(
+            u'Plugin.about needs to be defined by the plugin')
+
+    def initialise(self):
+        """
+        Called by the plugin Manager to initialise anything it needs.
+        """
+        if self.mediaItem:
+            self.mediaItem.initialise()
+        self.insertToolboxItem()
+
+    def finalise(self):
+        """
+        Called by the plugin Manager to cleanup things.
+        """
+        self.removeToolboxItem()
+
+    def removeToolboxItem(self):
+        """
+        Called by the plugin to remove toolbar
+        """
+        if self.mediaItem:
+            self.mediadock.remove_dock(self.name)
+        if self.settings_tab:
+            self.settingsForm.removeTab(self.name)
+
+    def insertToolboxItem(self):
+        """
+        Called by plugin to replace toolbar
+        """
+        if self.mediaItem:
+            self.mediadock.insert_dock(self.mediaItem, self.icon, self.weight)
+        if self.settings_tab:
+            self.settingsForm.insertTab(self.settings_tab, self.weight)
+
+    def usesTheme(self, theme):
+        """
+        Called to find out if a plugin is currently using a theme.
+
+        Returns True if the theme is being used, otherwise returns False.
+        """
+        return False
+
+    def renameTheme(self, oldTheme, newTheme):
+        """
+        Renames a theme a plugin is using making the plugin use the new name.
+
+        ``oldTheme``
+            The name of the theme the plugin should stop using.
+
+        ``newTheme``
+            The new name the plugin should now use.
+        """
+        pass
+     
+    def getString(self, name):
+        if name in self.strings:
+            return self.strings[name]
+        else:
+            # do something here?
+            return None
+
+    def set_plugin_strings(self):
+        """
+        Called to define all translatable texts of the plugin
+        """
+        self.name = u'Plugin'
+        self.name_lower = u'plugin'
+
+        self.strings = {}

=== modified file 'openlp/core/lib/settingstab.py'
--- openlp/core/lib/settingstab.py	2010-07-27 09:32:52 +0000
+++ openlp/core/lib/settingstab.py	2010-09-10 15:56:22 +0000
@@ -41,7 +41,7 @@
         QtGui.QWidget.__init__(self)
         self.tabTitle = title
         self.tabTitleVisible = None
-        self.settingsSection = self.tabTitle.lower()
+        self.settingsSection = self.tabTitle
         self.setupUi()
         self.retranslateUi()
         self.initialise()

=== modified file 'openlp/core/ui/mediadockmanager.py'
--- openlp/core/ui/mediadockmanager.py	2010-07-27 09:32:52 +0000
+++ openlp/core/ui/mediadockmanager.py	2010-09-10 15:56:22 +0000
@@ -1,83 +1,84 @@
-# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
-
-###############################################################################
-# OpenLP - Open Source Lyrics Projection                                      #
-# --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2010 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
-# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
-# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
-# Carsten Tinggaard, Frode Woldsund                                           #
-# --------------------------------------------------------------------------- #
-# 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                          #
-###############################################################################
-
-import logging
-
-log = logging.getLogger(__name__)
-
-class MediaDockManager(object):
-    """
-    Provide a repository for MediaManagerItems
-    """
-    def __init__(self, media_dock):
-        """
-        Initialise the media dock
-        """
-        self.media_dock = media_dock
-
-    def add_dock(self, media_item, icon, weight):
-        """
-        Add a MediaManagerItem to the dock
-
-        ``media_item``
-            The item to add to the dock
-
-        ``icon``
-            An icon for this dock item
-        """
-        log.info(u'Adding %s dock' % media_item.title)
-        self.media_dock.addItem(media_item, icon, media_item.title)
-
-    def insert_dock(self, media_item, icon, weight):
-        """
-        This should insert a dock item at a given location
-        This does not work as it gives a Segmentation error.
-        For now add at end of stack if not present
-        """
-        log.debug(u'Inserting %s dock' % media_item.title)
-        match = False
-        for dock_index in range(0, self.media_dock.count()):
-            if self.media_dock.widget(dock_index).settingsSection == \
-                media_item.title.lower():
-                match = True
-                break
-        if not match:
-            self.media_dock.addItem(media_item, icon, media_item.title)
-
-    def remove_dock(self, name):
-        """
-        Removes a MediaManagerItem from the dock
-
-        ``name``
-            The item to remove
-        """
-        log.debug(u'remove %s dock' % name)
-        for dock_index in range(0, self.media_dock.count()):
-            if self.media_dock.widget(dock_index):
-                if self.media_dock.widget(dock_index).settingsSection == \
-                    name.lower():
-                    self.media_dock.widget(dock_index).hide()
-                    self.media_dock.removeItem(dock_index)
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
+# Carsten Tinggaard, Frode Woldsund                                           #
+# --------------------------------------------------------------------------- #
+# 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                          #
+###############################################################################
+
+import logging
+
+log = logging.getLogger(__name__)
+
+class MediaDockManager(object):
+    """
+    Provide a repository for MediaManagerItems
+    """
+    def __init__(self, media_dock):
+        """
+        Initialise the media dock
+        """
+        self.media_dock = media_dock
+
+    def add_dock(self, media_item, icon, weight):
+        """
+        Add a MediaManagerItem to the dock
+
+        ``media_item``
+            The item to add to the dock
+
+        ``icon``
+            An icon for this dock item
+        """
+        log.info(u'Adding %s dock' % media_item.title)
+        self.media_dock.addItem(media_item, icon, media_item.title)
+
+    def insert_dock(self, media_item, icon, weight):
+        """
+        This should insert a dock item at a given location
+        This does not work as it gives a Segmentation error.
+        For now add at end of stack if not present
+        """
+        log.debug(u'Inserting %s dock' % media_item.title)
+        match = False
+        for dock_index in range(0, self.media_dock.count()):
+            if self.media_dock.widget(dock_index).settingsSection == \
+                media_item.parent.name_lower:
+                match = True
+                break
+        if not match:
+            self.media_dock.addItem(media_item, icon, media_item.title)
+
+    def remove_dock(self, name):
+        """
+        Removes a MediaManagerItem from the dock
+
+        ``name``
+            The item to remove
+        """
+        log.debug(u'remove %s dock' % name)
+        for dock_index in range(0, self.media_dock.count()):
+            if self.media_dock.widget(dock_index):
+                log.debug(u'%s %s' % (name,  self.media_dock.widget(dock_index).settingsSection))
+                if self.media_dock.widget(dock_index).settingsSection == \
+                    name:
+                    self.media_dock.widget(dock_index).hide()
+                    self.media_dock.removeItem(dock_index)

=== modified file 'openlp/core/ui/pluginform.py'
--- openlp/core/ui/pluginform.py	2010-09-08 19:36:39 +0000
+++ openlp/core/ui/pluginform.py	2010-09-10 15:56:22 +0000
@@ -1,3 +1,4 @@
+<<<<<<< TREE
 # -*- coding: utf-8 -*-
 # vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
 
@@ -139,3 +140,146 @@
                 translate('OpenLP.PluginForm', '%s (Disabled)'))
         self.pluginListWidget.currentItem().setText(
             status_text % self.activePlugin.name)
+=======
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
+# Carsten Tinggaard, Frode Woldsund                                           #
+# --------------------------------------------------------------------------- #
+# 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                          #
+###############################################################################
+
+import logging
+
+from PyQt4 import QtCore, QtGui
+
+from openlp.core.lib import PluginStatus, StringType, translate
+from plugindialog import Ui_PluginViewDialog
+
+log = logging.getLogger(__name__)
+
+class PluginForm(QtGui.QDialog, Ui_PluginViewDialog):
+
+    def __init__(self, parent=None):
+        QtGui.QDialog.__init__(self, parent)
+        self.parent = parent
+        self.activePlugin = None
+        self.programaticChange = False
+        self.setupUi(self)
+        self.load()
+        self._clearDetails()
+        # Right, now let's put some signals and slots together!
+        QtCore.QObject.connect(
+            self.pluginListWidget,
+            QtCore.SIGNAL(u'itemSelectionChanged()'),
+            self.onPluginListWidgetSelectionChanged)
+        QtCore.QObject.connect(
+            self.statusComboBox,
+            QtCore.SIGNAL(u'currentIndexChanged(int)'),
+            self.onStatusComboBoxChanged)
+
+    def load(self):
+        """
+        Load the plugin details into the screen
+        """
+        self.pluginListWidget.clear()
+        for plugin in self.parent.plugin_manager.plugins:
+            item = QtGui.QListWidgetItem(self.pluginListWidget)
+            # We do this just to make 100% sure the status is an integer as
+            # sometimes when it's loaded from the config, it isn't cast to int.
+            plugin.status = int(plugin.status)
+            # Set the little status text in brackets next to the plugin name.
+            status_text = unicode(
+                translate('OpenLP.PluginForm', '%s (Inactive)'))
+            if plugin.status == PluginStatus.Active:
+                status_text = unicode(
+                    translate('OpenLP.PluginForm', '%s (Active)'))
+            elif plugin.status == PluginStatus.Inactive:
+                status_text = unicode(
+                    translate('OpenLP.PluginForm', '%s (Inactive)'))
+            elif plugin.status == PluginStatus.Disabled:
+                status_text = unicode(
+                    translate('OpenLP.PluginForm', '%s (Disabled)'))
+            nameString = plugin.getString(StringType.Name)
+            item.setText(status_text % nameString[u'plural'])
+            # If the plugin has an icon, set it!
+            if plugin.icon:
+                item.setIcon(plugin.icon)
+            self.pluginListWidget.addItem(item)
+
+    def _clearDetails(self):
+        self.statusComboBox.setCurrentIndex(-1)
+        self.versionNumberLabel.setText(u'')
+        self.aboutTextBrowser.setHtml(u'')
+        self.statusComboBox.setEnabled(False)
+
+    def _setDetails(self):
+        log.debug('PluginStatus: %s', str(self.activePlugin.status))
+        self.versionNumberLabel.setText(self.activePlugin.version)
+        self.aboutTextBrowser.setHtml(self.activePlugin.about())
+        self.programaticChange = True
+        status = 1
+        if self.activePlugin.status == PluginStatus.Active:
+            status = 0
+        self.statusComboBox.setCurrentIndex(status)
+        self.statusComboBox.setEnabled(True)
+        self.programaticChange = False
+
+    def onPluginListWidgetSelectionChanged(self):
+        if self.pluginListWidget.currentItem() is None:
+            self._clearDetails()
+            return
+        plugin_name_more = self.pluginListWidget.currentItem().text().split(u' ')[0]
+        self.activePlugin = None
+        for plugin in self.parent.plugin_manager.plugins:
+            nameString = plugin.getString(StringType.Name)
+            if nameString[u'plural'] == plugin_name_more:
+                self.activePlugin = plugin
+                break
+        if self.activePlugin:
+            self._setDetails()
+        else:
+            self._clearDetails()
+
+    def onStatusComboBoxChanged(self, status):
+        if self.programaticChange:
+            return
+        if status == 0:
+            self.activePlugin.toggleStatus(PluginStatus.Active)
+            self.activePlugin.initialise()
+        else:
+            self.activePlugin.toggleStatus(PluginStatus.Inactive)
+            self.activePlugin.finalise()
+        status_text = unicode(
+            translate('OpenLP.PluginForm', '%s (Inactive)'))
+        if self.activePlugin.status == PluginStatus.Active:
+            status_text = unicode(
+                translate('OpenLP.PluginForm', '%s (Active)'))
+        elif self.activePlugin.status == PluginStatus.Inactive:
+            status_text = unicode(
+                translate('OpenLP.PluginForm', '%s (Inactive)'))
+        elif self.activePlugin.status == PluginStatus.Disabled:
+            status_text = unicode(
+                translate('OpenLP.PluginForm', '%s (Disabled)'))
+        nameString = self.activePlugin.getString(StringType.Name)
+        self.pluginListWidget.currentItem().setText(
+            status_text % nameString[u'plural'])
+>>>>>>> MERGE-SOURCE

=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py	2010-09-08 18:12:16 +0000
+++ openlp/core/ui/slidecontroller.py	2010-09-10 15:56:22 +0000
@@ -1,982 +1,988 @@
-# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
-
-###############################################################################
-# OpenLP - Open Source Lyrics Projection                                      #
-# --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2010 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
-# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
-# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
-# Carsten Tinggaard, Frode Woldsund                                           #
-# --------------------------------------------------------------------------- #
-# 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                          #
-###############################################################################
-
-import logging
-import os
-
-from PyQt4 import QtCore, QtGui
-from PyQt4.phonon import Phonon
-
-from openlp.core.ui import HideMode, MainDisplay
-from openlp.core.lib import OpenLPToolbar, Receiver, resize_image, \
-    ItemCapabilities, translate
-
-log = logging.getLogger(__name__)
-
-class SlideList(QtGui.QTableWidget):
-    """
-    Customised version of QTableWidget which can respond to keyboard
-    events.
-    """
-    def __init__(self, parent=None, name=None):
-        QtGui.QTableWidget.__init__(self, parent.Controller)
-        self.parent = parent
-        self.hotkeyMap = {
-           QtCore.Qt.Key_Return: 'servicemanager_next_item',
-           QtCore.Qt.Key_Space: 'slidecontroller_live_next_noloop',
-           QtCore.Qt.Key_Enter: 'slidecontroller_live_next_noloop',
-           QtCore.Qt.Key_0: 'servicemanager_next_item',
-           QtCore.Qt.Key_Backspace: 'slidecontroller_live_previous_noloop'}
-
-    def keyPressEvent(self, event):
-        if isinstance(event, QtGui.QKeyEvent):
-            #here accept the event and do something
-            if event.key() == QtCore.Qt.Key_Up:
-                self.parent.onSlideSelectedPrevious()
-                event.accept()
-            elif event.key() == QtCore.Qt.Key_Down:
-                self.parent.onSlideSelectedNext()
-                event.accept()
-            elif event.key() == QtCore.Qt.Key_PageUp:
-                self.parent.onSlideSelectedFirst()
-                event.accept()
-            elif event.key() == QtCore.Qt.Key_PageDown:
-                self.parent.onSlideSelectedLast()
-                event.accept()
-            elif event.key() in self.hotkeyMap and self.parent.isLive:
-                Receiver.send_message(self.hotkeyMap[event.key()])
-                event.accept()
-            event.ignore()
-        else:
-            event.ignore()
-
-class SlideController(QtGui.QWidget):
-    """
-    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, settingsmanager, screens, isLive=False):
-        """
-        Set up the Slide Controller.
-        """
-        QtGui.QWidget.__init__(self, parent)
-        self.settingsmanager = settingsmanager
-        self.isLive = isLive
-        self.parent = parent
-        self.screens = screens
-        self.ratio = float(self.screens.current[u'size'].width()) / \
-            float(self.screens.current[u'size'].height())
-        self.display = MainDisplay(self, screens, isLive)
-        self.loopList = [
-            u'Start Loop',
-            u'Loop Separator',
-            u'Image SpinBox'
-        ]
-        self.songEditList = [
-            u'Edit Song',
-        ]
-        self.volume = 10
-        self.timer_id = 0
-        self.songEdit = False
-        self.selectedRow = 0
-        self.serviceItem = None
-        self.alertTab = None
-        self.Panel = QtGui.QWidget(parent.ControlSplitter)
-        self.slideList = {}
-        # Layout for holding panel
-        self.PanelLayout = QtGui.QVBoxLayout(self.Panel)
-        self.PanelLayout.setSpacing(0)
-        self.PanelLayout.setMargin(0)
-        # Type label for the top of the slide controller
-        self.TypeLabel = QtGui.QLabel(self.Panel)
-        if self.isLive:
-            self.TypeLabel.setText(translate('OpenLP.SlideController', 'Live'))
-            self.split = 1
-            self.typePrefix = u'live'
-        else:
-            self.TypeLabel.setText(translate('OpenLP.SlideController',
-                'Preview'))
-            self.split = 0
-            self.typePrefix = u'preview'
-        self.TypeLabel.setStyleSheet(u'font-weight: bold; font-size: 12pt;')
-        self.TypeLabel.setAlignment(QtCore.Qt.AlignCenter)
-        self.PanelLayout.addWidget(self.TypeLabel)
-        # Splitter
-        self.Splitter = QtGui.QSplitter(self.Panel)
-        self.Splitter.setOrientation(QtCore.Qt.Vertical)
-        self.Splitter.setOpaqueResize(False)
-        self.PanelLayout.addWidget(self.Splitter)
-        # Actual controller section
-        self.Controller = QtGui.QWidget(self.Splitter)
-        self.Controller.setGeometry(QtCore.QRect(0, 0, 100, 536))
-        self.Controller.setSizePolicy(
-            QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred,
-            QtGui.QSizePolicy.Maximum))
-        self.ControllerLayout = QtGui.QVBoxLayout(self.Controller)
-        self.ControllerLayout.setSpacing(0)
-        self.ControllerLayout.setMargin(0)
-        # Controller list view
-        self.PreviewListWidget = SlideList(self)
-        self.PreviewListWidget.setColumnCount(1)
-        self.PreviewListWidget.horizontalHeader().setVisible(False)
-        self.PreviewListWidget.setColumnWidth(
-            0, self.Controller.width())
-        self.PreviewListWidget.isLive = self.isLive
-        self.PreviewListWidget.setObjectName(u'PreviewListWidget')
-        self.PreviewListWidget.setSelectionBehavior(1)
-        self.PreviewListWidget.setEditTriggers(
-            QtGui.QAbstractItemView.NoEditTriggers)
-        self.PreviewListWidget.setHorizontalScrollBarPolicy(
-            QtCore.Qt.ScrollBarAlwaysOff)
-        self.PreviewListWidget.setAlternatingRowColors(True)
-        self.ControllerLayout.addWidget(self.PreviewListWidget)
-        # Build the full toolbar
-        self.Toolbar = OpenLPToolbar(self)
-        sizeToolbarPolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed,
-            QtGui.QSizePolicy.Fixed)
-        sizeToolbarPolicy.setHorizontalStretch(0)
-        sizeToolbarPolicy.setVerticalStretch(0)
-        sizeToolbarPolicy.setHeightForWidth(
-            self.Toolbar.sizePolicy().hasHeightForWidth())
-        self.Toolbar.setSizePolicy(sizeToolbarPolicy)
-        self.Toolbar.addToolbarButton(
-            u'Previous Slide', u':/slides/slide_previous.png',
-            translate('OpenLP.SlideController', 'Move to previous'),
-            self.onSlideSelectedPrevious)
-        self.Toolbar.addToolbarButton(
-            u'Next Slide', u':/slides/slide_next.png',
-            translate('OpenLP.SlideController', 'Move to next'),
-            self.onSlideSelectedNext)
-        if self.isLive:
-            self.Toolbar.addToolbarSeparator(u'Close Separator')
-            self.HideMenu = QtGui.QToolButton(self.Toolbar)
-            self.HideMenu.setText(translate('OpenLP.SlideController', 'Hide'))
-            self.HideMenu.setPopupMode(QtGui.QToolButton.MenuButtonPopup)
-            self.Toolbar.addToolbarWidget(u'Hide Menu', self.HideMenu)
-            self.HideMenu.setMenu(QtGui.QMenu(
-                translate('OpenLP.SlideController', 'Hide'), self.Toolbar))
-            self.BlankScreen = QtGui.QAction(QtGui.QIcon(
-                u':/slides/slide_blank.png'), u'Blank Screen', self.HideMenu)
-            self.BlankScreen.setCheckable(True)
-            QtCore.QObject.connect(self.BlankScreen,
-                QtCore.SIGNAL("triggered(bool)"), self.onBlankDisplay)
-            self.ThemeScreen = QtGui.QAction(QtGui.QIcon(
-                u':/slides/slide_theme.png'), u'Blank to Theme', self.HideMenu)
-            self.ThemeScreen.setCheckable(True)
-            QtCore.QObject.connect(self.ThemeScreen,
-                QtCore.SIGNAL("triggered(bool)"), self.onThemeDisplay)
-            if self.screens.display_count > 1:
-                self.DesktopScreen = QtGui.QAction(QtGui.QIcon(
-                    u':/slides/slide_desktop.png'), u'Show Desktop',
-                    self.HideMenu)
-                self.DesktopScreen.setCheckable(True)
-                QtCore.QObject.connect(self.DesktopScreen,
-                    QtCore.SIGNAL("triggered(bool)"), self.onHideDisplay)
-            self.HideMenu.setDefaultAction(self.BlankScreen)
-            self.HideMenu.menu().addAction(self.BlankScreen)
-            self.HideMenu.menu().addAction(self.ThemeScreen)
-            if self.screens.display_count > 1:
-                self.HideMenu.menu().addAction(self.DesktopScreen)
-        if not self.isLive:
-            self.Toolbar.addToolbarSeparator(u'Close Separator')
-            self.Toolbar.addToolbarButton(
-                u'Go Live', u':/general/general_live.png',
-                translate('OpenLP.SlideController', 'Move to live'),
-                self.onGoLive)
-            self.Toolbar.addToolbarSeparator(u'Close Separator')
-            self.Toolbar.addToolbarButton(
-                u'Edit Song', u':/general/general_edit.png',
-                translate('OpenLP.SlideController', 'Edit and re-preview Song'),
-                self.onEditSong)
-        if isLive:
-            self.Toolbar.addToolbarSeparator(u'Loop Separator')
-            self.Toolbar.addToolbarButton(
-                u'Start Loop', u':/media/media_time.png',
-                translate('OpenLP.SlideController', 'Start continuous loop'),
-                self.onStartLoop)
-            self.Toolbar.addToolbarButton(
-                u'Stop Loop', u':/media/media_stop.png',
-                translate('OpenLP.SlideController', 'Stop continuous loop'),
-                self.onStopLoop)
-            self.DelaySpinBox = QtGui.QSpinBox()
-            self.DelaySpinBox.setMinimum(1)
-            self.DelaySpinBox.setMaximum(180)
-            self.Toolbar.addToolbarWidget(
-                u'Image SpinBox', self.DelaySpinBox)
-            self.DelaySpinBox.setSuffix(translate('OpenLP.SlideController',
-                's'))
-            self.DelaySpinBox.setToolTip(translate('OpenLP.SlideController',
-                'Delay between slides in seconds'))
-        self.ControllerLayout.addWidget(self.Toolbar)
-        # Build a Media ToolBar
-        self.Mediabar = OpenLPToolbar(self)
-        self.Mediabar.addToolbarButton(
-            u'Media Start', u':/slides/media_playback_start.png',
-            translate('OpenLP.SlideController', 'Start playing media'),
-            self.onMediaPlay)
-        self.Mediabar.addToolbarButton(
-            u'Media Pause', u':/slides/media_playback_pause.png',
-            translate('OpenLP.SlideController', 'Start playing media'),
-            self.onMediaPause)
-        self.Mediabar.addToolbarButton(
-            u'Media Stop', u':/slides/media_playback_stop.png',
-            translate('OpenLP.SlideController', 'Start playing media'),
-            self.onMediaStop)
-        if not self.isLive:
-            self.seekSlider = Phonon.SeekSlider()
-            self.seekSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
-            self.seekSlider.setObjectName(u'seekSlider')
-            self.Mediabar.addToolbarWidget(
-                u'Seek Slider', self.seekSlider)
-            self.volumeSlider = Phonon.VolumeSlider()
-            self.volumeSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
-            self.volumeSlider.setObjectName(u'volumeSlider')
-            self.Mediabar.addToolbarWidget(u'Audio Volume', self.volumeSlider)
-        else:
-            self.volumeSlider = QtGui.QSlider(QtCore.Qt.Horizontal)
-            self.volumeSlider.setTickInterval(1)
-            self.volumeSlider.setTickPosition(QtGui.QSlider.TicksAbove)
-            self.volumeSlider.setMinimum(0)
-            self.volumeSlider.setMaximum(10)
-            self.volumeSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
-            self.volumeSlider.setObjectName(u'volumeSlider')
-            self.Mediabar.addToolbarWidget(u'Audio Volume', self.volumeSlider)
-        self.ControllerLayout.addWidget(self.Mediabar)
-        # Build the Song Toolbar
-        if isLive:
-            self.SongMenu = QtGui.QToolButton(self.Toolbar)
-            self.SongMenu.setText(translate('OpenLP.SlideController',
-                'Go to'))
-            self.SongMenu.setPopupMode(QtGui.QToolButton.InstantPopup)
-            self.Toolbar.addToolbarWidget(u'Song Menu', self.SongMenu)
-            self.SongMenu.setMenu(QtGui.QMenu(
-                translate('OpenLP.SlideController', 'Go to'),
-                self.Toolbar))
-            self.Toolbar.makeWidgetsInvisible([u'Song Menu'])
-        # Screen preview area
-        self.PreviewFrame = QtGui.QFrame(self.Splitter)
-        self.PreviewFrame.setGeometry(QtCore.QRect(0, 0, 300, 225))
-        self.PreviewFrame.setSizePolicy(QtGui.QSizePolicy(
-            QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum,
-            QtGui.QSizePolicy.Label))
-        self.PreviewFrame.setFrameShape(QtGui.QFrame.StyledPanel)
-        self.PreviewFrame.setFrameShadow(QtGui.QFrame.Sunken)
-        self.PreviewFrame.setObjectName(u'PreviewFrame')
-        self.grid = QtGui.QGridLayout(self.PreviewFrame)
-        self.grid.setMargin(8)
-        self.grid.setObjectName(u'grid')
-        self.SlideLayout = QtGui.QVBoxLayout()
-        self.SlideLayout.setSpacing(0)
-        self.SlideLayout.setMargin(0)
-        self.SlideLayout.setObjectName(u'SlideLayout')
-        self.mediaObject = Phonon.MediaObject(self)
-        self.video = Phonon.VideoWidget()
-        self.video.setVisible(False)
-        self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self.mediaObject)
-        Phonon.createPath(self.mediaObject, self.video)
-        Phonon.createPath(self.mediaObject, self.audio)
-        if not self.isLive:
-            self.video.setGeometry(QtCore.QRect(0, 0, 300, 225))
-            self.video.setVisible(False)
-        self.SlideLayout.insertWidget(0, self.video)
-        # Actual preview screen
-        self.SlidePreview = QtGui.QLabel(self)
-        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed,
-            QtGui.QSizePolicy.Fixed)
-        sizePolicy.setHorizontalStretch(0)
-        sizePolicy.setVerticalStretch(0)
-        sizePolicy.setHeightForWidth(
-            self.SlidePreview.sizePolicy().hasHeightForWidth())
-        self.SlidePreview.setSizePolicy(sizePolicy)
-        self.SlidePreview.setFixedSize(
-            QtCore.QSize(self.settingsmanager.slidecontroller_image,
-            self.settingsmanager.slidecontroller_image / self.ratio))
-        self.SlidePreview.setFrameShape(QtGui.QFrame.Box)
-        self.SlidePreview.setFrameShadow(QtGui.QFrame.Plain)
-        self.SlidePreview.setLineWidth(1)
-        self.SlidePreview.setScaledContents(True)
-        self.SlidePreview.setObjectName(u'SlidePreview')
-        self.SlideLayout.insertWidget(0, self.SlidePreview)
-        self.grid.addLayout(self.SlideLayout, 0, 0, 1, 1)
-        # Signals
-        QtCore.QObject.connect(self.PreviewListWidget,
-            QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected)
-        if not self.isLive:
-            if QtCore.QSettings().value(u'advanced/double click live',
-                QtCore.QVariant(False)).toBool():
-                QtCore.QObject.connect(self.PreviewListWidget,
-                    QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.onGoLive)
-        if isLive:
-            QtCore.QObject.connect(Receiver.get_receiver(),
-                QtCore.SIGNAL(u'slidecontroller_live_spin_delay'),
-                    self.receiveSpinDelay)
-        if isLive:
-            self.Toolbar.makeWidgetsInvisible(self.loopList)
-            self.Toolbar.actions[u'Stop Loop'].setVisible(False)
-        else:
-            self.Toolbar.makeWidgetsInvisible(self.songEditList)
-        self.Mediabar.setVisible(False)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'slidecontroller_%s_stop_loop' % self.typePrefix),
-            self.onStopLoop)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'slidecontroller_%s_first' % self.typePrefix),
-            self.onSlideSelectedFirst)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'slidecontroller_%s_next' % self.typePrefix),
-            self.onSlideSelectedNext)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'slidecontroller_%s_previous' % self.typePrefix),
-            self.onSlideSelectedPrevious)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'slidecontroller_%s_next_noloop' % self.typePrefix),
-            self.onSlideSelectedNextNoloop)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'slidecontroller_%s_previous_noloop' %
-            self.typePrefix),
-            self.onSlideSelectedPreviousNoloop)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'slidecontroller_%s_last' % self.typePrefix),
-            self.onSlideSelectedLast)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'slidecontroller_%s_change' % self.typePrefix),
-            self.onSlideChange)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'slidecontroller_%s_set' % self.typePrefix),
-            self.onSlideSelectedIndex)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'slidecontroller_%s_blank' % self.typePrefix),
-            self.onSlideBlank)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'slidecontroller_%s_unblank' % self.typePrefix),
-            self.onSlideUnblank)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'slidecontroller_%s_text_request' % self.typePrefix),
-            self.onTextRequest)
-        QtCore.QObject.connect(self.Splitter,
-            QtCore.SIGNAL(u'splitterMoved(int, int)'), self.trackSplitter)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'config_updated'), self.refreshServiceItem)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'config_screen_changed'), self.screenSizeChanged)
-        if self.isLive:
-            QtCore.QObject.connect(self.volumeSlider,
-                QtCore.SIGNAL(u'sliderReleased()'), self.mediaVolume)
-
-    def screenSizeChanged(self):
-        """
-        Settings dialog has changed the screen size of adjust output and
-        screen previews
-        """
-        log.debug(u'screenSizeChanged live = %s' % self.isLive)
-        # rebuild display as screen size changed
-        self.display = MainDisplay(self, self.screens, self.isLive)
-        self.display.alertTab = self.alertTab
-        self.ratio = float(self.screens.current[u'size'].width()) / \
-            float(self.screens.current[u'size'].height())
-        self.display.setup()
-        self.SlidePreview.setFixedSize(
-            QtCore.QSize(self.settingsmanager.slidecontroller_image,
-            self.settingsmanager.slidecontroller_image / self.ratio))
-
-    def widthChanged(self):
-        """
-        Handle changes of width from the splitter between the live and preview
-        controller.  Event only issues when changes have finished
-        """
-        log.debug(u'widthChanged live = %s' % self.isLive)
-        width = self.parent.ControlSplitter.sizes()[self.split]
-        height = width * self.parent.RenderManager.screen_ratio
-        self.PreviewListWidget.setColumnWidth(0, width)
-        # Sort out image heights (Songs, bibles excluded)
-        if self.serviceItem and not self.serviceItem.is_text():
-            for framenumber in range(len(self.serviceItem.get_frames())):
-                self.PreviewListWidget.setRowHeight(framenumber, height)
-
-    def trackSplitter(self, tab, pos):
-        """
-        Splitter between the slide list and the preview panel
-        """
-        pass
-
-    def onSongBarHandler(self):
-        request = unicode(self.sender().text())
-        slideno = self.slideList[request]
-        if slideno > self.PreviewListWidget.rowCount():
-            self.PreviewListWidget.selectRow(self.PreviewListWidget.rowCount())
-        else:
-            self.PreviewListWidget.selectRow(slideno)
-        self.onSlideSelected()
-
-    def receiveSpinDelay(self, value):
-        self.DelaySpinBox.setValue(int(value))
-
-    def enableToolBar(self, item):
-        """
-        Allows the toolbars to be reconfigured based on Controller Type
-        and ServiceItem Type
-        """
-        if self.isLive:
-            self.enableLiveToolBar(item)
-        else:
-            self.enablePreviewToolBar(item)
-
-    def enableLiveToolBar(self, item):
-        """
-        Allows the live toolbar to be customised
-        """
-        self.Toolbar.setVisible(True)
-        self.Mediabar.setVisible(False)
-        self.Toolbar.makeWidgetsInvisible([u'Song Menu'])
-        self.Toolbar.makeWidgetsInvisible(self.loopList)
-        self.Toolbar.actions[u'Stop Loop'].setVisible(False)
-        if item.is_text():
-            if QtCore.QSettings().value(
-                self.parent.songsSettingsSection + u'/show songbar',
-                QtCore.QVariant(True)).toBool() and len(self.slideList) > 0:
-                self.Toolbar.makeWidgetsVisible([u'Song Menu'])
-        if item.is_capable(ItemCapabilities.AllowsLoop) and \
-            len(item.get_frames()) > 1:
-            self.Toolbar.makeWidgetsVisible(self.loopList)
-        if item.is_media():
-            self.Toolbar.setVisible(False)
-            self.Mediabar.setVisible(True)
-
-    def enablePreviewToolBar(self, item):
-        """
-        Allows the Preview toolbar to be customised
-        """
-        self.Toolbar.setVisible(True)
-        self.Mediabar.setVisible(False)
-        self.Toolbar.makeWidgetsInvisible(self.songEditList)
-        if item.is_capable(ItemCapabilities.AllowsEdit) and item.from_plugin:
-            self.Toolbar.makeWidgetsVisible(self.songEditList)
-        elif item.is_media():
-            self.Toolbar.setVisible(False)
-            self.Mediabar.setVisible(True)
-            self.volumeSlider.setAudioOutput(self.audio)
-
-    def refreshServiceItem(self):
-        """
-        Method to update the service item if the screen has changed
-        """
-        log.debug(u'refreshServiceItem live = %s' % self.isLive)
-        if self.serviceItem:
-            if self.serviceItem.is_text() or self.serviceItem.is_image():
-                item = self.serviceItem
-                item.render()
-                self._processItem(item, self.selectedRow)
-
-    def addServiceItem(self, item):
-        """
-        Method to install the service item into the controller
-        Called by plugins
-        """
-        log.debug(u'addServiceItem live = %s' % self.isLive)
-        item.render()
-        slideno = 0
-        if self.songEdit:
-            slideno = self.selectedRow
-        self.songEdit = False
-        self._processItem(item, slideno)
-
-    def replaceServiceManagerItem(self, item):
-        """
-        Replacement item following a remote edit
-        """
-        if item.__eq__(self.serviceItem):
-            self._processItem(item, self.PreviewListWidget.currentRow())
-
-    def addServiceManagerItem(self, item, slideno):
-        """
-        Method to install the service item into the controller and
-        request the correct toolbar for the plugin.
-        Called by ServiceManager
-        """
-        log.debug(u'addServiceManagerItem live = %s' % self.isLive)
-        # If service item is the same as the current on only change slide
-        if item.__eq__(self.serviceItem):
-            self.PreviewListWidget.selectRow(slideno)
-            self.onSlideSelected()
-            return
-        self._processItem(item, slideno)
-
-    def _processItem(self, serviceItem, slideno):
-        """
-        Loads a ServiceItem into the system from ServiceManager
-        Display the slide number passed
-        """
-        log.debug(u'processManagerItem live = %s' % self.isLive)
-        self.onStopLoop()
-        # If old item was a command tell it to stop
-        if self.serviceItem:
-            if self.serviceItem.is_command():
-                Receiver.send_message(u'%s_stop' %
-                    self.serviceItem.name.lower(), [serviceItem, self.isLive])
-            if self.serviceItem.is_media():
-                self.onMediaStop()
-        if self.isLive:
-            blanked = self.BlankScreen.isChecked()
-        else:
-            blanked = False
-        Receiver.send_message(u'%s_start' % serviceItem.name.lower(),
-            [serviceItem, self.isLive, blanked, slideno])
-        self.slideList = {}
-        width = self.parent.ControlSplitter.sizes()[self.split]
-        # Set pointing cursor when we have somthing to point at
-        self.PreviewListWidget.setCursor(QtCore.Qt.PointingHandCursor)
-        self.serviceItem = serviceItem
-        self.PreviewListWidget.clear()
-        self.PreviewListWidget.setRowCount(0)
-        self.PreviewListWidget.setColumnWidth(0, width)
-        if self.isLive:
-            self.SongMenu.menu().clear()
-        row = 0
-        text = []
-        for framenumber, frame in enumerate(self.serviceItem.get_frames()):
-            self.PreviewListWidget.setRowCount(
-                self.PreviewListWidget.rowCount() + 1)
-            item = QtGui.QTableWidgetItem()
-            slideHeight = 0
-            if self.serviceItem.is_text():
-                if frame[u'verseTag']:
-                    bits = frame[u'verseTag'].split(u':')
-                    tag = u'%s\n%s' % (bits[0][0], bits[1][0:] )
-                    tag1 = u'%s%s' % (bits[0][0], bits[1][0:] )
-                    row = tag
-                    if self.isLive:
-                        if tag1 not in self.slideList:
-                            self.slideList[tag1] = framenumber
-                            self.SongMenu.menu().addAction(tag1,
-                                self.onSongBarHandler)
-                else:
-                    row += 1
-                item.setText(frame[u'text'])
-            else:
-                label = QtGui.QLabel()
-                label.setMargin(4)
-                pixmap = resize_image(frame[u'image'],
-                                      self.parent.RenderManager.width,
-                                      self.parent.RenderManager.height)
-                label.setScaledContents(True)
-                label.setPixmap(QtGui.QPixmap.fromImage(pixmap))
-                self.PreviewListWidget.setCellWidget(framenumber, 0, label)
-                slideHeight = width * self.parent.RenderManager.screen_ratio
-                row += 1
-            text.append(unicode(row))
-            self.PreviewListWidget.setItem(framenumber, 0, item)
-            if slideHeight != 0:
-                self.PreviewListWidget.setRowHeight(framenumber, slideHeight)
-        self.PreviewListWidget.setVerticalHeaderLabels(text)
-        if self.serviceItem.is_text():
-            self.PreviewListWidget.resizeRowsToContents()
-        self.PreviewListWidget.setColumnWidth(0,
-            self.PreviewListWidget.viewport().size().width())
-        if slideno > self.PreviewListWidget.rowCount():
-            self.PreviewListWidget.selectRow(self.PreviewListWidget.rowCount())
-        else:
-            self.PreviewListWidget.selectRow(slideno)
-        self.enableToolBar(serviceItem)
-        # Pass to display for viewing
-        self.display.buildHtml(self.serviceItem)
-        if serviceItem.is_media():
-            self.onMediaStart(serviceItem)
-        self.onSlideSelected()
-        self.PreviewListWidget.setFocus()
-        Receiver.send_message(u'slidecontroller_%s_started' % self.typePrefix,
-            [serviceItem])
-
-    def onTextRequest(self):
-        """
-        Return the text for the current item in controller
-        """
-        data = []
-        if self.serviceItem:
-            for framenumber, frame in enumerate(self.serviceItem.get_frames()):
-                dataItem = {}
-                if self.serviceItem.is_text():
-                    dataItem[u'tag'] = unicode(frame[u'verseTag'])
-                    dataItem[u'text'] = unicode(frame[u'html'])
-                else:
-                    dataItem[u'tag'] = unicode(framenumber)
-                    dataItem[u'text'] = u''
-                dataItem[u'selected'] = \
-                    (self.PreviewListWidget.currentRow() == framenumber)
-                data.append(dataItem)
-        Receiver.send_message(u'slidecontroller_%s_text_response'
-            % self.typePrefix, data)
-
-    # Screen event methods
-    def onSlideSelectedFirst(self):
-        """
-        Go to the first slide.
-        """
-        if not self.serviceItem:
-            return
-        Receiver.send_message(u'%s_first' % self.serviceItem.name.lower(),
-            [self.serviceItem, self.isLive])
-        if self.serviceItem.is_command():
-            self.updatePreview()
-        else:
-            self.PreviewListWidget.selectRow(0)
-            self.onSlideSelected()
-
-    def onSlideSelectedIndex(self, message):
-        """
-        Go to the requested slide
-        """
-        index = int(message[0])
-        if not self.serviceItem:
-            return
-        Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
-            [self.serviceItem, self.isLive, index])
-        if self.serviceItem.is_command():
-            self.updatePreview()
-        else:
-            self.PreviewListWidget.selectRow(index)
-            self.onSlideSelected()
-
-    def mainDisplaySetBackground(self):
-        """
-        Allow the main display to blank the main display at startup time
-        """
-        log.debug(u'mainDisplaySetBackground live = %s' % self.isLive)
-        if not self.display.primary:
-            self.onHideDisplay(True)
-
-    def onSlideBlank(self):
-        """
-        Handle the slidecontroller blank event
-        """
-        self.onBlankDisplay(True)
-
-    def onSlideUnblank(self):
-        """
-        Handle the slidecontroller unblank event
-        """
-        self.onBlankDisplay(False)
-
-    def onBlankDisplay(self, checked):
-        """
-        Handle the blank screen button actions
-        """
-        log.debug(u'onBlankDisplay %s' % checked)
-        self.HideMenu.setDefaultAction(self.BlankScreen)
-        self.BlankScreen.setChecked(checked)
-        self.ThemeScreen.setChecked(False)
-        if self.screens.display_count > 1:
-            self.DesktopScreen.setChecked(False)
-        QtCore.QSettings().setValue(
-            self.parent.generalSettingsSection + u'/screen blank',
-            QtCore.QVariant(checked))
-        if checked:
-            Receiver.send_message(u'maindisplay_hide', HideMode.Blank)
-        else:
-            Receiver.send_message(u'maindisplay_show')
-        self.blankPlugin(checked)
-
-    def onThemeDisplay(self, checked):
-        """
-        Handle the Theme screen button
-        """
-        log.debug(u'onThemeDisplay %s' % checked)
-        self.HideMenu.setDefaultAction(self.ThemeScreen)
-        self.BlankScreen.setChecked(False)
-        self.ThemeScreen.setChecked(checked)
-        if self.screens.display_count > 1:
-            self.DesktopScreen.setChecked(False)
-        if checked:
-            Receiver.send_message(u'maindisplay_hide', HideMode.Theme)
-        else:
-            Receiver.send_message(u'maindisplay_show')
-        self.blankPlugin(checked)
-
-    def onHideDisplay(self, checked):
-        """
-        Handle the Hide screen button
-        """
-        log.debug(u'onHideDisplay %s' % checked)
-        self.HideMenu.setDefaultAction(self.DesktopScreen)
-        self.BlankScreen.setChecked(False)
-        self.ThemeScreen.setChecked(False)
-        if self.screens.display_count > 1:
-            self.DesktopScreen.setChecked(checked)
-        if checked:
-            Receiver.send_message(u'maindisplay_hide', HideMode.Screen)
-        else:
-            Receiver.send_message(u'maindisplay_show')
-        self.hidePlugin(checked)
-
-    def blankPlugin(self, blank):
-        """
-        Blank the display screen within a plugin if required.
-        """
-        log.debug(u'blankPlugin %s ', blank)
-        if self.serviceItem is not None:
-            if blank:
-                Receiver.send_message(u'%s_blank'
-                    % self.serviceItem.name.lower(),
-                    [self.serviceItem, self.isLive])
-            else:
-                Receiver.send_message(u'%s_unblank'
-                    % self.serviceItem.name.lower(),
-                    [self.serviceItem, self.isLive])
-
-    def hidePlugin(self, hide):
-        """
-        Tell the plugin to hide the display screen.
-        """
-        log.debug(u'hidePlugin %s ', hide)
-        if self.serviceItem is not None:
-            if hide:
-                Receiver.send_message(u'%s_hide'
-                    % self.serviceItem.name.lower(),
-                    [self.serviceItem, self.isLive])
-            else:
-                Receiver.send_message(u'%s_unblank'
-                    % self.serviceItem.name.lower(),
-                    [self.serviceItem, self.isLive])
-
-    def onSlideSelected(self):
-        """
-        Generate the preview when you click on a slide.
-        if this is the Live Controller also display on the screen
-        """
-        row = self.PreviewListWidget.currentRow()
-        self.selectedRow = 0
-        if row > -1 and row < self.PreviewListWidget.rowCount():
-            Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
-                [self.serviceItem, self.isLive, row])
-            if self.serviceItem.is_command() and self.isLive:
-                self.updatePreview()
-            else:
-                frame, raw_html = self.serviceItem.get_rendered_frame(row)
-                if self.serviceItem.is_text():
-                    frame = self.display.text(raw_html)
-                else:
-                    self.display.image(frame)
-                if isinstance(frame, QtGui.QImage):
-                    self.SlidePreview.setPixmap(QtGui.QPixmap.fromImage(frame))
-                else:
-                    self.SlidePreview.setPixmap(QtGui.QPixmap(frame))
-            self.selectedRow = row
-        Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix,
-            row)
-
-    def onSlideChange(self, row):
-        """
-        The slide has been changed. Update the slidecontroller accordingly
-        """
-        self.PreviewListWidget.selectRow(row)
-        self.updatePreview()
-        Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix,
-            row)
-
-    def updatePreview(self):
-        if not self.screens.current[u'primary']:
-            # Grab now, but try again in a couple of seconds if slide change
-            # is slow
-            QtCore.QTimer.singleShot(0.5, self.grabMainDisplay)
-            QtCore.QTimer.singleShot(2.5, self.grabMainDisplay)
-        else:
-            label = self.PreviewListWidget.cellWidget(
-                self.PreviewListWidget.currentRow(), 1)
-            if label:
-                self.SlidePreview.setPixmap(label.pixmap())
-
-    def grabMainDisplay(self):
-        winid = QtGui.QApplication.desktop().winId()
-        rect = self.screens.current[u'size']
-        winimg = QtGui.QPixmap.grabWindow(winid, rect.x(),
-            rect.y(), rect.width(), rect.height())
-        self.SlidePreview.setPixmap(winimg)
-
-    def onSlideSelectedNextNoloop(self):
-        self.onSlideSelectedNext(False)
-
-    def onSlideSelectedNext(self, loop=True):
-        """
-        Go to the next slide.
-        """
-        if not self.serviceItem:
-            return
-        Receiver.send_message(u'%s_next' % self.serviceItem.name.lower(),
-            [self.serviceItem, self.isLive])
-        if self.serviceItem.is_command() and self.isLive:
-            self.updatePreview()
-        else:
-            row = self.PreviewListWidget.currentRow() + 1
-            if row == self.PreviewListWidget.rowCount():
-                if loop:
-                    row = 0
-                else:
-                    Receiver.send_message('servicemanager_next_item')
-                    return
-            self.PreviewListWidget.selectRow(row)
-            self.onSlideSelected()
-
-    def onSlideSelectedPreviousNoloop(self):
-        self.onSlideSelectedPrevious(False)
-
-    def onSlideSelectedPrevious(self, loop=True):
-        """
-        Go to the previous slide.
-        """
-        if not self.serviceItem:
-            return
-        Receiver.send_message(u'%s_previous' % self.serviceItem.name.lower(),
-            [self.serviceItem, self.isLive])
-        if self.serviceItem.is_command() and self.isLive:
-            self.updatePreview()
-        else:
-            row = self.PreviewListWidget.currentRow() - 1
-            if row == -1:
-                if loop:
-                    row = self.PreviewListWidget.rowCount() - 1
-                else:
-                    row = 0
-            self.PreviewListWidget.selectRow(row)
-            self.onSlideSelected()
-
-    def onSlideSelectedLast(self):
-        """
-        Go to the last slide.
-        """
-        if not self.serviceItem:
-            return
-        Receiver.send_message(u'%s_last' % self.serviceItem.name.lower(),
-            [self.serviceItem, self.isLive])
-        if self.serviceItem.is_command():
-            self.updatePreview()
-        else:
-            self.PreviewListWidget.selectRow(
-                        self.PreviewListWidget.rowCount() - 1)
-            self.onSlideSelected()
-
-    def onStartLoop(self):
-        """
-        Start the timer loop running and store the timer id
-        """
-        if self.PreviewListWidget.rowCount() > 1:
-            self.timer_id = self.startTimer(
-                int(self.DelaySpinBox.value()) * 1000)
-            self.Toolbar.actions[u'Stop Loop'].setVisible(True)
-            self.Toolbar.actions[u'Start Loop'].setVisible(False)
-
-    def onStopLoop(self):
-        """
-        Stop the timer loop running
-        """
-        if self.timer_id != 0:
-            self.killTimer(self.timer_id)
-            self.timer_id = 0
-            self.Toolbar.actions[u'Start Loop'].setVisible(True)
-            self.Toolbar.actions[u'Stop Loop'].setVisible(False)
-
-    def timerEvent(self, event):
-        """
-        If the timer event is for this window select next slide
-        """
-        if event.timerId() == self.timer_id:
-            self.onSlideSelectedNext()
-
-    def onEditSong(self):
-        """
-        From the preview display requires the service Item to be editied
-        """
-        self.songEdit = True
-        Receiver.send_message(u'%s_edit' % self.serviceItem.name.lower(),
-            u'P:%s' % self.serviceItem.editId)
-
-    def onGoLive(self):
-        """
-        If preview copy slide item to live
-        """
-        row = self.PreviewListWidget.currentRow()
-        if row > -1 and row < self.PreviewListWidget.rowCount():
-            self.parent.LiveController.addServiceManagerItem(
-                self.serviceItem, row)
-
-    def onMediaStart(self, item):
-        """
-        Respond to the arrival of a media service item
-        """
-        log.debug(u'SlideController onMediaStart')
-        if self.isLive:
-            file = os.path.join(item.get_frame_path(), item.get_frame_title())
-            self.display.video(file, self.volume)
-            self.volumeSlider.setValue(self.volume)
-        else:
-            self.mediaObject.stop()
-            self.mediaObject.clearQueue()
-            file = os.path.join(item.get_frame_path(), item.get_frame_title())
-            self.mediaObject.setCurrentSource(Phonon.MediaSource(file))
-            self.seekSlider.setMediaObject(self.mediaObject)
-            self.seekSlider.show()
-            self.onMediaPlay()
-
-    def mediaVolume(self):
-        """
-        Respond to the release of Volume Slider
-        """
-        log.debug(u'SlideController mediaVolume')
-        self.volume = self.volumeSlider.value()
-        self.display.videoVolume(self.volume)
-
-    def onMediaPause(self):
-        """
-        Respond to the Pause from the media Toolbar
-        """
-        log.debug(u'SlideController onMediaPause')
-        if self.isLive:
-            self.display.videoPause()
-        else:
-            self.mediaObject.pause()
-
-    def onMediaPlay(self):
-        """
-        Respond to the Play from the media Toolbar
-        """
-        log.debug(u'SlideController onMediaPlay')
-        if self.isLive:
-            self.display.videoPlay()
-        else:
-            self.SlidePreview.hide()
-            self.video.show()
-            self.mediaObject.play()
-
-    def onMediaStop(self):
-        """
-        Respond to the Stop from the media Toolbar
-        """
-        log.debug(u'SlideController onMediaStop')
-        if self.isLive:
-            self.display.videoStop()
-        else:
-            self.mediaObject.stop()
-            self.video.hide()
-        self.SlidePreview.clear()
-        self.SlidePreview.show()
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
+# Carsten Tinggaard, Frode Woldsund                                           #
+# --------------------------------------------------------------------------- #
+# 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                          #
+###############################################################################
+
+import logging
+import os
+
+from PyQt4 import QtCore, QtGui
+from PyQt4.phonon import Phonon
+
+from openlp.core.ui import HideMode, MainDisplay
+from openlp.core.lib import OpenLPToolbar, Receiver, resize_image, \
+    ItemCapabilities, translate
+
+log = logging.getLogger(__name__)
+
+class SlideList(QtGui.QTableWidget):
+    """
+    Customised version of QTableWidget which can respond to keyboard
+    events.
+    """
+    def __init__(self, parent=None, name=None):
+        QtGui.QTableWidget.__init__(self, parent.Controller)
+        self.parent = parent
+        self.hotkeyMap = {
+           QtCore.Qt.Key_Return: 'servicemanager_next_item',
+           QtCore.Qt.Key_Space: 'slidecontroller_live_next_noloop',
+           QtCore.Qt.Key_Enter: 'slidecontroller_live_next_noloop',
+           QtCore.Qt.Key_0: 'servicemanager_next_item',
+           QtCore.Qt.Key_Backspace: 'slidecontroller_live_previous_noloop'}
+
+    def keyPressEvent(self, event):
+        if isinstance(event, QtGui.QKeyEvent):
+            #here accept the event and do something
+            if event.key() == QtCore.Qt.Key_Up:
+                self.parent.onSlideSelectedPrevious()
+                event.accept()
+            elif event.key() == QtCore.Qt.Key_Down:
+                self.parent.onSlideSelectedNext()
+                event.accept()
+            elif event.key() == QtCore.Qt.Key_PageUp:
+                self.parent.onSlideSelectedFirst()
+                event.accept()
+            elif event.key() == QtCore.Qt.Key_PageDown:
+                self.parent.onSlideSelectedLast()
+                event.accept()
+            elif event.key() in self.hotkeyMap and self.parent.isLive:
+                Receiver.send_message(self.hotkeyMap[event.key()])
+                event.accept()
+            event.ignore()
+        else:
+            event.ignore()
+
+class SlideController(QtGui.QWidget):
+    """
+    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, settingsmanager, screens, isLive=False):
+        """
+        Set up the Slide Controller.
+        """
+        QtGui.QWidget.__init__(self, parent)
+        self.settingsmanager = settingsmanager
+        self.isLive = isLive
+        self.parent = parent
+        self.screens = screens
+        self.ratio = float(self.screens.current[u'size'].width()) / \
+            float(self.screens.current[u'size'].height())
+        self.display = MainDisplay(self, screens, isLive)
+        self.loopList = [
+            u'Start Loop',
+            u'Loop Separator',
+            u'Image SpinBox'
+        ]
+        self.songEditList = [
+            u'Edit Song',
+        ]
+        self.volume = 10
+        self.timer_id = 0
+        self.songEdit = False
+        self.selectedRow = 0
+        self.serviceItem = None
+        self.alertTab = None
+        self.Panel = QtGui.QWidget(parent.ControlSplitter)
+        self.slideList = {}
+        # Layout for holding panel
+        self.PanelLayout = QtGui.QVBoxLayout(self.Panel)
+        self.PanelLayout.setSpacing(0)
+        self.PanelLayout.setMargin(0)
+        # Type label for the top of the slide controller
+        self.TypeLabel = QtGui.QLabel(self.Panel)
+        if self.isLive:
+            self.TypeLabel.setText(translate('OpenLP.SlideController', 'Live'))
+            self.split = 1
+            self.typePrefix = u'live'
+        else:
+            self.TypeLabel.setText(translate('OpenLP.SlideController',
+                'Preview'))
+            self.split = 0
+            self.typePrefix = u'preview'
+        self.TypeLabel.setStyleSheet(u'font-weight: bold; font-size: 12pt;')
+        self.TypeLabel.setAlignment(QtCore.Qt.AlignCenter)
+        self.PanelLayout.addWidget(self.TypeLabel)
+        # Splitter
+        self.Splitter = QtGui.QSplitter(self.Panel)
+        self.Splitter.setOrientation(QtCore.Qt.Vertical)
+        self.Splitter.setOpaqueResize(False)
+        self.PanelLayout.addWidget(self.Splitter)
+        # Actual controller section
+        self.Controller = QtGui.QWidget(self.Splitter)
+        self.Controller.setGeometry(QtCore.QRect(0, 0, 100, 536))
+        self.Controller.setSizePolicy(
+            QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred,
+            QtGui.QSizePolicy.Maximum))
+        self.ControllerLayout = QtGui.QVBoxLayout(self.Controller)
+        self.ControllerLayout.setSpacing(0)
+        self.ControllerLayout.setMargin(0)
+        # Controller list view
+        self.PreviewListWidget = SlideList(self)
+        self.PreviewListWidget.setColumnCount(1)
+        self.PreviewListWidget.horizontalHeader().setVisible(False)
+        self.PreviewListWidget.setColumnWidth(
+            0, self.Controller.width())
+        self.PreviewListWidget.isLive = self.isLive
+        self.PreviewListWidget.setObjectName(u'PreviewListWidget')
+        self.PreviewListWidget.setSelectionBehavior(1)
+        self.PreviewListWidget.setEditTriggers(
+            QtGui.QAbstractItemView.NoEditTriggers)
+        self.PreviewListWidget.setHorizontalScrollBarPolicy(
+            QtCore.Qt.ScrollBarAlwaysOff)
+        self.PreviewListWidget.setAlternatingRowColors(True)
+        self.ControllerLayout.addWidget(self.PreviewListWidget)
+        # Build the full toolbar
+        self.Toolbar = OpenLPToolbar(self)
+        sizeToolbarPolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed,
+            QtGui.QSizePolicy.Fixed)
+        sizeToolbarPolicy.setHorizontalStretch(0)
+        sizeToolbarPolicy.setVerticalStretch(0)
+        sizeToolbarPolicy.setHeightForWidth(
+            self.Toolbar.sizePolicy().hasHeightForWidth())
+        self.Toolbar.setSizePolicy(sizeToolbarPolicy)
+        self.Toolbar.addToolbarButton(
+            u'Previous Slide', u':/slides/slide_previous.png',
+            translate('OpenLP.SlideController', 'Move to previous'),
+            self.onSlideSelectedPrevious)
+        self.Toolbar.addToolbarButton(
+            u'Next Slide', u':/slides/slide_next.png',
+            translate('OpenLP.SlideController', 'Move to next'),
+            self.onSlideSelectedNext)
+        if self.isLive:
+            self.Toolbar.addToolbarSeparator(u'Close Separator')
+            self.HideMenu = QtGui.QToolButton(self.Toolbar)
+            self.HideMenu.setText(translate('OpenLP.SlideController', 'Hide'))
+            self.HideMenu.setPopupMode(QtGui.QToolButton.MenuButtonPopup)
+            self.Toolbar.addToolbarWidget(u'Hide Menu', self.HideMenu)
+            self.HideMenu.setMenu(QtGui.QMenu(
+                translate('OpenLP.SlideController', 'Hide'), self.Toolbar))
+            self.BlankScreen = QtGui.QAction(QtGui.QIcon(
+                u':/slides/slide_blank.png'), u'Blank Screen', self.HideMenu)
+            self.BlankScreen.setText(
+                translate('OpenLP.SlideController', 'Blank Screen'))
+            self.BlankScreen.setCheckable(True)
+            QtCore.QObject.connect(self.BlankScreen,
+                QtCore.SIGNAL("triggered(bool)"), self.onBlankDisplay)
+            self.ThemeScreen = QtGui.QAction(QtGui.QIcon(
+                u':/slides/slide_theme.png'), u'Blank to Theme', self.HideMenu)
+            self.ThemeScreen.setText(
+                translate('OpenLP.SlideController', 'Blank to Theme'))
+            self.ThemeScreen.setCheckable(True)
+            QtCore.QObject.connect(self.ThemeScreen,
+                QtCore.SIGNAL("triggered(bool)"), self.onThemeDisplay)
+            if self.screens.display_count > 1:
+                self.DesktopScreen = QtGui.QAction(QtGui.QIcon(
+                    u':/slides/slide_desktop.png'), u'Show Desktop',
+                    self.HideMenu)
+                self.DesktopScreen.setText(
+                    translate('OpenLP.SlideController', 'Show Desktop'))
+                self.DesktopScreen.setCheckable(True)
+                QtCore.QObject.connect(self.DesktopScreen,
+                    QtCore.SIGNAL("triggered(bool)"), self.onHideDisplay)
+            self.HideMenu.setDefaultAction(self.BlankScreen)
+            self.HideMenu.menu().addAction(self.BlankScreen)
+            self.HideMenu.menu().addAction(self.ThemeScreen)
+            if self.screens.display_count > 1:
+                self.HideMenu.menu().addAction(self.DesktopScreen)
+        if not self.isLive:
+            self.Toolbar.addToolbarSeparator(u'Close Separator')
+            self.Toolbar.addToolbarButton(
+                u'Go Live', u':/general/general_live.png',
+                translate('OpenLP.SlideController', 'Move to live'),
+                self.onGoLive)
+            self.Toolbar.addToolbarSeparator(u'Close Separator')
+            self.Toolbar.addToolbarButton(
+                u'Edit Song', u':/general/general_edit.png',
+                translate('OpenLP.SlideController', 'Edit and re-preview Song'),
+                self.onEditSong)
+        if isLive:
+            self.Toolbar.addToolbarSeparator(u'Loop Separator')
+            self.Toolbar.addToolbarButton(
+                u'Start Loop', u':/media/media_time.png',
+                translate('OpenLP.SlideController', 'Start continuous loop'),
+                self.onStartLoop)
+            self.Toolbar.addToolbarButton(
+                u'Stop Loop', u':/media/media_stop.png',
+                translate('OpenLP.SlideController', 'Stop continuous loop'),
+                self.onStopLoop)
+            self.DelaySpinBox = QtGui.QSpinBox()
+            self.DelaySpinBox.setMinimum(1)
+            self.DelaySpinBox.setMaximum(180)
+            self.Toolbar.addToolbarWidget(
+                u'Image SpinBox', self.DelaySpinBox)
+            self.DelaySpinBox.setSuffix(translate('OpenLP.SlideController',
+                's'))
+            self.DelaySpinBox.setToolTip(translate('OpenLP.SlideController',
+                'Delay between slides in seconds'))
+        self.ControllerLayout.addWidget(self.Toolbar)
+        # Build a Media ToolBar
+        self.Mediabar = OpenLPToolbar(self)
+        self.Mediabar.addToolbarButton(
+            u'Media Start', u':/slides/media_playback_start.png',
+            translate('OpenLP.SlideController', 'Start playing media'),
+            self.onMediaPlay)
+        self.Mediabar.addToolbarButton(
+            u'Media Pause', u':/slides/media_playback_pause.png',
+            translate('OpenLP.SlideController', 'Start playing media'),
+            self.onMediaPause)
+        self.Mediabar.addToolbarButton(
+            u'Media Stop', u':/slides/media_playback_stop.png',
+            translate('OpenLP.SlideController', 'Start playing media'),
+            self.onMediaStop)
+        if not self.isLive:
+            self.seekSlider = Phonon.SeekSlider()
+            self.seekSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
+            self.seekSlider.setObjectName(u'seekSlider')
+            self.Mediabar.addToolbarWidget(
+                u'Seek Slider', self.seekSlider)
+            self.volumeSlider = Phonon.VolumeSlider()
+            self.volumeSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
+            self.volumeSlider.setObjectName(u'volumeSlider')
+            self.Mediabar.addToolbarWidget(u'Audio Volume', self.volumeSlider)
+        else:
+            self.volumeSlider = QtGui.QSlider(QtCore.Qt.Horizontal)
+            self.volumeSlider.setTickInterval(1)
+            self.volumeSlider.setTickPosition(QtGui.QSlider.TicksAbove)
+            self.volumeSlider.setMinimum(0)
+            self.volumeSlider.setMaximum(10)
+            self.volumeSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
+            self.volumeSlider.setObjectName(u'volumeSlider')
+            self.Mediabar.addToolbarWidget(u'Audio Volume', self.volumeSlider)
+        self.ControllerLayout.addWidget(self.Mediabar)
+        # Build the Song Toolbar
+        if isLive:
+            self.SongMenu = QtGui.QToolButton(self.Toolbar)
+            self.SongMenu.setText(translate('OpenLP.SlideController',
+                'Go to'))
+            self.SongMenu.setPopupMode(QtGui.QToolButton.InstantPopup)
+            self.Toolbar.addToolbarWidget(u'Song Menu', self.SongMenu)
+            self.SongMenu.setMenu(QtGui.QMenu(
+                translate('OpenLP.SlideController', 'Go to'),
+                self.Toolbar))
+            self.Toolbar.makeWidgetsInvisible([u'Song Menu'])
+        # Screen preview area
+        self.PreviewFrame = QtGui.QFrame(self.Splitter)
+        self.PreviewFrame.setGeometry(QtCore.QRect(0, 0, 300, 225))
+        self.PreviewFrame.setSizePolicy(QtGui.QSizePolicy(
+            QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Minimum,
+            QtGui.QSizePolicy.Label))
+        self.PreviewFrame.setFrameShape(QtGui.QFrame.StyledPanel)
+        self.PreviewFrame.setFrameShadow(QtGui.QFrame.Sunken)
+        self.PreviewFrame.setObjectName(u'PreviewFrame')
+        self.grid = QtGui.QGridLayout(self.PreviewFrame)
+        self.grid.setMargin(8)
+        self.grid.setObjectName(u'grid')
+        self.SlideLayout = QtGui.QVBoxLayout()
+        self.SlideLayout.setSpacing(0)
+        self.SlideLayout.setMargin(0)
+        self.SlideLayout.setObjectName(u'SlideLayout')
+        self.mediaObject = Phonon.MediaObject(self)
+        self.video = Phonon.VideoWidget()
+        self.video.setVisible(False)
+        self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self.mediaObject)
+        Phonon.createPath(self.mediaObject, self.video)
+        Phonon.createPath(self.mediaObject, self.audio)
+        if not self.isLive:
+            self.video.setGeometry(QtCore.QRect(0, 0, 300, 225))
+            self.video.setVisible(False)
+        self.SlideLayout.insertWidget(0, self.video)
+        # Actual preview screen
+        self.SlidePreview = QtGui.QLabel(self)
+        sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed,
+            QtGui.QSizePolicy.Fixed)
+        sizePolicy.setHorizontalStretch(0)
+        sizePolicy.setVerticalStretch(0)
+        sizePolicy.setHeightForWidth(
+            self.SlidePreview.sizePolicy().hasHeightForWidth())
+        self.SlidePreview.setSizePolicy(sizePolicy)
+        self.SlidePreview.setFixedSize(
+            QtCore.QSize(self.settingsmanager.slidecontroller_image,
+            self.settingsmanager.slidecontroller_image / self.ratio))
+        self.SlidePreview.setFrameShape(QtGui.QFrame.Box)
+        self.SlidePreview.setFrameShadow(QtGui.QFrame.Plain)
+        self.SlidePreview.setLineWidth(1)
+        self.SlidePreview.setScaledContents(True)
+        self.SlidePreview.setObjectName(u'SlidePreview')
+        self.SlideLayout.insertWidget(0, self.SlidePreview)
+        self.grid.addLayout(self.SlideLayout, 0, 0, 1, 1)
+        # Signals
+        QtCore.QObject.connect(self.PreviewListWidget,
+            QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected)
+        if not self.isLive:
+            if QtCore.QSettings().value(u'advanced/double click live',
+                QtCore.QVariant(False)).toBool():
+                QtCore.QObject.connect(self.PreviewListWidget,
+                    QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.onGoLive)
+        if isLive:
+            QtCore.QObject.connect(Receiver.get_receiver(),
+                QtCore.SIGNAL(u'slidecontroller_live_spin_delay'),
+                    self.receiveSpinDelay)
+        if isLive:
+            self.Toolbar.makeWidgetsInvisible(self.loopList)
+            self.Toolbar.actions[u'Stop Loop'].setVisible(False)
+        else:
+            self.Toolbar.makeWidgetsInvisible(self.songEditList)
+        self.Mediabar.setVisible(False)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'slidecontroller_%s_stop_loop' % self.typePrefix),
+            self.onStopLoop)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'slidecontroller_%s_first' % self.typePrefix),
+            self.onSlideSelectedFirst)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'slidecontroller_%s_next' % self.typePrefix),
+            self.onSlideSelectedNext)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'slidecontroller_%s_previous' % self.typePrefix),
+            self.onSlideSelectedPrevious)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'slidecontroller_%s_next_noloop' % self.typePrefix),
+            self.onSlideSelectedNextNoloop)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'slidecontroller_%s_previous_noloop' %
+            self.typePrefix),
+            self.onSlideSelectedPreviousNoloop)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'slidecontroller_%s_last' % self.typePrefix),
+            self.onSlideSelectedLast)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'slidecontroller_%s_change' % self.typePrefix),
+            self.onSlideChange)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'slidecontroller_%s_set' % self.typePrefix),
+            self.onSlideSelectedIndex)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'slidecontroller_%s_blank' % self.typePrefix),
+            self.onSlideBlank)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'slidecontroller_%s_unblank' % self.typePrefix),
+            self.onSlideUnblank)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'slidecontroller_%s_text_request' % self.typePrefix),
+            self.onTextRequest)
+        QtCore.QObject.connect(self.Splitter,
+            QtCore.SIGNAL(u'splitterMoved(int, int)'), self.trackSplitter)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'config_updated'), self.refreshServiceItem)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'config_screen_changed'), self.screenSizeChanged)
+        if self.isLive:
+            QtCore.QObject.connect(self.volumeSlider,
+                QtCore.SIGNAL(u'sliderReleased()'), self.mediaVolume)
+
+    def screenSizeChanged(self):
+        """
+        Settings dialog has changed the screen size of adjust output and
+        screen previews
+        """
+        log.debug(u'screenSizeChanged live = %s' % self.isLive)
+        # rebuild display as screen size changed
+        self.display = MainDisplay(self, self.screens, self.isLive)
+        self.display.alertTab = self.alertTab
+        self.ratio = float(self.screens.current[u'size'].width()) / \
+            float(self.screens.current[u'size'].height())
+        self.display.setup()
+        self.SlidePreview.setFixedSize(
+            QtCore.QSize(self.settingsmanager.slidecontroller_image,
+            self.settingsmanager.slidecontroller_image / self.ratio))
+
+    def widthChanged(self):
+        """
+        Handle changes of width from the splitter between the live and preview
+        controller.  Event only issues when changes have finished
+        """
+        log.debug(u'widthChanged live = %s' % self.isLive)
+        width = self.parent.ControlSplitter.sizes()[self.split]
+        height = width * self.parent.RenderManager.screen_ratio
+        self.PreviewListWidget.setColumnWidth(0, width)
+        # Sort out image heights (Songs, bibles excluded)
+        if self.serviceItem and not self.serviceItem.is_text():
+            for framenumber in range(len(self.serviceItem.get_frames())):
+                self.PreviewListWidget.setRowHeight(framenumber, height)
+
+    def trackSplitter(self, tab, pos):
+        """
+        Splitter between the slide list and the preview panel
+        """
+        pass
+
+    def onSongBarHandler(self):
+        request = unicode(self.sender().text())
+        slideno = self.slideList[request]
+        if slideno > self.PreviewListWidget.rowCount():
+            self.PreviewListWidget.selectRow(self.PreviewListWidget.rowCount())
+        else:
+            self.PreviewListWidget.selectRow(slideno)
+        self.onSlideSelected()
+
+    def receiveSpinDelay(self, value):
+        self.DelaySpinBox.setValue(int(value))
+
+    def enableToolBar(self, item):
+        """
+        Allows the toolbars to be reconfigured based on Controller Type
+        and ServiceItem Type
+        """
+        if self.isLive:
+            self.enableLiveToolBar(item)
+        else:
+            self.enablePreviewToolBar(item)
+
+    def enableLiveToolBar(self, item):
+        """
+        Allows the live toolbar to be customised
+        """
+        self.Toolbar.setVisible(True)
+        self.Mediabar.setVisible(False)
+        self.Toolbar.makeWidgetsInvisible([u'Song Menu'])
+        self.Toolbar.makeWidgetsInvisible(self.loopList)
+        self.Toolbar.actions[u'Stop Loop'].setVisible(False)
+        if item.is_text():
+            if QtCore.QSettings().value(
+                self.parent.songsSettingsSection + u'/show songbar',
+                QtCore.QVariant(True)).toBool() and len(self.slideList) > 0:
+                self.Toolbar.makeWidgetsVisible([u'Song Menu'])
+        if item.is_capable(ItemCapabilities.AllowsLoop) and \
+            len(item.get_frames()) > 1:
+            self.Toolbar.makeWidgetsVisible(self.loopList)
+        if item.is_media():
+            self.Toolbar.setVisible(False)
+            self.Mediabar.setVisible(True)
+
+    def enablePreviewToolBar(self, item):
+        """
+        Allows the Preview toolbar to be customised
+        """
+        self.Toolbar.setVisible(True)
+        self.Mediabar.setVisible(False)
+        self.Toolbar.makeWidgetsInvisible(self.songEditList)
+        if item.is_capable(ItemCapabilities.AllowsEdit) and item.from_plugin:
+            self.Toolbar.makeWidgetsVisible(self.songEditList)
+        elif item.is_media():
+            self.Toolbar.setVisible(False)
+            self.Mediabar.setVisible(True)
+            self.volumeSlider.setAudioOutput(self.audio)
+
+    def refreshServiceItem(self):
+        """
+        Method to update the service item if the screen has changed
+        """
+        log.debug(u'refreshServiceItem live = %s' % self.isLive)
+        if self.serviceItem:
+            if self.serviceItem.is_text() or self.serviceItem.is_image():
+                item = self.serviceItem
+                item.render()
+                self._processItem(item, self.selectedRow)
+
+    def addServiceItem(self, item):
+        """
+        Method to install the service item into the controller
+        Called by plugins
+        """
+        log.debug(u'addServiceItem live = %s' % self.isLive)
+        item.render()
+        slideno = 0
+        if self.songEdit:
+            slideno = self.selectedRow
+        self.songEdit = False
+        self._processItem(item, slideno)
+
+    def replaceServiceManagerItem(self, item):
+        """
+        Replacement item following a remote edit
+        """
+        if item.__eq__(self.serviceItem):
+            self._processItem(item, self.PreviewListWidget.currentRow())
+
+    def addServiceManagerItem(self, item, slideno):
+        """
+        Method to install the service item into the controller and
+        request the correct toolbar for the plugin.
+        Called by ServiceManager
+        """
+        log.debug(u'addServiceManagerItem live = %s' % self.isLive)
+        # If service item is the same as the current on only change slide
+        if item.__eq__(self.serviceItem):
+            self.PreviewListWidget.selectRow(slideno)
+            self.onSlideSelected()
+            return
+        self._processItem(item, slideno)
+
+    def _processItem(self, serviceItem, slideno):
+        """
+        Loads a ServiceItem into the system from ServiceManager
+        Display the slide number passed
+        """
+        log.debug(u'processManagerItem live = %s' % self.isLive)
+        self.onStopLoop()
+        # If old item was a command tell it to stop
+        if self.serviceItem:
+            if self.serviceItem.is_command():
+                Receiver.send_message(u'%s_stop' %
+                    self.serviceItem.name.lower(), [serviceItem, self.isLive])
+            if self.serviceItem.is_media():
+                self.onMediaStop()
+        if self.isLive:
+            blanked = self.BlankScreen.isChecked()
+        else:
+            blanked = False
+        Receiver.send_message(u'%s_start' % serviceItem.name.lower(),
+            [serviceItem, self.isLive, blanked, slideno])
+        self.slideList = {}
+        width = self.parent.ControlSplitter.sizes()[self.split]
+        # Set pointing cursor when we have somthing to point at
+        self.PreviewListWidget.setCursor(QtCore.Qt.PointingHandCursor)
+        self.serviceItem = serviceItem
+        self.PreviewListWidget.clear()
+        self.PreviewListWidget.setRowCount(0)
+        self.PreviewListWidget.setColumnWidth(0, width)
+        if self.isLive:
+            self.SongMenu.menu().clear()
+        row = 0
+        text = []
+        for framenumber, frame in enumerate(self.serviceItem.get_frames()):
+            self.PreviewListWidget.setRowCount(
+                self.PreviewListWidget.rowCount() + 1)
+            item = QtGui.QTableWidgetItem()
+            slideHeight = 0
+            if self.serviceItem.is_text():
+                if frame[u'verseTag']:
+                    bits = frame[u'verseTag'].split(u':')
+                    tag = u'%s\n%s' % (bits[0][0], bits[1][0:] )
+                    tag1 = u'%s%s' % (bits[0][0], bits[1][0:] )
+                    row = tag
+                    if self.isLive:
+                        if tag1 not in self.slideList:
+                            self.slideList[tag1] = framenumber
+                            self.SongMenu.menu().addAction(tag1,
+                                self.onSongBarHandler)
+                else:
+                    row += 1
+                item.setText(frame[u'text'])
+            else:
+                label = QtGui.QLabel()
+                label.setMargin(4)
+                pixmap = resize_image(frame[u'image'],
+                                      self.parent.RenderManager.width,
+                                      self.parent.RenderManager.height)
+                label.setScaledContents(True)
+                label.setPixmap(QtGui.QPixmap.fromImage(pixmap))
+                self.PreviewListWidget.setCellWidget(framenumber, 0, label)
+                slideHeight = width * self.parent.RenderManager.screen_ratio
+                row += 1
+            text.append(unicode(row))
+            self.PreviewListWidget.setItem(framenumber, 0, item)
+            if slideHeight != 0:
+                self.PreviewListWidget.setRowHeight(framenumber, slideHeight)
+        self.PreviewListWidget.setVerticalHeaderLabels(text)
+        if self.serviceItem.is_text():
+            self.PreviewListWidget.resizeRowsToContents()
+        self.PreviewListWidget.setColumnWidth(0,
+            self.PreviewListWidget.viewport().size().width())
+        if slideno > self.PreviewListWidget.rowCount():
+            self.PreviewListWidget.selectRow(self.PreviewListWidget.rowCount())
+        else:
+            self.PreviewListWidget.selectRow(slideno)
+        self.enableToolBar(serviceItem)
+        # Pass to display for viewing
+        self.display.buildHtml(self.serviceItem)
+        if serviceItem.is_media():
+            self.onMediaStart(serviceItem)
+        self.onSlideSelected()
+        self.PreviewListWidget.setFocus()
+        Receiver.send_message(u'slidecontroller_%s_started' % self.typePrefix,
+            [serviceItem])
+
+    def onTextRequest(self):
+        """
+        Return the text for the current item in controller
+        """
+        data = []
+        if self.serviceItem:
+            for framenumber, frame in enumerate(self.serviceItem.get_frames()):
+                dataItem = {}
+                if self.serviceItem.is_text():
+                    dataItem[u'tag'] = unicode(frame[u'verseTag'])
+                    dataItem[u'text'] = unicode(frame[u'html'])
+                else:
+                    dataItem[u'tag'] = unicode(framenumber)
+                    dataItem[u'text'] = u''
+                dataItem[u'selected'] = \
+                    (self.PreviewListWidget.currentRow() == framenumber)
+                data.append(dataItem)
+        Receiver.send_message(u'slidecontroller_%s_text_response'
+            % self.typePrefix, data)
+
+    # Screen event methods
+    def onSlideSelectedFirst(self):
+        """
+        Go to the first slide.
+        """
+        if not self.serviceItem:
+            return
+        Receiver.send_message(u'%s_first' % self.serviceItem.name.lower(),
+            [self.serviceItem, self.isLive])
+        if self.serviceItem.is_command():
+            self.updatePreview()
+        else:
+            self.PreviewListWidget.selectRow(0)
+            self.onSlideSelected()
+
+    def onSlideSelectedIndex(self, message):
+        """
+        Go to the requested slide
+        """
+        index = int(message[0])
+        if not self.serviceItem:
+            return
+        Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
+            [self.serviceItem, self.isLive, index])
+        if self.serviceItem.is_command():
+            self.updatePreview()
+        else:
+            self.PreviewListWidget.selectRow(index)
+            self.onSlideSelected()
+
+    def mainDisplaySetBackground(self):
+        """
+        Allow the main display to blank the main display at startup time
+        """
+        log.debug(u'mainDisplaySetBackground live = %s' % self.isLive)
+        if not self.display.primary:
+            self.onHideDisplay(True)
+
+    def onSlideBlank(self):
+        """
+        Handle the slidecontroller blank event
+        """
+        self.onBlankDisplay(True)
+
+    def onSlideUnblank(self):
+        """
+        Handle the slidecontroller unblank event
+        """
+        self.onBlankDisplay(False)
+
+    def onBlankDisplay(self, checked):
+        """
+        Handle the blank screen button actions
+        """
+        log.debug(u'onBlankDisplay %s' % checked)
+        self.HideMenu.setDefaultAction(self.BlankScreen)
+        self.BlankScreen.setChecked(checked)
+        self.ThemeScreen.setChecked(False)
+        if self.screens.display_count > 1:
+            self.DesktopScreen.setChecked(False)
+        QtCore.QSettings().setValue(
+            self.parent.generalSettingsSection + u'/screen blank',
+            QtCore.QVariant(checked))
+        if checked:
+            Receiver.send_message(u'maindisplay_hide', HideMode.Blank)
+        else:
+            Receiver.send_message(u'maindisplay_show')
+        self.blankPlugin(checked)
+
+    def onThemeDisplay(self, checked):
+        """
+        Handle the Theme screen button
+        """
+        log.debug(u'onThemeDisplay %s' % checked)
+        self.HideMenu.setDefaultAction(self.ThemeScreen)
+        self.BlankScreen.setChecked(False)
+        self.ThemeScreen.setChecked(checked)
+        if self.screens.display_count > 1:
+            self.DesktopScreen.setChecked(False)
+        if checked:
+            Receiver.send_message(u'maindisplay_hide', HideMode.Theme)
+        else:
+            Receiver.send_message(u'maindisplay_show')
+        self.blankPlugin(checked)
+
+    def onHideDisplay(self, checked):
+        """
+        Handle the Hide screen button
+        """
+        log.debug(u'onHideDisplay %s' % checked)
+        self.HideMenu.setDefaultAction(self.DesktopScreen)
+        self.BlankScreen.setChecked(False)
+        self.ThemeScreen.setChecked(False)
+        if self.screens.display_count > 1:
+            self.DesktopScreen.setChecked(checked)
+        if checked:
+            Receiver.send_message(u'maindisplay_hide', HideMode.Screen)
+        else:
+            Receiver.send_message(u'maindisplay_show')
+        self.hidePlugin(checked)
+
+    def blankPlugin(self, blank):
+        """
+        Blank the display screen within a plugin if required.
+        """
+        log.debug(u'blankPlugin %s ', blank)
+        if self.serviceItem is not None:
+            if blank:
+                Receiver.send_message(u'%s_blank'
+                    % self.serviceItem.name.lower(),
+                    [self.serviceItem, self.isLive])
+            else:
+                Receiver.send_message(u'%s_unblank'
+                    % self.serviceItem.name.lower(),
+                    [self.serviceItem, self.isLive])
+
+    def hidePlugin(self, hide):
+        """
+        Tell the plugin to hide the display screen.
+        """
+        log.debug(u'hidePlugin %s ', hide)
+        if self.serviceItem is not None:
+            if hide:
+                Receiver.send_message(u'%s_hide'
+                    % self.serviceItem.name.lower(),
+                    [self.serviceItem, self.isLive])
+            else:
+                Receiver.send_message(u'%s_unblank'
+                    % self.serviceItem.name.lower(),
+                    [self.serviceItem, self.isLive])
+
+    def onSlideSelected(self):
+        """
+        Generate the preview when you click on a slide.
+        if this is the Live Controller also display on the screen
+        """
+        row = self.PreviewListWidget.currentRow()
+        self.selectedRow = 0
+        if row > -1 and row < self.PreviewListWidget.rowCount():
+            Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
+                [self.serviceItem, self.isLive, row])
+            if self.serviceItem.is_command() and self.isLive:
+                self.updatePreview()
+            else:
+                frame, raw_html = self.serviceItem.get_rendered_frame(row)
+                if self.serviceItem.is_text():
+                    frame = self.display.text(raw_html)
+                else:
+                    self.display.image(frame)
+                if isinstance(frame, QtGui.QImage):
+                    self.SlidePreview.setPixmap(QtGui.QPixmap.fromImage(frame))
+                else:
+                    self.SlidePreview.setPixmap(QtGui.QPixmap(frame))
+            self.selectedRow = row
+        Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix,
+            row)
+
+    def onSlideChange(self, row):
+        """
+        The slide has been changed. Update the slidecontroller accordingly
+        """
+        self.PreviewListWidget.selectRow(row)
+        self.updatePreview()
+        Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix,
+            row)
+
+    def updatePreview(self):
+        if not self.screens.current[u'primary']:
+            # Grab now, but try again in a couple of seconds if slide change
+            # is slow
+            QtCore.QTimer.singleShot(0.5, self.grabMainDisplay)
+            QtCore.QTimer.singleShot(2.5, self.grabMainDisplay)
+        else:
+            label = self.PreviewListWidget.cellWidget(
+                self.PreviewListWidget.currentRow(), 1)
+            if label:
+                self.SlidePreview.setPixmap(label.pixmap())
+
+    def grabMainDisplay(self):
+        winid = QtGui.QApplication.desktop().winId()
+        rect = self.screens.current[u'size']
+        winimg = QtGui.QPixmap.grabWindow(winid, rect.x(),
+            rect.y(), rect.width(), rect.height())
+        self.SlidePreview.setPixmap(winimg)
+
+    def onSlideSelectedNextNoloop(self):
+        self.onSlideSelectedNext(False)
+
+    def onSlideSelectedNext(self, loop=True):
+        """
+        Go to the next slide.
+        """
+        if not self.serviceItem:
+            return
+        Receiver.send_message(u'%s_next' % self.serviceItem.name.lower(),
+            [self.serviceItem, self.isLive])
+        if self.serviceItem.is_command() and self.isLive:
+            self.updatePreview()
+        else:
+            row = self.PreviewListWidget.currentRow() + 1
+            if row == self.PreviewListWidget.rowCount():
+                if loop:
+                    row = 0
+                else:
+                    Receiver.send_message('servicemanager_next_item')
+                    return
+            self.PreviewListWidget.selectRow(row)
+            self.onSlideSelected()
+
+    def onSlideSelectedPreviousNoloop(self):
+        self.onSlideSelectedPrevious(False)
+
+    def onSlideSelectedPrevious(self, loop=True):
+        """
+        Go to the previous slide.
+        """
+        if not self.serviceItem:
+            return
+        Receiver.send_message(u'%s_previous' % self.serviceItem.name.lower(),
+            [self.serviceItem, self.isLive])
+        if self.serviceItem.is_command() and self.isLive:
+            self.updatePreview()
+        else:
+            row = self.PreviewListWidget.currentRow() - 1
+            if row == -1:
+                if loop:
+                    row = self.PreviewListWidget.rowCount() - 1
+                else:
+                    row = 0
+            self.PreviewListWidget.selectRow(row)
+            self.onSlideSelected()
+
+    def onSlideSelectedLast(self):
+        """
+        Go to the last slide.
+        """
+        if not self.serviceItem:
+            return
+        Receiver.send_message(u'%s_last' % self.serviceItem.name.lower(),
+            [self.serviceItem, self.isLive])
+        if self.serviceItem.is_command():
+            self.updatePreview()
+        else:
+            self.PreviewListWidget.selectRow(
+                        self.PreviewListWidget.rowCount() - 1)
+            self.onSlideSelected()
+
+    def onStartLoop(self):
+        """
+        Start the timer loop running and store the timer id
+        """
+        if self.PreviewListWidget.rowCount() > 1:
+            self.timer_id = self.startTimer(
+                int(self.DelaySpinBox.value()) * 1000)
+            self.Toolbar.actions[u'Stop Loop'].setVisible(True)
+            self.Toolbar.actions[u'Start Loop'].setVisible(False)
+
+    def onStopLoop(self):
+        """
+        Stop the timer loop running
+        """
+        if self.timer_id != 0:
+            self.killTimer(self.timer_id)
+            self.timer_id = 0
+            self.Toolbar.actions[u'Start Loop'].setVisible(True)
+            self.Toolbar.actions[u'Stop Loop'].setVisible(False)
+
+    def timerEvent(self, event):
+        """
+        If the timer event is for this window select next slide
+        """
+        if event.timerId() == self.timer_id:
+            self.onSlideSelectedNext()
+
+    def onEditSong(self):
+        """
+        From the preview display requires the service Item to be editied
+        """
+        self.songEdit = True
+        Receiver.send_message(u'%s_edit' % self.serviceItem.name.lower(),
+            u'P:%s' % self.serviceItem.editId)
+
+    def onGoLive(self):
+        """
+        If preview copy slide item to live
+        """
+        row = self.PreviewListWidget.currentRow()
+        if row > -1 and row < self.PreviewListWidget.rowCount():
+            self.parent.LiveController.addServiceManagerItem(
+                self.serviceItem, row)
+
+    def onMediaStart(self, item):
+        """
+        Respond to the arrival of a media service item
+        """
+        log.debug(u'SlideController onMediaStart')
+        if self.isLive:
+            file = os.path.join(item.get_frame_path(), item.get_frame_title())
+            self.display.video(file, self.volume)
+            self.volumeSlider.setValue(self.volume)
+        else:
+            self.mediaObject.stop()
+            self.mediaObject.clearQueue()
+            file = os.path.join(item.get_frame_path(), item.get_frame_title())
+            self.mediaObject.setCurrentSource(Phonon.MediaSource(file))
+            self.seekSlider.setMediaObject(self.mediaObject)
+            self.seekSlider.show()
+            self.onMediaPlay()
+
+    def mediaVolume(self):
+        """
+        Respond to the release of Volume Slider
+        """
+        log.debug(u'SlideController mediaVolume')
+        self.volume = self.volumeSlider.value()
+        self.display.videoVolume(self.volume)
+
+    def onMediaPause(self):
+        """
+        Respond to the Pause from the media Toolbar
+        """
+        log.debug(u'SlideController onMediaPause')
+        if self.isLive:
+            self.display.videoPause()
+        else:
+            self.mediaObject.pause()
+
+    def onMediaPlay(self):
+        """
+        Respond to the Play from the media Toolbar
+        """
+        log.debug(u'SlideController onMediaPlay')
+        if self.isLive:
+            self.display.videoPlay()
+        else:
+            self.SlidePreview.hide()
+            self.video.show()
+            self.mediaObject.play()
+
+    def onMediaStop(self):
+        """
+        Respond to the Stop from the media Toolbar
+        """
+        log.debug(u'SlideController onMediaStop')
+        if self.isLive:
+            self.display.videoStop()
+        else:
+            self.mediaObject.stop()
+            self.video.hide()
+        self.SlidePreview.clear()
+        self.SlidePreview.show()

=== modified file 'openlp/plugins/alerts/alertsplugin.py'
--- openlp/plugins/alerts/alertsplugin.py	2010-08-28 15:49:51 +0000
+++ openlp/plugins/alerts/alertsplugin.py	2010-09-10 15:56:22 +0000
@@ -1,103 +1,117 @@
-# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
-
-###############################################################################
-# OpenLP - Open Source Lyrics Projection                                      #
-# --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2010 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
-# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
-# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
-# Carsten Tinggaard, Frode Woldsund                                           #
-# --------------------------------------------------------------------------- #
-# 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                          #
-###############################################################################
-
-import logging
-
-from PyQt4 import QtCore, QtGui
-
-from openlp.core.lib import Plugin, build_icon, translate
-from openlp.core.lib.db import Manager
-from openlp.plugins.alerts.lib import AlertsManager, AlertsTab
-from openlp.plugins.alerts.lib.db import init_schema
-from openlp.plugins.alerts.forms import AlertForm
-
-log = logging.getLogger(__name__)
-
-class AlertsPlugin(Plugin):
-    log.info(u'Alerts Plugin loaded')
-
-    def __init__(self, plugin_helpers):
-        Plugin.__init__(self, u'Alerts', u'1.9.2', plugin_helpers)
-        self.weight = -3
-        self.icon = build_icon(u':/plugins/plugin_alerts.png')
-        self.alertsmanager = AlertsManager(self)
-        self.manager = Manager(u'alerts', init_schema)
-        self.alertForm = AlertForm(self)
-
-    def getSettingsTab(self):
-        """
-        Return the settings tab for the Alerts plugin
-        """
-        self.alertsTab = AlertsTab(self)
-        return self.alertsTab
-
-    def addToolsMenuItem(self, tools_menu):
-        """
-        Give the alerts plugin the opportunity to add items to the
-        **Tools** menu.
-
-        ``tools_menu``
-            The actual **Tools** menu item, so that your actions can
-            use it as their parent.
-        """
-        log.info(u'add tools menu')
-        self.toolsAlertItem = QtGui.QAction(tools_menu)
-        self.toolsAlertItem.setIcon(build_icon(u':/plugins/plugin_alerts.png'))
-        self.toolsAlertItem.setObjectName(u'toolsAlertItem')
-        self.toolsAlertItem.setText(translate('AlertsPlugin', '&Alert'))
-        self.toolsAlertItem.setStatusTip(
-            translate('AlertsPlugin', 'Show an alert message.'))
-        self.toolsAlertItem.setShortcut(u'F7')
-        self.serviceManager.parent.ToolsMenu.addAction(self.toolsAlertItem)
-        QtCore.QObject.connect(self.toolsAlertItem,
-            QtCore.SIGNAL(u'triggered()'), self.onAlertsTrigger)
-        self.toolsAlertItem.setVisible(False)
-
-    def initialise(self):
-        log.info(u'Alerts Initialising')
-        Plugin.initialise(self)
-        self.toolsAlertItem.setVisible(True)
-        self.liveController.alertTab = self.alertsTab
-
-    def finalise(self):
-        log.info(u'Alerts Finalising')
-        Plugin.finalise(self)
-        self.toolsAlertItem.setVisible(False)
-
-    def toggleAlertsState(self):
-        self.alertsActive = not self.alertsActive
-        QtCore.QSettings().setValue(self.settingsSection + u'/active',
-            QtCore.QVariant(self.alertsActive))
-
-    def onAlertsTrigger(self):
-        self.alertForm.loadList()
-        self.alertForm.exec_()
-
-    def about(self):
-        about_text = translate('AlertsPlugin', '<strong>Alerts Plugin</strong>'
-            '<br />The alert plugin controls the displaying of nursery alerts '
-            'on the display screen')
-        return about_text
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
+# Carsten Tinggaard, Frode Woldsund                                           #
+# --------------------------------------------------------------------------- #
+# 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                          #
+###############################################################################
+
+import logging
+
+from PyQt4 import QtCore, QtGui
+
+from openlp.core.lib import Plugin, StringType, build_icon, translate
+from openlp.core.lib.db import Manager
+from openlp.plugins.alerts.lib import AlertsManager, AlertsTab
+from openlp.plugins.alerts.lib.db import init_schema
+from openlp.plugins.alerts.forms import AlertForm
+
+log = logging.getLogger(__name__)
+
+class AlertsPlugin(Plugin):
+    log.info(u'Alerts Plugin loaded')
+
+    def __init__(self, plugin_helpers):
+        self.set_plugin_strings()
+        Plugin.__init__(self, u'Alerts', u'1.9.2', plugin_helpers)
+        self.weight = -3
+        self.icon = build_icon(u':/plugins/plugin_alerts.png')
+        self.alertsmanager = AlertsManager(self)
+        self.manager = Manager(u'alerts', init_schema)
+        self.alertForm = AlertForm(self)
+
+    def getSettingsTab(self):
+        """
+        Return the settings tab for the Alerts plugin
+        """
+        self.alertsTab = AlertsTab(self)
+        return self.alertsTab
+
+    def addToolsMenuItem(self, tools_menu):
+        """
+        Give the alerts plugin the opportunity to add items to the
+        **Tools** menu.
+
+        ``tools_menu``
+            The actual **Tools** menu item, so that your actions can
+            use it as their parent.
+        """
+        log.info(u'add tools menu')
+        self.toolsAlertItem = QtGui.QAction(tools_menu)
+        self.toolsAlertItem.setIcon(build_icon(u':/plugins/plugin_alerts.png'))
+        self.toolsAlertItem.setObjectName(u'toolsAlertItem')
+        self.toolsAlertItem.setText(translate('AlertsPlugin', '&Alert'))
+        self.toolsAlertItem.setStatusTip(
+            translate('AlertsPlugin', 'Show an alert message.'))
+        self.toolsAlertItem.setShortcut(u'F7')
+        self.serviceManager.parent.ToolsMenu.addAction(self.toolsAlertItem)
+        QtCore.QObject.connect(self.toolsAlertItem,
+            QtCore.SIGNAL(u'triggered()'), self.onAlertsTrigger)
+        self.toolsAlertItem.setVisible(False)
+
+    def initialise(self):
+        log.info(u'Alerts Initialising')
+        Plugin.initialise(self)
+        self.toolsAlertItem.setVisible(True)
+        self.liveController.alertTab = self.alertsTab
+
+    def finalise(self):
+        log.info(u'Alerts Finalising')
+        Plugin.finalise(self)
+        self.toolsAlertItem.setVisible(False)
+
+    def toggleAlertsState(self):
+        self.alertsActive = not self.alertsActive
+        QtCore.QSettings().setValue(self.settingsSection + u'/active',
+            QtCore.QVariant(self.alertsActive))
+
+    def onAlertsTrigger(self):
+        self.alertForm.loadList()
+        self.alertForm.exec_()
+
+    def about(self):
+        about_text = translate('AlertsPlugin', '<strong>Alerts Plugin</strong>'
+            '<br />The alert plugin controls the displaying of nursery alerts '
+            'on the display screen')
+        return about_text
+    def set_plugin_strings(self):
+        """
+        Called to define all translatable texts of the plugin
+        """
+        self.name = u'Alerts'
+        self.name_lower = u'alerts'
+
+        self.strings = {}
+        # for names in mediamanagerdock and pluginlist
+        self.strings[StringType.Name] = {
+            u'singular': translate('AlertsPlugin', 'Alert'),
+            u'plural': translate('AlertsPlugin', 'Alerts')
+        }

=== modified file 'openlp/plugins/bibles/bibleplugin.py'
--- openlp/plugins/bibles/bibleplugin.py	2010-07-31 00:05:27 +0000
+++ openlp/plugins/bibles/bibleplugin.py	2010-09-10 15:56:22 +0000
@@ -1,118 +1,170 @@
-# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
-
-###############################################################################
-# OpenLP - Open Source Lyrics Projection                                      #
-# --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2010 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
-# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
-# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
-# Carsten Tinggaard, Frode Woldsund                                           #
-# --------------------------------------------------------------------------- #
-# 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                          #
-###############################################################################
-
-import logging
-
-from PyQt4 import QtCore, QtGui
-
-from openlp.core.lib import Plugin, build_icon, translate
-from openlp.plugins.bibles.lib import BibleManager, BiblesTab, BibleMediaItem
-
-log = logging.getLogger(__name__)
-
-class BiblePlugin(Plugin):
-    log.info(u'Bible Plugin loaded')
-
-    def __init__(self, plugin_helpers):
-        Plugin.__init__(self, u'Bibles', u'1.9.2', plugin_helpers)
-        self.weight = -9
-        self.icon_path = u':/plugins/plugin_bibles.png'
-        self.icon = build_icon(self.icon_path)
-        self.manager = None
-
-    def initialise(self):
-        log.info(u'bibles Initialising')
-        if self.manager is None:
-            self.manager = BibleManager(self)
-        Plugin.initialise(self)
-        self.importBibleItem.setVisible(True)
-        self.exportBibleItem.setVisible(True)
-
-    def finalise(self):
-        log.info(u'Plugin Finalise')
-        Plugin.finalise(self)
-        self.importBibleItem.setVisible(False)
-        self.exportBibleItem.setVisible(False)
-
-    def getSettingsTab(self):
-        return BiblesTab(self.name)
-
-    def getMediaManagerItem(self):
-        # Create the BibleManagerItem object.
-        return BibleMediaItem(self, self.icon, self.name)
-
-    def addImportMenuItem(self, import_menu):
-        self.importBibleItem = QtGui.QAction(import_menu)
-        self.importBibleItem.setObjectName(u'importBibleItem')
-        import_menu.addAction(self.importBibleItem)
-        self.importBibleItem.setText(
-            translate('BiblesPlugin', '&Bible'))
-        # signals and slots
-        QtCore.QObject.connect(self.importBibleItem,
-            QtCore.SIGNAL(u'triggered()'), self.onBibleImportClick)
-        self.importBibleItem.setVisible(False)
-
-    def addExportMenuItem(self, export_menu):
-        self.exportBibleItem = QtGui.QAction(export_menu)
-        self.exportBibleItem.setObjectName(u'exportBibleItem')
-        export_menu.addAction(self.exportBibleItem)
-        self.exportBibleItem.setText(translate(
-            'BiblesPlugin', '&Bible'))
-        self.exportBibleItem.setVisible(False)
-
-    def onBibleImportClick(self):
-        if self.mediaItem:
-            self.mediaItem.onImportClick()
-
-    def about(self):
-        about_text = translate('BiblesPlugin', '<strong>Bible Plugin</strong>'
-            '<br />The Bible plugin provides the ability to display bible '
-            'verses from different sources during the service.')
-        return about_text
-
-    def usesTheme(self, theme):
-        """
-        Called to find out if the bible plugin is currently using a theme.
-
-        Returns True if the theme is being used, otherwise returns False.
-        """
-        if self.settings_tab.bible_theme == theme:
-            return True
-        return False
-
-    def renameTheme(self, oldTheme, newTheme):
-        """
-        Rename the theme the bible plugin is using making the plugin use the
-        new name.
-
-        ``oldTheme``
-            The name of the theme the plugin should stop using. Unused for
-            this particular plugin.
-
-        ``newTheme``
-            The new name the plugin should now use.
-        """
-        self.settings_tab.bible_theme = newTheme
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
+# Carsten Tinggaard, Frode Woldsund                                           #
+# --------------------------------------------------------------------------- #
+# 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                          #
+###############################################################################
+
+import logging
+
+from PyQt4 import QtCore, QtGui
+
+from openlp.core.lib import Plugin, StringType, build_icon, translate
+from openlp.plugins.bibles.lib import BibleManager, BiblesTab, BibleMediaItem
+
+log = logging.getLogger(__name__)
+
+class BiblePlugin(Plugin):
+    log.info(u'Bible Plugin loaded')
+
+    def __init__(self, plugin_helpers):
+        self.set_plugin_strings()
+        Plugin.__init__(self, u'Bibles', u'1.9.2', plugin_helpers)
+        self.weight = -9
+        self.icon_path = u':/plugins/plugin_bibles.png'
+        self.icon = build_icon(self.icon_path)
+        self.manager = None
+
+    def initialise(self):
+        log.info(u'bibles Initialising')
+        if self.manager is None:
+            self.manager = BibleManager(self)
+        Plugin.initialise(self)
+        self.importBibleItem.setVisible(True)
+        self.exportBibleItem.setVisible(True)
+
+    def finalise(self):
+        log.info(u'Plugin Finalise')
+        Plugin.finalise(self)
+        self.importBibleItem.setVisible(False)
+        self.exportBibleItem.setVisible(False)
+
+    def getSettingsTab(self):
+        return BiblesTab(self.name)
+
+    def getMediaManagerItem(self):
+        # Create the BibleManagerItem object.
+        return BibleMediaItem(self, self.icon, self.name)
+
+    def addImportMenuItem(self, import_menu):
+        self.importBibleItem = QtGui.QAction(import_menu)
+        self.importBibleItem.setObjectName(u'importBibleItem')
+        import_menu.addAction(self.importBibleItem)
+        self.importBibleItem.setText(
+            translate('BiblesPlugin', '&Bible'))
+        # signals and slots
+        QtCore.QObject.connect(self.importBibleItem,
+            QtCore.SIGNAL(u'triggered()'), self.onBibleImportClick)
+        self.importBibleItem.setVisible(False)
+
+    def addExportMenuItem(self, export_menu):
+        self.exportBibleItem = QtGui.QAction(export_menu)
+        self.exportBibleItem.setObjectName(u'exportBibleItem')
+        export_menu.addAction(self.exportBibleItem)
+        self.exportBibleItem.setText(translate(
+            'BiblesPlugin', '&Bible'))
+        self.exportBibleItem.setVisible(False)
+
+    def onBibleImportClick(self):
+        if self.mediaItem:
+            self.mediaItem.onImportClick()
+
+    def about(self):
+        about_text = translate('BiblesPlugin', '<strong>Bible Plugin</strong>'
+            '<br />The Bible plugin provides the ability to display bible '
+            'verses from different sources during the service.')
+        return about_text
+
+    def usesTheme(self, theme):
+        """
+        Called to find out if the bible plugin is currently using a theme.
+
+        Returns True if the theme is being used, otherwise returns False.
+        """
+        if self.settings_tab.bible_theme == theme:
+            return True
+        return False
+
+    def renameTheme(self, oldTheme, newTheme):
+        """
+        Rename the theme the bible plugin is using making the plugin use the
+        new name.
+
+        ``oldTheme``
+            The name of the theme the plugin should stop using. Unused for
+            this particular plugin.
+
+        ``newTheme``
+            The new name the plugin should now use.
+        """
+        self.settings_tab.bible_theme = newTheme
+
+    def set_plugin_strings(self):
+        """
+        Called to define all translatable texts of the plugin
+        """
+        self.name = u'Bibles'
+        self.name_lower = u'Bibles'
+
+        self.strings = {}
+        # for names in mediamanagerdock and pluginlist
+        self.strings[StringType.Name] = {
+            u'singular': translate('BiblesPlugin', 'Bible'),
+            u'plural': translate('BiblesPlugin', 'Bibles')
+        }
+
+        # Middle Header Bar
+        ## Import Button ##
+        self.strings[StringType.Import] = {
+            u'title': translate('BiblesPlugin', 'Import'),
+            u'tooltip': translate('BiblesPlugin', 'Import a Bible')
+        }
+        ## New Button ##
+        self.strings[StringType.New] = {
+            u'title': translate('BiblesPlugin', 'Add'),
+            u'tooltip': translate('BiblesPlugin', 'Add a new Bible')
+        }
+        ## Edit Button ##
+        self.strings[StringType.Edit] = {
+            u'title': translate('BiblesPlugin', 'Edit'),
+            u'tooltip': translate('BiblesPlugin', 'Edit the selected Bible')
+        }
+        ## Delete Button ##
+        self.strings[StringType.Delete] = {
+            u'title': translate('BiblesPlugin', 'Delete'),
+            u'tooltip': translate('BiblesPlugin', 'Delete the selected Bible')
+        }
+        ## Preview ##
+        self.strings[StringType.Preview] = {
+            u'title': translate('BiblesPlugin', 'Preview'),
+            u'tooltip': translate('BiblesPlugin', 'Preview the selected Bible')
+        }
+        ## Live  Button ##
+        self.strings[StringType.Live] = {
+            u'title': translate('BiblesPlugin', 'Live'),
+            u'tooltip': translate('BiblesPlugin', 'Send the selected Bible live')
+        }
+        ## Add to service Button ##
+        self.strings[StringType.Service] = {
+            u'title': translate('BiblesPlugin', 'Service'),
+            u'tooltip': translate('BiblesPlugin', 'Add the selected Bible to the service')
+        }

=== modified file 'openlp/plugins/bibles/lib/mediaitem.py'
--- openlp/plugins/bibles/lib/mediaitem.py	2010-09-04 19:40:28 +0000
+++ openlp/plugins/bibles/lib/mediaitem.py	2010-09-10 15:56:22 +0000
@@ -54,8 +54,6 @@
     log.info(u'Bible Media Item loaded')
 
     def __init__(self, parent, icon, title):
-        self.PluginNameShort = u'Bible'
-        self.pluginNameVisible = translate('BiblesPlugin.MediaItem', 'Bible')
         self.IconPath = u'songs/song'
         self.ListViewWithDnD_class = BibleListView
         MediaManagerItem.__init__(self, parent, icon, title)

=== modified file 'openlp/plugins/custom/customplugin.py'
--- openlp/plugins/custom/customplugin.py	2010-07-31 00:05:27 +0000
+++ openlp/plugins/custom/customplugin.py	2010-09-10 15:56:22 +0000
@@ -1,98 +1,154 @@
-# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
-
-###############################################################################
-# OpenLP - Open Source Lyrics Projection                                      #
-# --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2010 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
-# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
-# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
-# Carsten Tinggaard, Frode Woldsund                                           #
-# --------------------------------------------------------------------------- #
-# 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                          #
-###############################################################################
-
-import logging
-
-from forms import EditCustomForm
-
-from openlp.core.lib import Plugin, build_icon, translate
-from openlp.core.lib.db import Manager
-from openlp.plugins.custom.lib import CustomMediaItem, CustomTab
-from openlp.plugins.custom.lib.db import CustomSlide, init_schema
-
-log = logging.getLogger(__name__)
-
-class CustomPlugin(Plugin):
-    """
-    This plugin enables the user to create, edit and display
-    custom slide shows. Custom shows are divided into slides.
-    Each show is able to have it's own theme.
-    Custom shows are designed to replace the use of songs where
-    the songs plugin has become restrictive. Examples could be
-    Welcome slides, Bible Reading information, Orders of service.
-    """
-    log.info(u'Custom Plugin loaded')
-
-    def __init__(self, plugin_helpers):
-        Plugin.__init__(self, u'Custom', u'1.9.2', plugin_helpers)
-        self.weight = -5
-        self.custommanager = Manager(u'custom', init_schema)
-        self.edit_custom_form = EditCustomForm(self.custommanager)
-        self.icon_path = u':/plugins/plugin_custom.png'
-        self.icon = build_icon(self.icon_path)
-
-    def getSettingsTab(self):
-        return CustomTab(self.name)
-
-    def getMediaManagerItem(self):
-        # Create the CustomManagerItem object
-        return CustomMediaItem(self, self.icon, self.name)
-
-    def about(self):
-        about_text = translate('CustomPlugin', '<strong>Custom Plugin</strong>'
-            '<br />The custom plugin provides the ability to set up custom '
-            'text slides that can be displayed on the screen the same way '
-            'songs are. This plugin provides greater freedom over the songs '
-            'plugin.')
-        return about_text
-
-    def usesTheme(self, theme):
-        """
-        Called to find out if the custom plugin is currently using a theme.
-
-        Returns True if the theme is being used, otherwise returns False.
-        """
-        if self.custommanager.get_all_objects(CustomSlide,
-            CustomSlide.theme_name == theme):
-            return True
-        return False
-
-    def renameTheme(self, oldTheme, newTheme):
-        """
-        Renames a theme the custom plugin is using making the plugin use the
-        new name.
-
-        ``oldTheme``
-            The name of the theme the plugin should stop using.
-
-        ``newTheme``
-            The new name the plugin should now use.
-        """
-        customsUsingTheme = self.custommanager.get_all_objects(CustomSlide,
-            CustomSlide.theme_name == oldTheme)
-        for custom in customsUsingTheme:
-            custom.theme_name = newTheme
-            self.custommanager.save_object(custom)
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
+# Carsten Tinggaard, Frode Woldsund                                           #
+# --------------------------------------------------------------------------- #
+# 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                          #
+###############################################################################
+
+import logging
+
+from forms import EditCustomForm
+
+from openlp.core.lib import Plugin, StringType, build_icon, translate
+from openlp.core.lib.db import Manager
+from openlp.plugins.custom.lib import CustomMediaItem, CustomTab
+from openlp.plugins.custom.lib.db import CustomSlide, init_schema
+
+log = logging.getLogger(__name__)
+
+class CustomPlugin(Plugin):
+    """
+    This plugin enables the user to create, edit and display
+    custom slide shows. Custom shows are divided into slides.
+    Each show is able to have it's own theme.
+    Custom shows are designed to replace the use of Customs where
+    the Customs plugin has become restrictive. Examples could be
+    Welcome slides, Bible Reading information, Orders of service.
+    """
+    log.info(u'Custom Plugin loaded')
+
+    def __init__(self, plugin_helpers):
+        self.set_plugin_strings()
+        Plugin.__init__(self, u'Custom', u'1.9.2', plugin_helpers)
+        self.weight = -5
+        self.custommanager = Manager(u'custom', init_schema)
+        self.edit_custom_form = EditCustomForm(self.custommanager)
+        self.icon_path = u':/plugins/plugin_custom.png'
+        self.icon = build_icon(self.icon_path)
+
+    def getSettingsTab(self):
+        return CustomTab(self.name)
+
+    def getMediaManagerItem(self):
+        # Create the CustomManagerItem object
+        return CustomMediaItem(self, self.icon, self.name)
+
+    def about(self):
+        about_text = translate('CustomPlugin', '<strong>Custom Plugin</strong>'
+            '<br />The custom plugin provides the ability to set up custom '
+            'text slides that can be displayed on the screen the same way '
+            'Customs are. This plugin provides greater freedom over the Customs '
+            'plugin.')
+        return about_text
+
+    def usesTheme(self, theme):
+        """
+        Called to find out if the custom plugin is currently using a theme.
+
+        Returns True if the theme is being used, otherwise returns False.
+        """
+        if self.custommanager.get_all_objects(CustomSlide,
+            CustomSlide.theme_name == theme):
+            return True
+        return False
+
+    def renameTheme(self, oldTheme, newTheme):
+        """
+        Renames a theme the custom plugin is using making the plugin use the
+        new name.
+
+        ``oldTheme``
+            The name of the theme the plugin should stop using.
+
+        ``newTheme``
+            The new name the plugin should now use.
+        """
+        customsUsingTheme = self.custommanager.get_all_objects(CustomSlide,
+            CustomSlide.theme_name == oldTheme)
+        for custom in customsUsingTheme:
+            custom.theme_name = newTheme
+            self.custommanager.save_object(custom)
+    def set_plugin_strings(self):
+        """
+        Called to define all translatable texts of the plugin
+        """
+        self.name = u'Custom'
+        self.name_lower = u'custom'
+
+        self.strings = {}
+        # for names in mediamanagerdock and pluginlist
+        self.strings[StringType.Name] = {
+            u'singular': translate('CustomsPlugin', 'Custom'),
+            u'plural': translate('CustomsPlugin', 'Customs')
+        }
+
+        # Middle Header Bar
+        ## Import Button ##
+        self.strings[StringType.Import] = {
+            u'title': translate('CustomsPlugin', 'Import'),
+            u'tooltip': translate('CustomsPlugin', 'Import a Custom')
+        }
+        ## Load Button ##
+        self.strings[StringType.Load] = {
+            u'title': translate('CustomsPlugin', 'Load'),
+            u'tooltip': translate('CustomsPlugin', 'Load a new Custom')
+        }
+        ## New Button ##
+        self.strings[StringType.New] = {
+            u'title': translate('CustomsPlugin', 'Add'),
+            u'tooltip': translate('CustomsPlugin', 'Add a new Custom')
+        }
+        ## Edit Button ##
+        self.strings[StringType.Edit] = {
+            u'title': translate('CustomsPlugin', 'Edit'),
+            u'tooltip': translate('CustomsPlugin', 'Edit the selected Custom')
+        }
+        ## Delete Button ##
+        self.strings[StringType.Delete] = {
+            u'title': translate('CustomsPlugin', 'Delete'),
+            u'tooltip': translate('CustomsPlugin', 'Delete the selected Custom')
+        }
+        ## Preview ##
+        self.strings[StringType.Preview] = {
+            u'title': translate('CustomsPlugin', 'Preview'),
+            u'tooltip': translate('CustomsPlugin', 'Preview the selected Custom')
+        }
+        ## Live  Button ##
+        self.strings[StringType.Live] = {
+            u'title': translate('CustomsPlugin', 'Live'),
+            u'tooltip': translate('CustomsPlugin', 'Send the selected Custom live')
+        }
+        ## Add to service Button ##
+        self.strings[StringType.Service] = {
+            u'title': translate('CustomsPlugin', 'Service'),
+            u'tooltip': translate('CustomsPlugin', 'Add the selected Custom to the service')
+        }

=== modified file 'openlp/plugins/custom/lib/mediaitem.py'
--- openlp/plugins/custom/lib/mediaitem.py	2010-07-27 09:32:52 +0000
+++ openlp/plugins/custom/lib/mediaitem.py	2010-09-10 15:56:22 +0000
@@ -47,8 +47,6 @@
     log.info(u'Custom Media Item loaded')
 
     def __init__(self, parent, icon, title):
-        self.PluginNameShort = u'Custom'
-        self.pluginNameVisible = translate('CustomPlugin.MediaItem', 'Custom')
         self.IconPath = u'custom/custom'
         # this next is a class, not an instance of a class - it will
         # be instanced by the base MediaManagerItem

=== modified file 'openlp/plugins/images/imageplugin.py'
--- openlp/plugins/images/imageplugin.py	2010-07-31 00:05:27 +0000
+++ openlp/plugins/images/imageplugin.py	2010-09-10 15:56:22 +0000
@@ -1,59 +1,111 @@
-# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
-
-###############################################################################
-# OpenLP - Open Source Lyrics Projection                                      #
-# --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2010 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
-# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
-# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
-# Carsten Tinggaard, Frode Woldsund                                           #
-# --------------------------------------------------------------------------- #
-# 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                          #
-###############################################################################
-
-import logging
-
-from openlp.core.lib import Plugin, build_icon, translate
-from openlp.plugins.images.lib import ImageMediaItem
-
-log = logging.getLogger(__name__)
-
-class ImagePlugin(Plugin):
-    log.info(u'Image Plugin loaded')
-
-    def __init__(self, plugin_helpers):
-        Plugin.__init__(self, u'Images', u'1.9.2', plugin_helpers)
-        self.weight = -7
-        self.icon_path = u':/plugins/plugin_images.png'
-        self.icon = build_icon(self.icon_path)
-
-    def getMediaManagerItem(self):
-        # Create the MediaManagerItem object
-        return ImageMediaItem(self, self.icon, self.name)
-
-    def about(self):
-        about_text = translate('ImagePlugin', '<strong>Image Plugin</strong>'
-            '<br />The image plugin provides displaying of images.<br />One '
-            'of the distinguishing features of this plugin is the ability to '
-            'group a number of images together in the service manager, making '
-            'the displaying of multiple images easier. This plugin can also '
-            'make use of OpenLP\'s "timed looping" feature to create a slide '
-            'show that runs automatically. In addition to this, images from '
-            'the plugin can be used to override the current theme\'s '
-            'background, which renders text-based items like songs with the '
-            'selected image as a background instead of the background '
-            'provided by the theme.')
-        return about_text
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
+# Carsten Tinggaard, Frode Woldsund                                           #
+# --------------------------------------------------------------------------- #
+# 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                          #
+###############################################################################
+
+import logging
+
+from openlp.core.lib import Plugin, StringType, build_icon, translate
+from openlp.plugins.images.lib import ImageMediaItem
+
+log = logging.getLogger(__name__)
+
+class ImagePlugin(Plugin):
+    log.info(u'Image Plugin loaded')
+
+    def __init__(self, plugin_helpers):
+        self.set_plugin_strings()
+        Plugin.__init__(self, u'Images', u'1.9.2', plugin_helpers)
+        self.weight = -7
+        self.icon_path = u':/plugins/plugin_images.png'
+        self.icon = build_icon(self.icon_path)
+
+    def getMediaManagerItem(self):
+        # Create the MediaManagerItem object
+        return ImageMediaItem(self, self.icon, self.name)
+
+    def about(self):
+        about_text = translate('ImagePlugin', '<strong>Image Plugin</strong>'
+            '<br />The image plugin provides displaying of images.<br />One '
+            'of the distinguishing features of this plugin is the ability to '
+            'group a number of images together in the service manager, making '
+            'the displaying of multiple images easier. This plugin can also '
+            'make use of OpenLP\'s "timed looping" feature to create a slide '
+            'show that runs automatically. In addition to this, images from '
+            'the plugin can be used to override the current theme\'s '
+            'background, which renders text-based items like Images with the '
+            'selected image as a background instead of the background '
+            'provided by the theme.')
+        return about_text
+    # rimach
+    def set_plugin_strings(self):
+        """
+        Called to define all translatable texts of the plugin
+        """
+        self.name = u'Images'
+        self.name_lower = u'images'
+
+        self.strings = {}
+        # for names in mediamanagerdock and pluginlist
+        self.strings[StringType.Name] = {
+            u'singular': translate('ImagePlugin', 'Image'),
+            u'plural': translate('ImagePlugin', 'Images')
+        }
+
+        # Middle Header Bar
+        ## Load Button ##
+        self.strings[StringType.Load] = {
+            u'title': translate('ImagePlugin', 'Load'),
+            u'tooltip': translate('ImagePlugin', 'Load a new Image')
+        }
+        ## New Button ##
+        self.strings[StringType.New] = {
+            u'title': translate('ImagePlugin', 'Add'),
+            u'tooltip': translate('ImagePlugin', 'Add a new Image')
+        }
+        ## Edit Button ##
+        self.strings[StringType.Edit] = {
+            u'title': translate('ImagePlugin', 'Edit'),
+            u'tooltip': translate('ImagePlugin', 'Edit the selected Image')
+        }
+        ## Delete Button ##
+        self.strings[StringType.Delete] = {
+            u'title': translate('ImagePlugin', 'Delete'),
+            u'tooltip': translate('ImagePlugin', 'Delete the selected Image')
+        }
+        ## Preview ##
+        self.strings[StringType.Preview] = {
+            u'title': translate('ImagePlugin', 'Preview'),
+            u'tooltip': translate('ImagePlugin', 'Preview the selected Image')
+        }
+        ## Live  Button ##
+        self.strings[StringType.Live] = {
+            u'title': translate('ImagePlugin', 'Live'),
+            u'tooltip': translate('ImagePlugin', 'Send the selected Image live')
+        }
+        ## Add to service Button ##
+        self.strings[StringType.Service] = {
+            u'title': translate('ImagePlugin', 'Service'),
+            u'tooltip': translate('ImagePlugin', 'Add the selected Image to the service')
+        }

=== modified file 'openlp/plugins/images/lib/mediaitem.py'
--- openlp/plugins/images/lib/mediaitem.py	2010-07-28 17:21:32 +0000
+++ openlp/plugins/images/lib/mediaitem.py	2010-09-10 15:56:22 +0000
@@ -50,8 +50,6 @@
     log.info(u'Image Media Item loaded')
 
     def __init__(self, parent, icon, title):
-        self.PluginNameShort = u'Image'
-        self.pluginNameVisible = translate('ImagePlugin.MediaItem', 'Image')
         self.IconPath = u'images/image'
         # this next is a class, not an instance of a class - it will
         # be instanced by the base MediaManagerItem

=== modified file 'openlp/plugins/media/lib/mediaitem.py'
--- openlp/plugins/media/lib/mediaitem.py	2010-08-04 19:09:43 +0000
+++ openlp/plugins/media/lib/mediaitem.py	2010-09-10 15:56:22 +0000
@@ -47,8 +47,6 @@
     log.info(u'%s MediaMediaItem loaded', __name__)
 
     def __init__(self, parent, icon, title):
-        self.PluginNameShort = u'Media'
-        self.pluginNameVisible = translate('MediaPlugin.MediaItem', 'Media')
         self.IconPath = u'images/image'
         self.background = False
         # this next is a class, not an instance of a class - it will

=== modified file 'openlp/plugins/media/mediaplugin.py'
--- openlp/plugins/media/mediaplugin.py	2010-07-31 00:05:27 +0000
+++ openlp/plugins/media/mediaplugin.py	2010-09-10 15:56:22 +0000
@@ -1,78 +1,129 @@
-# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
-
-###############################################################################
-# OpenLP - Open Source Lyrics Projection                                      #
-# --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2010 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
-# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
-# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
-# Carsten Tinggaard, Frode Woldsund                                           #
-# --------------------------------------------------------------------------- #
-# 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                          #
-###############################################################################
-
-import logging
-
-from PyQt4.phonon import Phonon
-
-from openlp.core.lib import Plugin, build_icon, translate
-from openlp.plugins.media.lib import MediaMediaItem
-
-log = logging.getLogger(__name__)
-
-class MediaPlugin(Plugin):
-    log.info(u'%s MediaPlugin loaded', __name__)
-
-    def __init__(self, plugin_helpers):
-        Plugin.__init__(self, u'Media', u'1.9.2', plugin_helpers)
-        self.weight = -6
-        self.icon_path = u':/plugins/plugin_media.png'
-        self.icon = build_icon(self.icon_path)
-        # passed with drag and drop messages
-        self.dnd_id = u'Media'
-        self.audio_list = u''
-        self.video_list = u''
-        for mimetype in Phonon.BackendCapabilities.availableMimeTypes():
-            mimetype = unicode(mimetype)
-            type = mimetype.split(u'audio/x-')
-            self.audio_list, mimetype = self._addToList(self.audio_list,
-                type, mimetype)
-            type = mimetype.split(u'audio/')
-            self.audio_list, mimetype = self._addToList(self.audio_list,
-                type, mimetype)
-            type = mimetype.split(u'video/x-')
-            self.video_list, mimetype = self._addToList(self.video_list,
-                type, mimetype)
-            type = mimetype.split(u'video/')
-            self.video_list, mimetype = self._addToList(self.video_list,
-                type, mimetype)
-
-    def _addToList(self, list, value, type):
-        if len(value) == 2:
-            if list.find(value[1]) == -1:
-                list += u'*.%s ' % value[1]
-                self.serviceManager.supportedSuffixes(value[1])
-            type = u''
-        return list, type
-
-    def getMediaManagerItem(self):
-        # Create the MediaManagerItem object
-        return MediaMediaItem(self, self.icon, self.name)
-
-    def about(self):
-        about_text = translate('MediaPlugin', '<strong>Media Plugin</strong>'
-            '<br />The media plugin provides playback of audio and video.')
-        return about_text
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
+# Carsten Tinggaard, Frode Woldsund                                           #
+# --------------------------------------------------------------------------- #
+# 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                          #
+###############################################################################
+
+import logging
+
+from PyQt4.phonon import Phonon
+
+from openlp.core.lib import Plugin, StringType, build_icon, translate
+from openlp.plugins.media.lib import MediaMediaItem
+
+log = logging.getLogger(__name__)
+
+class MediaPlugin(Plugin):
+    log.info(u'%s MediaPlugin loaded', __name__)
+
+    def __init__(self, plugin_helpers):
+        self.set_plugin_strings()
+        Plugin.__init__(self, u'Media', u'1.9.2', plugin_helpers)
+        self.weight = -6
+        self.icon_path = u':/plugins/plugin_media.png'
+        self.icon = build_icon(self.icon_path)
+        # passed with drag and drop messages
+        self.dnd_id = u'Media'
+        self.audio_list = u''
+        self.video_list = u''
+        for mimetype in Phonon.BackendCapabilities.availableMimeTypes():
+            mimetype = unicode(mimetype)
+            type = mimetype.split(u'audio/x-')
+            self.audio_list, mimetype = self._addToList(self.audio_list,
+                type, mimetype)
+            type = mimetype.split(u'audio/')
+            self.audio_list, mimetype = self._addToList(self.audio_list,
+                type, mimetype)
+            type = mimetype.split(u'video/x-')
+            self.video_list, mimetype = self._addToList(self.video_list,
+                type, mimetype)
+            type = mimetype.split(u'video/')
+            self.video_list, mimetype = self._addToList(self.video_list,
+                type, mimetype)
+
+    def _addToList(self, list, value, type):
+        if len(value) == 2:
+            if list.find(value[1]) == -1:
+                list += u'*.%s ' % value[1]
+                self.serviceManager.supportedSuffixes(value[1])
+            type = u''
+        return list, type
+
+    def getMediaManagerItem(self):
+        # Create the MediaManagerItem object
+        return MediaMediaItem(self, self.icon, self.name)
+
+    def about(self):
+        about_text = translate('MediaPlugin', '<strong>Media Plugin</strong>'
+            '<br />The media plugin provides playback of audio and video.')
+        return about_text
+    def set_plugin_strings(self):
+        """
+        Called to define all translatable texts of the plugin
+        """
+        self.name = u'Media'
+        self.name_lower = u'media'
+
+        self.strings = {}
+        # for names in mediamanagerdock and pluginlist
+        self.strings[StringType.Name] = {
+            u'singular': translate('MediaPlugin', 'Media'),
+            u'plural': translate('MediaPlugin', 'Medias')
+        }
+
+        # Middle Header Bar
+        ## Load Button ##
+        self.strings[StringType.Load] = {
+            u'title': translate('MediaPlugin', 'Load'),
+            u'tooltip': translate('MediaPlugin', 'Load a new Media')
+        }
+        ## New Button ##
+        self.strings[StringType.New] = {
+            u'title': translate('MediaPlugin', 'Add'),
+            u'tooltip': translate('MediaPlugin', 'Add a new Media')
+        }
+        ## Edit Button ##
+        self.strings[StringType.Edit] = {
+            u'title': translate('MediaPlugin', 'Edit'),
+            u'tooltip': translate('MediaPlugin', 'Edit the selected Media')
+        }
+        ## Delete Button ##
+        self.strings[StringType.Delete] = {
+            u'title': translate('MediaPlugin', 'Delete'),
+            u'tooltip': translate('MediaPlugin', 'Delete the selected Media')
+        }
+        ## Preview ##
+        self.strings[StringType.Preview] = {
+            u'title': translate('MediaPlugin', 'Preview'),
+            u'tooltip': translate('MediaPlugin', 'Preview the selected Media')
+        }
+        ## Live  Button ##
+        self.strings[StringType.Live] = {
+            u'title': translate('MediaPlugin', 'Live'),
+            u'tooltip': translate('MediaPlugin', 'Send the selected Media live')
+        }
+        ## Add to service Button ##
+        self.strings[StringType.Service] = {
+            u'title': translate('MediaPlugin', 'Service'),
+            u'tooltip': translate('MediaPlugin', 'Add the selected Media to the service')
+        }

=== modified file 'openlp/plugins/presentations/lib/mediaitem.py'
--- openlp/plugins/presentations/lib/mediaitem.py	2010-07-31 02:06:44 +0000
+++ openlp/plugins/presentations/lib/mediaitem.py	2010-09-10 15:56:22 +0000
@@ -58,9 +58,6 @@
         Constructor. Setup defaults
         """
         self.controllers = controllers
-        self.PluginNameShort = u'Presentation'
-        self.pluginNameVisible = translate('PresentationPlugin.MediaItem',
-            'Presentation')
         self.IconPath = u'presentations/presentation'
         self.Automatic = u''
         # this next is a class, not an instance of a class - it will

=== modified file 'openlp/plugins/presentations/presentationplugin.py'
--- openlp/plugins/presentations/presentationplugin.py	2010-07-31 00:05:27 +0000
+++ openlp/plugins/presentations/presentationplugin.py	2010-09-10 15:56:22 +0000
@@ -1,145 +1,187 @@
-# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
-
-###############################################################################
-# OpenLP - Open Source Lyrics Projection                                      #
-# --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2010 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
-# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
-# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
-# Carsten Tinggaard, Frode Woldsund                                           #
-# --------------------------------------------------------------------------- #
-# This program is free software; you can redistribute it and/or modify it     #
-# under the terms of the GNU General Public License as published by the Free  #
-# Software Foundation; version 2 of the License.                              #
-#                                                                             #
-# This program is distributed in the hope that it will be useful, but WITHOUT #
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
-# more details.                                                               #
-#                                                                             #
-# You should have received a copy of the GNU General Public License along     #
-# with this program; if not, write to the Free Software Foundation, Inc., 59  #
-# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
-###############################################################################
-"""
-The :mod:`presentationplugin` module provides the ability for OpenLP to display
-presentations from a variety of document formats.
-"""
-import os
-import logging
-
-from openlp.core.lib import Plugin, build_icon, translate
-from openlp.core.utils import AppLocation
-from openlp.plugins.presentations.lib import PresentationController, \
-    PresentationMediaItem, PresentationTab
-
-log = logging.getLogger(__name__)
-
-class PresentationPlugin(Plugin):
-    """
-    This plugin allowed a Presentation to be opened, controlled and displayed
-    on the output display. The plugin controls third party applications such
-    as OpenOffice.org Impress, Microsoft PowerPoint and the PowerPoint viewer
-    """
-    log = logging.getLogger(u'PresentationPlugin')
-
-    def __init__(self, plugin_helpers):
-        """
-        PluginPresentation constructor.
-        """
-        log.debug(u'Initialised')
-        self.controllers = {}
-        Plugin.__init__(self, u'Presentations', u'1.9.2', plugin_helpers)
-        self.weight = -8
-        self.icon_path = u':/plugins/plugin_presentations.png'
-        self.icon = build_icon(self.icon_path)
-
-    def getSettingsTab(self):
-        """
-        Create the settings Tab
-        """
-        return PresentationTab(self.name, self.controllers)
-
-    def initialise(self):
-        """
-        Initialise the plugin. Determine which controllers are enabled
-        are start their processes.
-        """
-        log.info(u'Presentations Initialising')
-        Plugin.initialise(self)
-        self.insertToolboxItem()
-        for controller in self.controllers:
-            if self.controllers[controller].enabled():
-                self.controllers[controller].start_process()
-        self.mediaItem.buildFileMaskString()
-
-    def finalise(self):
-        """
-        Finalise the plugin. Ask all the enabled presentation applications
-        to close down their applications and release resources.
-        """
-        log.info(u'Plugin Finalise')
-        #Ask each controller to tidy up
-        for key in self.controllers:
-            controller = self.controllers[key]
-            if controller.enabled():
-                controller.kill()
-        Plugin.finalise(self)
-
-    def getMediaManagerItem(self):
-        """
-        Create the Media Manager List
-        """
-        return PresentationMediaItem(
-            self, self.icon, self.name, self.controllers)
-
-    def registerControllers(self, controller):
-        """
-        Register each presentation controller (Impress, PPT etc) and
-        store for later use
-        """
-        self.controllers[controller.name] = controller
-
-    def checkPreConditions(self):
-        """
-        Check to see if we have any presentation software available
-        If Not do not install the plugin.
-        """
-        log.debug(u'checkPreConditions')
-        controller_dir = os.path.join(
-            AppLocation.get_directory(AppLocation.PluginsDir),
-            u'presentations', u'lib')
-        for filename in os.listdir(controller_dir):
-            if filename.endswith(u'controller.py') and \
-                not filename == 'presentationcontroller.py':
-                path = os.path.join(controller_dir, filename)
-                if os.path.isfile(path):
-                    modulename = u'openlp.plugins.presentations.lib.' + \
-                        os.path.splitext(filename)[0]
-                    log.debug(u'Importing controller %s', modulename)
-                    try:
-                        __import__(modulename, globals(), locals(), [])
-                    except ImportError:
-                        log.exception(u'Failed to import %s on path %s',
-                            modulename, path)
-        controller_classes = PresentationController.__subclasses__()
-        for controller_class in controller_classes:
-            controller = controller_class(self)
-            self.registerControllers(controller)
-        if self.controllers:
-            return True
-        else:
-            return False
-
-    def about(self):
-        """
-        Return information about this plugin
-        """
-        about_text = translate('PresentationPlugin', '<strong>Presentation '
-            'Plugin</strong><br />The presentation plugin provides the '
-            'ability to show presentations using a number of different '
-            'programs. The choice of available presentation programs is '
-            'available to the user in a drop down box.')
-        return about_text
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
+# Carsten Tinggaard, Frode Woldsund                                           #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it     #
+# under the terms of the GNU General Public License as published by the Free  #
+# Software Foundation; version 2 of the License.                              #
+#                                                                             #
+# This program is distributed in the hope that it will be useful, but WITHOUT #
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
+# more details.                                                               #
+#                                                                             #
+# You should have received a copy of the GNU General Public License along     #
+# with this program; if not, write to the Free Software Foundation, Inc., 59  #
+# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
+###############################################################################
+"""
+The :mod:`presentationplugin` module provides the ability for OpenLP to display
+presentations from a variety of document formats.
+"""
+import os
+import logging
+
+from openlp.core.lib import Plugin, StringType, build_icon, translate
+from openlp.core.utils import AppLocation
+from openlp.plugins.presentations.lib import PresentationController, \
+    PresentationMediaItem, PresentationTab
+
+log = logging.getLogger(__name__)
+
+class PresentationPlugin(Plugin):
+    """
+    This plugin allowed a Presentation to be opened, controlled and displayed
+    on the output display. The plugin controls third party applications such
+    as OpenOffice.org Impress, Microsoft PowerPoint and the PowerPoint viewer
+    """
+    log = logging.getLogger(u'PresentationPlugin')
+
+    def __init__(self, plugin_helpers):
+        """
+        PluginPresentation constructor.
+        """
+        log.debug(u'Initialised')
+        self.controllers = {}
+        self.set_plugin_strings()
+        Plugin.__init__(self, u'Presentations', u'1.9.2', plugin_helpers)
+        self.weight = -8
+        self.icon_path = u':/plugins/plugin_presentations.png'
+        self.icon = build_icon(self.icon_path)
+
+    def getSettingsTab(self):
+        """
+        Create the settings Tab
+        """
+        return PresentationTab(self.name, self.controllers)
+
+    def initialise(self):
+        """
+        Initialise the plugin. Determine which controllers are enabled
+        are start their processes.
+        """
+        log.info(u'Presentations Initialising')
+        Plugin.initialise(self)
+        self.insertToolboxItem()
+        for controller in self.controllers:
+            if self.controllers[controller].enabled():
+                self.controllers[controller].start_process()
+        self.mediaItem.buildFileMaskString()
+
+    def finalise(self):
+        """
+        Finalise the plugin. Ask all the enabled presentation applications
+        to close down their applications and release resources.
+        """
+        log.info(u'Plugin Finalise')
+        #Ask each controller to tidy up
+        for key in self.controllers:
+            controller = self.controllers[key]
+            if controller.enabled():
+                controller.kill()
+        Plugin.finalise(self)
+
+    def getMediaManagerItem(self):
+        """
+        Create the Media Manager List
+        """
+        return PresentationMediaItem(
+            self, self.icon, self.name, self.controllers)
+
+    def registerControllers(self, controller):
+        """
+        Register each presentation controller (Impress, PPT etc) and
+        store for later use
+        """
+        self.controllers[controller.name] = controller
+
+    def checkPreConditions(self):
+        """
+        Check to see if we have any presentation software available
+        If Not do not install the plugin.
+        """
+        log.debug(u'checkPreConditions')
+        controller_dir = os.path.join(
+            AppLocation.get_directory(AppLocation.PluginsDir),
+            u'presentations', u'lib')
+        for filename in os.listdir(controller_dir):
+            if filename.endswith(u'controller.py') and \
+                not filename == 'presentationcontroller.py':
+                path = os.path.join(controller_dir, filename)
+                if os.path.isfile(path):
+                    modulename = u'openlp.plugins.presentations.lib.' + \
+                        os.path.splitext(filename)[0]
+                    log.debug(u'Importing controller %s', modulename)
+                    try:
+                        __import__(modulename, globals(), locals(), [])
+                    except ImportError:
+                        log.exception(u'Failed to import %s on path %s',
+                            modulename, path)
+        controller_classes = PresentationController.__subclasses__()
+        for controller_class in controller_classes:
+            controller = controller_class(self)
+            self.registerControllers(controller)
+        if self.controllers:
+            return True
+        else:
+            return False
+
+    def about(self):
+        """
+        Return information about this plugin
+        """
+        about_text = translate('PresentationPlugin', '<strong>Presentation '
+            'Plugin</strong><br />The presentation plugin provides the '
+            'ability to show presentations using a number of different '
+            'programs. The choice of available presentation programs is '
+            'available to the user in a drop down box.')
+        return about_text
+
+    def set_plugin_strings(self):
+        """
+        Called to define all translatable texts of the plugin
+        """
+        self.name = u'Presentations'
+        self.name_lower = u'presentations'
+
+        self.strings = {}
+        # for names in mediamanagerdock and pluginlist
+        self.strings[StringType.Name] = {
+            u'singular': translate('PresentationPlugin', 'Presentation'),
+            u'plural': translate('PresentationPlugin', 'Presentations')
+        }
+
+        # Middle Header Bar
+        ## Load Button ##
+        self.strings[StringType.Load] = {
+            u'title': translate('PresentationPlugin', 'Load'),
+            u'tooltip': translate('PresentationPlugin', 'Load a new Presentation')
+        }
+        ## Delete Button ##
+        self.strings[StringType.Delete] = {
+            u'title': translate('PresentationPlugin', 'Delete'),
+            u'tooltip': translate('PresentationPlugin', 'Delete the selected Presentation')
+        }
+        ## Preview ##
+        self.strings[StringType.Preview] = {
+            u'title': translate('PresentationPlugin', 'Preview'),
+            u'tooltip': translate('PresentationPlugin', 'Preview the selected Presentation')
+        }
+        ## Live  Button ##
+        self.strings[StringType.Live] = {
+            u'title': translate('PresentationPlugin', 'Live'),
+            u'tooltip': translate('PresentationPlugin', 'Send the selected Presentation live')
+        }
+        ## Add to service Button ##
+        self.strings[StringType.Service] = {
+            u'title': translate('PresentationPlugin', 'Service'),
+            u'tooltip': translate('PresentationPlugin', 'Add the selected Presentation to the service')
+        }

=== modified file 'openlp/plugins/remotes/remoteplugin.py'
--- openlp/plugins/remotes/remoteplugin.py	2010-07-30 22:12:04 +0000
+++ openlp/plugins/remotes/remoteplugin.py	2010-09-10 15:56:22 +0000
@@ -1,78 +1,93 @@
-# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
-
-###############################################################################
-# OpenLP - Open Source Lyrics Projection                                      #
-# --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2010 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
-# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
-# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
-# Carsten Tinggaard, Frode Woldsund                                           #
-# --------------------------------------------------------------------------- #
-# 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                          #
-###############################################################################
-
-import logging
-
-from openlp.core.lib import Plugin, translate, build_icon
-from openlp.plugins.remotes.lib import RemoteTab, HttpServer
-
-log = logging.getLogger(__name__)
-
-class RemotesPlugin(Plugin):
-    log.info(u'Remote Plugin loaded')
-
-    def __init__(self, plugin_helpers):
-        """
-        remotes constructor
-        """
-        Plugin.__init__(self, u'Remotes', u'1.9.2', plugin_helpers)
-        self.icon = build_icon(u':/plugins/plugin_remote.png')
-        self.weight = -1
-        self.server = None
-
-    def initialise(self):
-        """
-        Initialise the remotes plugin, and start the http server
-        """
-        log.debug(u'initialise')
-        Plugin.initialise(self)
-        self.insertToolboxItem()
-        self.server = HttpServer(self)
-
-    def finalise(self):
-        """
-        Tidy up and close down the http server
-        """
-        log.debug(u'finalise')
-        Plugin.finalise(self)
-        if self.server:
-            self.server.close()
-
-    def getSettingsTab(self):
-        """
-        Create the settings Tab
-        """
-        return RemoteTab(self.name)
-
-    def about(self):
-        """
-        Information about this plugin
-        """
-        about_text = translate('RemotePlugin', '<strong>Remote Plugin</strong>'
-            '<br />The remote plugin provides the ability to send messages to '
-            'a running version of OpenLP on a different computer via a web '
-            'browser or through the remote API.')
-        return about_text
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
+# Carsten Tinggaard, Frode Woldsund                                           #
+# --------------------------------------------------------------------------- #
+# 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                          #
+###############################################################################
+
+import logging
+
+from openlp.core.lib import Plugin, StringType, translate, build_icon
+from openlp.plugins.remotes.lib import RemoteTab, HttpServer
+
+log = logging.getLogger(__name__)
+
+class RemotesPlugin(Plugin):
+    log.info(u'Remote Plugin loaded')
+
+    def __init__(self, plugin_helpers):
+        """
+        remotes constructor
+        """
+        self.set_plugin_strings()
+        Plugin.__init__(self, u'Remotes', u'1.9.2', plugin_helpers)
+        self.icon = build_icon(u':/plugins/plugin_remote.png')
+        self.weight = -1
+        self.server = None
+
+    def initialise(self):
+        """
+        Initialise the remotes plugin, and start the http server
+        """
+        log.debug(u'initialise')
+        Plugin.initialise(self)
+        self.insertToolboxItem()
+        self.server = HttpServer(self)
+
+    def finalise(self):
+        """
+        Tidy up and close down the http server
+        """
+        log.debug(u'finalise')
+        Plugin.finalise(self)
+        if self.server:
+            self.server.close()
+
+    def getSettingsTab(self):
+        """
+        Create the settings Tab
+        """
+        return RemoteTab(self.name)
+
+    def about(self):
+        """
+        Information about this plugin
+        """
+        about_text = translate('RemotePlugin', '<strong>Remote Plugin</strong>'
+            '<br />The remote plugin provides the ability to send messages to '
+            'a running version of OpenLP on a different computer via a web '
+            'browser or through the remote API.')
+        return about_text
+    
+    def set_plugin_strings(self):
+        """
+        Called to define all translatable texts of the plugin
+        """
+        self.name = u'Remotes'
+        self.name_lower = u'remotes'
+
+        self.strings = {}
+        # for names in mediamanagerdock and pluginlist
+        self.strings[StringType.Name] = {
+            u'singular': translate('RemotePlugin', 'Remote'),
+            u'plural': translate('RemotePlugin', 'Remotes')
+        }

=== modified file 'openlp/plugins/songs/lib/mediaitem.py'
--- openlp/plugins/songs/lib/mediaitem.py	2010-09-04 17:39:03 +0000
+++ openlp/plugins/songs/lib/mediaitem.py	2010-09-10 15:56:22 +0000
@@ -49,8 +49,6 @@
     log.info(u'Song Media Item loaded')
 
     def __init__(self, parent, icon, title):
-        self.PluginNameShort = u'Song'
-        self.pluginNameVisible = translate('SongsPlugin.MediaItem', 'Song')
         self.IconPath = u'songs/song'
         self.ListViewWithDnD_class = SongListView
         MediaManagerItem.__init__(self, parent, icon, title)

=== modified file 'openlp/plugins/songs/songsplugin.py'
--- openlp/plugins/songs/songsplugin.py	2010-08-26 19:45:09 +0000
+++ openlp/plugins/songs/songsplugin.py	2010-09-10 15:56:22 +0000
@@ -1,149 +1,196 @@
-# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
-
-###############################################################################
-# OpenLP - Open Source Lyrics Projection                                      #
-# --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2010 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
-# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
-# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
-# Carsten Tinggaard, Frode Woldsund                                           #
-# --------------------------------------------------------------------------- #
-# 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                          #
-###############################################################################
-
-import logging
-
-from PyQt4 import QtCore, QtGui
-
-from openlp.core.lib import Plugin, build_icon, translate
-from openlp.core.lib.db import Manager
-from openlp.plugins.songs.lib import SongMediaItem, SongsTab
-from openlp.plugins.songs.lib.db import init_schema, Song
-from openlp.plugins.songs.lib.importer import SongFormat
-
-log = logging.getLogger(__name__)
-
-class SongsPlugin(Plugin):
-    """
-    This is the number 1 plugin, if importance were placed on any
-    plugins. This plugin enables the user to create, edit and display
-    songs. Songs are divided into verses, and the verse order can be
-    specified. Authors, topics and song books can be assigned to songs
-    as well.
-    """
-    log.info(u'Song Plugin loaded')
-
-    def __init__(self, plugin_helpers):
-        """
-        Create and set up the Songs plugin.
-        """
-        Plugin.__init__(self, u'Songs', u'1.9.2', plugin_helpers)
-        self.weight = -10
-        self.manager = Manager(u'songs', init_schema)
-        self.icon_path = u':/plugins/plugin_songs.png'
-        self.icon = build_icon(self.icon_path)
-
-    def getSettingsTab(self):
-        return SongsTab(self.name)
-
-    def initialise(self):
-        log.info(u'Songs Initialising')
-        Plugin.initialise(self)
-        self.mediaItem.displayResultsSong(
-            self.manager.get_all_objects(Song, order_by_ref=Song.title))
-
-    def getMediaManagerItem(self):
-        """
-        Create the MediaManagerItem object, which is displaed in the
-        Media Manager.
-        """
-        return SongMediaItem(self, self.icon, self.name)
-
-    def addImportMenuItem(self, import_menu):
-        """
-        Give the Songs plugin the opportunity to add items to the
-        **Import** menu.
-
-        ``import_menu``
-            The actual **Import** menu item, so that your actions can
-            use it as their parent.
-        """
-        # Main song import menu item - will eventually be the only one
-        self.SongImportItem = QtGui.QAction(import_menu)
-        self.SongImportItem.setObjectName(u'SongImportItem')
-        self.SongImportItem.setText(translate(
-            'SongsPlugin', '&Song'))
-        self.SongImportItem.setToolTip(translate('SongsPlugin',
-            'Import songs using the import wizard.'))
-        import_menu.addAction(self.SongImportItem)
-        # Signals and slots
-        QtCore.QObject.connect(self.SongImportItem,
-            QtCore.SIGNAL(u'triggered()'), self.onSongImportItemClicked)
-
-    def addExportMenuItem(self, export_menu):
-        """
-        Give the Songs plugin the opportunity to add items to the
-        **Export** menu.
-
-        ``export_menu``
-            The actual **Export** menu item, so that your actions can
-            use it as their parent.
-        """
-        # No menu items for now.
-        pass
-
-    def onSongImportItemClicked(self):
-        if self.mediaItem:
-            self.mediaItem.onImportClick()
-
-    def about(self):
-        about_text = translate('SongsPlugin', '<strong>Songs Plugin</strong>'
-            '<br />The songs plugin provides the ability to display and '
-            'manage songs.')
-        return about_text
-
-    def usesTheme(self, theme):
-        """
-        Called to find out if the song plugin is currently using a theme.
-
-        Returns True if the theme is being used, otherwise returns False.
-        """
-        if self.manager.get_all_objects(Song, Song.theme_name == theme):
-            return True
-        return False
-
-    def renameTheme(self, oldTheme, newTheme):
-        """
-        Renames a theme the song plugin is using making the plugin use the new
-        name.
-
-        ``oldTheme``
-            The name of the theme the plugin should stop using.
-
-        ``newTheme``
-            The new name the plugin should now use.
-        """
-        songsUsingTheme = self.manager.get_all_objects(Song,
-            Song.theme_name == oldTheme)
-        for song in songsUsingTheme:
-            song.theme_name = newTheme
-            self.custommanager.save_object(song)
-
-    def importSongs(self, format, **kwargs):
-        class_ = SongFormat.get_class(format)
-        importer = class_(self.manager, **kwargs)
-        importer.register(self.mediaItem.import_wizard)
-        return importer
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
+# Carsten Tinggaard, Frode Woldsund                                           #
+# --------------------------------------------------------------------------- #
+# 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                          #
+###############################################################################
+
+import logging
+
+from PyQt4 import QtCore, QtGui
+
+from openlp.core.lib import Plugin, StringType, build_icon, translate
+from openlp.core.lib.db import Manager
+from openlp.plugins.songs.lib import SongMediaItem, SongsTab
+from openlp.plugins.songs.lib.db import init_schema, Song
+from openlp.plugins.songs.lib.importer import SongFormat
+
+log = logging.getLogger(__name__)
+
+class SongsPlugin(Plugin):
+    """
+    This is the number 1 plugin, if importance were placed on any
+    plugins. This plugin enables the user to create, edit and display
+    songs. Songs are divided into verses, and the verse order can be
+    specified. Authors, topics and song books can be assigned to songs
+    as well.
+    """
+    log.info(u'Song Plugin loaded')
+
+    def __init__(self, plugin_helpers):
+        """
+        Create and set up the Songs plugin.
+        """
+        self.set_plugin_strings()
+        Plugin.__init__(self, u'Songs', u'1.9.2', plugin_helpers)
+        self.weight = -10
+        self.manager = Manager(u'songs', init_schema)
+        self.icon_path = u':/plugins/plugin_songs.png'
+        self.icon = build_icon(self.icon_path)
+
+    def getSettingsTab(self):
+        return SongsTab(self.name)
+
+    def initialise(self):
+        log.info(u'Songs Initialising')
+        Plugin.initialise(self)
+        self.mediaItem.displayResultsSong(
+            self.manager.get_all_objects(Song, order_by_ref=Song.title))
+
+    def getMediaManagerItem(self):
+        """
+        Create the MediaManagerItem object, which is displaed in the
+        Media Manager.
+        """
+        return SongMediaItem(self, self.icon, self.name)
+
+    def addImportMenuItem(self, import_menu):
+        """
+        Give the Songs plugin the opportunity to add items to the
+        **Import** menu.
+
+        ``import_menu``
+            The actual **Import** menu item, so that your actions can
+            use it as their parent.
+        """
+        # Main song import menu item - will eventually be the only one
+        self.SongImportItem = QtGui.QAction(import_menu)
+        self.SongImportItem.setObjectName(u'SongImportItem')
+        self.SongImportItem.setText(translate(
+            'SongsPlugin', '&Song'))
+        self.SongImportItem.setToolTip(translate('SongsPlugin',
+            'Import songs using the import wizard.'))
+        import_menu.addAction(self.SongImportItem)
+        # Signals and slots
+        QtCore.QObject.connect(self.SongImportItem,
+            QtCore.SIGNAL(u'triggered()'), self.onSongImportItemClicked)
+
+    def addExportMenuItem(self, export_menu):
+        """
+        Give the Songs plugin the opportunity to add items to the
+        **Export** menu.
+
+        ``export_menu``
+            The actual **Export** menu item, so that your actions can
+            use it as their parent.
+        """
+        # No menu items for now.
+        pass
+
+    def onSongImportItemClicked(self):
+        if self.mediaItem:
+            self.mediaItem.onImportClick()
+
+    def about(self):
+        about_text = translate('SongsPlugin', '<strong>Songs Plugin</strong>'
+            '<br />The songs plugin provides the ability to display and '
+            'manage songs.')
+        return about_text
+
+    def usesTheme(self, theme):
+        """
+        Called to find out if the song plugin is currently using a theme.
+
+        Returns True if the theme is being used, otherwise returns False.
+        """
+        if self.manager.get_all_objects(Song, Song.theme_name == theme):
+            return True
+        return False
+
+    def renameTheme(self, oldTheme, newTheme):
+        """
+        Renames a theme the song plugin is using making the plugin use the new
+        name.
+
+        ``oldTheme``
+            The name of the theme the plugin should stop using.
+
+        ``newTheme``
+            The new name the plugin should now use.
+        """
+        songsUsingTheme = self.manager.get_all_objects(Song,
+            Song.theme_name == oldTheme)
+        for song in songsUsingTheme:
+            song.theme_name = newTheme
+            self.custommanager.save_object(song)
+
+    def importSongs(self, format, **kwargs):
+        class_ = SongFormat.get_class(format)
+        importer = class_(self.manager, **kwargs)
+        importer.register(self.mediaItem.import_wizard)
+        return importer
+
+    def set_plugin_strings(self):
+        """
+        Called to define all translatable texts of the plugin
+        """
+        self.name = u'Songs'
+        self.name_lower = u'songs'
+
+        self.strings = {}
+        # for names in mediamanagerdock and pluginlist
+        self.strings[StringType.Name] = {
+            u'singular': translate('SongsPlugin', 'Song'),
+            u'plural': translate('SongsPlugin', 'Songs')
+        }
+
+        # Middle Header Bar
+        ## New Button ##
+        self.strings[StringType.New] = {
+            u'title': translate('SongsPlugin', 'Add'),
+            u'tooltip': translate('SongsPlugin', 'Add a new Song')
+        }
+        ## Edit Button ##
+        self.strings[StringType.Edit] = {
+            u'title': translate('SongsPlugin', 'Edit'),
+            u'tooltip': translate('SongsPlugin', 'Edit the selected Song')
+        }
+        ## Delete Button ##
+        self.strings[StringType.Delete] = {
+            u'title': translate('SongsPlugin', 'Delete'),
+            u'tooltip': translate('SongsPlugin', 'Delete the selected Song')
+        }
+        ## Preview ##
+        self.strings[StringType.Preview] = {
+            u'title': translate('SongsPlugin', 'Preview'),
+            u'tooltip': translate('SongsPlugin', 'Preview the selected Song')
+        }
+        ## Live  Button ##
+        self.strings[StringType.Live] = {
+            u'title': translate('SongsPlugin', 'Live'),
+            u'tooltip': translate('SongsPlugin', 'Send the selected Song live')
+        }
+        ## Add to service Button ##
+        self.strings[StringType.Service] = {
+            u'title': translate('SongsPlugin', 'Service'),
+            u'tooltip': translate('SongsPlugin', 'Add the selected Song to the service')
+        }

=== modified file 'openlp/plugins/songusage/songusageplugin.py'
--- openlp/plugins/songusage/songusageplugin.py	2010-08-02 19:05:40 +0000
+++ openlp/plugins/songusage/songusageplugin.py	2010-09-10 15:56:22 +0000
@@ -1,164 +1,179 @@
-# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
-
-###############################################################################
-# OpenLP - Open Source Lyrics Projection                                      #
-# --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2010 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
-# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
-# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
-# Carsten Tinggaard, Frode Woldsund                                           #
-# --------------------------------------------------------------------------- #
-# 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                          #
-###############################################################################
-
-import logging
-from datetime import datetime
-
-from PyQt4 import QtCore, QtGui
-
-from openlp.core.lib import Plugin, Receiver, build_icon, translate
-from openlp.core.lib.db import Manager
-from openlp.plugins.songusage.forms import SongUsageDetailForm, \
-    SongUsageDeleteForm
-from openlp.plugins.songusage.lib.db import init_schema, SongUsageItem
-
-log = logging.getLogger(__name__)
-
-class SongUsagePlugin(Plugin):
-    log.info(u'SongUsage Plugin loaded')
-
-    def __init__(self, plugin_helpers):
-        Plugin.__init__(self, u'SongUsage', u'1.9.2', plugin_helpers)
-        self.weight = -4
-        self.icon = build_icon(u':/plugins/plugin_songusage.png')
-        self.songusagemanager = None
-        self.songusageActive = False
-
-    def addToolsMenuItem(self, tools_menu):
-        """
-        Give the SongUsage plugin the opportunity to add items to the
-        **Tools** menu.
-
-        ``tools_menu``
-            The actual **Tools** menu item, so that your actions can
-            use it as their parent.
-        """
-        log.info(u'add tools menu')
-        self.toolsMenu = tools_menu
-        self.SongUsageMenu = QtGui.QMenu(tools_menu)
-        self.SongUsageMenu.setObjectName(u'SongUsageMenu')
-        self.SongUsageMenu.setTitle(translate(
-            'SongUsagePlugin', '&Song Usage Tracking'))
-        #SongUsage Delete
-        self.SongUsageDelete = QtGui.QAction(tools_menu)
-        self.SongUsageDelete.setText(translate('SongUsagePlugin',
-            '&Delete Tracking Data'))
-        self.SongUsageDelete.setStatusTip(translate('SongUsagePlugin',
-            'Delete song usage data up to a specified date.'))
-        self.SongUsageDelete.setObjectName(u'SongUsageDelete')
-        #SongUsage Report
-        self.SongUsageReport = QtGui.QAction(tools_menu)
-        self.SongUsageReport.setText(
-            translate('SongUsagePlugin', '&Extract Tracking Data'))
-        self.SongUsageReport.setStatusTip(
-            translate('SongUsagePlugin', 'Generate a report on song usage.'))
-        self.SongUsageReport.setObjectName(u'SongUsageReport')
-        #SongUsage activation
-        self.SongUsageStatus = QtGui.QAction(tools_menu)
-        self.SongUsageStatus.setCheckable(True)
-        self.SongUsageStatus.setChecked(False)
-        self.SongUsageStatus.setText(translate(
-            'SongUsagePlugin', 'Toggle Tracking'))
-        self.SongUsageStatus.setStatusTip(translate('SongUsagePlugin',
-                'Toggle the tracking of song usage.'))
-        self.SongUsageStatus.setShortcut(u'F4')
-        self.SongUsageStatus.setObjectName(u'SongUsageStatus')
-        #Add Menus together
-        self.toolsMenu.addAction(self.SongUsageMenu.menuAction())
-        self.SongUsageMenu.addAction(self.SongUsageStatus)
-        self.SongUsageMenu.addSeparator()
-        self.SongUsageMenu.addAction(self.SongUsageDelete)
-        self.SongUsageMenu.addAction(self.SongUsageReport)
-        # Signals and slots
-        QtCore.QObject.connect(self.SongUsageStatus,
-            QtCore.SIGNAL(u'visibilityChanged(bool)'),
-            self.SongUsageStatus.setChecked)
-        QtCore.QObject.connect(self.SongUsageStatus,
-            QtCore.SIGNAL(u'triggered(bool)'),
-            self.toggleSongUsageState)
-        QtCore.QObject.connect(self.SongUsageDelete,
-            QtCore.SIGNAL(u'triggered()'), self.onSongUsageDelete)
-        QtCore.QObject.connect(self.SongUsageReport,
-            QtCore.SIGNAL(u'triggered()'), self.onSongUsageReport)
-        self.SongUsageMenu.menuAction().setVisible(False)
-
-    def initialise(self):
-        log.info(u'SongUsage Initialising')
-        Plugin.initialise(self)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'slidecontroller_live_started'),
-            self.onReceiveSongUsage)
-        self.SongUsageActive = QtCore.QSettings().value(
-            self.settingsSection + u'/active',
-            QtCore.QVariant(False)).toBool()
-        self.SongUsageStatus.setChecked(self.SongUsageActive)
-        if self.songusagemanager is None:
-            self.songusagemanager = Manager(u'songusage', init_schema)
-        self.SongUsagedeleteform = SongUsageDeleteForm(self.songusagemanager,
-            self.formparent)
-        self.SongUsagedetailform = SongUsageDetailForm(self, self.formparent)
-        self.SongUsageMenu.menuAction().setVisible(True)
-
-    def finalise(self):
-        log.info(u'Plugin Finalise')
-        self.SongUsageMenu.menuAction().setVisible(False)
-        #stop any events being processed
-        self.SongUsageActive = False
-
-    def toggleSongUsageState(self):
-        self.SongUsageActive = not self.SongUsageActive
-        QtCore.QSettings().setValue(self.settingsSection + u'/active',
-            QtCore.QVariant(self.SongUsageActive))
-
-    def onReceiveSongUsage(self, item):
-        """
-        Song Usage for live song from SlideController
-        """
-        audit = item[0].audit
-        if self.SongUsageActive and audit:
-            song_usage_item = SongUsageItem()
-            song_usage_item.usagedate = datetime.today()
-            song_usage_item.usagetime = datetime.now().time()
-            song_usage_item.title = audit[0]
-            song_usage_item.copyright = audit[2]
-            song_usage_item.ccl_number = audit[3]
-            song_usage_item.authors = u''
-            for author in audit[1]:
-                song_usage_item.authors += author + u' '
-            self.songusagemanager.save_object(song_usage_item)
-
-    def onSongUsageDelete(self):
-        self.SongUsagedeleteform.exec_()
-
-    def onSongUsageReport(self):
-        self.SongUsagedetailform.initialise()
-        self.SongUsagedetailform.exec_()
-
-    def about(self):
-        about_text = translate('SongUsagePlugin', '<strong>SongUsage Plugin'
-            '</strong><br />This plugin tracks the usage of songs in '
-            'services.')
-        return about_text
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection                                      #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2010 Raoul Snyman                                        #
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael      #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian      #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble,    #
+# Carsten Tinggaard, Frode Woldsund                                           #
+# --------------------------------------------------------------------------- #
+# 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                          #
+###############################################################################
+
+import logging
+from datetime import datetime
+
+from PyQt4 import QtCore, QtGui
+
+from openlp.core.lib import Plugin, StringType, Receiver, build_icon, translate
+from openlp.core.lib.db import Manager
+from openlp.plugins.songusage.forms import SongUsageDetailForm, \
+    SongUsageDeleteForm
+from openlp.plugins.songusage.lib.db import init_schema, SongUsageItem
+
+log = logging.getLogger(__name__)
+
+class SongUsagePlugin(Plugin):
+    log.info(u'SongUsage Plugin loaded')
+
+    def __init__(self, plugin_helpers):
+        self.set_plugin_strings()
+        Plugin.__init__(self, u'SongUsage', u'1.9.2', plugin_helpers)
+        self.weight = -4
+        self.icon = build_icon(u':/plugins/plugin_songusage.png')
+        self.songusagemanager = None
+        self.songusageActive = False
+
+    def addToolsMenuItem(self, tools_menu):
+        """
+        Give the SongUsage plugin the opportunity to add items to the
+        **Tools** menu.
+
+        ``tools_menu``
+            The actual **Tools** menu item, so that your actions can
+            use it as their parent.
+        """
+        log.info(u'add tools menu')
+        self.toolsMenu = tools_menu
+        self.SongUsageMenu = QtGui.QMenu(tools_menu)
+        self.SongUsageMenu.setObjectName(u'SongUsageMenu')
+        self.SongUsageMenu.setTitle(translate(
+            'SongUsagePlugin', '&Song Usage Tracking'))
+        #SongUsage Delete
+        self.SongUsageDelete = QtGui.QAction(tools_menu)
+        self.SongUsageDelete.setText(translate('SongUsagePlugin',
+            '&Delete Tracking Data'))
+        self.SongUsageDelete.setStatusTip(translate('SongUsagePlugin',
+            'Delete song usage data up to a specified date.'))
+        self.SongUsageDelete.setObjectName(u'SongUsageDelete')
+        #SongUsage Report
+        self.SongUsageReport = QtGui.QAction(tools_menu)
+        self.SongUsageReport.setText(
+            translate('SongUsagePlugin', '&Extract Tracking Data'))
+        self.SongUsageReport.setStatusTip(
+            translate('SongUsagePlugin', 'Generate a report on song usage.'))
+        self.SongUsageReport.setObjectName(u'SongUsageReport')
+        #SongUsage activation
+        self.SongUsageStatus = QtGui.QAction(tools_menu)
+        self.SongUsageStatus.setCheckable(True)
+        self.SongUsageStatus.setChecked(False)
+        self.SongUsageStatus.setText(translate(
+            'SongUsagePlugin', 'Toggle Tracking'))
+        self.SongUsageStatus.setStatusTip(translate('SongUsagePlugin',
+                'Toggle the tracking of song usage.'))
+        self.SongUsageStatus.setShortcut(u'F4')
+        self.SongUsageStatus.setObjectName(u'SongUsageStatus')
+        #Add Menus together
+        self.toolsMenu.addAction(self.SongUsageMenu.menuAction())
+        self.SongUsageMenu.addAction(self.SongUsageStatus)
+        self.SongUsageMenu.addSeparator()
+        self.SongUsageMenu.addAction(self.SongUsageDelete)
+        self.SongUsageMenu.addAction(self.SongUsageReport)
+        # Signals and slots
+        QtCore.QObject.connect(self.SongUsageStatus,
+            QtCore.SIGNAL(u'visibilityChanged(bool)'),
+            self.SongUsageStatus.setChecked)
+        QtCore.QObject.connect(self.SongUsageStatus,
+            QtCore.SIGNAL(u'triggered(bool)'),
+            self.toggleSongUsageState)
+        QtCore.QObject.connect(self.SongUsageDelete,
+            QtCore.SIGNAL(u'triggered()'), self.onSongUsageDelete)
+        QtCore.QObject.connect(self.SongUsageReport,
+            QtCore.SIGNAL(u'triggered()'), self.onSongUsageReport)
+        self.SongUsageMenu.menuAction().setVisible(False)
+
+    def initialise(self):
+        log.info(u'SongUsage Initialising')
+        Plugin.initialise(self)
+        QtCore.QObject.connect(Receiver.get_receiver(),
+            QtCore.SIGNAL(u'slidecontroller_live_started'),
+            self.onReceiveSongUsage)
+        self.SongUsageActive = QtCore.QSettings().value(
+            self.settingsSection + u'/active',
+            QtCore.QVariant(False)).toBool()
+        self.SongUsageStatus.setChecked(self.SongUsageActive)
+        if self.songusagemanager is None:
+            self.songusagemanager = Manager(u'songusage', init_schema)
+        self.SongUsagedeleteform = SongUsageDeleteForm(self.songusagemanager,
+            self.formparent)
+        self.SongUsagedetailform = SongUsageDetailForm(self, self.formparent)
+        self.SongUsageMenu.menuAction().setVisible(True)
+
+    def finalise(self):
+        log.info(u'Plugin Finalise')
+        self.SongUsageMenu.menuAction().setVisible(False)
+        #stop any events being processed
+        self.SongUsageActive = False
+
+    def toggleSongUsageState(self):
+        self.SongUsageActive = not self.SongUsageActive
+        QtCore.QSettings().setValue(self.settingsSection + u'/active',
+            QtCore.QVariant(self.SongUsageActive))
+
+    def onReceiveSongUsage(self, item):
+        """
+        Song Usage for live song from SlideController
+        """
+        audit = item[0].audit
+        if self.SongUsageActive and audit:
+            song_usage_item = SongUsageItem()
+            song_usage_item.usagedate = datetime.today()
+            song_usage_item.usagetime = datetime.now().time()
+            song_usage_item.title = audit[0]
+            song_usage_item.copyright = audit[2]
+            song_usage_item.ccl_number = audit[3]
+            song_usage_item.authors = u''
+            for author in audit[1]:
+                song_usage_item.authors += author + u' '
+            self.songusagemanager.save_object(song_usage_item)
+
+    def onSongUsageDelete(self):
+        self.SongUsagedeleteform.exec_()
+
+    def onSongUsageReport(self):
+        self.SongUsagedetailform.initialise()
+        self.SongUsagedetailform.exec_()
+
+    def about(self):
+        about_text = translate('SongUsagePlugin', '<strong>SongUsage Plugin'
+            '</strong><br />This plugin tracks the usage of songs in '
+            'services.')
+        return about_text
+
+    def set_plugin_strings(self):
+        """
+        Called to define all translatable texts of the plugin
+        """
+        self.name = u'SongUsage'
+        self.name_lower = u'songusage'
+
+        self.strings = {}
+        # for names in mediamanagerdock and pluginlist
+        self.strings[StringType.Name] = {
+            u'singular': translate('SongUsagePlugin', 'SongUsage'),
+            u'plural': translate('SongUsagePlugin', 'SongUsage')
+        }

=== modified file 'resources/i18n/openlp_af.ts'
--- resources/i18n/openlp_af.ts	2010-08-26 08:07:16 +0000
+++ resources/i18n/openlp_af.ts	2010-09-10 15:56:22 +0000
@@ -3,20 +3,30 @@
 <context>
     <name>AlertsPlugin</name>
     <message>
-        <location filename="openlp/plugins/alerts/alertsplugin.py" line="70"/>
+        <location filename="openlp/plugins/alerts/alertsplugin.py" line="71"/>
         <source>&amp;Alert</source>
         <translation type="unfinished">W&amp;aarskuwing</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/alerts/alertsplugin.py" line="71"/>
+        <location filename="openlp/plugins/alerts/alertsplugin.py" line="72"/>
         <source>Show an alert message.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/alerts/alertsplugin.py" line="99"/>
+        <location filename="openlp/plugins/alerts/alertsplugin.py" line="101"/>
         <source>&lt;strong&gt;Alerts Plugin&lt;/strong&gt;&lt;br /&gt;The alert plugin controls the displaying of nursery alerts on the display screen</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="openlp/plugins/alerts/alertsplugin.py" line="116"/>
+        <source>Alert</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/alerts/alertsplugin.py" line="117"/>
+        <source>Alerts</source>
+        <translation type="unfinished">Waarskuwings</translation>
+    </message>
 </context>
 <context>
     <name>AlertsPlugin.AlertForm</name>
@@ -79,7 +89,7 @@
 <context>
     <name>AlertsPlugin.AlertsManager</name>
     <message>
-        <location filename="openlp/plugins/alerts/lib/alertsmanager.py" line="81"/>
+        <location filename="openlp/plugins/alerts/lib/alertsmanager.py" line="72"/>
         <source>Alert message created and displayed.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -165,15 +175,95 @@
 <context>
     <name>BiblesPlugin</name>
     <message>
-        <location filename="openlp/plugins/bibles/bibleplugin.py" line="82"/>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="83"/>
         <source>&amp;Bible</source>
         <translation type="unfinished">&amp;Bybel</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/bibleplugin.py" line="91"/>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="92"/>
         <source>&lt;strong&gt;Bible Plugin&lt;/strong&gt;&lt;br /&gt;The Bible plugin provides the ability to display bible verses from different sources during the service.</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="131"/>
+        <source>Bible</source>
+        <translation type="unfinished">Bybel</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="132"/>
+        <source>Bibles</source>
+        <translation type="unfinished">Bybels</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="138"/>
+        <source>Import</source>
+        <translation type="unfinished">Invoer</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="139"/>
+        <source>Import a Bible</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="143"/>
+        <source>Add</source>
+        <translation type="unfinished">Byvoeg</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="144"/>
+        <source>Add a new Bible</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="148"/>
+        <source>Edit</source>
+        <translation type="unfinished">Redigeer</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="149"/>
+        <source>Edit the selected Bible</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="153"/>
+        <source>Delete</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="154"/>
+        <source>Delete the selected Bible</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="158"/>
+        <source>Preview</source>
+        <translation type="unfinished">Voorskou</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="159"/>
+        <source>Preview the selected Bible</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="163"/>
+        <source>Live</source>
+        <translation type="unfinished">Regstreeks</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="164"/>
+        <source>Send the selected Bible live</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="168"/>
+        <source>Service</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="169"/>
+        <source>Add the selected Bible to the service</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>BiblesPlugin.BibleDB</name>
@@ -554,112 +644,107 @@
 <context>
     <name>BiblesPlugin.MediaItem</name>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="59"/>
-        <source>Bible</source>
-        <translation type="unfinished">Bybel</translation>
-    </message>
-    <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="149"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="146"/>
         <source>Quick</source>
         <translation type="unfinished">Vinnig</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="234"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="231"/>
         <source>Advanced</source>
         <translation type="unfinished">Gevorderd</translation>
     </message>
     <message>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="303"/>
+        <source>Version:</source>
+        <translation type="unfinished">Weergawe:</translation>
+    </message>
+    <message>
         <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="305"/>
-        <source>Version:</source>
-        <translation type="unfinished">Weergawe:</translation>
-    </message>
-    <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="307"/>
         <source>Dual:</source>
         <translation type="unfinished">Dubbel:</translation>
     </message>
     <message>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="295"/>
+        <source>Search type:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="297"/>
-        <source>Search type:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="299"/>
         <source>Find:</source>
         <translation type="unfinished">Vind:</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="321"/>
-        <source>Search</source>
-        <translation type="unfinished">Soek</translation>
-    </message>
-    <message>
         <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="319"/>
+        <source>Search</source>
+        <translation type="unfinished">Soek</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="317"/>
         <source>Results:</source>
         <translation type="unfinished">&amp;Resultate:</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="309"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="307"/>
         <source>Book:</source>
         <translation type="unfinished">Boek:</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="311"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="309"/>
         <source>Chapter:</source>
         <translation type="unfinished">Hoofstuk:</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="313"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="311"/>
         <source>Verse:</source>
         <translation type="unfinished">Vers:</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="315"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="313"/>
         <source>From:</source>
         <translation type="unfinished">Vanaf:</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="317"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="315"/>
         <source>To:</source>
         <translation type="unfinished">Aan:</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="323"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="321"/>
         <source>Verse Search</source>
         <translation type="unfinished">Soek Vers</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="325"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="323"/>
         <source>Text Search</source>
         <translation type="unfinished">Teks Soektog</translation>
     </message>
     <message>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="329"/>
+        <source>Clear</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="331"/>
-        <source>Clear</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="333"/>
         <source>Keep</source>
         <translation type="unfinished">Behou</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="387"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="385"/>
         <source>No Book Found</source>
         <translation type="unfinished">Geeb Boek Gevind nie</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="387"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="385"/>
         <source>No matching book could be found in this Bible.</source>
         <translation type="unfinished">Geen bypassende boek kon in dié Bybel gevind word nie.</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="569"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="567"/>
         <source>etc</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="621"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="625"/>
         <source>Bible not fully loaded.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -675,8 +760,8 @@
 <context>
     <name>CustomPlugin</name>
     <message>
-        <location filename="openlp/plugins/custom/customplugin.py" line="65"/>
-        <source>&lt;strong&gt;Custom Plugin&lt;/strong&gt;&lt;br /&gt;The custom plugin provides the ability to set up custom text slides that can be displayed on the screen the same way songs are. This plugin provides greater freedom over the songs plugin.</source>
+        <location filename="openlp/plugins/custom/customplugin.py" line="66"/>
+        <source>&lt;strong&gt;Custom Plugin&lt;/strong&gt;&lt;br /&gt;The custom plugin provides the ability to set up custom text slides that can be displayed on the screen the same way Customs are. This plugin provides greater freedom over the Customs plugin.</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
@@ -829,73 +914,241 @@
 <context>
     <name>CustomPlugin.MediaItem</name>
     <message>
-        <location filename="openlp/plugins/custom/lib/mediaitem.py" line="51"/>
-        <source>Custom</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/plugins/custom/lib/mediaitem.py" line="123"/>
+        <location filename="openlp/plugins/custom/lib/mediaitem.py" line="121"/>
         <source>You haven&apos;t selected an item to edit.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/custom/lib/mediaitem.py" line="136"/>
+        <location filename="openlp/plugins/custom/lib/mediaitem.py" line="134"/>
         <source>You haven&apos;t selected an item to delete.</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
+    <name>CustomsPlugin</name>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="111"/>
+        <source>Custom</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="112"/>
+        <source>Customs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="118"/>
+        <source>Import</source>
+        <translation type="unfinished">Invoer</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="119"/>
+        <source>Import a Custom</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="123"/>
+        <source>Load</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="124"/>
+        <source>Load a new Custom</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="128"/>
+        <source>Add</source>
+        <translation type="unfinished">Byvoeg</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="129"/>
+        <source>Add a new Custom</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="133"/>
+        <source>Edit</source>
+        <translation type="unfinished">Redigeer</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="134"/>
+        <source>Edit the selected Custom</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="138"/>
+        <source>Delete</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="139"/>
+        <source>Delete the selected Custom</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="143"/>
+        <source>Preview</source>
+        <translation type="unfinished">Voorskou</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="144"/>
+        <source>Preview the selected Custom</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="148"/>
+        <source>Live</source>
+        <translation type="unfinished">Regstreeks</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="149"/>
+        <source>Send the selected Custom live</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="153"/>
+        <source>Service</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="154"/>
+        <source>Add the selected Custom to the service</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ImagePlugin</name>
     <message>
-        <location filename="openlp/plugins/images/imageplugin.py" line="48"/>
-        <source>&lt;strong&gt;Image Plugin&lt;/strong&gt;&lt;br /&gt;The image plugin provides displaying of images.&lt;br /&gt;One of the distinguishing features of this plugin is the ability to group a number of images together in the service manager, making the displaying of multiple images easier. This plugin can also make use of OpenLP&apos;s &quot;timed looping&quot; feature to create a slide show that runs automatically. In addition to this, images from the plugin can be used to override the current theme&apos;s background, which renders text-based items like songs with the selected image as a background instead of the background provided by the theme.</source>
+        <location filename="openlp/plugins/images/imageplugin.py" line="49"/>
+        <source>&lt;strong&gt;Image Plugin&lt;/strong&gt;&lt;br /&gt;The image plugin provides displaying of images.&lt;br /&gt;One of the distinguishing features of this plugin is the ability to group a number of images together in the service manager, making the displaying of multiple images easier. This plugin can also make use of OpenLP&apos;s &quot;timed looping&quot; feature to create a slide show that runs automatically. In addition to this, images from the plugin can be used to override the current theme&apos;s background, which renders text-based items like Images with the selected image as a background instead of the background provided by the theme.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="73"/>
+        <source>Image</source>
+        <translation type="unfinished">Beeld</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="74"/>
+        <source>Images</source>
+        <translation type="unfinished">Beelde</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="80"/>
+        <source>Load</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="81"/>
+        <source>Load a new Image</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="85"/>
+        <source>Add</source>
+        <translation type="unfinished">Byvoeg</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="86"/>
+        <source>Add a new Image</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="90"/>
+        <source>Edit</source>
+        <translation type="unfinished">Redigeer</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="91"/>
+        <source>Edit the selected Image</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="95"/>
+        <source>Delete</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="96"/>
+        <source>Delete the selected Image</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="100"/>
+        <source>Preview</source>
+        <translation type="unfinished">Voorskou</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="101"/>
+        <source>Preview the selected Image</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="105"/>
+        <source>Live</source>
+        <translation type="unfinished">Regstreeks</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="106"/>
+        <source>Send the selected Image live</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="110"/>
+        <source>Service</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="111"/>
+        <source>Add the selected Image to the service</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
     <name>ImagePlugin.MediaItem</name>
     <message>
-        <location filename="openlp/plugins/images/lib/mediaitem.py" line="54"/>
-        <source>Image</source>
-        <translation type="unfinished">Beeld</translation>
-    </message>
-    <message>
-        <location filename="openlp/plugins/images/lib/mediaitem.py" line="62"/>
+        <location filename="openlp/plugins/images/lib/mediaitem.py" line="60"/>
         <source>Select Image(s)</source>
         <translation type="unfinished">Selekteer beeld(e)</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/images/lib/mediaitem.py" line="65"/>
+        <location filename="openlp/plugins/images/lib/mediaitem.py" line="63"/>
         <source>All Files</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/images/lib/mediaitem.py" line="108"/>
+        <location filename="openlp/plugins/images/lib/mediaitem.py" line="106"/>
         <source>Replace Live Background</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/images/lib/mediaitem.py" line="108"/>
+        <location filename="openlp/plugins/images/lib/mediaitem.py" line="106"/>
         <source>Replace Background</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/images/lib/mediaitem.py" line="120"/>
+        <location filename="openlp/plugins/media/lib/mediaitem.py" line="98"/>
+        <source>Reset Live Background</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/lib/mediaitem.py" line="124"/>
         <source>You must select an image to delete.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/images/lib/mediaitem.py" line="156"/>
+        <location filename="openlp/plugins/images/lib/mediaitem.py" line="160"/>
         <source>Image(s)</source>
         <translation type="unfinished">Beeld(e)</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/images/lib/mediaitem.py" line="173"/>
+        <location filename="openlp/plugins/images/lib/mediaitem.py" line="181"/>
         <source>You must select an image to replace the background with.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/media/lib/mediaitem.py" line="104"/>
+        <location filename="openlp/plugins/media/lib/mediaitem.py" line="111"/>
         <source>You must select a media file to replace the background with.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -903,35 +1156,115 @@
 <context>
     <name>MediaPlugin</name>
     <message>
-        <location filename="openlp/plugins/media/mediaplugin.py" line="76"/>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="77"/>
         <source>&lt;strong&gt;Media Plugin&lt;/strong&gt;&lt;br /&gt;The media plugin provides playback of audio and video.</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="91"/>
+        <source>Media</source>
+        <translation type="unfinished">Media</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="92"/>
+        <source>Medias</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="98"/>
+        <source>Load</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="99"/>
+        <source>Load a new Media</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="103"/>
+        <source>Add</source>
+        <translation type="unfinished">Byvoeg</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="104"/>
+        <source>Add a new Media</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="108"/>
+        <source>Edit</source>
+        <translation type="unfinished">Redigeer</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="109"/>
+        <source>Edit the selected Media</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="113"/>
+        <source>Delete</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="114"/>
+        <source>Delete the selected Media</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="118"/>
+        <source>Preview</source>
+        <translation type="unfinished">Voorskou</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="119"/>
+        <source>Preview the selected Media</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="123"/>
+        <source>Live</source>
+        <translation type="unfinished">Regstreeks</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="124"/>
+        <source>Send the selected Media live</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="128"/>
+        <source>Service</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="129"/>
+        <source>Add the selected Media to the service</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>MediaPlugin.MediaItem</name>
     <message>
-        <location filename="openlp/plugins/media/lib/mediaitem.py" line="117"/>
-        <source>Media</source>
-        <translation type="unfinished">Media</translation>
-    </message>
-    <message>
-        <location filename="openlp/plugins/media/lib/mediaitem.py" line="64"/>
+        <location filename="openlp/plugins/media/lib/mediaitem.py" line="62"/>
         <source>Select Media</source>
         <translation type="unfinished">Selekteer Media</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/media/lib/mediaitem.py" line="95"/>
+        <location filename="openlp/plugins/media/lib/mediaitem.py" line="93"/>
         <source>Replace Live Background</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/media/lib/mediaitem.py" line="95"/>
+        <location filename="openlp/plugins/media/lib/mediaitem.py" line="93"/>
         <source>Replace Background</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/media/lib/mediaitem.py" line="136"/>
+        <location filename="openlp/plugins/media/lib/mediaitem.py" line="125"/>
+        <source>Media</source>
+        <translation type="unfinished">Media</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/lib/mediaitem.py" line="144"/>
         <source>You must select a media file to delete.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -939,7 +1272,7 @@
 <context>
     <name>OpenLP</name>
     <message>
-        <location filename="openlp/core/utils/__init__.py" line="255"/>
+        <location filename="openlp/core/utils/__init__.py" line="262"/>
         <source>Image Files</source>
         <translation type="unfinished"></translation>
     </message>
@@ -1201,322 +1534,315 @@
 <context>
     <name>OpenLP.AmendThemeForm</name>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="660"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="638"/>
         <source>Theme Maintenance</source>
         <translation type="unfinished">Tema Onderhoud</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="662"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="640"/>
         <source>Theme &amp;name:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="664"/>
-        <source>&amp;Visibility:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="666"/>
-        <source>Opaque</source>
-        <translation type="unfinished">Deursigtigheid</translation>
-    </message>
-    <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="668"/>
-        <source>Transparent</source>
-        <translation type="unfinished">Deursigtig</translation>
-    </message>
-    <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="670"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="642"/>
         <source>Type:</source>
         <translation type="unfinished">Tipe:</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="672"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="644"/>
         <source>Solid Color</source>
         <translation type="unfinished">Soliede Kleur</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="674"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="646"/>
         <source>Gradient</source>
         <translation type="unfinished">Gradiënt</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="676"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="648"/>
         <source>Image</source>
         <translation type="unfinished">Beeld</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="680"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="652"/>
         <source>Image:</source>
         <translation type="unfinished">Beeld:</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="682"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="654"/>
         <source>Gradient:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="684"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="656"/>
         <source>Horizontal</source>
         <translation type="unfinished">Horisontaal</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="686"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="658"/>
         <source>Vertical</source>
         <translation type="unfinished">Vertikaal</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="688"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="660"/>
         <source>Circular</source>
         <translation type="unfinished">Sirkelvormig</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="690"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="662"/>
         <source>&amp;Background</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="693"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="665"/>
         <source>Main Font</source>
         <translation type="unfinished">Hoof Skrif</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="742"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="712"/>
         <source>Font:</source>
         <translation type="unfinished">Skrif:</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemeform.py" line="662"/>
+        <location filename="openlp/core/ui/amendthemeform.py" line="615"/>
         <source>Color:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="746"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="716"/>
         <source>Size:</source>
         <translation type="unfinished">Grootte:</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="748"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="718"/>
         <source>pt</source>
         <translation type="unfinished">pt</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="703"/>
-        <source>Wrap indentation:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="705"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="675"/>
         <source>Adjust line spacing:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="750"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="720"/>
         <source>Normal</source>
         <translation type="unfinished">Normaal</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="752"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="722"/>
         <source>Bold</source>
         <translation type="unfinished">Vetgedruk</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="754"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="724"/>
         <source>Italics</source>
         <translation type="unfinished">Kursief</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="756"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="726"/>
         <source>Bold/Italics</source>
         <translation type="unfinished">Bold/Italics</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="758"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="728"/>
         <source>Style:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="760"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="730"/>
         <source>Display Location</source>
         <translation type="unfinished">Vertoon Ligging</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="762"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="732"/>
         <source>Use default location</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="764"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="734"/>
         <source>X position:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="766"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="736"/>
         <source>Y position:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="768"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="738"/>
         <source>Width:</source>
         <translation type="unfinished">Wydte:</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="770"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="740"/>
         <source>Height:</source>
         <translation type="unfinished">Hoogte:</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="797"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="767"/>
         <source>px</source>
         <translation type="unfinished">px</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="737"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="707"/>
         <source>&amp;Main Font</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="740"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="710"/>
         <source>Footer Font</source>
         <translation type="unfinished">Voetnota Skriftipe</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="780"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="750"/>
         <source>&amp;Footer Font</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="783"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="753"/>
         <source>Outline</source>
         <translation type="unfinished">Buitelyn</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="785"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="755"/>
         <source>Outline size:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="789"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="759"/>
         <source>Outline color:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="791"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="761"/>
         <source>Show outline:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="793"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="763"/>
         <source>Shadow</source>
         <translation type="unfinished">Skaduwee</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="795"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="765"/>
         <source>Shadow size:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="799"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="769"/>
         <source>Shadow color:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="801"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="771"/>
         <source>Show shadow:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="803"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="773"/>
         <source>Alignment</source>
         <translation type="unfinished">Belyning</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="805"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="775"/>
         <source>Horizontal align:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="807"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="777"/>
         <source>Left</source>
         <translation type="unfinished">Links</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="809"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="779"/>
         <source>Right</source>
         <translation type="unfinished">Regs</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="811"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="781"/>
         <source>Center</source>
         <translation type="unfinished">Middel</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="813"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="783"/>
         <source>Vertical align:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="815"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="785"/>
         <source>Top</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="817"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="787"/>
         <source>Middle</source>
         <translation type="unfinished">Middel</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="819"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="789"/>
         <source>Bottom</source>
         <translation type="unfinished">Onder</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="821"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="791"/>
         <source>Slide Transition</source>
         <translation type="unfinished">Skyfie Verandering</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="823"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="793"/>
         <source>Transition active</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="825"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="795"/>
         <source>&amp;Other Options</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="828"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="798"/>
         <source>Preview</source>
         <translation type="unfinished">Voorskou</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemeform.py" line="222"/>
+        <location filename="openlp/core/ui/amendthemeform.py" line="211"/>
         <source>All Files</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemeform.py" line="224"/>
+        <location filename="openlp/core/ui/amendthemeform.py" line="213"/>
         <source>Select Image</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemeform.py" line="678"/>
+        <location filename="openlp/core/ui/amendthemeform.py" line="631"/>
         <source>First color:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemeform.py" line="680"/>
+        <location filename="openlp/core/ui/amendthemeform.py" line="633"/>
         <source>Second color:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemeform.py" line="749"/>
+        <location filename="openlp/core/ui/amendthemeform.py" line="702"/>
         <source>Slide height is %s rows.</source>
         <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
+    <name>OpenLP.ExceptionDialog</name>
+    <message>
+        <location filename="openlp/core/ui/exceptiondialog.py" line="75"/>
+        <source>Error Occured</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/core/ui/exceptiondialog.py" line="77"/>
+        <source>Oops! OpenLP hit a problem, and couldn&apos;t recover. The text in the box below contains information that might be helpful to the OpenLP developers, so please e-mail it to bugs@xxxxxxxxxx, along with a detailed description of what you were doing when the problem occurred.</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>OpenLP.GeneralTab</name>
     <message>
         <location filename="openlp/core/ui/generaltab.py" line="65"/>
@@ -1660,7 +1986,7 @@
 <context>
     <name>OpenLP.MainWindow</name>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="343"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="345"/>
         <source>OpenLP 2.0</source>
         <translation type="unfinished">OpenLP 2.0</translation>
     </message>
@@ -1670,404 +1996,404 @@
         <translation type="unfinished">Engels</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="346"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="348"/>
         <source>&amp;File</source>
         <translation type="unfinished">&amp;Lêer</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="347"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="349"/>
         <source>&amp;Import</source>
         <translation type="unfinished">&amp;Invoer</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="348"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="350"/>
         <source>&amp;Export</source>
         <translation type="unfinished">&amp;Uitvoer</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="349"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="351"/>
         <source>&amp;View</source>
         <translation type="unfinished">&amp;Bekyk</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="350"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="352"/>
         <source>M&amp;ode</source>
         <translation type="unfinished">M&amp;odus</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="351"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="353"/>
         <source>&amp;Tools</source>
         <translation type="unfinished">&amp;Gereedskap</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="352"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="354"/>
         <source>&amp;Settings</source>
         <translation type="unfinished">Ver&amp;stellings</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="400"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="402"/>
         <source>&amp;Language</source>
         <translation type="unfinished">Taa&amp;l</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="355"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="357"/>
         <source>&amp;Help</source>
         <translation type="unfinished">&amp;Hulp</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="356"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="358"/>
         <source>Media Manager</source>
         <translation type="unfinished">Media Bestuurder</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="358"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="360"/>
         <source>Service Manager</source>
         <translation type="unfinished">Diens Bestuurder</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="360"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="362"/>
         <source>Theme Manager</source>
         <translation type="unfinished">Tema Bestuurder</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="362"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="364"/>
         <source>&amp;New</source>
         <translation type="unfinished">&amp;Nuwe</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="363"/>
-        <source>New Service</source>
-        <translation type="unfinished">Nuwe Diens</translation>
-    </message>
-    <message>
         <location filename="openlp/core/ui/mainwindow.py" line="365"/>
+        <source>New Service</source>
+        <translation type="unfinished">Nuwe Diens</translation>
+    </message>
+    <message>
+        <location filename="openlp/core/ui/mainwindow.py" line="367"/>
         <source>Create a new service.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="367"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="369"/>
         <source>Ctrl+N</source>
         <translation type="unfinished">Ctrl+N</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="368"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="370"/>
         <source>&amp;Open</source>
         <translation type="unfinished">Maak &amp;Oop</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="369"/>
-        <source>Open Service</source>
-        <translation type="unfinished">Maak Diens Oop</translation>
-    </message>
-    <message>
         <location filename="openlp/core/ui/mainwindow.py" line="371"/>
+        <source>Open Service</source>
+        <translation type="unfinished">Maak Diens Oop</translation>
+    </message>
+    <message>
+        <location filename="openlp/core/ui/mainwindow.py" line="373"/>
         <source>Open an existing service.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="373"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="375"/>
         <source>Ctrl+O</source>
         <translation type="unfinished">Ctrl+O</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="374"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="376"/>
         <source>&amp;Save</source>
         <translation type="unfinished">&amp;Stoor</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="375"/>
-        <source>Save Service</source>
-        <translation type="unfinished">Stoor Diens</translation>
-    </message>
-    <message>
         <location filename="openlp/core/ui/mainwindow.py" line="377"/>
+        <source>Save Service</source>
+        <translation type="unfinished">Stoor Diens</translation>
+    </message>
+    <message>
+        <location filename="openlp/core/ui/mainwindow.py" line="379"/>
         <source>Save the current service to disk.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="379"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="381"/>
         <source>Ctrl+S</source>
         <translation type="unfinished">Ctrl+S</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="380"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="382"/>
         <source>Save &amp;As...</source>
         <translation type="unfinished">Stoor &amp;As...</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="382"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="384"/>
         <source>Save Service As</source>
         <translation type="unfinished">Stoor Diens As</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="384"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="386"/>
         <source>Save the current service under a new name.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="386"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="388"/>
         <source>Ctrl+Shift+S</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="388"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="390"/>
         <source>E&amp;xit</source>
         <translation type="unfinished">&amp;Uitgang</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="390"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="392"/>
         <source>Quit OpenLP</source>
         <translation type="unfinished">Sluit OpenLP Af</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="392"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="394"/>
         <source>Alt+F4</source>
         <translation type="unfinished">Alt+F4</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="398"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="400"/>
         <source>&amp;Theme</source>
         <translation type="unfinished">&amp;Tema</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="402"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="404"/>
         <source>&amp;Configure OpenLP...</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="404"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="406"/>
         <source>&amp;Media Manager</source>
         <translation type="unfinished">&amp;Media Bestuurder</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="406"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="408"/>
         <source>Toggle Media Manager</source>
         <translation type="unfinished">Wissel Media Bestuurder</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="408"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="410"/>
         <source>Toggle the visibility of the media manager.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="410"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="412"/>
         <source>F8</source>
         <translation type="unfinished">F8</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="412"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="414"/>
         <source>&amp;Theme Manager</source>
         <translation type="unfinished">&amp;Tema Bestuurder</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="414"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="416"/>
         <source>Toggle Theme Manager</source>
         <translation type="unfinished">Wissel Tema Bestuurder</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="416"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="418"/>
         <source>Toggle the visibility of the theme manager.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="418"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="420"/>
         <source>F10</source>
         <translation type="unfinished">F10</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="420"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="422"/>
         <source>&amp;Service Manager</source>
         <translation type="unfinished">&amp;Diens Bestuurder</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="422"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="424"/>
         <source>Toggle Service Manager</source>
         <translation type="unfinished">Wissel Diens Bestuurder</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="424"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="426"/>
         <source>Toggle the visibility of the service manager.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="426"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="428"/>
         <source>F9</source>
         <translation type="unfinished">F9</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="428"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="430"/>
         <source>&amp;Preview Panel</source>
         <translation type="unfinished">&amp;Voorskou Paneel</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="430"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="432"/>
         <source>Toggle Preview Panel</source>
         <translation type="unfinished">Wissel Voorskou Paneel</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="432"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="434"/>
         <source>Toggle the visibility of the preview panel.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="434"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="436"/>
         <source>F11</source>
         <translation type="unfinished">F11</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="436"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="438"/>
         <source>&amp;Live Panel</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="438"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="440"/>
         <source>Toggle Live Panel</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="440"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="442"/>
         <source>Toggle the visibility of the live panel.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="442"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="444"/>
         <source>F12</source>
         <translation type="unfinished">F12</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="444"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="446"/>
         <source>&amp;Plugin List</source>
         <translation type="unfinished">In&amp;prop Lys</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="446"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="448"/>
         <source>List the Plugins</source>
         <translation type="unfinished">Lys die Inproppe</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="448"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="450"/>
         <source>Alt+F7</source>
         <translation type="unfinished">Alt+F7</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="450"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="452"/>
         <source>&amp;User Guide</source>
         <translation type="unfinished">&amp;Gebruikers Gids</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="452"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="454"/>
         <source>&amp;About</source>
         <translation type="unfinished">&amp;Aangaande</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="453"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="455"/>
         <source>More information about OpenLP</source>
         <translation type="unfinished">Meer inligting aangaande OpenLP</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="455"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="457"/>
         <source>Ctrl+F1</source>
         <translation type="unfinished">Ctrl+F1</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="457"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="459"/>
         <source>&amp;Online Help</source>
         <translation type="unfinished">&amp;Aanlyn Hulp</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="459"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="461"/>
         <source>&amp;Web Site</source>
         <translation type="unfinished">&amp;Web Tuiste</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="461"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="463"/>
         <source>&amp;Auto Detect</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="463"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="465"/>
         <source>Use the system language, if available.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="468"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="470"/>
         <source>Set the interface language to %s</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="470"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="472"/>
         <source>Add &amp;Tool...</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="472"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="474"/>
         <source>Add an application to the list of tools.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="475"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="477"/>
         <source>&amp;Default</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="477"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="479"/>
         <source>Set the view mode back to the default.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="480"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="482"/>
         <source>&amp;Setup</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="481"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="483"/>
         <source>Set the view mode to Setup.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="484"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="486"/>
         <source>&amp;Live</source>
         <translation type="unfinished">&amp;Regstreeks</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="485"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="487"/>
         <source>Set the view mode to Live.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="652"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="651"/>
         <source>Version %s of OpenLP is now available for download (you are currently running version %s). 
 
-You can download the latest version from &lt;a href=&quot;http://openlp.org/&quot;&gt;http://openlp.org/&lt;/a&gt;.</source>
+You can download the latest version from http://openlp.org/.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="657"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="655"/>
         <source>OpenLP Version Updated</source>
         <translation type="unfinished">OpenLP Weergawe is Opdateer</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="686"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="685"/>
         <source>OpenLP Main Display Blanked</source>
         <translation type="unfinished">OpenLP Hoof Vertoning Blanko</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="686"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="685"/>
         <source>The Main Display has been blanked out</source>
         <translation type="unfinished">Die Hoof Skerm is blanko</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="758"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="757"/>
         <source>Save Changes to Service?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="758"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="757"/>
         <source>Your service has changed. Do you want to save those changes?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="824"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="823"/>
         <source>Default Theme: %s</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2075,157 +2401,82 @@
 <context>
     <name>OpenLP.MediaManagerItem</name>
     <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="478"/>
+        <location filename="openlp/core/lib/mediamanageritem.py" line="467"/>
         <source>No Items Selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="211"/>
-        <source>Import %s</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="211"/>
-        <source>Import a %s</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="219"/>
-        <source>Load %s</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="219"/>
-        <source>Load a new %s</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="227"/>
-        <source>New %s</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="227"/>
-        <source>Add a new %s</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="235"/>
-        <source>Edit %s</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="235"/>
-        <source>Edit the selected %s</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="244"/>
-        <source>Delete %s</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="244"/>
-        <source>Delete the selected item</source>
-        <translation type="unfinished">Wis geselekteerde item uit</translation>
-    </message>
-    <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="253"/>
-        <source>Preview %s</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="253"/>
-        <source>Preview the selected item</source>
-        <translation type="unfinished">Voorskou die geselekteerde item</translation>
-    </message>
-    <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="259"/>
-        <source>Send the selected item live</source>
-        <translation type="unfinished">Stuur die geselekteerde item na regstreekse vertoning</translation>
-    </message>
-    <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="264"/>
-        <source>Add %s to Service</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="264"/>
-        <source>Add the selected item(s) to the service</source>
-        <translation type="unfinished">Voeg die geselekteerde item(s) by die diens</translation>
-    </message>
-    <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="290"/>
+        <location filename="openlp/core/lib/mediamanageritem.py" line="279"/>
         <source>&amp;Edit %s</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="298"/>
+        <location filename="openlp/core/lib/mediamanageritem.py" line="287"/>
         <source>&amp;Delete %s</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="306"/>
+        <location filename="openlp/core/lib/mediamanageritem.py" line="295"/>
         <source>&amp;Preview %s</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="312"/>
+        <location filename="openlp/core/lib/mediamanageritem.py" line="301"/>
         <source>&amp;Show Live</source>
         <translation type="unfinished">&amp;Vertoon Regstreeks</translation>
     </message>
     <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="317"/>
+        <location filename="openlp/core/lib/mediamanageritem.py" line="306"/>
         <source>&amp;Add to Service</source>
         <translation type="unfinished">&amp;Voeg by Diens</translation>
     </message>
     <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="323"/>
+        <location filename="openlp/core/lib/mediamanageritem.py" line="312"/>
         <source>&amp;Add to selected Service Item</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="445"/>
+        <location filename="openlp/core/lib/mediamanageritem.py" line="434"/>
         <source>You must select one or more items to preview.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="462"/>
+        <location filename="openlp/core/lib/mediamanageritem.py" line="451"/>
         <source>You must select one or more items to send live.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="478"/>
+        <location filename="openlp/core/lib/mediamanageritem.py" line="467"/>
         <source>You must select one or more items.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="505"/>
+        <location filename="openlp/core/lib/mediamanageritem.py" line="494"/>
         <source>No items selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="505"/>
+        <location filename="openlp/core/lib/mediamanageritem.py" line="494"/>
         <source>You must select one or more items</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="513"/>
+        <location filename="openlp/core/lib/mediamanageritem.py" line="502"/>
         <source>No Service Item Selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="513"/>
+        <location filename="openlp/core/lib/mediamanageritem.py" line="502"/>
         <source>You must select an existing service item to add to.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="524"/>
+        <location filename="openlp/core/lib/mediamanageritem.py" line="513"/>
         <source>Invalid Service Item</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/lib/mediamanageritem.py" line="524"/>
+        <location filename="openlp/core/lib/mediamanageritem.py" line="513"/>
         <source>You must select a %s service item.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2233,57 +2484,57 @@
 <context>
     <name>OpenLP.PluginForm</name>
     <message>
-        <location filename="openlp/core/ui/plugindialog.py" line="103"/>
+        <location filename="openlp/core/ui/plugindialog.py" line="102"/>
         <source>Plugin List</source>
         <translation type="unfinished">Inprop Lys</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/plugindialog.py" line="105"/>
+        <location filename="openlp/core/ui/plugindialog.py" line="104"/>
         <source>Plugin Details</source>
         <translation type="unfinished">Inprop Besonderhede</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/plugindialog.py" line="107"/>
+        <location filename="openlp/core/ui/plugindialog.py" line="106"/>
         <source>Version:</source>
         <translation type="unfinished">Weergawe:</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/plugindialog.py" line="109"/>
+        <location filename="openlp/core/ui/plugindialog.py" line="108"/>
         <source>TextLabel</source>
         <translation type="unfinished">TeksEtiket</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/plugindialog.py" line="111"/>
+        <location filename="openlp/core/ui/plugindialog.py" line="110"/>
         <source>About:</source>
         <translation type="unfinished">Aangaande:</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/plugindialog.py" line="113"/>
+        <location filename="openlp/core/ui/plugindialog.py" line="112"/>
         <source>Status:</source>
         <translation type="unfinished">Status:</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/plugindialog.py" line="115"/>
+        <location filename="openlp/core/ui/plugindialog.py" line="114"/>
         <source>Active</source>
         <translation type="unfinished">Aktief</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/plugindialog.py" line="117"/>
+        <location filename="openlp/core/ui/plugindialog.py" line="116"/>
         <source>Inactive</source>
         <translation type="unfinished">Onaktief</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/pluginform.py" line="132"/>
+        <location filename="openlp/core/ui/pluginform.py" line="134"/>
         <source>%s (Inactive)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/pluginform.py" line="129"/>
+        <location filename="openlp/core/ui/pluginform.py" line="131"/>
         <source>%s (Active)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/pluginform.py" line="135"/>
+        <location filename="openlp/core/ui/pluginform.py" line="137"/>
         <source>%s (Disabled)</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2324,7 +2575,7 @@
         <translation type="unfinished">Skep &apos;n nuwe diens</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/servicemanager.py" line="638"/>
+        <location filename="openlp/core/ui/servicemanager.py" line="637"/>
         <source>Open Service</source>
         <translation type="unfinished">Maak Diens Oop</translation>
     </message>
@@ -2334,7 +2585,7 @@
         <translation type="unfinished">Laai &apos;n bestaande diens</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/servicemanager.py" line="580"/>
+        <location filename="openlp/core/ui/servicemanager.py" line="579"/>
         <source>Save Service</source>
         <translation type="unfinished">Stoor Diens</translation>
     </message>
@@ -2444,48 +2695,48 @@
         <translation type="unfinished">&amp;Verander Item Tema</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/servicemanager.py" line="652"/>
+        <location filename="openlp/core/ui/servicemanager.py" line="651"/>
         <source>Save Changes to Service?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/servicemanager.py" line="495"/>
+        <location filename="openlp/core/ui/servicemanager.py" line="494"/>
         <source>Your service is unsaved, do you want to save those changes before creating a new one?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/servicemanager.py" line="580"/>
+        <location filename="openlp/core/ui/servicemanager.py" line="579"/>
         <source>OpenLP Service Files (*.osz)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/servicemanager.py" line="652"/>
+        <location filename="openlp/core/ui/servicemanager.py" line="651"/>
         <source>Your current service is unsaved, do you want to save the changes before opening a new one?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/servicemanager.py" line="716"/>
+        <location filename="openlp/core/ui/servicemanager.py" line="715"/>
         <source>Error</source>
         <translation type="unfinished">Fout</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/servicemanager.py" line="681"/>
+        <location filename="openlp/core/ui/servicemanager.py" line="680"/>
         <source>File is not a valid service.
 The content encoding is not UTF-8.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/servicemanager.py" line="716"/>
+        <location filename="openlp/core/ui/servicemanager.py" line="715"/>
         <source>File is not a valid service.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/servicemanager.py" line="877"/>
+        <location filename="openlp/core/ui/servicemanager.py" line="881"/>
         <source>Missing Display Handler</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/servicemanager.py" line="877"/>
+        <location filename="openlp/core/ui/servicemanager.py" line="881"/>
         <source>Your item cannot be displayed as there is no handler to display it</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2509,85 +2760,88 @@
 <context>
     <name>OpenLP.SlideController</name>
     <message>
-        <location filename="openlp/core/ui/slidecontroller.py" line="132"/>
+        <location filename="openlp/core/ui/slidecontroller.py" line="116"/>
         <source>Live</source>
         <translation type="unfinished">Regstreeks</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/slidecontroller.py" line="136"/>
+        <location filename="openlp/core/ui/slidecontroller.py" line="120"/>
         <source>Preview</source>
         <translation type="unfinished">Voorskou</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/slidecontroller.py" line="181"/>
-        <source>Move to first</source>
-        <translation type="unfinished">Verskuif na eerste</translation>
-    </message>
-    <message>
-        <location filename="openlp/core/ui/slidecontroller.py" line="185"/>
+        <location filename="openlp/core/ui/slidecontroller.py" line="165"/>
         <source>Move to previous</source>
         <translation type="unfinished">Beweeg na vorige</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/slidecontroller.py" line="189"/>
+        <location filename="openlp/core/ui/slidecontroller.py" line="169"/>
         <source>Move to next</source>
         <translation type="unfinished">Verskuif na volgende</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/slidecontroller.py" line="194"/>
-        <source>Move to last</source>
-        <translation type="unfinished">Verskuif na laaste posisie</translation>
-    </message>
-    <message>
-        <location filename="openlp/core/ui/slidecontroller.py" line="204"/>
+        <location filename="openlp/core/ui/slidecontroller.py" line="179"/>
         <source>Hide</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/slidecontroller.py" line="227"/>
+        <location filename="openlp/core/ui/slidecontroller.py" line="205"/>
         <source>Move to live</source>
         <translation type="unfinished">Verskuif na regstreekse skerm</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/slidecontroller.py" line="231"/>
+        <location filename="openlp/core/ui/slidecontroller.py" line="210"/>
         <source>Edit and re-preview Song</source>
         <translation type="unfinished">Redigeer en sien weer &apos;n voorskou van die Lied</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/slidecontroller.py" line="237"/>
+        <location filename="openlp/core/ui/slidecontroller.py" line="216"/>
         <source>Start continuous loop</source>
         <translation type="unfinished">Begin aaneenlopende lus</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/slidecontroller.py" line="241"/>
+        <location filename="openlp/core/ui/slidecontroller.py" line="220"/>
         <source>Stop continuous loop</source>
         <translation type="unfinished">Stop deurlopende lus</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/slidecontroller.py" line="250"/>
+        <location filename="openlp/core/ui/slidecontroller.py" line="229"/>
         <source>s</source>
         <translation type="unfinished">s</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/slidecontroller.py" line="251"/>
+        <location filename="openlp/core/ui/slidecontroller.py" line="231"/>
         <source>Delay between slides in seconds</source>
         <translation type="unfinished">Vertraging in sekondes tussen skyfies</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/slidecontroller.py" line="264"/>
+        <location filename="openlp/core/ui/slidecontroller.py" line="244"/>
         <source>Start playing media</source>
         <translation type="unfinished">Begin media speel</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/slidecontroller.py" line="285"/>
-        <source>Go to Verse</source>
-        <translation type="unfinished">Gaan na Vers</translation>
+        <location filename="openlp/core/ui/slidecontroller.py" line="275"/>
+        <source>Go to</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>OpenLP.SpellTextEdit</name>
+    <message>
+        <location filename="openlp/core/lib/spelltextedit.py" line="72"/>
+        <source>Spelling Suggestions</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/core/lib/spelltextedit.py" line="83"/>
+        <source>Formatting Tags</source>
+        <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
     <name>OpenLP.ThemeManager</name>
     <message>
-        <location filename="openlp/core/ui/thememanager.py" line="684"/>
+        <location filename="openlp/core/ui/thememanager.py" line="682"/>
         <source>New Theme</source>
         <translation type="unfinished">Nuwe Tema</translation>
     </message>
@@ -2657,7 +2911,7 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/thememanager.py" line="630"/>
+        <location filename="openlp/core/ui/thememanager.py" line="629"/>
         <source>%s (default)</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2682,7 +2936,7 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/thememanager.py" line="486"/>
+        <location filename="openlp/core/ui/thememanager.py" line="485"/>
         <source>Error</source>
         <translation type="unfinished">Fout</translation>
     </message>
@@ -2742,23 +2996,23 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/thememanager.py" line="433"/>
+        <location filename="openlp/core/ui/thememanager.py" line="432"/>
         <source>File is not a valid theme.
 The content encoding is not UTF-8.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/thememanager.py" line="486"/>
+        <location filename="openlp/core/ui/thememanager.py" line="485"/>
         <source>File is not a valid theme.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/thememanager.py" line="576"/>
+        <location filename="openlp/core/ui/thememanager.py" line="575"/>
         <source>Theme Exists</source>
         <translation type="unfinished">Tema Bestaan</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/thememanager.py" line="576"/>
+        <location filename="openlp/core/ui/thememanager.py" line="575"/>
         <source>A theme with this name already exists.  Would you like to overwrite it?</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2814,55 +3068,110 @@
 <context>
     <name>PresentationPlugin</name>
     <message>
-        <location filename="openlp/plugins/presentations/presentationplugin.py" line="140"/>
+        <location filename="openlp/plugins/presentations/presentationplugin.py" line="141"/>
         <source>&lt;strong&gt;Presentation Plugin&lt;/strong&gt;&lt;br /&gt;The presentation plugin provides the ability to show presentations using a number of different programs. The choice of available presentation programs is available to the user in a drop down box.</source>
         <translation type="unfinished"></translation>
     </message>
-</context>
-<context>
-    <name>PresentationPlugin.MediaItem</name>
     <message>
-        <location filename="openlp/plugins/presentations/lib/mediaitem.py" line="62"/>
+        <location filename="openlp/plugins/presentations/presentationplugin.py" line="159"/>
         <source>Presentation</source>
         <translation type="unfinished">Aanbieding</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/presentations/lib/mediaitem.py" line="78"/>
+        <location filename="openlp/plugins/presentations/presentationplugin.py" line="160"/>
+        <source>Presentations</source>
+        <translation type="unfinished">Aanbiedinge</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/presentations/presentationplugin.py" line="166"/>
+        <source>Load</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/presentations/presentationplugin.py" line="167"/>
+        <source>Load a new Presentation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/presentations/presentationplugin.py" line="171"/>
+        <source>Delete</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/presentations/presentationplugin.py" line="172"/>
+        <source>Delete the selected Presentation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/presentations/presentationplugin.py" line="176"/>
+        <source>Preview</source>
+        <translation type="unfinished">Voorskou</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/presentations/presentationplugin.py" line="177"/>
+        <source>Preview the selected Presentation</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/presentations/presentationplugin.py" line="181"/>
+        <source>Live</source>
+        <translation type="unfinished">Regstreeks</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/presentations/presentationplugin.py" line="182"/>
+        <source>Send the selected Presentation live</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/presentations/presentationplugin.py" line="186"/>
+        <source>Service</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/presentations/presentationplugin.py" line="187"/>
+        <source>Add the selected Presentation to the service</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
+    <name>PresentationPlugin.MediaItem</name>
+    <message>
+        <location filename="openlp/plugins/presentations/lib/mediaitem.py" line="75"/>
         <source>Select Presentation(s)</source>
         <translation type="unfinished">Selekteer Aanbieding(e)</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/presentations/lib/mediaitem.py" line="80"/>
+        <location filename="openlp/plugins/presentations/lib/mediaitem.py" line="77"/>
         <source>Automatic</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/presentations/lib/mediaitem.py" line="129"/>
+        <location filename="openlp/plugins/presentations/lib/mediaitem.py" line="126"/>
         <source>Present using:</source>
         <translation type="unfinished">Bied aan met:</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/presentations/lib/mediaitem.py" line="187"/>
+        <location filename="openlp/plugins/presentations/lib/mediaitem.py" line="184"/>
         <source>File Exists</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/presentations/lib/mediaitem.py" line="187"/>
+        <location filename="openlp/plugins/presentations/lib/mediaitem.py" line="184"/>
         <source>A presentation with that filename already exists.</source>
         <translation type="unfinished">&apos;n Voorstelling met daardie lêernaam bestaan reeds.</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/presentations/lib/mediaitem.py" line="211"/>
+        <location filename="openlp/plugins/presentations/lib/mediaitem.py" line="208"/>
         <source>Unsupported File</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/presentations/lib/mediaitem.py" line="211"/>
+        <location filename="openlp/plugins/presentations/lib/mediaitem.py" line="208"/>
         <source>This type of presentation is not supported</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/presentations/lib/mediaitem.py" line="226"/>
+        <location filename="openlp/plugins/presentations/lib/mediaitem.py" line="223"/>
         <source>You must select an item to delete.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -2893,10 +3202,20 @@
 <context>
     <name>RemotePlugin</name>
     <message>
-        <location filename="openlp/plugins/remotes/remoteplugin.py" line="74"/>
+        <location filename="openlp/plugins/remotes/remoteplugin.py" line="75"/>
         <source>&lt;strong&gt;Remote Plugin&lt;/strong&gt;&lt;br /&gt;The remote plugin provides the ability to send messages to a running version of OpenLP on a different computer via a web browser or through the remote API.</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="openlp/plugins/remotes/remoteplugin.py" line="92"/>
+        <source>Remote</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/remotes/remoteplugin.py" line="93"/>
+        <source>Remotes</source>
+        <translation type="unfinished">Afstandbehere</translation>
+    </message>
 </context>
 <context>
     <name>RemotePlugin.RemoteTab</name>
@@ -2924,45 +3243,50 @@
 <context>
     <name>SongUsagePlugin</name>
     <message>
-        <location filename="openlp/plugins/songusage/songusageplugin.py" line="63"/>
+        <location filename="openlp/plugins/songusage/songusageplugin.py" line="64"/>
         <source>&amp;Song Usage Tracking</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songusage/songusageplugin.py" line="67"/>
+        <location filename="openlp/plugins/songusage/songusageplugin.py" line="68"/>
         <source>&amp;Delete Tracking Data</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songusage/songusageplugin.py" line="69"/>
+        <location filename="openlp/plugins/songusage/songusageplugin.py" line="70"/>
         <source>Delete song usage data up to a specified date.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songusage/songusageplugin.py" line="74"/>
+        <location filename="openlp/plugins/songusage/songusageplugin.py" line="75"/>
         <source>&amp;Extract Tracking Data</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songusage/songusageplugin.py" line="76"/>
+        <location filename="openlp/plugins/songusage/songusageplugin.py" line="77"/>
         <source>Generate a report on song usage.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songusage/songusageplugin.py" line="83"/>
+        <location filename="openlp/plugins/songusage/songusageplugin.py" line="84"/>
         <source>Toggle Tracking</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songusage/songusageplugin.py" line="85"/>
+        <location filename="openlp/plugins/songusage/songusageplugin.py" line="86"/>
         <source>Toggle the tracking of song usage.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songusage/songusageplugin.py" line="161"/>
+        <location filename="openlp/plugins/songusage/songusageplugin.py" line="162"/>
         <source>&lt;strong&gt;SongUsage Plugin&lt;/strong&gt;&lt;br /&gt;This plugin tracks the usage of songs in services.</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="openlp/plugins/songusage/songusageplugin.py" line="179"/>
+        <source>SongUsage</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>SongUsagePlugin.SongUsageDeleteForm</name>
@@ -3013,20 +3337,90 @@
 <context>
     <name>SongsPlugin</name>
     <message>
-        <location filename="openlp/plugins/songs/songsplugin.py" line="87"/>
+        <location filename="openlp/plugins/songs/songsplugin.py" line="88"/>
         <source>&amp;Song</source>
         <translation>&amp;Lied</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/songsplugin.py" line="89"/>
+        <location filename="openlp/plugins/songs/songsplugin.py" line="90"/>
         <source>Import songs using the import wizard.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/songsplugin.py" line="113"/>
+        <location filename="openlp/plugins/songs/songsplugin.py" line="114"/>
         <source>&lt;strong&gt;Songs Plugin&lt;/strong&gt;&lt;br /&gt;The songs plugin provides the ability to display and manage songs.</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="openlp/plugins/songs/songsplugin.py" line="162"/>
+        <source>Song</source>
+        <translation type="unfinished">Lied</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/songsplugin.py" line="163"/>
+        <source>Songs</source>
+        <translation type="unfinished">Liedere</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/songsplugin.py" line="169"/>
+        <source>Add</source>
+        <translation type="unfinished">Byvoeg</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/songsplugin.py" line="170"/>
+        <source>Add a new Song</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/songsplugin.py" line="174"/>
+        <source>Edit</source>
+        <translation type="unfinished">Redigeer</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/songsplugin.py" line="175"/>
+        <source>Edit the selected Song</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/songsplugin.py" line="179"/>
+        <source>Delete</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/songsplugin.py" line="180"/>
+        <source>Delete the selected Song</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/songsplugin.py" line="184"/>
+        <source>Preview</source>
+        <translation type="unfinished">Voorskou</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/songsplugin.py" line="185"/>
+        <source>Preview the selected Song</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/songsplugin.py" line="189"/>
+        <source>Live</source>
+        <translation type="unfinished">Regstreeks</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/songsplugin.py" line="190"/>
+        <source>Send the selected Song live</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/songsplugin.py" line="194"/>
+        <source>Service</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/songsplugin.py" line="195"/>
+        <source>Add the selected Song to the service</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>SongsPlugin.AuthorsForm</name>
@@ -3074,232 +3468,237 @@
 <context>
     <name>SongsPlugin.EditSongForm</name>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="406"/>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="412"/>
         <source>Song Editor</source>
         <translation type="unfinished">Lied Redigeerder</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="408"/>
-        <source>&amp;Title:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="410"/>
-        <source>Alt&amp;ernate title:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="412"/>
-        <source>&amp;Lyrics:</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
         <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="414"/>
-        <source>&amp;Verse order:</source>
+        <source>&amp;Title:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
         <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="416"/>
-        <source>&amp;Add</source>
+        <source>Alt&amp;ernate title:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
         <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="418"/>
-        <source>&amp;Edit</source>
-        <translation type="unfinished">R&amp;edigeer</translation>
+        <source>&amp;Lyrics:</source>
+        <translation type="unfinished"></translation>
     </message>
     <message>
         <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="420"/>
-        <source>Ed&amp;it All</source>
+        <source>&amp;Verse order:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
         <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="422"/>
-        <source>&amp;Delete</source>
+        <source>&amp;Add</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
         <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="424"/>
+        <source>&amp;Edit</source>
+        <translation type="unfinished">R&amp;edigeer</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="426"/>
+        <source>Ed&amp;it All</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="428"/>
+        <source>&amp;Delete</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="430"/>
         <source>Title &amp;&amp; Lyrics</source>
         <translation type="unfinished">Titel &amp;&amp; Lirieke</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="427"/>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="433"/>
         <source>Authors</source>
         <translation type="unfinished">Skrywers</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="429"/>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="435"/>
         <source>&amp;Add to Song</source>
         <translation type="unfinished">&amp;Voeg by Lied</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="431"/>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="437"/>
         <source>&amp;Remove</source>
         <translation type="unfinished">&amp;Verwyder</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="433"/>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="439"/>
         <source>&amp;Manage Authors, Topics, Song Books</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="435"/>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="441"/>
         <source>Topic</source>
         <translation type="unfinished">Onderwerp</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="437"/>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="443"/>
         <source>A&amp;dd to Song</source>
         <translation type="unfinished">Voeg by Lie&amp;d</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="439"/>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="445"/>
         <source>R&amp;emove</source>
         <translation type="unfinished">V&amp;erwyder</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="441"/>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="447"/>
         <source>Song Book</source>
         <translation type="unfinished">Lied Boek</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="443"/>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="449"/>
+        <source>Song No.:</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="451"/>
         <source>Authors, Topics &amp;&amp; Song Book</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="447"/>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="455"/>
         <source>Theme</source>
         <translation type="unfinished">Tema</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="449"/>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="457"/>
         <source>New &amp;Theme</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="451"/>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="459"/>
         <source>Copyright Information</source>
         <translation type="unfinished">Kopiereg Informasie</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="453"/>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="461"/>
         <source>&#xa9;</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="455"/>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="463"/>
         <source>CCLI number:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="457"/>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="465"/>
         <source>Comments</source>
         <translation type="unfinished">Kommentaar</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="459"/>
+        <location filename="openlp/plugins/songs/forms/editsongdialog.py" line="467"/>
         <source>Theme, Copyright Info &amp;&amp; Comments</source>
         <translation type="unfinished">Tema, Kopiereg Informasie &amp;&amp; Kommentaar</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="99"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="96"/>
         <source>Save &amp;&amp; Preview</source>
         <translation type="unfinished">Stoor &amp;&amp; Voorskou</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="289"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="285"/>
         <source>Add Author</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="289"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="285"/>
         <source>This author does not exist, do you want to add them?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="572"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="545"/>
         <source>Error</source>
         <translation type="unfinished">Fout</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="316"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="312"/>
         <source>This author is already in the list.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="329"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="324"/>
         <source>No Author Selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="329"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="324"/>
         <source>You have not selected a valid author. Either select an author from the list, or type in a new author and click the &quot;Add Author to Song&quot; button to add the new author.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="353"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="345"/>
         <source>Add Topic</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="353"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="345"/>
         <source>This topic does not exist, do you want to add it?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="374"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="366"/>
         <source>This topic is already in the list.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="386"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="377"/>
         <source>No Topic Selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="386"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="377"/>
         <source>You have not selected a valid topic. Either select a topic from the list, or type in a new topic and click the &quot;Add Topic to Song&quot; button to add the new topic.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="524"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="497"/>
         <source>You need to type in a song title.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="532"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="505"/>
         <source>You need to type in at least one verse.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="583"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="556"/>
         <source>Warning</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="540"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="513"/>
         <source>You have not added any authors for this song. Do you want to add an author now?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="572"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="545"/>
         <source>The verse order is invalid. There is no verse corresponding to %s. Valid entries are %s.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="583"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="556"/>
         <source>You have not used %s anywhere in the verse order. Are you sure you want to save the song like this?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="626"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="608"/>
         <source>Add Book</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editsongform.py" line="626"/>
+        <location filename="openlp/plugins/songs/forms/editsongform.py" line="608"/>
         <source>This song book does not exist, do you want to add it?</source>
         <translation type="unfinished"></translation>
     </message>
@@ -3307,17 +3706,17 @@
 <context>
     <name>SongsPlugin.EditVerseForm</name>
     <message>
-        <location filename="openlp/plugins/songs/forms/editversedialog.py" line="87"/>
+        <location filename="openlp/plugins/songs/forms/editversedialog.py" line="88"/>
         <source>Edit Verse</source>
         <translation type="unfinished">Redigeer Vers</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editversedialog.py" line="89"/>
+        <location filename="openlp/plugins/songs/forms/editversedialog.py" line="90"/>
         <source>&amp;Verse type:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/editversedialog.py" line="105"/>
+        <location filename="openlp/plugins/songs/forms/editversedialog.py" line="106"/>
         <source>&amp;Insert</source>
         <translation type="unfinished"></translation>
     </message>
@@ -3325,315 +3724,325 @@
 <context>
     <name>SongsPlugin.ImportWizardForm</name>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="124"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="129"/>
         <source>No OpenLP 2.0 Song Database Selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="124"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="129"/>
         <source>You need to select an OpenLP 2.0 song database file to import from.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="134"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="139"/>
         <source>No openlp.org 1.x Song Database Selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="134"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="139"/>
         <source>You need to select an openlp.org 1.x song database file to import from.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="144"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="149"/>
         <source>No OpenLyrics Files Selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="144"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="149"/>
         <source>You need to add at least one OpenLyrics song file to import from.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="154"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="159"/>
         <source>No OpenSong Files Selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="154"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="159"/>
         <source>You need to add at least one OpenSong song file to import from.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="164"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="169"/>
         <source>No Words of Worship Files Selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="164"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="169"/>
         <source>You need to add at least one Words of Worship file to import from.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="174"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="179"/>
         <source>No CCLI Files Selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="174"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="179"/>
         <source>You need to add at least one CCLI file to import from.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="184"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="189"/>
         <source>No Songs of Fellowship File Selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="184"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="189"/>
         <source>You need to add at least one Songs of Fellowship file to import from.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="194"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="199"/>
         <source>No Document/Presentation Selected</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="194"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="199"/>
         <source>You need to add at least one document or presentation file to import from.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="237"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="242"/>
         <source>Select OpenLP 2.0 Database File</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="244"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="249"/>
         <source>Select openlp.org 1.x Database File</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="251"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="256"/>
         <source>Select OpenLyrics Files</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="261"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="266"/>
         <source>Select Open Song Files</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="271"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="276"/>
         <source>Select Words of Worship Files</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="281"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="286"/>
+        <source>Select CCLI Files</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="296"/>
         <source>Select Songs of Fellowship Files</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="291"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="306"/>
         <source>Select Document/Presentation Files</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="343"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="359"/>
         <source>Starting import...</source>
         <translation type="unfinished">Invoer begin...</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="372"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="392"/>
         <source>Song Import Wizard</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="374"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="394"/>
         <source>Welcome to the Song Import Wizard</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="378"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="398"/>
         <source>This wizard will help you to import songs from a variety of formats. Click the next button below to start the process by selecting a format to import from.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="383"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="403"/>
         <source>Select Import Source</source>
         <translation type="unfinished">Selekteer Invoer Bron</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="385"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="405"/>
         <source>Select the import format, and where to import from.</source>
         <translation type="unfinished">Selekteer die invoer formaat en van waar af om in te voer.</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="388"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="408"/>
         <source>Format:</source>
         <translation type="unfinished">Formaat:</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="390"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="410"/>
         <source>OpenLP 2.0</source>
         <translation type="unfinished">OpenLP 2.0</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="392"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="412"/>
         <source>openlp.org 1.x</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="394"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="414"/>
         <source>OpenLyrics</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="396"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="416"/>
         <source>OpenSong</source>
         <translation type="unfinished">OpenSong</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="398"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="418"/>
         <source>Words of Worship</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="400"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="420"/>
         <source>CCLI/SongSelect</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="402"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="422"/>
         <source>Songs of Fellowship</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="404"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="424"/>
         <source>Generic Document/Presentation</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="413"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="433"/>
         <source>Filename:</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="415"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="435"/>
         <source>Browse...</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="437"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="457"/>
         <source>Add Files...</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="439"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="459"/>
         <source>Remove File(s)</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="445"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="465"/>
         <source>Importing</source>
         <translation type="unfinished">Invoer</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="447"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="467"/>
         <source>Please wait while your songs are imported.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="450"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="470"/>
         <source>Ready.</source>
         <translation type="unfinished">Gereed.</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="452"/>
+        <location filename="openlp/plugins/songs/forms/songimportwizard.py" line="472"/>
         <source>%p%</source>
         <translation type="unfinished"></translation>
     </message>
+    <message>
+        <location filename="openlp/plugins/songs/lib/olp1import.py" line="90"/>
+        <source>Importing &quot;%s&quot;...</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/songs/lib/opensongimport.py" line="153"/>
+        <source>Importing %s...</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>SongsPlugin.MediaItem</name>
     <message>
-        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="53"/>
-        <source>Song</source>
-        <translation type="unfinished">Lied</translation>
-    </message>
-    <message>
-        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="72"/>
+        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="70"/>
         <source>Song Maintenance</source>
         <translation type="unfinished">Lied Onderhoud</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="72"/>
+        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="70"/>
         <source>Maintain the lists of authors, topics and books</source>
         <translation type="unfinished">Handhaaf die lys van skrywers, onderwerpe en boeke</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="144"/>
+        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="142"/>
         <source>Search:</source>
         <translation type="unfinished">Soek:</translation>
     </message>
     <message>
+        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="144"/>
+        <source>Type:</source>
+        <translation type="unfinished">Tipe:</translation>
+    </message>
+    <message>
         <location filename="openlp/plugins/songs/lib/mediaitem.py" line="146"/>
-        <source>Type:</source>
-        <translation type="unfinished">Tipe:</translation>
+        <source>Clear</source>
+        <translation type="unfinished"></translation>
     </message>
     <message>
         <location filename="openlp/plugins/songs/lib/mediaitem.py" line="148"/>
-        <source>Clear</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="150"/>
         <source>Search</source>
         <translation type="unfinished">Soek</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="154"/>
+        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="152"/>
         <source>Titles</source>
         <translation type="unfinished">Titels</translation>
     </message>
     <message>
+        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="154"/>
+        <source>Lyrics</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
         <location filename="openlp/plugins/songs/lib/mediaitem.py" line="156"/>
-        <source>Lyrics</source>
-        <translation type="unfinished"></translation>
-    </message>
-    <message>
-        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="158"/>
         <source>Authors</source>
         <translation type="unfinished">Skrywers</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="273"/>
+        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="271"/>
         <source>You must select an item to edit.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="285"/>
+        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="283"/>
         <source>You must select an item to delete.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="290"/>
+        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="288"/>
         <source>Are you sure you want to delete the selected song?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="293"/>
+        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="291"/>
         <source>Are you sure you want to delete the %d selected songs?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="296"/>
+        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="294"/>
         <source>Delete Song(s)?</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="370"/>
+        <location filename="openlp/plugins/songs/lib/mediaitem.py" line="363"/>
         <source>CCLI Licence: </source>
         <translation type="unfinished">CCLI Lisensie:</translation>
     </message>
@@ -3669,12 +4078,12 @@
 <context>
     <name>SongsPlugin.SongImport</name>
     <message>
-        <location filename="openlp/plugins/songs/lib/songimport.py" line="66"/>
+        <location filename="openlp/plugins/songs/lib/songimport.py" line="76"/>
         <source>copyright</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/lib/songimport.py" line="68"/>
+        <location filename="openlp/plugins/songs/lib/songimport.py" line="78"/>
         <source>&#xa9;</source>
         <translation type="unfinished"></translation>
     </message>
@@ -3682,12 +4091,12 @@
 <context>
     <name>SongsPlugin.SongImportForm</name>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="400"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="416"/>
         <source>Finished import.</source>
         <translation type="unfinished">Invoer voltooi.</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songimportform.py" line="403"/>
+        <location filename="openlp/plugins/songs/forms/songimportform.py" line="419"/>
         <source>Your song import failed.</source>
         <translation type="unfinished"></translation>
     </message>
@@ -3730,112 +4139,112 @@
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="392"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="391"/>
         <source>Error</source>
         <translation type="unfinished">Fout</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="234"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="233"/>
         <source>Could not add your author.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="239"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="238"/>
         <source>This author already exists.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="251"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="250"/>
         <source>Could not add your topic.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="256"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="255"/>
         <source>This topic already exists.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="269"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="268"/>
         <source>Could not add your book.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="274"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="273"/>
         <source>This book already exists.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="387"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="386"/>
         <source>Could not save your changes.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="325"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="324"/>
         <source>Could not save your modified author, because he already exists.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="362"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="361"/>
         <source>Could not save your modified topic, because it already exists.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="470"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="469"/>
         <source>Delete Author</source>
         <translation type="unfinished">Wis Skrywer Uit</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="470"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="469"/>
         <source>Are you sure you want to delete the selected author?</source>
         <translation type="unfinished">Is u seker u wil die geselekteerde skrywer uitwis?</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="470"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="469"/>
         <source>This author cannot be deleted, they are currently assigned to at least one song.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="470"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="469"/>
         <source>No author selected!</source>
         <translation type="unfinished">Geen skrywer geselekteer nie!</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="483"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="482"/>
         <source>Delete Topic</source>
         <translation type="unfinished">Wis Onderwerp Uit</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="483"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="482"/>
         <source>Are you sure you want to delete the selected topic?</source>
         <translation type="unfinished">Is u seker u wil die geselekteerde onderwerp uitwis?</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="483"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="482"/>
         <source>This topic cannot be deleted, it is currently assigned to at least one song.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="483"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="482"/>
         <source>No topic selected!</source>
         <translation type="unfinished">Geen onderwerp geselekteer nie!</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="496"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="495"/>
         <source>Delete Book</source>
         <translation type="unfinished">Wis Boek Uit</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="496"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="495"/>
         <source>Are you sure you want to delete the selected book?</source>
         <translation type="unfinished">Is jy seker jy wil die geselekteerde boek uitwis?</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="496"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="495"/>
         <source>This book cannot be deleted, it is currently assigned to at least one song.</source>
         <translation type="unfinished"></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="496"/>
+        <location filename="openlp/plugins/songs/forms/songmaintenanceform.py" line="495"/>
         <source>No book selected!</source>
         <translation type="unfinished">Geen boek geselekteer nie!</translation>
     </message>

=== modified file 'resources/i18n/openlp_de.ts'
--- resources/i18n/openlp_de.ts	2010-08-26 08:07:16 +0000
+++ resources/i18n/openlp_de.ts	2010-09-10 15:56:22 +0000
@@ -3,20 +3,30 @@
 <context>
     <name>AlertsPlugin</name>
     <message>
-        <location filename="openlp/plugins/alerts/alertsplugin.py" line="70"/>
+        <location filename="openlp/plugins/alerts/alertsplugin.py" line="71"/>
         <source>&amp;Alert</source>
         <translation>&amp;Hinweis</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/alerts/alertsplugin.py" line="71"/>
+        <location filename="openlp/plugins/alerts/alertsplugin.py" line="72"/>
         <source>Show an alert message.</source>
-        <translation></translation>
+        <translation>Hinweis anzeigen</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/alerts/alertsplugin.py" line="99"/>
+        <location filename="openlp/plugins/alerts/alertsplugin.py" line="101"/>
         <source>&lt;strong&gt;Alerts Plugin&lt;/strong&gt;&lt;br /&gt;The alert plugin controls the displaying of nursery alerts on the display screen</source>
         <translation></translation>
     </message>
+    <message>
+        <location filename="openlp/plugins/alerts/alertsplugin.py" line="116"/>
+        <source>Alert</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/alerts/alertsplugin.py" line="117"/>
+        <source>Alerts</source>
+        <translation type="unfinished">Hinweise</translation>
+    </message>
 </context>
 <context>
     <name>AlertsPlugin.AlertForm</name>
@@ -79,7 +89,7 @@
 <context>
     <name>AlertsPlugin.AlertsManager</name>
     <message>
-        <location filename="openlp/plugins/alerts/lib/alertsmanager.py" line="81"/>
+        <location filename="openlp/plugins/alerts/lib/alertsmanager.py" line="72"/>
         <source>Alert message created and displayed.</source>
         <translation></translation>
     </message>
@@ -165,15 +175,95 @@
 <context>
     <name>BiblesPlugin</name>
     <message>
-        <location filename="openlp/plugins/bibles/bibleplugin.py" line="82"/>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="83"/>
         <source>&amp;Bible</source>
         <translation>&amp;Bibel</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/bibleplugin.py" line="91"/>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="92"/>
         <source>&lt;strong&gt;Bible Plugin&lt;/strong&gt;&lt;br /&gt;The Bible plugin provides the ability to display bible verses from different sources during the service.</source>
         <translation></translation>
     </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="131"/>
+        <source>Bible</source>
+        <translation type="unfinished">Bibel</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="132"/>
+        <source>Bibles</source>
+        <translation type="unfinished">Bibeln</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="138"/>
+        <source>Import</source>
+        <translation type="unfinished">Import</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="139"/>
+        <source>Import a Bible</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="143"/>
+        <source>Add</source>
+        <translation type="unfinished">Hinzufügen</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="144"/>
+        <source>Add a new Bible</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="148"/>
+        <source>Edit</source>
+        <translation type="unfinished">Bearbeiten</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="149"/>
+        <source>Edit the selected Bible</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="153"/>
+        <source>Delete</source>
+        <translation type="unfinished">Löschen</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="154"/>
+        <source>Delete the selected Bible</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="158"/>
+        <source>Preview</source>
+        <translation type="unfinished">Vorschau</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="159"/>
+        <source>Preview the selected Bible</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="163"/>
+        <source>Live</source>
+        <translation type="unfinished">Live</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="164"/>
+        <source>Send the selected Bible live</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="168"/>
+        <source>Service</source>
+        <translation type="unfinished">Ablauf</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/bibleplugin.py" line="169"/>
+        <source>Add the selected Bible to the service</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>BiblesPlugin.BibleDB</name>
@@ -554,112 +644,107 @@
 <context>
     <name>BiblesPlugin.MediaItem</name>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="59"/>
-        <source>Bible</source>
-        <translation>Bibel</translation>
-    </message>
-    <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="149"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="146"/>
         <source>Quick</source>
         <translation>Schnellsuche</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="234"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="231"/>
         <source>Advanced</source>
         <translation>Erweitert</translation>
     </message>
     <message>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="303"/>
+        <source>Version:</source>
+        <translation>Version:</translation>
+    </message>
+    <message>
         <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="305"/>
-        <source>Version:</source>
-        <translation>Version:</translation>
-    </message>
-    <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="307"/>
         <source>Dual:</source>
         <translation>Parallel:</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="299"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="297"/>
         <source>Find:</source>
         <translation>Suchen:</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="321"/>
-        <source>Search</source>
-        <translation>Suche</translation>
-    </message>
-    <message>
         <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="319"/>
+        <source>Search</source>
+        <translation>Suche</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="317"/>
         <source>Results:</source>
         <translation>Ergebnisse:</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="309"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="307"/>
         <source>Book:</source>
         <translation>Buch:</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="311"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="309"/>
         <source>Chapter:</source>
         <translation>Kapitel:</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="313"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="311"/>
         <source>Verse:</source>
         <translation>Vers:</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="315"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="313"/>
         <source>From:</source>
         <translation>Von:</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="317"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="315"/>
         <source>To:</source>
         <translation>Bis:</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="323"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="321"/>
         <source>Verse Search</source>
         <translation>Stelle suchen</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="325"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="323"/>
         <source>Text Search</source>
         <translation>Textsuche</translation>
     </message>
     <message>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="329"/>
+        <source>Clear</source>
+        <translation></translation>
+    </message>
+    <message>
         <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="331"/>
-        <source>Clear</source>
-        <translation></translation>
-    </message>
-    <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="333"/>
         <source>Keep</source>
         <translation>Behalten</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="387"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="385"/>
         <source>No Book Found</source>
         <translation>Kein Buch gefunden</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="387"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="385"/>
         <source>No matching book could be found in this Bible.</source>
         <translation>Das Buch wurde in dieser Bibelausgabe nicht gefunden.</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="569"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="567"/>
         <source>etc</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="297"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="295"/>
         <source>Search type:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="621"/>
+        <location filename="openlp/plugins/bibles/lib/mediaitem.py" line="625"/>
         <source>Bible not fully loaded.</source>
         <translation></translation>
     </message>
@@ -675,9 +760,9 @@
 <context>
     <name>CustomPlugin</name>
     <message>
-        <location filename="openlp/plugins/custom/customplugin.py" line="65"/>
-        <source>&lt;strong&gt;Custom Plugin&lt;/strong&gt;&lt;br /&gt;The custom plugin provides the ability to set up custom text slides that can be displayed on the screen the same way songs are. This plugin provides greater freedom over the songs plugin.</source>
-        <translation></translation>
+        <location filename="openlp/plugins/custom/customplugin.py" line="66"/>
+        <source>&lt;strong&gt;Custom Plugin&lt;/strong&gt;&lt;br /&gt;The custom plugin provides the ability to set up custom text slides that can be displayed on the screen the same way Customs are. This plugin provides greater freedom over the Customs plugin.</source>
+        <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
@@ -829,109 +914,357 @@
 <context>
     <name>CustomPlugin.MediaItem</name>
     <message>
-        <location filename="openlp/plugins/custom/lib/mediaitem.py" line="51"/>
-        <source>Custom</source>
-        <translation>Sonderfolien</translation>
-    </message>
-    <message>
-        <location filename="openlp/plugins/custom/lib/mediaitem.py" line="123"/>
+        <location filename="openlp/plugins/custom/lib/mediaitem.py" line="121"/>
         <source>You haven&apos;t selected an item to edit.</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/custom/lib/mediaitem.py" line="136"/>
+        <location filename="openlp/plugins/custom/lib/mediaitem.py" line="134"/>
         <source>You haven&apos;t selected an item to delete.</source>
         <translation></translation>
     </message>
 </context>
 <context>
+    <name>CustomsPlugin</name>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="111"/>
+        <source>Custom</source>
+        <translation type="unfinished">Sonderfolien</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="112"/>
+        <source>Customs</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="118"/>
+        <source>Import</source>
+        <translation type="unfinished">Import</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="119"/>
+        <source>Import a Custom</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="123"/>
+        <source>Load</source>
+        <translation type="unfinished">Laden</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="124"/>
+        <source>Load a new Custom</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="128"/>
+        <source>Add</source>
+        <translation type="unfinished">Hinzufügen</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="129"/>
+        <source>Add a new Custom</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="133"/>
+        <source>Edit</source>
+        <translation type="unfinished">Bearbeiten</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="134"/>
+        <source>Edit the selected Custom</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="138"/>
+        <source>Delete</source>
+        <translation type="unfinished">Löschen</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="139"/>
+        <source>Delete the selected Custom</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="143"/>
+        <source>Preview</source>
+        <translation type="unfinished">Vorschau</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="144"/>
+        <source>Preview the selected Custom</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="148"/>
+        <source>Live</source>
+        <translation type="unfinished">Live</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="149"/>
+        <source>Send the selected Custom live</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="153"/>
+        <source>Service</source>
+        <translation type="unfinished">Ablauf</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/custom/customplugin.py" line="154"/>
+        <source>Add the selected Custom to the service</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>ImagePlugin</name>
     <message>
-        <location filename="openlp/plugins/images/imageplugin.py" line="48"/>
-        <source>&lt;strong&gt;Image Plugin&lt;/strong&gt;&lt;br /&gt;The image plugin provides displaying of images.&lt;br /&gt;One of the distinguishing features of this plugin is the ability to group a number of images together in the service manager, making the displaying of multiple images easier. This plugin can also make use of OpenLP&apos;s &quot;timed looping&quot; feature to create a slide show that runs automatically. In addition to this, images from the plugin can be used to override the current theme&apos;s background, which renders text-based items like songs with the selected image as a background instead of the background provided by the theme.</source>
-        <translation></translation>
+        <location filename="openlp/plugins/images/imageplugin.py" line="49"/>
+        <source>&lt;strong&gt;Image Plugin&lt;/strong&gt;&lt;br /&gt;The image plugin provides displaying of images.&lt;br /&gt;One of the distinguishing features of this plugin is the ability to group a number of images together in the service manager, making the displaying of multiple images easier. This plugin can also make use of OpenLP&apos;s &quot;timed looping&quot; feature to create a slide show that runs automatically. In addition to this, images from the plugin can be used to override the current theme&apos;s background, which renders text-based items like Images with the selected image as a background instead of the background provided by the theme.</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="73"/>
+        <source>Image</source>
+        <translation type="unfinished">Bild</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="74"/>
+        <source>Images</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="80"/>
+        <source>Load</source>
+        <translation type="unfinished">Laden</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="81"/>
+        <source>Load a new Image</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="85"/>
+        <source>Add</source>
+        <translation type="unfinished">Hinzufügen</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="86"/>
+        <source>Add a new Image</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="90"/>
+        <source>Edit</source>
+        <translation type="unfinished">Bearbeiten</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="91"/>
+        <source>Edit the selected Image</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="95"/>
+        <source>Delete</source>
+        <translation type="unfinished">Löschen</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="96"/>
+        <source>Delete the selected Image</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="100"/>
+        <source>Preview</source>
+        <translation type="unfinished">Vorschau</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="101"/>
+        <source>Preview the selected Image</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="105"/>
+        <source>Live</source>
+        <translation type="unfinished">Live</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="106"/>
+        <source>Send the selected Image live</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="110"/>
+        <source>Service</source>
+        <translation type="unfinished">Ablauf</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/images/imageplugin.py" line="111"/>
+        <source>Add the selected Image to the service</source>
+        <translation type="unfinished"></translation>
     </message>
 </context>
 <context>
     <name>ImagePlugin.MediaItem</name>
     <message>
-        <location filename="openlp/plugins/images/lib/mediaitem.py" line="54"/>
-        <source>Image</source>
-        <translation>Bild</translation>
-    </message>
-    <message>
-        <location filename="openlp/plugins/images/lib/mediaitem.py" line="62"/>
+        <location filename="openlp/plugins/images/lib/mediaitem.py" line="60"/>
         <source>Select Image(s)</source>
         <translation>Bild(er) auswählen</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/images/lib/mediaitem.py" line="65"/>
+        <location filename="openlp/plugins/images/lib/mediaitem.py" line="63"/>
         <source>All Files</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/images/lib/mediaitem.py" line="108"/>
+        <location filename="openlp/plugins/images/lib/mediaitem.py" line="106"/>
         <source>Replace Live Background</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/images/lib/mediaitem.py" line="156"/>
+        <location filename="openlp/plugins/images/lib/mediaitem.py" line="160"/>
         <source>Image(s)</source>
         <translation>Bild(er)</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/images/lib/mediaitem.py" line="108"/>
+        <location filename="openlp/plugins/images/lib/mediaitem.py" line="106"/>
         <source>Replace Background</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/images/lib/mediaitem.py" line="120"/>
+        <location filename="openlp/plugins/images/lib/mediaitem.py" line="124"/>
         <source>You must select an image to delete.</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/images/lib/mediaitem.py" line="173"/>
+        <location filename="openlp/plugins/images/lib/mediaitem.py" line="181"/>
         <source>You must select an image to replace the background with.</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/media/lib/mediaitem.py" line="104"/>
+        <location filename="openlp/plugins/media/lib/mediaitem.py" line="111"/>
         <source>You must select a media file to replace the background with.</source>
         <translation></translation>
     </message>
+    <message>
+        <location filename="openlp/plugins/media/lib/mediaitem.py" line="98"/>
+        <source>Reset Live Background</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>MediaPlugin</name>
     <message>
-        <location filename="openlp/plugins/media/mediaplugin.py" line="76"/>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="77"/>
         <source>&lt;strong&gt;Media Plugin&lt;/strong&gt;&lt;br /&gt;The media plugin provides playback of audio and video.</source>
         <translation></translation>
     </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="91"/>
+        <source>Media</source>
+        <translation type="unfinished">Medien</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="92"/>
+        <source>Medias</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="98"/>
+        <source>Load</source>
+        <translation type="unfinished">Laden</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="99"/>
+        <source>Load a new Media</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="103"/>
+        <source>Add</source>
+        <translation type="unfinished">Hinzufügen</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="104"/>
+        <source>Add a new Media</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="108"/>
+        <source>Edit</source>
+        <translation type="unfinished">Bearbeiten</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="109"/>
+        <source>Edit the selected Media</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="113"/>
+        <source>Delete</source>
+        <translation type="unfinished">Löschen</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="114"/>
+        <source>Delete the selected Media</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="118"/>
+        <source>Preview</source>
+        <translation type="unfinished">Vorschau</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="119"/>
+        <source>Preview the selected Media</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="123"/>
+        <source>Live</source>
+        <translation type="unfinished">Live</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="124"/>
+        <source>Send the selected Media live</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="128"/>
+        <source>Service</source>
+        <translation type="unfinished">Ablauf</translation>
+    </message>
+    <message>
+        <location filename="openlp/plugins/media/mediaplugin.py" line="129"/>
+        <source>Add the selected Media to the service</source>
+        <translation type="unfinished"></translation>
+    </message>
 </context>
 <context>
     <name>MediaPlugin.MediaItem</name>
     <message>
-        <location filename="openlp/plugins/media/lib/mediaitem.py" line="117"/>
+        <location filename="openlp/plugins/media/lib/mediaitem.py" line="125"/>
         <source>Media</source>
         <translation>Medien</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/media/lib/mediaitem.py" line="64"/>
+        <location filename="openlp/plugins/media/lib/mediaitem.py" line="62"/>
         <source>Select Media</source>
         <translation>Medien auswählen</translation>
     </message>
     <message>
-        <location filename="openlp/plugins/media/lib/mediaitem.py" line="95"/>
+        <location filename="openlp/plugins/media/lib/mediaitem.py" line="93"/>
         <source>Replace Live Background</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/media/lib/mediaitem.py" line="95"/>
+        <location filename="openlp/plugins/media/lib/mediaitem.py" line="93"/>
         <source>Replace Background</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/plugins/media/lib/mediaitem.py" line="136"/>
+        <location filename="openlp/plugins/media/lib/mediaitem.py" line="144"/>
         <source>You must select a media file to delete.</source>
         <translation></translation>
     </message>
@@ -939,7 +1272,7 @@
 <context>
     <name>OpenLP</name>
     <message>
-        <location filename="openlp/core/utils/__init__.py" line="255"/>
+        <location filename="openlp/core/utils/__init__.py" line="262"/>
         <source>Image Files</source>
         <translation></translation>
     </message>
@@ -1201,322 +1534,315 @@
 <context>
     <name>OpenLP.AmendThemeForm</name>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="660"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="638"/>
         <source>Theme Maintenance</source>
         <translation>Designverwaltung</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="662"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="640"/>
         <source>Theme &amp;name:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="664"/>
-        <source>&amp;Visibility:</source>
-        <translation></translation>
-    </message>
-    <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="666"/>
-        <source>Opaque</source>
-        <translation>Fest</translation>
-    </message>
-    <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="668"/>
-        <source>Transparent</source>
-        <translation>Durchsichtig</translation>
-    </message>
-    <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="670"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="642"/>
         <source>Type:</source>
         <translation>Art:</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="672"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="644"/>
         <source>Solid Color</source>
         <translation>Füllfarbe</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="674"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="646"/>
         <source>Gradient</source>
         <translation>Farbverlauf</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="676"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="648"/>
         <source>Image</source>
         <translation>Bild</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="680"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="652"/>
         <source>Image:</source>
         <translation>Bild:</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="682"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="654"/>
         <source>Gradient:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="684"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="656"/>
         <source>Horizontal</source>
         <translation>Horizontal</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="686"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="658"/>
         <source>Vertical</source>
         <translation>Vertikal</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="688"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="660"/>
         <source>Circular</source>
         <translation>Radial</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="690"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="662"/>
         <source>&amp;Background</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="693"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="665"/>
         <source>Main Font</source>
         <translation>Hauptschriftart</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="742"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="712"/>
         <source>Font:</source>
         <translation>Schriftart:</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemeform.py" line="662"/>
+        <location filename="openlp/core/ui/amendthemeform.py" line="615"/>
         <source>Color:</source>
         <translation>Farbe:</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="746"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="716"/>
         <source>Size:</source>
         <translation>Größe:</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="748"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="718"/>
         <source>pt</source>
         <translation>pt</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="703"/>
-        <source>Wrap indentation:</source>
-        <translation></translation>
-    </message>
-    <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="705"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="675"/>
         <source>Adjust line spacing:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="750"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="720"/>
         <source>Normal</source>
         <translation>Normal</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="752"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="722"/>
         <source>Bold</source>
         <translation>Fett</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="754"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="724"/>
         <source>Italics</source>
         <translation>Kursiv</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="756"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="726"/>
         <source>Bold/Italics</source>
         <translation>Fett/Kursiv</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="758"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="728"/>
         <source>Style:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="760"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="730"/>
         <source>Display Location</source>
         <translation>Anzeige Ort</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="762"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="732"/>
         <source>Use default location</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="764"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="734"/>
         <source>X position:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="766"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="736"/>
         <source>Y position:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="768"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="738"/>
         <source>Width:</source>
         <translation>Breite:</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="770"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="740"/>
         <source>Height:</source>
         <translation>Höhe:</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="797"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="767"/>
         <source>px</source>
         <translation>px</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="737"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="707"/>
         <source>&amp;Main Font</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="740"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="710"/>
         <source>Footer Font</source>
         <translation>Schriftart der Fußzeile</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="780"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="750"/>
         <source>&amp;Footer Font</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="783"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="753"/>
         <source>Outline</source>
         <translation>Rand</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="785"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="755"/>
         <source>Outline size:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="789"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="759"/>
         <source>Outline color:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="791"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="761"/>
         <source>Show outline:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="793"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="763"/>
         <source>Shadow</source>
         <translation>Schatten</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="795"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="765"/>
         <source>Shadow size:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="799"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="769"/>
         <source>Shadow color:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="801"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="771"/>
         <source>Show shadow:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="803"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="773"/>
         <source>Alignment</source>
         <translation>Ausrichtung</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="805"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="775"/>
         <source>Horizontal align:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="807"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="777"/>
         <source>Left</source>
         <translation>Links</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="809"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="779"/>
         <source>Right</source>
         <translation>Rechts</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="811"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="781"/>
         <source>Center</source>
         <translation>Mitte</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="813"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="783"/>
         <source>Vertical align:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="815"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="785"/>
         <source>Top</source>
         <translation>Oben</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="817"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="787"/>
         <source>Middle</source>
         <translation>Mittig</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="819"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="789"/>
         <source>Bottom</source>
         <translation>Unten</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="821"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="791"/>
         <source>Slide Transition</source>
         <translation>Folienübergang</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="823"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="793"/>
         <source>Transition active</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="825"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="795"/>
         <source>&amp;Other Options</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemedialog.py" line="828"/>
+        <location filename="openlp/core/ui/amendthemedialog.py" line="798"/>
         <source>Preview</source>
         <translation>Vorschau</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemeform.py" line="222"/>
+        <location filename="openlp/core/ui/amendthemeform.py" line="211"/>
         <source>All Files</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemeform.py" line="224"/>
+        <location filename="openlp/core/ui/amendthemeform.py" line="213"/>
         <source>Select Image</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemeform.py" line="678"/>
+        <location filename="openlp/core/ui/amendthemeform.py" line="631"/>
         <source>First color:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemeform.py" line="680"/>
+        <location filename="openlp/core/ui/amendthemeform.py" line="633"/>
         <source>Second color:</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/amendthemeform.py" line="749"/>
+        <location filename="openlp/core/ui/amendthemeform.py" line="702"/>
         <source>Slide height is %s rows.</source>
         <translation></translation>
     </message>
 </context>
 <context>
+    <name>OpenLP.ExceptionDialog</name>
+    <message>
+        <location filename="openlp/core/ui/exceptiondialog.py" line="75"/>
+        <source>Error Occured</source>
+        <translation type="unfinished"></translation>
+    </message>
+    <message>
+        <location filename="openlp/core/ui/exceptiondialog.py" line="77"/>
+        <source>Oops! OpenLP hit a problem, and couldn&apos;t recover. The text in the box below contains information that might be helpful to the OpenLP developers, so please e-mail it to bugs@xxxxxxxxxx, along with a detailed description of what you were doing when the problem occurred.</source>
+        <translation type="unfinished"></translation>
+    </message>
+</context>
+<context>
     <name>OpenLP.GeneralTab</name>
     <message>
         <location filename="openlp/core/ui/generaltab.py" line="65"/>
@@ -1660,7 +1986,7 @@
 <context>
     <name>OpenLP.MainWindow</name>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="343"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="345"/>
         <source>OpenLP 2.0</source>
         <translation>OpenLP 2.0</translation>
     </message>
@@ -1670,562 +1996,487 @@
         <translation>Deutsch</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="346"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="348"/>
         <source>&amp;File</source>
         <translation>&amp;Datei</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="347"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="349"/>
         <source>&amp;Import</source>
         <translation>&amp;Importieren</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="348"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="350"/>
         <source>&amp;Export</source>
         <translation>&amp;Exportieren</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="349"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="351"/>
         <source>&amp;View</source>
         <translation>&amp;Ansicht</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="350"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="352"/>
         <source>M&amp;ode</source>
         <translation>M&amp;odus</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="351"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="353"/>
         <source>&amp;Tools</source>
         <translation>&amp;Extras</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="352"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="354"/>
         <source>&amp;Settings</source>
         <translation>Ein&amp;stellungen</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="400"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="402"/>
         <source>&amp;Language</source>
         <translation>&amp;Sprache</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="355"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="357"/>
         <source>&amp;Help</source>
         <translation>&amp;Hilfe</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="356"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="358"/>
         <source>Media Manager</source>
         <translation>Medienmanager</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="358"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="360"/>
         <source>Service Manager</source>
         <translation>Ablaufverwaltung</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="360"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="362"/>
         <source>Theme Manager</source>
         <translation>Designmanager</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="362"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="364"/>
         <source>&amp;New</source>
         <translation>&amp;Neu</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="363"/>
-        <source>New Service</source>
-        <translation>Neuer Ablauf</translation>
-    </message>
-    <message>
         <location filename="openlp/core/ui/mainwindow.py" line="365"/>
+        <source>New Service</source>
+        <translation>Neuer Ablauf</translation>
+    </message>
+    <message>
+        <location filename="openlp/core/ui/mainwindow.py" line="367"/>
         <source>Create a new service.</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="367"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="369"/>
         <source>Ctrl+N</source>
         <translation>Strg+N</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="368"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="370"/>
         <source>&amp;Open</source>
         <translation>&amp;Öffnen</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="369"/>
-        <source>Open Service</source>
-        <translation>Öffnen Ablauf</translation>
-    </message>
-    <message>
         <location filename="openlp/core/ui/mainwindow.py" line="371"/>
+        <source>Open Service</source>
+        <translation>Öffnen Ablauf</translation>
+    </message>
+    <message>
+        <location filename="openlp/core/ui/mainwindow.py" line="373"/>
         <source>Open an existing service.</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="373"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="375"/>
         <source>Ctrl+O</source>
         <translation>Strg+O</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="374"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="376"/>
         <source>&amp;Save</source>
         <translation>&amp;Speichern</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="375"/>
-        <source>Save Service</source>
-        <translation>Ablauf speichern</translation>
-    </message>
-    <message>
         <location filename="openlp/core/ui/mainwindow.py" line="377"/>
+        <source>Save Service</source>
+        <translation>Ablauf speichern</translation>
+    </message>
+    <message>
+        <location filename="openlp/core/ui/mainwindow.py" line="379"/>
         <source>Save the current service to disk.</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="379"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="381"/>
         <source>Ctrl+S</source>
         <translation>Strg+S</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="380"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="382"/>
         <source>Save &amp;As...</source>
         <translation>Speichern &amp;als...</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="382"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="384"/>
         <source>Save Service As</source>
         <translation>Speicher Gottesdienst unter</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="384"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="386"/>
         <source>Save the current service under a new name.</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="386"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="388"/>
         <source>Ctrl+Shift+S</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="388"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="390"/>
         <source>E&amp;xit</source>
         <translation>&amp;Beenden</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="390"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="392"/>
         <source>Quit OpenLP</source>
         <translation>OpenLP beenden</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="392"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="394"/>
         <source>Alt+F4</source>
         <translation>Alt+F4</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="398"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="400"/>
         <source>&amp;Theme</source>
         <translation>&amp;Design</translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="402"/>
+        <location filename="openlp/core/ui/mainwindow.py" line="404"/>
         <source>&amp;Configure OpenLP...</source>
         <translation></translation>
     </message>
     <message>
-        <location filename="openlp/core/ui/mainwindow.py" line="404"/>
+        <location filename=

Follow ups