← Back to team overview

openlp-core team mailing list archive

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

 

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

Requested reviews:
  Jon Tibble (meths)


For real this time.

New Renderer and Display Code providing lots of new features and goodies.
All know code paths tested and seem to work.
-- 
https://code.launchpad.net/~trb143/openlp/renderer/+merge/33560
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp.pyw'
--- openlp.pyw	2010-07-30 22:48:09 +0000
+++ openlp.pyw	2010-08-24 17:30:21 +0000
@@ -34,7 +34,8 @@
 
 from openlp.core.lib import Receiver
 from openlp.core.resources import qInitResources
-from openlp.core.ui import MainWindow, SplashScreen, ScreenList
+from openlp.core.ui.mainwindow import MainWindow
+from openlp.core.ui import SplashScreen, ScreenList
 from openlp.core.utils import AppLocation, LanguageManager, VersionThread
 
 log = logging.getLogger()

=== modified file 'openlp/core/lib/__init__.py'
--- openlp/core/lib/__init__.py	2010-07-27 09:32:52 +0000
+++ openlp/core/lib/__init__.py	2010-08-24 17:30:21 +0000
@@ -35,6 +35,67 @@
 
 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'<font color=red>', \
+                          u'end tag':u'{/r}', u'end html':u'</font>', \
+                          u'protected':False})
+html_expands.append({u'desc':u'Black', u'start tag':u'{b}', \
+                          u'start html':u'<font color=black>', \
+                          u'end tag':u'{/b}', u'end html':u'</font>', \
+                          u'protected':False})
+html_expands.append({u'desc':u'Blue', u'start tag':u'{bl}', \
+                          u'start html':u'<font color=blue>', \
+                          u'end tag':u'{/bl}', u'end html':u'</font>', \
+                          u'protected':False})
+html_expands.append({u'desc':u'Yellow', u'start tag':u'{y}', \
+                          u'start html':u'<font color=yellow>', \
+                          u'end tag':u'{/y}', u'end html':u'</font>', \
+                          u'protected':False})
+html_expands.append({u'desc':u'Green', u'start tag':u'{g}', \
+                          u'start html':u'<font color=green>', \
+                          u'end tag':u'{/g}', u'end html':u'</font>', \
+                          u'protected':False})
+html_expands.append({u'desc':u'Pink', u'start tag':u'{pk}', \
+                          u'start html':u'<font color=#CC33CC>', \
+                          u'end tag':u'{/pk}', u'end html':u'</font>', \
+                          u'protected':False})
+html_expands.append({u'desc':u'Orange', u'start tag':u'{o}', \
+                          u'start html':u'<font color=#CC0033>', \
+                          u'end tag':u'{/o}', u'end html':u'</font>', \
+                          u'protected':False})
+html_expands.append({u'desc':u'Purple', u'start tag':u'{pp}', \
+                          u'start html':u'<font color=#9900FF>', \
+                          u'end tag':u'{/pp}', u'end html':u'</font>', \
+                          u'protected':False})
+html_expands.append({u'desc':u'White', u'start tag':u'{w}', \
+                          u'start html':u'<font color=white>', \
+                          u'end tag':u'{/w}', u'end html':u'</font>', \
+                          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.
@@ -166,15 +227,42 @@
     action.setSeparator(True)
     return action
 
-def resize_image(image, width, height):
+def image_to_byte(image):
+    """
+    Resize an image to fit on the current screen for the web and retuns
+    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,
@@ -184,7 +272,7 @@
     # and move it to the centre of the preview space
     new_image = QtGui.QImage(width, height,
         QtGui.QImage.Format_ARGB32_Premultiplied)
-    new_image.fill(QtCore.Qt.black)
+    new_image.fill(background)
     painter = QtGui.QPainter(new_image)
     painter.drawImage((width - realw) / 2, (height - realh) / 2, preview)
     return new_image
@@ -205,6 +293,25 @@
         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 eventreceiver import Receiver
 from settingsmanager import SettingsManager
 from plugin import PluginStatus, Plugin
@@ -213,6 +320,7 @@
 from serviceitem import ServiceItem
 from serviceitem import ServiceItemType
 from serviceitem import ItemCapabilities
+from htmlbuilder import build_html
 from toolbar import OpenLPToolbar
 from dockwidget import OpenLPDockWidget
 from theme import ThemeLevel, ThemeXML

=== added file 'openlp/core/lib/htmlbuilder.py'
--- openlp/core/lib/htmlbuilder.py	1970-01-01 00:00:00 +0000
+++ openlp/core/lib/htmlbuilder.py	2010-08-24 17:30:21 +0000
@@ -0,0 +1,442 @@
+# -*- 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                          #
+###############################################################################
+
+from openlp.core.lib import image_to_byte
+
+HTMLSRC = u"""
+<html>
+<head>
+<title>OpenLP Display</title>
+<style>
+*{
+    margin: 0;
+    padding: 0;
+    border: 0;
+}
+body {
+    background-color: black;
+}
+.dim {
+    position: absolute;
+    left: 0px;
+    top: 0px;
+    width: %spx;
+    height: %spx;
+}
+#black {
+    z-index:8;
+    background-color: black;
+    display: none;
+}
+#video {
+    z-index:2;
+}
+#alert {
+    position: absolute;
+    left: 0px;
+    top: 0px;
+    z-index:10;
+    %s
+}
+#footer {
+    position: absolute;
+    z-index:5;
+    %s
+}
+/* lyric css */
+%s
+
+</style>
+<script language="javascript">
+    var timer = null;
+    var transition = %s;
+
+    function show_video(state, path, volume, loop){
+        var vid = document.getElementById('video');
+        if(path != null)
+            vid.src = path;
+        if(loop != null){
+            if(loop)
+                vid.loop = 'loop';
+            else
+                vid.loop = '';
+        }
+        if(volume != null){
+            vid.volume = volume;
+        }
+        switch(state){
+            case 'play':
+                vid.play();
+                vid.style.display = 'block';
+                break;
+            case 'pause':
+                vid.pause();
+                vid.style.display = 'block';
+                break;
+            case 'stop':
+                vid.pause();
+                vid.style.display = 'none';
+                break;
+            case 'close':
+                vid.pause();
+                vid.style.display = 'none';
+                vid.src = '';
+                break;
+        }
+    }
+
+    function show_image(src){
+        var img = document.getElementById('image');
+        img.src = src;
+        if(src == '')
+            img.style.display = 'none';
+        else
+            img.style.display = 'block';
+    }
+
+    function show_blank(state){
+        var black = 'none';
+        var lyrics = '';
+        var pause = false;
+        switch(state){
+            case 'theme':
+                lyrics = 'hidden';
+                pause = true;
+                break;
+            case 'black':
+                black = 'block';
+                pause = true;
+                break;
+            case 'desktop':
+                pause = true;
+                break;
+        }
+        document.getElementById('black').style.display = black;
+        document.getElementById('lyricsmain').style.visibility = lyrics;
+        document.getElementById('lyricsoutline').style.visibility = lyrics;
+        document.getElementById('lyricsshadow').style.visibility = lyrics;
+        document.getElementById('footer').style.visibility = lyrics;
+        var vid = document.getElementById('video');
+        if(vid.src != ''){
+            if(pause)
+                vid.pause();
+            else
+                vid.play();
+        }
+    }
+
+    function show_alert(alerttext, position){
+        var text = document.getElementById('alert');
+        text.innerHTML = alerttext;
+        if(alerttext == '') {
+            text.style.visibility = 'hidden';
+            return 0;
+        }
+        if(position == ''){
+            position = window.getComputedStyle(text, '').verticalAlign;
+        }
+        switch(position)
+        {
+            case 'top':
+                text.style.top = '0px';
+                break;
+            case 'middle':
+                text.style.top = ((window.innerHeight - text.clientHeight) / 2)
+                    + 'px';
+                break;
+            case 'bottom':
+                text.style.top = (window.innerHeight - text.clientHeight)
+                    + 'px';
+                break;
+        }
+        text.style.visibility = 'visible';
+        return text.clientHeight;
+    }
+
+    function show_footer(footertext){
+        document.getElementById('footer').innerHTML = footertext;
+    }
+
+    function show_text(newtext){
+        var text1 = document.getElementById('lyricsmain');
+        var texto1 = document.getElementById('lyricsoutline');
+        var texts1 = document.getElementById('lyricsshadow');
+        if(!transition){
+            text1.innerHTML = newtext;
+            texto1.innerHTML = newtext;
+            texts1.innerHTML = newtext;
+            return;
+        }
+        var text2 = document.getElementById('lyricsmain2');
+        var texto2 = document.getElementById('lyricsoutline2');
+        var texts2 = document.getElementById('lyricsshadow2');
+        if((text2.style.opacity == '')||(parseFloat(text2.style.opacity) < 0.5))
+        {
+            text2.innerHTML = text1.innerHTML;
+            text2.style.opacity = text1.style.opacity;
+            texto2.innerHTML = text1.innerHTML;
+            texto2.style.opacity = text1.style.opacity;
+            texts2.innerHTML = text1.innerHTML;
+            texts2.style.opacity = text1.style.opacity;
+        }
+        text1.style.opacity = 0;
+        text1.innerHTML = newtext;
+        texto1.style.opacity = 0;
+        texto1.innerHTML = newtext;
+        texts1.style.opacity = 0;
+        texts1.innerHTML = newtext;
+        // For performance reasons, we'll not animate the shadow for now
+        texts2.style.opacity = 0;
+        if(timer != null)
+            clearTimeout(timer);
+        timer = setTimeout('text_fade()', 50);
+    }
+
+    function text_fade(){
+        var text1 = document.getElementById('lyricsmain');
+        var texto1 = document.getElementById('lyricsoutline');
+        var texts1 = document.getElementById('lyricsshadow');
+        var text2 = document.getElementById('lyricsmain2');
+        var texto2 = document.getElementById('lyricsoutline2');
+        var texts2 = document.getElementById('lyricsshadow2');
+        if(parseFloat(text1.style.opacity) < 1){
+            text1.style.opacity = parseFloat(text1.style.opacity) + 0.1;
+            texto1.style.opacity = parseFloat(texto1.style.opacity) + 0.1;
+            // Don't animate shadow (performance)
+            //texts1.style.opacity = parseFloat(texts1.style.opacity) + 0.1;
+        }
+        if(parseFloat(text2.style.opacity) > 0){
+            text2.style.opacity = parseFloat(text2.style.opacity) - 0.1;
+            texto2.style.opacity = parseFloat(texto2.style.opacity) - 0.1;
+            // Don't animate shadow (performance)
+            //texts2.style.opacity = parseFloat(texts2.style.opacity) - 0.1;
+        }
+        if((parseFloat(text1.style.opacity) < 1) ||
+            (parseFloat(text2.style.opacity) > 0)){
+            t = setTimeout('text_fade()', 50);
+        } else {
+            text1.style.opacity = 1;
+            texto1.style.opacity = 1;
+            texts1.style.opacity = 1;
+            text2.style.opacity = 0;
+            texto2.style.opacity = 0;
+            texts2.style.opacity = 0;
+        }
+    }
+
+    function show_text_complete(){
+       return (document.getElementById('lyricsmain').style.opacity == 1);
+    }
+</script>
+</head>
+<body>
+<!--
+Using tables, rather than div's to make use of the vertical-align style that
+doesn't work on div's. This avoids the need to do positioning manually which
+could get messy when changing verses esp. with transitions
+
+Would prefer to use a single table and make use of -webkit-text-fill-color
+-webkit-text-stroke and text-shadow styles, but they have problems working/
+co-operating in qwebkit. https://bugs.webkit.org/show_bug.cgi?id=43187
+Therefore one table for text, one for outline and one for shadow.
+-->
+<table class="lyricstable lyricscommon">
+    <tr><td id="lyricsmain" class="lyrics"></td></tr>
+</table>
+<table class="lyricsoutlinetable lyricscommon">
+    <tr><td id="lyricsoutline" class="lyricsoutline lyrics"></td></tr>
+</table>
+<table class="lyricsshadowtable lyricscommon">
+    <tr><td id="lyricsshadow" class="lyricsshadow lyrics"></td></tr>
+</table>
+<table class="lyricstable lyricscommon">
+    <tr><td id="lyricsmain2" class="lyrics"></td></tr>
+</table>
+<table class="lyricsoutlinetable lyricscommon">
+    <tr><td id="lyricsoutline2" class="lyricsoutline lyrics"></td></tr>
+</table>
+<table class="lyricsshadowtable lyricscommon">
+    <tr><td id="lyricsshadow2" class="lyricsshadow lyrics"></td></tr>
+</table>
+<div id="alert" style="visibility:hidden;"></div>
+<div id="footer" class="footer"></div>
+<video class="dim" id="video"></video>
+<div class="dim" id="black"></div>
+<img class="dim" id="image" src="%s" />
+</body>
+</html>
+    """
+
+def build_html(item, screen, alert):
+    """
+    Build the full web paged structure for display
+
+    `item`
+        Service Item to be displayed
+    `screen`
+        Current display information
+    `alert`
+        Alert display display information
+    """
+    width = screen[u'size'].width()
+    height = screen[u'size'].height()
+    theme = item.themedata
+    if item.bg_frame:
+        image = u'data:image/png;base64,%s' % image_to_byte(item.bg_frame)
+    else:
+        image = u''
+    html = HTMLSRC % (width, height,
+        build_alert(alert, width),
+        build_footer(item),
+        build_lyrics(item),
+        u'true' if theme and theme.display_slideTransition else u'false',
+        image)
+    return html
+
+def build_lyrics(item):
+    """
+    Build the video display div
+
+    `item`
+        Service Item containing theme and location information
+    """
+    style = """
+    .lyricscommon { position: absolute;  %s }
+    .lyricstable { z-index:4;  %s }
+    .lyricsoutlinetable { z-index:3; %s }
+    .lyricsshadowtable { z-index:2; %s }
+    .lyrics { %s }
+    .lyricsoutline { %s }
+    .lyricsshadow { %s }
+     """
+    theme = item.themedata
+    lyricscommon = u''
+    lyricstable = u''
+    outlinetable = u''
+    shadowtable = u''
+    lyrics = u''
+    outline = u'display: none;'
+    shadow = u'display: none;'
+    if theme:
+        lyricscommon =  u'width: %spx; height: %spx; word-wrap: break-word;  ' \
+            u'font-family: %s; font-size: %spx; color: %s; line-height: %d%%' % \
+            (item.main.width(), item.main.height(),
+            theme.font_main_name, theme.font_main_proportion,
+            theme.font_main_color, 100 + int(theme.font_main_line_adjustment))
+        lyricstable = u'left: %spx; top: %spx;' % \
+            (item.main.x(), item.main.y())
+        outlinetable = u'left: %spx; top: %spx;' % \
+            (item.main.x(),  item.main.y())
+        shadowtable = u'left: %spx; top: %spx;' % \
+            (item.main.x() + float(theme.display_shadow_size),
+            item.main.y() + float(theme.display_shadow_size))
+        align = u''
+        if theme.display_horizontalAlign == 2:
+            align = u'text-align:center;'
+        elif theme.display_horizontalAlign == 1:
+            align = u'text-align:right;'
+        else:
+            align = u'text-align:left;'
+        if theme.display_verticalAlign == 2:
+            valign = u'vertical-align:bottom;'
+        elif theme.display_verticalAlign == 1:
+            valign = u'vertical-align:middle;'
+        else:
+            valign = u'vertical-align:top;'
+        lyrics = u'%s %s' % (align, valign)
+        if theme.display_outline:
+            outline = u'-webkit-text-stroke: %sem %s; ' % \
+                (float(theme.display_outline_size) / 16,
+                theme.display_outline_color)
+            if theme.display_shadow:
+                shadow = u'-webkit-text-stroke: %sem %s; ' % \
+                    (float(theme.display_outline_size) / 16,
+                    theme.display_shadow_color)
+        else:
+            if theme.display_shadow:
+                shadow = u'color: %s;' % (theme.display_shadow_color)
+    lyrics_html = style % (lyricscommon, lyricstable, outlinetable,
+        shadowtable, lyrics, outline, shadow)
+    return lyrics_html
+
+def build_footer(item):
+    """
+    Build the display of the item footer
+
+    `item`
+        Service Item to be processed.
+    """
+    style = """
+    left: %spx;
+    top: %spx;
+    width: %spx;
+    height: %spx;
+    font-family: %s;
+    font-size: %spx;
+    color: %s;
+    text-align: %s;
+    """
+    theme = item.themedata
+    if not theme:
+        return u''
+    if theme.display_horizontalAlign == 2:
+        align = u'center'
+    elif theme.display_horizontalAlign == 1:
+        align = u'right'
+    else:
+        align = u'left'
+    lyrics_html = style % (item.footer.x(),  item.footer.y(),
+        item.footer.width(), item.footer.height(), theme.font_footer_name,
+        theme.font_footer_proportion, theme.font_footer_color, align)
+    return lyrics_html
+
+def build_alert(alertTab, width):
+    """
+    Build the display of the footer
+
+    `alertTab`
+        Details from the Alert tab for fonts etc
+    """
+    style = """
+    width: %s;
+    vertical-align: %s;
+    font-family: %s;
+    font-size: %spx;
+    color: %s;
+    background-color: %s;
+    """
+    if not alertTab:
+        return u''
+    align = u''
+    if alertTab.location == 2:
+        align = u'bottom'
+    elif alertTab.location == 1:
+        align = u'middle'
+    else:
+        align = u'top'
+    alert = style % (width, align, alertTab.font_face, alertTab.font_size,
+        alertTab.font_color, alertTab.bg_color)
+    return alert

=== modified file 'openlp/core/lib/plugin.py'
--- openlp/core/lib/plugin.py	2010-07-31 00:34:37 +0000
+++ openlp/core/lib/plugin.py	2010-08-24 17:30:21 +0000
@@ -131,7 +131,6 @@
         self.serviceManager = plugin_helpers[u'service']
         self.settingsForm = plugin_helpers[u'settings form']
         self.mediadock = plugin_helpers[u'toolbox']
-        self.displayManager = plugin_helpers[u'displaymanager']
         self.pluginManager = plugin_helpers[u'pluginmanager']
         self.formparent = plugin_helpers[u'formparent']
         QtCore.QObject.connect(Receiver.get_receiver(),

=== modified file 'openlp/core/lib/renderer.py'
--- openlp/core/lib/renderer.py	2010-07-27 09:32:52 +0000
+++ openlp/core/lib/renderer.py	2010-08-24 17:30:21 +0000
@@ -31,7 +31,7 @@
 
 from PyQt4 import QtGui, QtCore
 
-from openlp.core.lib import resize_image
+from openlp.core.lib import resize_image, expand_tags
 
 log = logging.getLogger(__name__)
 
@@ -80,7 +80,6 @@
         self.bg_image = None
         self._bg_image_filename = None
         self.theme_name = theme.theme_name
-        self._set_theme_font()
         if theme.background_type == u'image':
             if theme.background_filename:
                 self.set_bg_image(theme.background_filename)
@@ -99,6 +98,20 @@
                                          self.frame.width(),
                                          self.frame.height())
 
+    def set_text_rectangle(self, rect_main, rect_footer):
+        """
+        Sets the rectangle within which text should be rendered.
+
+        ``rect_main``
+            The main text block.
+
+        ``rect_footer``
+            The footer text block.
+        """
+        log.debug(u'set_text_rectangle %s , %s' % (rect_main, rect_footer))
+        self._rect = rect_main
+        self._rect_footer = rect_footer
+
     def set_frame_dest(self, frame_width, frame_height, preview=False):
         """
         Set the size of the slide.
@@ -118,26 +131,24 @@
             frame_height)
         self.frame = QtGui.QImage(frame_width, frame_height,
             QtGui.QImage.Format_ARGB32_Premultiplied)
-        self.frame_opaque = QtGui.QImage(frame_width, frame_height,
-            QtGui.QImage.Format_ARGB32_Premultiplied)
         if self._bg_image_filename and not self.bg_image:
             self.bg_image = resize_image(self._bg_image_filename,
                 self.frame.width(), self.frame.height())
         if self.bg_frame is None:
             self._generate_background_frame()
 
-    def format_slide(self, words, footer):
+    def format_slide(self, words, line_break):
         """
         Figure out how much text can appear on a slide, using the current
         theme settings.
 
         ``words``
             The words to be fitted on the slide.
-
-        ``footer``
-            The footer of the slide.
         """
         log.debug(u'format_slide - Start')
+        line_end = u''
+        if line_break:
+            line_end = u'<br>'
         words = words.replace(u'\r\n', u'\n')
         verses_text = words.split(u'\n')
         text = []
@@ -145,124 +156,43 @@
             lines = verse.split(u'\n')
             for line in lines:
                 text.append(line)
-        split_text = self.pre_render_text(text)
+        doc = QtGui.QTextDocument()
+        doc.setPageSize(QtCore.QSizeF(self._rect.width(), self._rect.height()))
+        df = doc.defaultFont()
+        df.setPixelSize(self._theme.font_main_proportion)
+        df.setFamily(self._theme.font_main_name)
+        main_weight = 50
+        if self._theme.font_main_weight == u'Bold':
+            main_weight = 75
+        df.setWeight(main_weight)
+        doc.setDefaultFont(df)
+        layout = doc.documentLayout()
+        formatted = []
+        if self._theme.font_main_weight == u'Bold' and \
+            self._theme.font_main_italics:
+            shell = u'{p}{st}{it}%s{/it}{/st}{/p}'
+        elif self._theme.font_main_weight == u'Bold' and \
+            not self._theme.font_main_italics:
+            shell = u'{p}{st}%s{/st}{/p}'
+        elif self._theme.font_main_italics:
+            shell = u'{p}{it}%s{/it}{/p}'
+        else:
+            shell = u'{p}%s{/p}'
+        temp_text = u''
+        old_html_text = u''
+        for line in text:
+            # mark line ends
+            temp_text = temp_text + line + line_end
+            html_text = shell % expand_tags(temp_text)
+            doc.setHtml(html_text)
+            # Text too long so gone to next mage
+            if layout.pageCount() != 1:
+                formatted.append(shell % old_html_text)
+                temp_text = line
+            old_html_text = temp_text
+        formatted.append(shell % old_html_text)
         log.debug(u'format_slide - End')
-        return split_text
-
-    def pre_render_text(self, text):
-        metrics = QtGui.QFontMetrics(self.main_font)
-        #work out line width
-        line_width = self._rect.width()
-        #number of lines on a page - adjust for rounding up.
-        line_height = metrics.height()
-        if self._theme.display_shadow:
-            line_height += int(self._theme.display_shadow_size)
-        if self._theme.display_outline:
-            #  pixels top/bottom
-            line_height += 2 * int(self._theme.display_outline_size)
-        page_length = int(self._rect.height() / line_height )
-        #Average number of characters in line
-        ave_line_width = line_width / metrics.averageCharWidth()
-        #Maximum size of a character
-        max_char_width = metrics.maxWidth()
-        #Max characters pre line based on min size of a character
-        char_per_line = line_width / metrics.width(u'i')
-        log.debug(u'Page Length  area height %s , metrics %s , lines %s' %
-                  (int(self._rect.height()), metrics.height(), page_length ))
-        split_pages = []
-        page = []
-        split_lines = []
-        count = 0
-        for line in text:
-            #Must be a blank line so keep it.
-            if len(line) == 0:
-                line = u' '
-            while line:
-                pos = char_per_line
-                split_text = line[:pos]
-                #line needs splitting
-                if metrics.width(split_text, -1) > line_width:
-                    #We have no spaces
-                    if split_text.find(u' ') == -1:
-                        #Move back 1 char at a time till it fits
-                        while metrics.width(split_text, -1) > line_width:
-                            split_text = split_text[:-1]
-                            pos = len(split_text)
-                    else:
-                        #We have spaces so split at previous one
-                        while metrics.width(split_text, -1) > line_width:
-                            pos = split_text.rfind(u' ')
-                            #no more spaces and we are still too long
-                            if pos == -1:
-                                while \
-                                    metrics.width(split_text, -1) > line_width:
-                                    split_text = split_text[:-1]
-                                    pos = len(split_text)
-                            else:
-                                split_text = line[:pos]
-                split_lines.append(split_text)
-                line = line[pos:].lstrip()
-                #if we have more text add up to 10 spaces on the front.
-                if line and self._theme.font_main_indentation > 0:
-                    line = u'%s%s' % \
-                        (u'          '[:int(self._theme.font_main_indentation)],
-                        line)
-                #Text fits in a line now
-        for count, line in enumerate(split_lines):
-            page.append(line)
-            #last but one line and only 2 lines to go or end of page
-            if (len(page) == page_length - 1 and
-                len(split_lines) - 3 == count) or \
-                len(page) == page_length:
-                split_pages.append(page)
-                page = []
-        if page and page != u' ':
-            split_pages.append(page)
-        return split_pages
-
-    def set_text_rectangle(self, rect_main, rect_footer):
-        """
-        Sets the rectangle within which text should be rendered.
-
-        ``rect_main``
-            The main text block.
-
-        ``rect_footer``
-            The footer text block.
-        """
-        log.debug(u'set_text_rectangle %s , %s' % (rect_main, rect_footer))
-        self._rect = rect_main
-        self._rect_footer = rect_footer
-
-    def generate_frame_from_lines(self, lines, footer_lines=None):
-        """
-        Render a set of lines according to the theme, and return the block
-        dimensions.
-
-        ``lines``
-            The lines to be rendered.
-
-        ``footer_lines``
-            Defaults to *None*. The footer to render.
-        """
-        log.debug(u'generate_frame_from_lines - Start')
-        bbox = self._render_lines_unaligned(lines, False)
-        if footer_lines:
-            bbox1 = self._render_lines_unaligned(footer_lines, True)
-        # reset the frame. first time do not worry about what you paint on.
-        self.frame = QtGui.QImage(self.bg_frame)
-        if self._theme.display_slideTransition:
-            self.frame_opaque = QtGui.QImage(self.bg_frame)
-        x, y = self._correct_alignment(self._rect, bbox)
-        bbox = self._render_lines_unaligned(lines, False, (x, y), True)
-        if footer_lines:
-            bbox = self._render_lines_unaligned(footer_lines, True,
-                (self._rect_footer.left(), self._rect_footer.top()), True)
-        log.debug(u'generate_frame_from_lines - Finish')
-        if self._theme.display_slideTransition:
-            return {u'main':self.frame, u'trans':self.frame_opaque}
-        else:
-            return {u'main':self.frame, u'trans':None}
+        return formatted
 
     def _generate_background_frame(self):
         """
@@ -270,327 +200,47 @@
         Results are cached for performance reasons.
         """
         assert(self._theme)
-        if self._theme.background_mode == u'transparent':
-            self.bg_frame = \
-                QtGui.QPixmap(self.frame.width(), self.frame.height())
-            self.bg_frame.fill(QtCore.Qt.transparent)
-        else:
-            self.bg_frame = QtGui.QImage(self.frame.width(),
-                self.frame.height(), QtGui.QImage.Format_ARGB32_Premultiplied)
+        self.bg_frame = QtGui.QImage(self.frame.width(),
+            self.frame.height(), QtGui.QImage.Format_ARGB32_Premultiplied)
         log.debug(u'render background %s start', self._theme.background_type)
         painter = QtGui.QPainter()
         painter.begin(self.bg_frame)
-        if self._theme.background_mode == u'transparent':
-            painter.fillRect(self.frame.rect(), QtCore.Qt.transparent)
-        else:
-            if self._theme.background_type == u'solid':
-                painter.fillRect(self.frame.rect(),
-                    QtGui.QColor(self._theme.background_color))
-            elif self._theme.background_type == u'gradient':
-                # gradient
-                gradient = None
-                if self._theme.background_direction == u'horizontal':
-                    w = int(self.frame.width()) / 2
-                    # vertical
-                    gradient = QtGui.QLinearGradient(w, 0, w,
-                        self.frame.height())
-                elif self._theme.background_direction == u'vertical':
-                    h = int(self.frame.height()) / 2
-                    # Horizontal
-                    gradient = QtGui.QLinearGradient(0, h, self.frame.width(),
-                        h)
-                else:
-                    w = int(self.frame.width()) / 2
-                    h = int(self.frame.height()) / 2
-                    # Circular
-                    gradient = QtGui.QRadialGradient(w, h, w)
-                gradient.setColorAt(0,
-                    QtGui.QColor(self._theme.background_startColor))
-                gradient.setColorAt(1,
-                    QtGui.QColor(self._theme.background_endColor))
-                painter.setBrush(QtGui.QBrush(gradient))
-                rect_path = QtGui.QPainterPath()
-                max_x = self.frame.width()
-                max_y = self.frame.height()
-                rect_path.moveTo(0, 0)
-                rect_path.lineTo(0, max_y)
-                rect_path.lineTo(max_x, max_y)
-                rect_path.lineTo(max_x, 0)
-                rect_path.closeSubpath()
-                painter.drawPath(rect_path)
-            elif self._theme.background_type == u'image':
-                # image
-                painter.fillRect(self.frame.rect(), QtCore.Qt.black)
-                if self.bg_image:
-                    painter.drawImage(0, 0, self.bg_image)
+        if self._theme.background_type == u'solid':
+            painter.fillRect(self.frame.rect(),
+                QtGui.QColor(self._theme.background_color))
+        elif self._theme.background_type == u'gradient':
+            # gradient
+            gradient = None
+            if self._theme.background_direction == u'horizontal':
+                w = int(self.frame.width()) / 2
+                # vertical
+                gradient = QtGui.QLinearGradient(w, 0, w, self.frame.height())
+            elif self._theme.background_direction == u'vertical':
+                h = int(self.frame.height()) / 2
+                # Horizontal
+                gradient = QtGui.QLinearGradient(0, h, self.frame.width(), h)
+            else:
+                w = int(self.frame.width()) / 2
+                h = int(self.frame.height()) / 2
+                # Circular
+                gradient = QtGui.QRadialGradient(w, h, w)
+            gradient.setColorAt(0,
+                QtGui.QColor(self._theme.background_startColor))
+            gradient.setColorAt(1,
+                QtGui.QColor(self._theme.background_endColor))
+            painter.setBrush(QtGui.QBrush(gradient))
+            rect_path = QtGui.QPainterPath()
+            max_x = self.frame.width()
+            max_y = self.frame.height()
+            rect_path.moveTo(0, 0)
+            rect_path.lineTo(0, max_y)
+            rect_path.lineTo(max_x, max_y)
+            rect_path.lineTo(max_x, 0)
+            rect_path.closeSubpath()
+            painter.drawPath(rect_path)
+        elif self._theme.background_type == u'image':
+            # image
+            painter.fillRect(self.frame.rect(), QtCore.Qt.black)
+            if self.bg_image:
+                painter.drawImage(0, 0, self.bg_image)
         painter.end()
-        log.debug(u'render background End')
-
-    def _correct_alignment(self, rect, bbox):
-        """
-        Corrects the vertical alignment of text.
-
-        ``rect``
-            The block dimentions.
-
-        ``bbox``
-            Footer dimensions?
-        """
-        x = rect.left()
-        if self._theme.display_verticalAlign == 0:
-            # top align
-            y = rect.top()
-        elif self._theme.display_verticalAlign == 2:
-            # bottom align
-            y = rect.bottom() - bbox.height()
-        elif self._theme.display_verticalAlign == 1:
-            # centre align
-            y = rect.top() + (rect.height() - bbox.height()) / 2
-        else:
-            log.error(u'Invalid value for theme.VerticalAlign:%s',
-                self._theme.display_verticalAlign)
-        return x, y
-
-    def _render_lines_unaligned(self, lines, footer, tlcorner=(0, 0),
-        live=False):
-        """
-        Given a list of lines to render, render each one in turn (using the
-        ``_render_single_line`` fn - which may result in going off the bottom).
-        They are expected to be pre-arranged to less than a screenful (eg. by
-        using split_set_of_lines).
-
-        Returns the bounding box of the text as QRect.
-
-        ``lines``
-            The lines of text to render.
-
-        ``footer``
-            The slide footer.
-
-        ``tlcorner``
-            Defaults to *``(0, 0)``*. Co-ordinates of the top left corner.
-
-        ``live``
-            Defaults to *False*. Whether or not this is a live screen.
-        """
-        x, y = tlcorner
-        brx = x
-        bry = y
-        for line in lines:
-            # render after current bottom, but at original left edge
-            # keep track of right edge to see which is biggest
-            (thisx, bry) = self._render_and_wrap_single_line(line, footer,
-                (x, bry), live)
-            if (thisx > brx):
-                brx = thisx
-        retval = QtCore.QRect(x, y, brx - x, bry - y)
-        if self._debug:
-            painter = QtGui.QPainter()
-            painter.begin(self.frame)
-            painter.setPen(QtGui.QPen(QtGui.QColor(0, 0, 255)))
-            painter.drawRect(retval)
-            painter.end()
-        return retval
-
-    def _render_and_wrap_single_line(self, line, footer, tlcorner=(0, 0),
-        live=False):
-        """
-        Render a single line of words onto the DC, top left corner specified.
-        If the line is too wide for the context, it wraps, but right-aligns
-        the surplus words in the manner of song lyrics.
-
-        Returns the bottom-right corner (of what was rendered) as a tuple(x, y).
-
-        ``line``
-            Line of text to be rendered.
-
-        ``footer``
-            The footer of the slide.
-
-        ``tlcorner``
-            Defaults to *``(0, 0)``*. The top left corner.
-
-        ``live``
-            Defaults to *False*. Whether or not this is a live screen.
-        """
-        x, y = tlcorner
-        maxx = self._rect.width()
-        maxy = self._rect.height()
-        lines = []
-        lines.append(line)
-        startx = x
-        starty = y
-        rightextent = None
-        self.painter = QtGui.QPainter()
-        self.painter.begin(self.frame)
-        self.painter.setRenderHint(QtGui.QPainter.Antialiasing)
-        if self._theme.display_slideTransition:
-            self.painter2 = QtGui.QPainter()
-            self.painter2.begin(self.frame_opaque)
-            self.painter2.setRenderHint(QtGui.QPainter.Antialiasing)
-            self.painter2.setOpacity(0.7)
-        # dont allow alignment messing with footers
-        if footer:
-            align = 0
-            display_shadow_size = self._display_shadow_size_footer
-            display_outline_size = self._display_outline_size_footer
-        else:
-            align = self._theme.display_horizontalAlign
-            display_shadow_size = int(self._theme.display_shadow_size)
-            display_outline_size = int(self._theme.display_outline_size)
-        for linenum in range(len(lines)):
-            line = lines[linenum]
-            #find out how wide line is
-            w, h = self._get_extent_and_render(line, footer, tlcorner=(x, y),
-                draw=False)
-            if self._theme.display_shadow:
-                w += display_shadow_size
-                h += display_shadow_size
-            if self._theme.display_outline:
-                # pixels either side
-                w += 2 * display_outline_size
-                #  pixels top/bottom
-                h += 2 * display_outline_size
-            if align == 0: # left align
-                rightextent = x + w
-                # shift right from last line's rh edge
-                if self._theme.display_wrapStyle == 1 and linenum != 0:
-                    rightextent = self._first_line_right_extent
-                    if rightextent > maxx:
-                        rightextent = maxx
-                    x = rightextent - w
-            # right align
-            elif align == 1:
-                rightextent = maxx
-                x = maxx - w
-            # centre
-            elif align == 2:
-                x = (maxx - w) / 2
-                rightextent = x + w
-            if live:
-                # now draw the text, and any outlines/shadows
-                if self._theme.display_shadow:
-                    self._get_extent_and_render(line, footer,
-                        tlcorner=(x + display_shadow_size,
-                            y + display_shadow_size),
-                            draw=True, color=self._theme.display_shadow_color)
-                self._get_extent_and_render(line, footer, tlcorner=(x, y),
-                        draw=True, outline_size=display_outline_size)
-            y += h
-            if linenum == 0:
-                self._first_line_right_extent = rightextent
-        # draw a box around the text - debug only
-
-        if self._debug:
-            self.painter.setPen(QtGui.QPen(QtGui.QColor(0, 255, 0)))
-            self.painter.drawRect(startx, starty, rightextent-startx, y-starty)
-        brcorner = (rightextent, y)
-        self.painter.end()
-        if self._theme.display_slideTransition:
-            self.painter2.end()
-        return brcorner
-
-    def _set_theme_font(self):
-        """
-        Set the fonts from the current theme settings.
-        """
-        footer_weight = 50
-        if self._theme.font_footer_weight == u'Bold':
-            footer_weight = 75
-        #TODO Add  myfont.setPixelSize((screen_height / 100) * font_size)
-        self.footer_font = QtGui.QFont(self._theme.font_footer_name,
-                     self._theme.font_footer_proportion, # size
-                     footer_weight, # weight
-                     self._theme.font_footer_italics) # italic
-        self.footer_font.setPixelSize(self._theme.font_footer_proportion)
-        main_weight = 50
-        if self._theme.font_main_weight == u'Bold':
-            main_weight = 75
-        self.main_font = QtGui.QFont(self._theme.font_main_name,
-                     self._theme.font_main_proportion, # size
-                     main_weight, # weight
-                     self._theme.font_main_italics)# italic
-        self.main_font.setPixelSize(self._theme.font_main_proportion)
-
-    def _get_extent_and_render(self, line, footer, tlcorner=(0, 0), draw=False,
-        color=None, outline_size=0):
-        """
-        Find bounding box of text - as render_single_line. If draw is set,
-        actually draw the text to the current DC as well return width and
-        height of text as a tuple (w, h).
-
-        ``line``
-            The line of text to render.
-
-        ``footer``
-            The footer text.
-
-        ``tlcorner``
-            Defaults to *``(0, 0)``*. The top left corner co-ordinates.
-
-        ``draw``
-            Defaults to *False*. Draw the text to the current surface.
-
-        ``color``
-            Defaults to *None*. The colour to draw with.
-        """
-        # setup defaults
-        if footer:
-            font = self.footer_font
-        else:
-            font = self.main_font
-        metrics = QtGui.QFontMetrics(font)
-        w = metrics.width(line)
-        if footer:
-            h = metrics.height()
-        else:
-            h = metrics.height() + int(self._theme.font_main_line_adjustment)
-        if draw:
-            self.painter.setFont(font)
-            if color is None:
-                if footer:
-                    pen = QtGui.QColor(self._theme.font_footer_color)
-                else:
-                    pen = QtGui.QColor(self._theme.font_main_color)
-            else:
-                pen = QtGui.QColor(color)
-            x, y = tlcorner
-            rowpos = y + metrics.ascent()
-            if self._theme.display_outline and outline_size != 0 and not footer:
-                path = QtGui.QPainterPath()
-                path.addText(QtCore.QPointF(x, rowpos), font, line)
-                self.painter.setBrush(self.painter.pen().brush())
-                self.painter.setPen(QtGui.QPen(QtGui.QColor(
-                    self._theme.display_outline_color), outline_size))
-                self.painter.drawPath(path)
-            self.painter.setPen(pen)
-            self.painter.drawText(x, rowpos, line)
-            if self._theme.display_slideTransition:
-                # Print 2nd image with 70% weight
-                if self._theme.display_outline and outline_size != 0 and \
-                    not footer:
-                    path = QtGui.QPainterPath()
-                    path.addText(QtCore.QPointF(x, rowpos), font, line)
-                    self.painter2.setBrush(self.painter2.pen().brush())
-                    self.painter2.setPen(QtGui.QPen(
-                            QtGui.QColor(self._theme.display_outline_color),
-                            outline_size))
-                    self.painter2.drawPath(path)
-                self.painter2.setFont(font)
-                self.painter2.setPen(pen)
-                self.painter2.drawText(x, rowpos, line)
-        return (w, h)
-
-    def snoop_image(self, image, image2=None):
-        """
-        Debugging method to allow images to be viewed.
-
-        ``image``
-            An image to save to disk.
-
-        ``image2``
-            Defaults to *None*. Another image to save to disk.
-        """
-        image.save(u'renderer.png', u'png')
-        if image2:
-            image2.save(u'renderer2.png', u'png')

=== modified file 'openlp/core/lib/rendermanager.py'
--- openlp/core/lib/rendermanager.py	2010-07-27 09:32:52 +0000
+++ openlp/core/lib/rendermanager.py	2010-08-24 17:30:21 +0000
@@ -28,7 +28,8 @@
 
 from PyQt4 import QtCore
 
-from openlp.core.lib import Renderer, ThemeLevel
+from openlp.core.lib import Renderer, ThemeLevel, ServiceItem
+from openlp.core.ui import MainDisplay
 
 log = logging.getLogger(__name__)
 
@@ -55,6 +56,8 @@
         """
         log.debug(u'Initilisation started')
         self.screens = screens
+        self.display = MainDisplay(self, screens, False)
+        self.display.setup()
         self.theme_manager = theme_manager
         self.renderer = Renderer()
         self.calculate_default(self.screens.current[u'size'])
@@ -63,6 +66,7 @@
         self.theme_level = u''
         self.override_background = None
         self.themedata = None
+        self.alertTab = None
 
     def update_display(self):
         """
@@ -70,6 +74,8 @@
         """
         log.debug(u'Update Display')
         self.calculate_default(self.screens.current[u'size'])
+        self.display = MainDisplay(self, self.screens, False)
+        self.display.setup()
         self.renderer.bg_frame = None
 
     def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global):
@@ -96,17 +102,22 @@
         """
         self.service_theme = service_theme
 
-    def set_override_theme(self, theme):
+    def set_override_theme(self, theme, overrideLevels=False):
         """
         Set the appropriate theme depending on the theme level.
+        Called by the service item when building a display frame
 
         ``theme``
-            The name of the song-level theme.
+            The name of the song-level theme. None means the service
+            item wants to use the given value.
         """
         log.debug(u'set override theme to %s', theme)
-        if self.theme_level == ThemeLevel.Global:
+        theme_level = self.theme_level
+        if overrideLevels:
+            theme_level = ThemeLevel.Song
+        if theme_level == ThemeLevel.Global:
             self.theme = self.global_theme
-        elif self.theme_level == ThemeLevel.Service:
+        elif theme_level == ThemeLevel.Service:
             if self.service_theme == u'':
                 self.theme = self.global_theme
             else:
@@ -114,20 +125,26 @@
         else:
             if theme:
                 self.theme = theme
-            elif self.theme_level == ThemeLevel.Song or \
-                self.theme_level == ThemeLevel.Service:
+            elif theme_level == ThemeLevel.Song or \
+                theme_level == ThemeLevel.Service:
                 if self.service_theme == u'':
                     self.theme = self.global_theme
                 else:
                     self.theme = self.service_theme
             else:
                 self.theme = self.global_theme
-        if self.theme != self.renderer.theme_name or self.themedata is None:
+        if self.theme != self.renderer.theme_name or self.themedata is None \
+            or overrideLevels:
             log.debug(u'theme is now %s', self.theme)
-            self.themedata = self.theme_manager.getThemeData(self.theme)
+            if overrideLevels:
+                self.themedata = theme
+            else:
+                self.themedata = self.theme_manager.getThemeData(self.theme)
             self.calculate_default(self.screens.current[u'size'])
             self.renderer.set_theme(self.themedata)
             self.build_text_rectangle(self.themedata)
+            self.renderer.set_frame_dest(self.width, self.height)
+        return self.renderer._rect, self.renderer._rect_footer
 
     def build_text_rectangle(self, theme):
         """
@@ -163,13 +180,8 @@
             The theme to generated a preview for.
         """
         log.debug(u'generate preview')
-        #set the default image size for previews
+        # set the default image size for previews
         self.calculate_default(self.screens.preview[u'size'])
-        self.renderer.set_theme(themedata)
-        self.build_text_rectangle(themedata)
-        self.renderer.set_frame_dest(self.width, self.height, True)
-        #Reset the real screen size for subsequent render requests
-        self.calculate_default(self.screens.current[u'size'])
         verse = u'Amazing Grace!\n'\
         'How sweet the sound\n'\
         'To save a wretch like me;\n'\
@@ -179,12 +191,23 @@
         footer.append(u'Amazing Grace (John Newton)' )
         footer.append(u'Public Domain')
         footer.append(u'CCLI 123456')
-        formatted = self.renderer.format_slide(verse, False)
-        #Only Render the first slide page returned
-        return self.renderer.generate_frame_from_lines(formatted[0],
-            footer)[u'main']
+        # Previews do not need the transition switched on!
+        themedata.display_slideTransition = False
+        # build a service item to generate preview
+        serviceItem = ServiceItem()
+        serviceItem.theme = themedata
+        serviceItem.add_from_text(u'', verse, footer)
+        serviceItem.render_manager = self
+        serviceItem.raw_footer = footer
+        serviceItem.render(True)
+        self.display.buildHtml(serviceItem)
+        frame, raw_html = serviceItem.get_rendered_frame(0)
+        preview = self.display.text(raw_html)
+        # Reset the real screen size for subsequent render requests
+        self.calculate_default(self.screens.current[u'size'])
+        return preview
 
-    def format_slide(self, words):
+    def format_slide(self, words, line_break):
         """
         Calculate how much text can fit on a slide.
 
@@ -193,22 +216,7 @@
         """
         log.debug(u'format slide')
         self.build_text_rectangle(self.themedata)
-        return self.renderer.format_slide(words, False)
-
-    def generate_slide(self, main_text, footer_text):
-        """
-        Generate the actual slide image.
-
-        ``main_text``
-            The text for the main area of the slide.
-
-        ``footer_text``
-            The text for the slide footer.
-        """
-        log.debug(u'generate slide')
-        self.build_text_rectangle(self.themedata)
-        self.renderer.set_frame_dest(self.width, self.height)
-        return self.renderer.generate_frame_from_lines(main_text, footer_text)
+        return self.renderer.format_slide(words, line_break)
 
     def calculate_default(self, screen):
         """

=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py	2010-08-02 19:23:39 +0000
+++ openlp/core/lib/serviceitem.py	2010-08-24 17:30:21 +0000
@@ -35,7 +35,7 @@
 
 from PyQt4 import QtGui
 
-from openlp.core.lib import build_icon, resize_image
+from openlp.core.lib import build_icon, resize_image, clean_tags, expand_tags
 
 log = logging.getLogger(__name__)
 
@@ -57,6 +57,7 @@
     RequiresMedia = 4
     AllowsLoop = 5
     AllowsAdditions = 6
+    NoLineBreaks = 7
 
 class ServiceItem(object):
     """
@@ -82,6 +83,7 @@
         self.items = []
         self.iconic_representation = None
         self.raw_footer = None
+        self.foot_text = None
         self.theme = None
         self.service_item_type = None
         self._raw_frames = []
@@ -91,10 +93,18 @@
         self.from_plugin = False
         self.capabilities = []
         self.is_valid = True
-        self.cache = {}
         self.icon = None
+        self.themedata = None
+        self.main = None
+        self.footer = None
+        self.bg_frame = None
 
     def _new_item(self):
+        """
+        Method to set the internal id of the item
+        This is used to compare service items to see if they are
+        the same
+        """
         self._uuid = unicode(uuid.uuid1())
 
     def add_capability(self, capability):
@@ -126,34 +136,38 @@
         self.icon = icon
         self.iconic_representation = build_icon(icon)
 
-    def render(self):
+    def render(self, useOverride=False):
         """
-        The render method is what generates the frames for the screen.
+        The render method is what generates the frames for the screen and
+        obtains the display information from the renderemanager.
+        At this point all the slides are build for the given
+        display size.
         """
         log.debug(u'Render called')
         self._display_frames = []
-        self.clear_cache()
+        self.bg_frame = None
+        line_break = True
+        if self.is_capable(ItemCapabilities.NoLineBreaks):
+            line_break = False
         if self.service_item_type == ServiceItemType.Text:
             log.debug(u'Formatting slides')
-            if self.theme is None:
-                self.render_manager.set_override_theme(None)
-            else:
-                self.render_manager.set_override_theme(self.theme)
+            theme = None
+            if self.theme:
+                theme = self.theme
+            self.main, self.footer = \
+                self.render_manager.set_override_theme(theme, useOverride)
+            self.bg_frame = self.render_manager.renderer.bg_frame
+            self.themedata = self.render_manager.renderer._theme
             for slide in self._raw_frames:
                 before = time.time()
-                formated = self.render_manager.format_slide(slide[u'raw_slide'])
-                for format in formated:
-                    lines = u''
-                    title = u''
-                    for line in format:
-                        if title == u'':
-                            title = line
-                        lines += line + u'\n'
-                    self._display_frames.append({u'title': title,
-                        u'text': lines.rstrip(),
+                formated = self.render_manager \
+                    .format_slide(slide[u'raw_slide'], line_break)
+                for page in formated:
+                    self._display_frames.append(
+                        {u'title': clean_tags(page),
+                        u'text': clean_tags(page.rstrip()),
+                        u'html': expand_tags(page.rstrip()),
                         u'verseTag': slide[u'verseTag'] })
-                    if len(self._display_frames) in self.cache.keys():
-                        del self.cache[len(self._display_frames)]
                 log.log(15, u'Formatting took %4s' % (time.time() - before))
         elif self.service_item_type == ServiceItemType.Image:
             for slide in self._raw_frames:
@@ -163,29 +177,14 @@
             pass
         else:
             log.error(u'Invalid value renderer :%s' % self.service_item_type)
-
-    def render_individual(self, row):
-        """
-        Takes an array of text and generates an Image from the
-        theme.  It assumes the text will fit on the screen as it
-        has generated by the render method above.
-        """
-        log.debug(u'render individual')
-        if self.theme is None:
-            self.render_manager.set_override_theme(None)
-        else:
-            self.render_manager.set_override_theme(self.theme)
-        format = self._display_frames[row][u'text'].split(u'\n')
-        if self.cache.get(row):
-            frame = self.cache[row]
-        else:
-            if format[0]:
-                frame = self.render_manager.generate_slide(format,
-                    self.raw_footer)
-            else:
-                frame = self.render_manager.generate_slide(format, u'')
-            self.cache[row] = frame
-        return frame
+        self.title = clean_tags(self.title)
+        self.foot_text = None
+        if self.raw_footer:
+            for foot in self.raw_footer:
+                if not self.foot_text:
+                    self.foot_text = foot
+                else:
+                    self.foot_text = u'%s<br>%s' % (self.foot_text, foot)
 
     def add_from_image(self, path, title, image):
         """
@@ -375,9 +374,9 @@
         renders it if required.
         """
         if self.service_item_type == ServiceItemType.Text:
-            return self.render_individual(row)
+            return None, self._display_frames[row][u'html'].split(u'\n')[0]
         else:
-            return {u'main':self._raw_frames[row][u'image'], u'trans':None}
+            return self._raw_frames[row][u'image'], u''
 
     def get_frame_title(self, row=0):
         """
@@ -390,9 +389,3 @@
         Returns the title of the raw frame
         """
         return self._raw_frames[row][u'path']
-
-    def clear_cache(self):
-        """
-        Clear's the service item's cache.
-        """
-        self.cache = {}

=== modified file 'openlp/core/lib/theme.py'
--- openlp/core/lib/theme.py	2010-07-27 09:32:52 +0000
+++ openlp/core/lib/theme.py	2010-08-24 17:30:21 +0000
@@ -55,7 +55,6 @@
       <proportion>30</proportion>
       <weight>Normal</weight>
       <italics>False</italics>
-      <indentation>0</indentation>
       <line_adjustment>0</line_adjustment>
       <location override="False" x="10" y="10" width="1004" height="730"/>
    </font>
@@ -65,7 +64,6 @@
       <proportion>12</proportion>
       <weight>Normal</weight>
       <italics>False</italics>
-      <indentation>0</indentation>
       <line_adjustment>0</line_adjustment>
       <location override="False" x="10" y="730" width="1004" height="38"/>
    </font>
@@ -184,7 +182,7 @@
         self.child_element(background, u'filename', filename)
 
     def add_font(self, name, color, proportion, override, fonttype=u'main',
-        weight=u'Normal', italics=u'False', indentation=0, line_adjustment=0,
+        weight=u'Normal', italics=u'False', line_adjustment=0,
         xpos=0, ypos=0, width=0, height=0):
         """
         Add a Font.
@@ -210,9 +208,6 @@
         ``italics``
             Does the font render to italics Defaults to 0 Normal
 
-        ``indentation``
-            Number of characters the wrap line is indented
-
         ``xpos``
             The X position of the text block.
 
@@ -239,8 +234,6 @@
         #Create italics name element
         self.child_element(background, u'italics', italics)
         #Create indentation name element
-        self.child_element(background, u'indentation', unicode(indentation))
-        #Create indentation name element
         self.child_element(
             background, u'line_adjustment', unicode(line_adjustment))
 

=== modified file 'openlp/core/ui/__init__.py'
--- openlp/core/ui/__init__.py	2010-07-27 09:32:52 +0000
+++ openlp/core/ui/__init__.py	2010-08-24 17:30:21 +0000
@@ -19,7 +19,7 @@
 # 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     #
+# You should have received a copy of the GNU General Pu__init__.pyblic License along     #
 # with this program; if not, write to the Free Software Foundation, Inc., 59  #
 # Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
 ###############################################################################
@@ -27,6 +27,143 @@
 The :mod:`ui` module provides the core user interface for OpenLP
 """
 
+# http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/
+
+import re
+import sys
+try:
+    import enchant
+    enchant_available = True
+except ImportError:
+    enchant_available = False
+
+from PyQt4 import QtCore, QtGui
+from openlp.core.lib import html_expands, translate, context_menu_action
+
+class SpellTextEdit(QtGui.QPlainTextEdit):
+
+    def __init__(self, *args):
+        QtGui.QPlainTextEdit.__init__(self, *args)
+        # Default dictionary based on the current locale.
+        if enchant_available:
+            self.dict = enchant.Dict()
+            self.highlighter = Highlighter(self.document())
+            self.highlighter.setDict(self.dict)
+
+    def mousePressEvent(self, event):
+        if event.button() == QtCore.Qt.RightButton:
+            # Rewrite the mouse event to a left button event so the cursor is
+            # moved to the location of the pointer.
+            event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress, event.pos(),
+                QtCore.Qt.LeftButton, QtCore.Qt.LeftButton, QtCore.Qt.NoModifier)
+        QtGui.QPlainTextEdit.mousePressEvent(self, event)
+
+    def contextMenuEvent(self, event):
+        popup_menu = self.createStandardContextMenu()
+
+        # Select the word under the cursor.
+        cursor = self.textCursor()
+        cursor.select(QtGui.QTextCursor.WordUnderCursor)
+        self.setTextCursor(cursor)
+
+        # Check if the selected word is misspelled and offer spelling
+        # suggestions if it is.
+        if enchant_available and self.textCursor().hasSelection():
+            text = unicode(self.textCursor().selectedText())
+            if not self.dict.check(text):
+                spell_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
+                    'Spelling Suggestions'))
+                for word in self.dict.suggest(text):
+                    action = SpellAction(word, spell_menu)
+                    action.correct.connect(self.correctWord)
+                    spell_menu.addAction(action)
+                # Only add the spelling suggests to the menu if there are
+                # suggestions.
+                if len(spell_menu.actions()) != 0:
+                    popup_menu.insertSeparator(popup_menu.actions()[0])
+                    popup_menu.insertMenu(popup_menu.actions()[0], spell_menu)
+        tag_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
+            'Formatting Tags'))
+        for html in html_expands:
+            action = SpellAction( html[u'desc'], tag_menu)
+            action.correct.connect(self.htmlTag)
+            tag_menu.addAction(action)
+        popup_menu.insertSeparator(popup_menu.actions()[0])
+        popup_menu.insertMenu(popup_menu.actions()[0], tag_menu)
+
+        popup_menu.exec_(event.globalPos())
+
+    def correctWord(self, word):
+        '''
+        Replaces the selected text with word.
+        '''
+        cursor = self.textCursor()
+        cursor.beginEditBlock()
+
+        cursor.removeSelectedText()
+        cursor.insertText(word)
+
+        cursor.endEditBlock()
+
+    def htmlTag(self, tag):
+        '''
+        Replaces the selected text with word.
+        '''
+        for html in html_expands:
+            if tag == html[u'desc']:
+                cursor = self.textCursor()
+                if self.textCursor().hasSelection():
+                    text = cursor.selectedText()
+                    cursor.beginEditBlock()
+                    cursor.removeSelectedText()
+                    cursor.insertText(html[u'start tag'])
+                    cursor.insertText(text)
+                    cursor.insertText(html[u'end tag'])
+                    cursor.endEditBlock()
+                else:
+                    cursor = self.textCursor()
+                    cursor.insertText(html[u'start tag'])
+                    cursor.insertText(html[u'end tag'])
+
+class Highlighter(QtGui.QSyntaxHighlighter):
+
+    WORDS = u'(?iu)[\w\']+'
+
+    def __init__(self, *args):
+        QtGui.QSyntaxHighlighter.__init__(self, *args)
+
+        self.dict = None
+
+    def setDict(self, dict):
+        self.dict = dict
+
+    def highlightBlock(self, text):
+        if not self.dict:
+            return
+
+        text = unicode(text)
+
+        format = QtGui.QTextCharFormat()
+        format.setUnderlineColor(QtCore.Qt.red)
+        format.setUnderlineStyle(QtGui.QTextCharFormat.SpellCheckUnderline)
+
+        for word_object in re.finditer(self.WORDS, text):
+            if not self.dict.check(word_object.group()):
+                self.setFormat(word_object.start(),
+                    word_object.end() - word_object.start(), format)
+
+class SpellAction(QtGui.QAction):
+    '''
+    A special QAction that returns the text in a signal.
+    '''
+    correct = QtCore.pyqtSignal(unicode)
+
+    def __init__(self, *args):
+        QtGui.QAction.__init__(self, *args)
+
+        self.triggered.connect(lambda x: self.correct.emit(
+            unicode(self.text())))
+
 class HideMode(object):
     """
     This is basically an enumeration class which specifies the mode of a Bible.
@@ -37,13 +174,11 @@
     Theme = 2
     Screen = 3
 
+from maindisplay import MainDisplay
 from slidecontroller import HideMode
 from servicenoteform import ServiceNoteForm
 from serviceitemeditform import ServiceItemEditForm
 from screen import ScreenList
-from maindisplay import MainDisplay
-from maindisplay import VideoDisplay
-from maindisplay import DisplayManager
 from amendthemeform import AmendThemeForm
 from slidecontroller import SlideController
 from splashscreen import SplashScreen
@@ -56,8 +191,7 @@
 from mediadockmanager import MediaDockManager
 from servicemanager import ServiceManager
 from thememanager import ThemeManager
-from mainwindow import MainWindow
 
-__all__ = ['SplashScreen', 'AboutForm', 'SettingsForm', 'MainWindow',
+__all__ = ['SplashScreen', 'AboutForm', 'SettingsForm',
     'MainDisplay', 'SlideController', 'ServiceManager', 'ThemeManager',
     'AmendThemeForm', 'MediaDockManager', 'ServiceItemEditForm']

=== modified file 'openlp/core/ui/amendthemedialog.py'
--- openlp/core/ui/amendthemedialog.py	2010-07-27 10:46:39 +0000
+++ openlp/core/ui/amendthemedialog.py	2010-08-24 17:30:21 +0000
@@ -68,17 +68,6 @@
         self.backgroundLayout.setMargin(8)
         self.backgroundLayout.setSpacing(8)
         self.backgroundLayout.setObjectName(u'backgroundLayout')
-        self.backgroundLabel = QtGui.QLabel(self.backgroundTab)
-        self.backgroundLabel.setObjectName(u'backgroundLabel')
-        self.backgroundLayout.setWidget(0, QtGui.QFormLayout.LabelRole,
-            self.backgroundLabel)
-        self.backgroundComboBox = QtGui.QComboBox(self.backgroundTab)
-        self.backgroundComboBox.setObjectName(u'backgroundComboBox')
-        self.backgroundLabel.setBuddy(self.backgroundComboBox)
-        self.backgroundComboBox.addItem(QtCore.QString())
-        self.backgroundComboBox.addItem(QtCore.QString())
-        self.backgroundLayout.setWidget(0, QtGui.QFormLayout.FieldRole,
-            self.backgroundComboBox)
         self.backgroundTypeLabel = QtGui.QLabel(self.backgroundTab)
         self.backgroundTypeLabel.setObjectName(u'backgroundTypeLabel')
         self.backgroundLayout.setWidget(1, QtGui.QFormLayout.LabelRole,
@@ -216,17 +205,6 @@
         self.fontMainLineAdjustmentSpinBox.setMinimum(-99)
         self.mainFontLayout.setWidget(4, QtGui.QFormLayout.FieldRole,
             self.fontMainLineAdjustmentSpinBox)
-        self.fontMainWrapIndentationLabel = QtGui.QLabel(self.fontMainGroupBox)
-        self.fontMainWrapIndentationLabel.setObjectName(
-            u'fontMainWrapIndentationLabel')
-        self.mainFontLayout.setWidget(5, QtGui.QFormLayout.LabelRole,
-            self.fontMainWrapIndentationLabel)
-        self.fontMainLineSpacingSpinBox = QtGui.QSpinBox(self.fontMainGroupBox)
-        self.fontMainLineSpacingSpinBox.setObjectName(
-            u'fontMainLineSpacingSpinBox')
-        self.fontMainLineSpacingSpinBox.setMaximum(10)
-        self.mainFontLayout.setWidget(5, QtGui.QFormLayout.FieldRole,
-            self.fontMainLineSpacingSpinBox)
         self.fontMainLinesPageLabel = QtGui.QLabel(self.fontMainGroupBox)
         self.fontMainLinesPageLabel.setObjectName(u'fontMainLinesPageLabel')
         self.mainFontLayout.addRow(self.fontMainLinesPageLabel)
@@ -661,12 +639,6 @@
             translate('OpenLP.AmendThemeForm', 'Theme Maintenance'))
         self.themeNameLabel.setText(
             translate('OpenLP.AmendThemeForm', 'Theme &name:'))
-        self.backgroundLabel.setText(
-            translate('OpenLP.AmendThemeForm', '&Visibility:'))
-        self.backgroundComboBox.setItemText(0,
-            translate('OpenLP.AmendThemeForm', 'Opaque'))
-        self.backgroundComboBox.setItemText(1,
-            translate('OpenLP.AmendThemeForm', 'Transparent'))
         self.backgroundTypeLabel.setText(
             translate('OpenLP.AmendThemeForm', 'Type:'))
         self.backgroundTypeComboBox.setItemText(0,
@@ -700,8 +672,6 @@
             translate('OpenLP.AmendThemeForm', 'Size:'))
         self.fontMainSizeSpinBox.setSuffix(
             translate('OpenLP.AmendThemeForm', 'pt'))
-        self.fontMainWrapIndentationLabel.setText(
-            translate('OpenLP.AmendThemeForm', 'Wrap indentation:'))
         self.fontMainWrapLineAdjustmentLabel.setText(
             translate('OpenLP.AmendThemeForm', 'Adjust line spacing:'))
         self.fontMainWeightComboBox.setItemText(0,

=== modified file 'openlp/core/ui/amendthemeform.py'
--- openlp/core/ui/amendthemeform.py	2010-07-27 10:20:20 +0000
+++ openlp/core/ui/amendthemeform.py	2010-08-24 17:30:21 +0000
@@ -50,7 +50,6 @@
         self.path = None
         self.theme = ThemeXML()
         self.setupUi(self)
-        # define signals
         # Buttons
         QtCore.QObject.connect(self.color1PushButton,
             QtCore.SIGNAL(u'pressed()'), self.onColor1PushButtonClicked)
@@ -68,8 +67,6 @@
         QtCore.QObject.connect(self.imageToolButton,
             QtCore.SIGNAL(u'clicked()'), self.onImageToolButtonClicked)
         # Combo boxes
-        QtCore.QObject.connect(self.backgroundComboBox,
-            QtCore.SIGNAL(u'activated(int)'), self.onBackgroundComboBoxSelected)
         QtCore.QObject.connect(self.backgroundTypeComboBox,
             QtCore.SIGNAL(u'activated(int)'),
             self.onBackgroundTypeComboBoxSelected)
@@ -109,9 +106,6 @@
         QtCore.QObject.connect(self.fontMainLineAdjustmentSpinBox,
             QtCore.SIGNAL(u'editingFinished()'),
             self.onFontMainLineAdjustmentSpinBoxChanged)
-        QtCore.QObject.connect(self.fontMainLineSpacingSpinBox,
-            QtCore.SIGNAL(u'editingFinished()'),
-            self.onFontMainLineSpacingSpinBoxChanged)
         QtCore.QObject.connect(self.fontFooterXSpinBox,
             QtCore.SIGNAL(u'editingFinished()'),
             self.onFontFooterXSpinBoxChanged)
@@ -151,30 +145,26 @@
         new_theme.new_document(theme_name)
         save_from = None
         save_to = None
-        if self.theme.background_mode == u'transparent':
-            new_theme.add_background_transparent()
+        if self.theme.background_type == u'solid':
+            new_theme.add_background_solid(
+                unicode(self.theme.background_color))
+        elif self.theme.background_type == u'gradient':
+            new_theme.add_background_gradient(
+                unicode(self.theme.background_startColor),
+                unicode(self.theme.background_endColor),
+                self.theme.background_direction)
         else:
-            if self.theme.background_type == u'solid':
-                new_theme.add_background_solid(
-                    unicode(self.theme.background_color))
-            elif self.theme.background_type == u'gradient':
-                new_theme.add_background_gradient(
-                    unicode(self.theme.background_startColor),
-                    unicode(self.theme.background_endColor),
-                    self.theme.background_direction)
-            else:
-                filename = \
-                    os.path.split(unicode(self.theme.background_filename))[1]
-                new_theme.add_background_image(filename)
-                save_to = os.path.join(self.path, theme_name, filename)
-                save_from = self.theme.background_filename
+            filename = \
+                os.path.split(unicode(self.theme.background_filename))[1]
+            new_theme.add_background_image(filename)
+            save_to = os.path.join(self.path, theme_name, filename)
+            save_from = self.theme.background_filename
         new_theme.add_font(unicode(self.theme.font_main_name),
                 unicode(self.theme.font_main_color),
                 unicode(self.theme.font_main_proportion),
                 unicode(self.theme.font_main_override), u'main',
                 unicode(self.theme.font_main_weight),
                 unicode(self.theme.font_main_italics),
-                unicode(self.theme.font_main_indentation),
                 unicode(self.theme.font_main_line_adjustment),
                 unicode(self.theme.font_main_x),
                 unicode(self.theme.font_main_y),
@@ -186,7 +176,6 @@
                 unicode(self.theme.font_footer_override), u'footer',
                 unicode(self.theme.font_footer_weight),
                 unicode(self.theme.font_footer_italics),
-                0, # indentation
                 0, # line adjustment
                 unicode(self.theme.font_footer_x),
                 unicode(self.theme.font_footer_y),
@@ -230,7 +219,7 @@
             self.previewTheme()
 
     #
-    #Main Font Tab
+    # Main Font Tab
     #
     def onFontMainComboBoxSelected(self):
         self.theme.font_main_name = self.fontMainComboBox.currentFont().family()
@@ -283,8 +272,6 @@
             self.fontMainHeightSpinBox.setValue(self.theme.font_main_height)
             self.fontMainLineAdjustmentSpinBox.setValue(
                 self.theme.font_main_line_adjustment)
-            self.fontMainLineSpacingSpinBox.setValue(
-                self.theme.font_main_indentation)
         self.stateChanging(self.theme)
         self.previewTheme()
 
@@ -310,20 +297,13 @@
                 self.fontMainLineAdjustmentSpinBox.value()
             self.previewTheme()
 
-    def onFontMainLineSpacingSpinBoxChanged(self):
-        if self.theme.font_main_indentation != \
-            self.fontMainLineSpacingSpinBox.value():
-            self.theme.font_main_indentation = \
-                self.fontMainLineSpacingSpinBox.value()
-            self.previewTheme()
-
     def onFontMainHeightSpinBoxChanged(self):
         if self.theme.font_main_height != self.fontMainHeightSpinBox.value():
             self.theme.font_main_height = self.fontMainHeightSpinBox.value()
             self.previewTheme()
 
     #
-    #Footer Font Tab
+    # Footer Font Tab
     #
     def onFontFooterComboBoxSelected(self):
         self.theme.font_footer_name = \
@@ -404,20 +384,12 @@
             self.previewTheme()
 
     #
-    #Background Tab
+    # Background Tab
     #
     def onGradientComboBoxSelected(self, currentIndex):
         self.setBackground(self.backgroundTypeComboBox.currentIndex(),
             currentIndex)
 
-    def onBackgroundComboBoxSelected(self, currentIndex):
-        if currentIndex == 0: # Opaque
-            self.theme.background_mode = u'opaque'
-        else:
-            self.theme.background_mode = u'transparent'
-        self.stateChanging(self.theme)
-        self.previewTheme()
-
     def onBackgroundTypeComboBoxSelected(self, currentIndex):
         self.setBackground(currentIndex, self.gradientComboBox.currentIndex())
 
@@ -472,7 +444,7 @@
             self.previewTheme()
 
     #
-    #Other Tab
+    # Other Tab
     #
     def onOutlineCheckBoxChanged(self, value):
         if value == 2:  # checked
@@ -537,16 +509,12 @@
         self.previewTheme()
 
     #
-    #Local Methods
+    # Local Methods
     #
     def paintUi(self, theme):
         self.stateChanging(theme)
         self.themeNameEdit.setText(self.theme.theme_name)
         # Background Tab
-        if self.theme.background_mode == u'opaque':
-            self.backgroundComboBox.setCurrentIndex(0)
-        else:
-            self.backgroundComboBox.setCurrentIndex(1)
         self.imageLineEdit.setText(u'')
         if theme.background_type == u'solid':
             self.backgroundTypeComboBox.setCurrentIndex(0)
@@ -576,8 +544,6 @@
             self.fontMainWeightComboBox.setCurrentIndex(2)
         else:
             self.fontMainWeightComboBox.setCurrentIndex(3)
-        self.fontMainLineSpacingSpinBox.setValue(
-            self.theme.font_main_indentation)
         self.fontMainXSpinBox.setValue(self.theme.font_main_x)
         self.fontMainYSpinBox.setValue(self.theme.font_main_y)
         self.fontMainWidthSpinBox.setValue(self.theme.font_main_width)
@@ -641,63 +607,50 @@
         self.verticalComboBox.setCurrentIndex(self.theme.display_verticalAlign)
 
     def stateChanging(self, theme):
-        if theme.background_mode == u'transparent':
+        self.backgroundTypeComboBox.setVisible(True)
+        self.backgroundTypeLabel.setVisible(True)
+        if theme.background_type == u'solid':
+            self.color1PushButton.setStyleSheet(
+                u'background-color: %s' % unicode(theme.background_color))
+            self.color1Label.setText(
+                translate('OpenLP.AmendThemeForm', 'Color:'))
+            self.color1Label.setVisible(True)
+            self.color1PushButton.setVisible(True)
+            self.color2Label.setVisible(False)
+            self.color2PushButton.setVisible(False)
+            self.imageLabel.setVisible(False)
+            self.imageLineEdit.setVisible(False)
+            self.imageFilenameWidget.setVisible(False)
+            self.gradientLabel.setVisible(False)
+            self.gradientComboBox.setVisible(False)
+        elif theme.background_type == u'gradient':
+            self.color1PushButton.setStyleSheet(u'background-color: %s' \
+                % unicode(theme.background_startColor))
+            self.color2PushButton.setStyleSheet(u'background-color: %s' \
+                % unicode(theme.background_endColor))
+            self.color1Label.setText(
+                translate('OpenLP.AmendThemeForm', 'First color:'))
+            self.color2Label.setText(
+                translate('OpenLP.AmendThemeForm', 'Second color:'))
+            self.color1Label.setVisible(True)
+            self.color1PushButton.setVisible(True)
+            self.color2Label.setVisible(True)
+            self.color2PushButton.setVisible(True)
+            self.imageLabel.setVisible(False)
+            self.imageLineEdit.setVisible(False)
+            self.imageFilenameWidget.setVisible(False)
+            self.gradientLabel.setVisible(True)
+            self.gradientComboBox.setVisible(True)
+        else: # must be image
             self.color1Label.setVisible(False)
             self.color1PushButton.setVisible(False)
             self.color2Label.setVisible(False)
             self.color2PushButton.setVisible(False)
-            self.imageLabel.setVisible(False)
-            self.imageLineEdit.setVisible(False)
-            self.imageFilenameWidget.setVisible(False)
+            self.imageLabel.setVisible(True)
+            self.imageLineEdit.setVisible(True)
+            self.imageFilenameWidget.setVisible(True)
             self.gradientLabel.setVisible(False)
             self.gradientComboBox.setVisible(False)
-            self.backgroundTypeComboBox.setVisible(False)
-            self.backgroundTypeLabel.setVisible(False)
-        else:
-            self.backgroundTypeComboBox.setVisible(True)
-            self.backgroundTypeLabel.setVisible(True)
-            if theme.background_type == u'solid':
-                self.color1PushButton.setStyleSheet(
-                    u'background-color: %s' % unicode(theme.background_color))
-                self.color1Label.setText(
-                    translate('OpenLP.AmendThemeForm', 'Color:'))
-                self.color1Label.setVisible(True)
-                self.color1PushButton.setVisible(True)
-                self.color2Label.setVisible(False)
-                self.color2PushButton.setVisible(False)
-                self.imageLabel.setVisible(False)
-                self.imageLineEdit.setVisible(False)
-                self.imageFilenameWidget.setVisible(False)
-                self.gradientLabel.setVisible(False)
-                self.gradientComboBox.setVisible(False)
-            elif theme.background_type == u'gradient':
-                self.color1PushButton.setStyleSheet(u'background-color: %s' \
-                    % unicode(theme.background_startColor))
-                self.color2PushButton.setStyleSheet(u'background-color: %s' \
-                    % unicode(theme.background_endColor))
-                self.color1Label.setText(
-                    translate('OpenLP.AmendThemeForm', 'First color:'))
-                self.color2Label.setText(
-                    translate('OpenLP.AmendThemeForm', 'Second color:'))
-                self.color1Label.setVisible(True)
-                self.color1PushButton.setVisible(True)
-                self.color2Label.setVisible(True)
-                self.color2PushButton.setVisible(True)
-                self.imageLabel.setVisible(False)
-                self.imageLineEdit.setVisible(False)
-                self.imageFilenameWidget.setVisible(False)
-                self.gradientLabel.setVisible(True)
-                self.gradientComboBox.setVisible(True)
-            else: # must be image
-                self.color1Label.setVisible(False)
-                self.color1PushButton.setVisible(False)
-                self.color2Label.setVisible(False)
-                self.color2PushButton.setVisible(False)
-                self.imageLabel.setVisible(True)
-                self.imageLineEdit.setVisible(True)
-                self.imageFilenameWidget.setVisible(True)
-                self.gradientLabel.setVisible(False)
-                self.gradientComboBox.setVisible(False)
         if not theme.font_main_override:
             self.fontMainXSpinBox.setEnabled(False)
             self.fontMainYSpinBox.setEnabled(False)

=== modified file 'openlp/core/ui/generaltab.py'
--- openlp/core/ui/generaltab.py	2010-08-02 16:54:21 +0000
+++ openlp/core/ui/generaltab.py	2010-08-24 17:30:21 +0000
@@ -436,10 +436,10 @@
             QtCore.QVariant(self.saveCheckServiceCheckBox.isChecked()))
         settings.setValue(u'auto preview',
             QtCore.QVariant(self.autoPreviewCheckBox.isChecked()))
-        settings.setValue(u'loop delay', 
+        settings.setValue(u'loop delay',
             QtCore.QVariant(self.timeoutSpinBox.value()))
         Receiver.send_message(u'slidecontroller_live_spin_delay',
-            self.timeoutSpinBox.value())            
+            self.timeoutSpinBox.value())
         settings.setValue(u'ccli number',
             QtCore.QVariant(self.numberEdit.displayText()))
         settings.setValue(u'songselect username',
@@ -486,6 +486,7 @@
             else:
                 self.screens.reset_current_display()
                 Receiver.send_message(u'config_screen_changed')
+        self.overrideChanged = False
 
     def onOverrideCheckBoxToggled(self, checked):
         """

=== modified file 'openlp/core/ui/maindisplay.py'
--- openlp/core/ui/maindisplay.py	2010-08-02 16:54:21 +0000
+++ openlp/core/ui/maindisplay.py	2010-08-24 17:30:21 +0000
@@ -26,149 +26,43 @@
 
 import logging
 import os
-import time
 
 from PyQt4 import QtCore, QtGui, QtWebKit
 from PyQt4.phonon import Phonon
 
-from openlp.core.lib import Receiver, resize_image
+from openlp.core.lib import Receiver, resize_image, build_html, ServiceItem, \
+image_to_byte
 from openlp.core.ui import HideMode
 
 log = logging.getLogger(__name__)
 
 #http://www.steveheffernan.com/html5-video-player/demo-video-player.html
-HTMLVIDEO = u"""<html>
-    <head>
-    <style>
-    *{
-        margin: 0;
-        padding:0
-    }
-    </style>
-    <script type="text/javascript" charset="utf-8">
-    var video;
-    var bodyLoaded = function(){
-        video = document.getElementById("video");
-        video.volume = 0;
-    }
-    </script>
-    </head>
-    <body id="body" onload="bodyLoaded();">
-    <video id="video" src="%s" autoplay="autoplay" loop="loop"
-    width="%s" height="%s" autobuffer="autobuffer" preload="preload" />
-    </body></html>
-    """
-
-class DisplayManager(QtGui.QWidget):
-    """
-    Wrapper class to hold the display widgets.
-    I will provide API's in future to access the screens allow for
-    extra displays to be added.
-    RenderManager is poked in by MainWindow
-    """
-    def __init__(self, screens):
-        QtGui.QWidget.__init__(self)
-        self.screens = screens
-        self.videoDisplay = VideoDisplay(self, screens)
-        self.audioPlayer = AudioPlayer(self)
-        self.mainDisplay = MainDisplay(self, screens)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'maindisplay_hide'), self.hideDisplay)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'maindisplay_show'), self.showDisplay)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'videodisplay_start'), self.onStartVideo)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'videodisplay_stop'), self.onStopVideo)
-
-    def setup(self):
-        self.videoDisplay.setup()
-        self.mainDisplay.setup()
-
-    def hideDisplay(self, message):
-        """
-        Hide the output displays
-        """
-        self.videoDisplay.mediaHide(message)
-        self.mainDisplay.hideDisplay(message)
-
-    def showDisplay(self):
-        """
-        Hide the output displays
-        """
-        self.videoDisplay.mediaShow()
-        self.mainDisplay.showDisplay()
-
-    def addAlert(self, alertMessage, location):
-        """
-        Handles the addition of an Alert Message to the Displays
-        """
-        self.mainDisplay.addAlert(alertMessage, location)
-
-    def displayImageWithText(self, frame):
-        """
-        Handles the addition of a background Image to the displays
-        """
-        self.mainDisplay.addImageWithText(frame)
-
-    def displayImage(self, frame):
-        """
-        Handles the addition of a background Image to the displays
-        """
-        self.mainDisplay.displayImage(frame)
-
-    def displayVideo(self, path):
-        """
-        Handles the addition of a background Video to the displays
-        """
-        self.mainDisplay.displayVideo(path)
-
-    def onStartVideo(self, item):
-        """
-        Handles the Starting of a Video and Display Management
-        """
-        self.videoDisplay.setVisible(True)
-        self.mainDisplay.setVisible(False)
-        self.videoDisplay.onMediaQueue(item)
-
-    def onStopVideo(self):
-        """
-        Handles the Stopping of a Video and Display Management
-        """
-        self.mainDisplay.setVisible(True)
-        self.videoDisplay.setVisible(False)
-        self.videoDisplay.onMediaStop()
-
-    def close(self):
-        """
-        Handles the closure of the displays
-        """
-        self.videoDisplay.close()
-        self.audioPlayer.close()
-        self.mainDisplay.close()
-
 
 class DisplayWidget(QtGui.QGraphicsView):
     """
     Customised version of QTableWidget which can respond to keyboard
     events.
     """
-    log.info(u'MainDisplay loaded')
+    log.info(u'Display Widget loaded')
 
-    def __init__(self, parent=None, name=None, primary=False):
-        QtGui.QWidget.__init__(self, None)
+    def __init__(self, live, parent=None):
+        QtGui.QGraphicsView.__init__(self)
         self.parent = parent
-        self.primary = primary
+        self.live = live
         self.hotkey_map = {
             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'}
+        self.setStyleSheet(u'border: none;')
 
     def keyPressEvent(self, event):
+        # Key events only needed for live
+        if not self.live:
+            return
         if isinstance(event, QtGui.QKeyEvent):
-            #here accept the event and do something
+            # Here accept the event and do something
             if event.key() == QtCore.Qt.Key_Up:
                 Receiver.send_message(u'slidecontroller_live_previous')
                 event.accept()
@@ -185,157 +79,253 @@
                 Receiver.send_message(self.hotkey_map[event.key()])
                 event.accept()
             elif event.key() == QtCore.Qt.Key_Escape:
-                self.resetDisplay()
+                self.setVisible(False)
                 event.accept()
             event.ignore()
         else:
             event.ignore()
 
-    def resetDisplay(self):
-        log.debug(u'resetDisplay')
-        Receiver.send_message(u'slidecontroller_live_stop_loop')
-        if self.primary:
-            self.setVisible(False)
-        else:
-            self.setVisible(True)
-
 class MainDisplay(DisplayWidget):
-    """
-    This is the form that is used to display things on the projector.
-    """
-    log.info(u'MainDisplay Loaded')
-
-    def __init__(self, parent, screens):
-        """
-        The constructor for the display form.
-
-        ``parent``
-            The parent widget.
-
-        ``screens``
-            The list of screens.
-        """
-        log.debug(u'Initialisation started')
-        DisplayWidget.__init__(self, parent, primary=True)
-        self.setWindowFlags(QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint)
-        self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
-        self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
-        # WA_TranslucentBackground is not available in QT4.4
-        try:
-            self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
-        except AttributeError:
-            pass
+
+    def __init__(self, parent, screens, live):
+        DisplayWidget.__init__(self, live, parent=None)
+        self.parent = parent
         self.screens = screens
-        self.setupScene()
-        self.setupVideo()
-        self.setupImage()
-        self.setupText()
-        self.setupAlert()
-        self.setupBlank()
-        self.blankFrame = None
-        self.frame = None
-        #Hide desktop for now until we know where to put it
-        #and what size it should be.
-        self.setVisible(False)
+        self.isLive = live
+        self.alertTab = None
+        self.setWindowTitle(u'OpenLP Display')
+        self.setWindowFlags(QtCore.Qt.FramelessWindowHint |
+            QtCore.Qt.WindowStaysOnTopHint)
+        if self.isLive:
+            QtCore.QObject.connect(Receiver.get_receiver(),
+                QtCore.SIGNAL(u'maindisplay_hide'), self.hideDisplay)
+            QtCore.QObject.connect(Receiver.get_receiver(),
+                QtCore.SIGNAL(u'maindisplay_show'), self.showDisplay)
 
     def setup(self):
-        """
-        Sets up the screen on a particular screen.
-        """
         log.debug(u'Setup %s for %s ' % (
             self.screens, self.screens.monitor_number))
+        self.screen = self.screens.current
         self.setVisible(False)
-        self.screen = self.screens.current
-        #Sort out screen locations and sizes
         self.setGeometry(self.screen[u'size'])
-        self.scene.setSceneRect(0, 0, self.size().width(),
-            self.size().height())
-        self.webView.setGeometry(0, 0, self.size().width(),
-            self.size().height())
-        self.alertText.setTextWidth(self.size().width())
-        #Build a custom splash screen
-        self.initialFrame = QtGui.QImage(
-            self.screen[u'size'].width(),
-            self.screen[u'size'].height(),
-            QtGui.QImage.Format_ARGB32_Premultiplied)
-        splash_image = QtGui.QImage(u':/graphics/openlp-splash-screen.png')
-        painter_image = QtGui.QPainter()
-        painter_image.begin(self.initialFrame)
-        painter_image.fillRect(self.initialFrame.rect(), QtCore.Qt.white)
-        painter_image.drawImage(
-            (self.screen[u'size'].width() - splash_image.width()) / 2,
-            (self.screen[u'size'].height() - splash_image.height()) / 2,
-            splash_image)
-        #build a blank transparent image
-        self.transparent = QtGui.QPixmap(
-            self.screen[u'size'].width(), self.screen[u'size'].height())
-        self.transparent.fill(QtCore.Qt.transparent)
-        self.displayImage(self.initialFrame)
-        self.repaint()
-        #Build a Black screen
-        painter = QtGui.QPainter()
-        self.blankFrame = QtGui.QImage(
-            self.screen[u'size'].width(),
-            self.screen[u'size'].height(),
-            QtGui.QImage.Format_ARGB32_Premultiplied)
-        painter.begin(self.blankFrame)
-        painter.fillRect(self.blankFrame.rect(), QtCore.Qt.black)
-        # To display or not to display?
-        if not self.screen[u'primary']:
-            self.setVisible(True)
-            self.primary = False
-        else:
-            self.setVisible(False)
-            self.primary = True
-
-    def setupScene(self):
-        self.scene = QtGui.QGraphicsScene(self)
-        self.scene.setSceneRect(0, 0, self.size().width(), self.size().height())
-        self.setScene(self.scene)
-
-    def setupVideo(self):
-        self.webView = QtWebKit.QWebView()
+        self.webView = QtWebKit.QWebView(self)
+        self.webView.setGeometry(0, 0, self.screen[u'size'].width(), \
+            self.screen[u'size'].height())
         self.page = self.webView.page()
-        self.videoDisplay = self.page.mainFrame()
-        self.videoDisplay.setScrollBarPolicy(QtCore.Qt.Vertical,
-            QtCore.Qt.ScrollBarAlwaysOff)
-        self.videoDisplay.setScrollBarPolicy(QtCore.Qt.Horizontal,
-            QtCore.Qt.ScrollBarAlwaysOff)
-        self.proxy = QtGui.QGraphicsProxyWidget()
-        self.proxy.setWidget(self.webView)
-        self.proxy.setWindowFlags(QtCore.Qt.Window |
-            QtCore.Qt.FramelessWindowHint)
-        self.proxy.setZValue(1)
-        self.scene.addItem(self.proxy)
-
-    def setupImage(self):
-        self.imageDisplay = QtGui.QGraphicsPixmapItem()
-        self.imageDisplay.setZValue(2)
-        self.scene.addItem(self.imageDisplay)
-
-    def setupText(self):
-        #self.displayText = QtGui.QGraphicsTextItem()
-        self.displayText = QtGui.QGraphicsPixmapItem()
-        #self.displayText.setPos(0,0)
-        #self.displayText.setTextWidth(self.size().width())
-        self.displayText.setZValue(4)
-        self.scene.addItem(self.displayText)
-
-    def setupAlert(self):
-        self.alertText = QtGui.QGraphicsTextItem()
-        self.alertText.setZValue(8)
-        self.scene.addItem(self.alertText)
-
-    def setupBlank(self):
-        self.displayBlank = QtGui.QGraphicsPixmapItem()
-        self.displayBlank.setZValue(10)
-        self.scene.addItem(self.displayBlank)
-
-#    def hideDisplayForVideo(self):
-#        """
-#        Hides the main display if for the video to be played
-#        """
-#        self.hideDisplay(HideMode.Screen)
+        self.frame = self.page.mainFrame()
+        QtCore.QObject.connect(self.webView,
+            QtCore.SIGNAL(u'loadFinished(bool)'), self.isLoaded)
+        self.frame.setScrollBarPolicy(QtCore.Qt.Vertical,
+            QtCore.Qt.ScrollBarAlwaysOff)
+        self.frame.setScrollBarPolicy(QtCore.Qt.Horizontal,
+            QtCore.Qt.ScrollBarAlwaysOff)
+        if self.isLive:
+            # Build the initial frame.
+            self.black = QtGui.QImage(
+                self.screens.current[u'size'].width(),
+                self.screens.current[u'size'].height(),
+                QtGui.QImage.Format_ARGB32_Premultiplied)
+            painter_image = QtGui.QPainter()
+            painter_image.begin(self.black)
+            painter_image.fillRect(self.black.rect(), QtCore.Qt.black)
+            #Build the initial frame.
+            initialFrame = QtGui.QImage(
+                self.screens.current[u'size'].width(),
+                self.screens.current[u'size'].height(),
+                QtGui.QImage.Format_ARGB32_Premultiplied)
+            splash_image = QtGui.QImage(u':/graphics/openlp-splash-screen.png')
+            painter_image = QtGui.QPainter()
+            painter_image.begin(initialFrame)
+            painter_image.fillRect(initialFrame.rect(), QtCore.Qt.white)
+            painter_image.drawImage(
+                (self.screens.current[u'size'].width() \
+                    - splash_image.width()) / 2,
+                (self.screens.current[u'size'].height() \
+                    - splash_image.height()) / 2,
+                splash_image)
+            serviceItem = ServiceItem()
+            serviceItem.bg_frame = initialFrame
+            self.webView.setHtml(build_html(serviceItem, self.screen, \
+                self.parent.alertTab))
+            self.initialFrame = True
+            self.show()
+            # To display or not to display?
+            if not self.screen[u'primary']:
+                self.primary = False
+            else:
+                self.primary = True
+
+    def text(self, slide):
+        """
+        Add the slide text from slideController
+
+        `slide`
+            The slide text to be displayed
+        """
+        log.debug(u'text')
+        self.frame.evaluateJavaScript(u'show_text("%s")' % \
+            slide.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
+        return self.preview()
+
+    def alert(self, text):
+        """
+        Add the alert text
+
+        `slide`
+            The slide text to be displayed
+        """
+        log.debug(u'alert')
+        if self.height() != self.screen[u'size'].height() \
+            or not self.isVisible():
+            shrink = True
+        else:
+            shrink = False
+        js =  u'show_alert("%s", "%s")' % (
+            text.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'),
+            u'top' if shrink else u'')
+        height = self.frame.evaluateJavaScript(js)
+        if shrink:
+            if text:
+                self.resize(self.width(), int(height.toString()))
+                self.setVisible(True)
+            else:
+                self.setGeometry(self.screen[u'size'])
+                self.setVisible(False)
+
+    def image(self, image):
+        """
+        Add an image as the background.  The image is converted to a
+        bytestream on route.
+
+        `Image`
+            The Image to be displayed can be QImage or QPixmap
+        """
+        log.debug(u'image')
+        image = resize_image(image, self.screen[u'size'].width(),
+            self.screen[u'size'].height())
+        self.resetVideo()
+        self.displayImage(image)
+
+    def displayImage(self, image):
+        """
+        Display an image, as is.
+        """
+        if image:
+            js = u'show_image("data:image/png;base64,%s");' % \
+                image_to_byte(image)
+        else:
+            js = u'show_image("");'
+        self.frame.evaluateJavaScript(js)
+
+    def resetImage(self):
+        """
+        Reset the backgound image to the service item image.
+        Used after Image plugin has changed the background
+        """
+        log.debug(u'resetImage')
+        self.displayImage(self.serviceItem.bg_frame)
+
+    def resetVideo(self):
+        """
+        Used after Video plugin has changed the background
+        """
+        log.debug(u'resetVideo')
+        self.frame.evaluateJavaScript(u'show_video("close");')
+
+    def videoPlay(self):
+        """
+        Responds to the request to play a loaded video
+        """
+        log.debug(u'videoPlay')
+        self.frame.evaluateJavaScript(u'show_video("play");')
+
+    def videoPause(self):
+        """
+        Responds to the request to pause a loaded video
+        """
+        log.debug(u'videoPause')
+        self.frame.evaluateJavaScript(u'show_video("pause");')
+
+    def videoStop(self):
+        """
+        Responds to the request to stop a loaded video
+        """
+        log.debug(u'videoStop')
+        self.frame.evaluateJavaScript(u'show_video("stop");')
+
+    def videoVolume(self, volume):
+        """
+        Changes the volume of a running video
+        """
+        log.debug(u'videoVolume %d' % volume)
+        self.frame.evaluateJavaScript(u'show_video(null, null, %s);' %
+            str(float(volume)/float(10)))
+
+    def video(self, videoPath, volume):
+        """
+        Loads and starts a video to run with the option of sound
+        """
+        log.debug(u'video')
+        self.loaded = True
+        js = u'show_video("play", "%s", %s, true);' % \
+            (videoPath.replace(u'\\', u'\\\\'), str(float(volume)/float(10)))
+        self.frame.evaluateJavaScript(js)
+        return self.preview()
+
+    def isLoaded(self):
+        """
+        Called by webView event to show display is fully loaded
+        """
+        log.debug(u'loaded')
+        self.loaded = True
+
+    def preview(self):
+        """
+        Generates a preview of the image displayed.
+        """
+        log.debug(u'preview')
+        # Wait for the fade to finish before geting the preview.
+        # Important otherwise preview will have incorrect text if at all !
+        if self.serviceItem.themedata and \
+            self.serviceItem.themedata.display_slideTransition:
+            while self.frame.evaluateJavaScript(u'show_text_complete()') \
+                .toString() == u'false':
+                Receiver.send_message(u'openlp_process_events')
+        # Wait for the webview to update before geting the preview.
+        # Important otherwise first preview will miss the background !
+        while not self.loaded:
+            Receiver.send_message(u'openlp_process_events')
+        preview = QtGui.QImage(self.screen[u'size'].width(),
+            self.screen[u'size'].height(),
+            QtGui.QImage.Format_ARGB32_Premultiplied)
+        painter = QtGui.QPainter(preview)
+        painter.setRenderHint(QtGui.QPainter.Antialiasing)
+        self.frame.render(painter)
+        painter.end()
+        # save preview for debugging
+        if log.isEnabledFor(logging.DEBUG):
+            preview.save(u'temp.png', u'png')
+        return preview
+
+    def buildHtml(self, serviceItem):
+        """
+        Store the serviceItem and build the new HTML from it. Add the
+        HTML to the display
+        """
+        log.debug(u'buildHtml')
+        self.loaded = False
+        self.initialFrame = False
+        self.serviceItem = serviceItem
+        html = build_html(self.serviceItem, self.screen, self.parent.alertTab)
+        self.webView.setHtml(html)
+        if serviceItem.foot_text and serviceItem.foot_text:
+            self.footer(serviceItem.foot_text)
+
+    def footer(self, text):
+        log.debug(u'footer')
+        js =  "show_footer('" + \
+            text.replace("\\", "\\\\").replace("\'", "\\\'") + "')"
+        self.frame.evaluateJavaScript(js)
 
     def hideDisplay(self, mode=HideMode.Screen):
         """
@@ -343,20 +333,13 @@
         Store the images so they can be replaced when required
         """
         log.debug(u'hideDisplay mode = %d', mode)
-        #self.displayText.setPixmap(self.transparent)
         if mode == HideMode.Screen:
-            #self.display_image.setPixmap(self.transparent)
+            self.frame.evaluateJavaScript(u'show_blank("desktop");')
             self.setVisible(False)
-        elif mode == HideMode.Blank:
-            self.displayBlank.setPixmap(
-                QtGui.QPixmap.fromImage(self.blankFrame))
+        elif mode == HideMode.Blank or self.initialFrame:
+            self.frame.evaluateJavaScript(u'show_blank("black");')
         else:
-            if self.parent.renderManager.renderer.bg_frame:
-                self.displayBlank.setPixmap(QtGui.QPixmap.fromImage(
-                    self.parent.renderManager.renderer.bg_frame))
-            else:
-                self.displayBlank.setPixmap(
-                    QtGui.QPixmap.fromImage(self.blankFrame))
+            self.frame.evaluateJavaScript(u'show_blank("theme");')
         if mode != HideMode.Screen and self.isHidden():
             self.setVisible(True)
 
@@ -367,275 +350,16 @@
         Make the stored images None to release memory.
         """
         log.debug(u'showDisplay')
-        self.displayBlank.setPixmap(self.transparent)
+        self.frame.evaluateJavaScript('show_blank("show");')
         if self.isHidden():
             self.setVisible(True)
-        #Trigger actions when display is active again
+        # Trigger actions when display is active again
         Receiver.send_message(u'maindisplay_active')
 
-    def addImageWithText(self, frame):
-        log.debug(u'addImageWithText')
-        frame = resize_image(
-            frame, self.screen[u'size'].width(), self.screen[u'size'].height())
-        self.imageDisplay.setPixmap(QtGui.QPixmap.fromImage(frame))
-        self.videoDisplay.setHtml(u'<html></html>')
-
-    def addAlert(self, message, location):
-        """
-        Places the Alert text on the display at the correct location
-        ``message``
-            Text to be displayed
-        ``location``
-            Where on the screen the text should be.  From the AlertTab
-            Combo box.
-        """
-        log.debug(u'addAlertImage')
-        if location == 0:
-            self.alertText.setPos(0, 0)
-        elif location == 1:
-            self.alertText.setPos(0, self.size().height() / 2)
-        else:
-            self.alertText.setPos(0, self.size().height() - 76)
-        self.alertText.setHtml(message)
-
-    def displayImage(self, frame):
-        """
-        Places the Image passed on the display screen
-        ``frame``
-            The image to be displayed
-        """
-        log.debug(u'adddisplayImage')
-        if isinstance(frame, QtGui.QImage):
-            self.imageDisplay.setPixmap(QtGui.QPixmap.fromImage(frame))
-        else:
-            self.imageDisplay.setPixmap(frame)
-        self.frameView(self.transparent)
-        self.videoDisplay.setHtml(u'<html></html>')
-
-    def displayVideo(self, path):
-        """
-        Places the Video passed on the display screen
-        ``path``
-            The path to the image to be displayed
-        """
-        log.debug(u'adddisplayVideo')
-        self.displayImage(self.transparent)
-        self.videoDisplay.setHtml(HTMLVIDEO %
-            (path, self.screen[u'size'].width(),
-            self.screen[u'size'].height()))
-
-    def frameView(self, frame, transition=False):
-        """
-        Called from a slide controller to display a frame
-        if the alert is in progress the alert is added on top
-        ``frame``
-            Image frame to be rendered
-        ``transition``
-            Are transitions required.
-        """
-        log.debug(u'frameView')
-        if transition:
-            if self.frame is not None:
-                self.displayText.setPixmap(
-                    QtGui.QPixmap.fromImage(self.frame))
-                self.repaint()
-                Receiver.send_message(u'openlp_process_events')
-                time.sleep(0.1)
-            self.frame = None
-            if frame[u'trans'] is not None:
-                self.displayText.setPixmap(
-                    QtGui.QPixmap.fromImage(frame[u'trans']))
-                self.repaint()
-                Receiver.send_message(u'openlp_process_events')
-                time.sleep(0.1)
-                self.frame = frame[u'trans']
-            self.displayText.setPixmap(
-                QtGui.QPixmap.fromImage(frame[u'main']))
-        else:
-            if isinstance(frame, QtGui.QPixmap):
-                self.displayText.setPixmap(frame)
-            else:
-                self.displayText.setPixmap(QtGui.QPixmap.fromImage(frame))
-        if not self.isVisible() and self.screens.current['primary']:
-            self.setVisible(True)
-
-class VideoDisplay(Phonon.VideoWidget):
-    """
-    This is the form that is used to display videos on the projector.
-    """
-    log.info(u'VideoDisplay Loaded')
-
-    def __init__(self, parent, screens,
-        aspect=Phonon.VideoWidget.AspectRatioWidget):
-        """
-        The constructor for the display form.
-
-        ``parent``
-            The parent widget.
-
-        ``screens``
-            The list of screens.
-        """
-        log.debug(u'VideoDisplay Initialisation started')
-        Phonon.VideoWidget.__init__(self)
-        self.setWindowTitle(u'OpenLP Video Display')
-        self.parent = parent
-        self.screens = screens
-        self.hidden = False
-        self.message = None
-        self.mediaActive = False
-        self.mediaObject = Phonon.MediaObject()
-        self.setAspectRatio(aspect)
-        self.audioObject = Phonon.AudioOutput(Phonon.VideoCategory)
-        Phonon.createPath(self.mediaObject, self)
-        Phonon.createPath(self.mediaObject, self.audioObject)
-        flags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Dialog
-##        # WindowsStaysOnBottomHint is not available in QT4.4
-#        try:
-#            flags = flags | QtCore.Qt.WindowStaysOnBottomHint
-#        except AttributeError:
-#            pass
-        self.setWindowFlags(flags)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'videodisplay_play'), self.onMediaPlay)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'videodisplay_pause'), self.onMediaPause)
-#        QtCore.QObject.connect(Receiver.get_receiver(),
-#            QtCore.SIGNAL(u'videodisplay_background'), self.onMediaBackground)
-        QtCore.QObject.connect(Receiver.get_receiver(),
-            QtCore.SIGNAL(u'config_updated'), self.setup)
-        QtCore.QObject.connect(self.mediaObject,
-            QtCore.SIGNAL(u'finished()'), self.onMediaStop)
-        self.setVisible(False)
-
-    def keyPressEvent(self, event):
-        if isinstance(event, QtGui.QKeyEvent):
-            #here accept the event and do something
-            if event.key() == QtCore.Qt.Key_Escape:
-                self.onMediaStop()
-                event.accept()
-            event.ignore()
-        else:
-            event.ignore()
-
-    def setup(self):
-        """
-        Sets up the screen on a particular screen.
-        """
-        log.debug(u'VideoDisplay Setup %s for %s ' % (self.screens,
-            self.screens.monitor_number))
-        self.screen = self.screens.current
-        #Sort out screen locations and sizes
-        self.setGeometry(self.screen[u'size'])
-        # To display or not to display?
-        if not self.screen[u'primary']: # and self.isVisible():
-            #self.showFullScreen()
-            self.setVisible(False)
-            self.primary = False
-        else:
-            self.setVisible(False)
-            self.primary = True
-
-    def closeEvent(self, event):
-        """
-        Shutting down so clean up connections
-        """
-        self.onMediaStop()
-        for path in self.outputPaths():
-            path.disconnect()
-
-#    def onMediaBackground(self, message=None):
-#        """
-#        Play a video triggered from the video plugin with the
-#        file name passed in on the event.
-#        Also triggered from the Finish event so the video will loop
-#        if it is triggered from the plugin
-#        """
-#        log.debug(u'VideoDisplay Queue new media message %s' % message)
-#        #If not file take the stored one
-#        if not message:
-#            message = self.message
-#        # still no file name then stop as it was a normal video stopping
-#        if message:
-#            self.mediaObject.setCurrentSource(Phonon.MediaSource(message))
-#            self.message = message
-#            self._play()
-
-    def onMediaQueue(self, message):
-        """
-        Set up a video to play from the serviceitem.
-        """
-        log.debug(u'VideoDisplay Queue new media message %s' % message)
-        file = os.path.join(message.get_frame_path(),
-            message.get_frame_title())
-        self.mediaObject.setCurrentSource(Phonon.MediaSource(file))
-        self.mediaActive = True
-        self._play()
-
-    def onMediaPlay(self):
-        """
-        Respond to the Play button on the slide controller unless the display
-        has been hidden by the slidecontroller
-        """
-        if not self.hidden:
-            log.debug(u'VideoDisplay Play the new media, Live ')
-            self._play()
-
-    def _play(self):
-        """
-        We want to play the video so start it and display the screen
-        """
-        log.debug(u'VideoDisplay _play called')
-        self.mediaObject.play()
-        self.setVisible(True)
-
-    def onMediaPause(self):
-        """
-        Pause the video and refresh the screen
-        """
-        log.debug(u'VideoDisplay Media paused by user')
-        self.mediaObject.pause()
-        self.show()
-
-    def onMediaStop(self):
-        """
-        Stop the video and clean up
-        """
-        log.debug(u'VideoDisplay Media stopped by user')
-        self.message = None
-        self.mediaActive = False
-        self.mediaObject.stop()
-        self.onMediaFinish()
-
-    def onMediaFinish(self):
-        """
-        Clean up the Object queue
-        """
-        log.debug(u'VideoDisplay Reached end of media playlist')
-        self.mediaObject.clearQueue()
-        self.setVisible(False)
-
-    def mediaHide(self, message=u''):
-        """
-        Hide the video display
-        """
-        self.mediaObject.pause()
-        self.hidden = True
-        self.setVisible(False)
-
-    def mediaShow(self):
-        """
-        Show the video display if it was already hidden
-        """
-        if self.hidden:
-            self.hidden = False
-            if self.mediaActive:
-                self._play()
-
 class AudioPlayer(QtCore.QObject):
     """
     This Class will play audio only allowing components to work with a
-    soundtrack which does not take over the user interface.
+    soundtrack independent of the user interface.
     """
     log.info(u'AudioPlayer Loaded')
 
@@ -675,9 +399,9 @@
         Set up a video to play from the serviceitem.
         """
         log.debug(u'AudioPlayer Queue new media message %s' % message)
-        file = os.path.join(message[0].get_frame_path(),
+        mfile = os.path.join(message[0].get_frame_path(),
             message[0].get_frame_title())
-        self.mediaObject.setCurrentSource(Phonon.MediaSource(file))
+        self.mediaObject.setCurrentSource(Phonon.MediaSource(mfile))
         self.onMediaPlay()
 
     def onMediaPlay(self):

=== modified file 'openlp/core/ui/mainwindow.py'
--- openlp/core/ui/mainwindow.py	2010-07-31 00:34:37 +0000
+++ openlp/core/ui/mainwindow.py	2010-08-24 17:30:21 +0000
@@ -29,7 +29,7 @@
 from PyQt4 import QtCore, QtGui
 
 from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \
-    ThemeManager, SlideController, PluginForm, MediaDockManager, DisplayManager
+    ThemeManager, SlideController, PluginForm, MediaDockManager
 from openlp.core.lib import RenderManager, build_icon, OpenLPDockWidget, \
     SettingsManager, PluginManager, Receiver, translate
 from openlp.core.utils import AppLocation, add_actions, LanguageManager
@@ -94,8 +94,8 @@
         self.ControlSplitter.setObjectName(u'ControlSplitter')
         self.MainContentLayout.addWidget(self.ControlSplitter)
         # Create slide controllers
-        self.PreviewController = SlideController(self, self.settingsmanager)
-        self.LiveController = SlideController(self, self.settingsmanager, True)
+        self.PreviewController = SlideController(self, self.settingsmanager, self.screens)
+        self.LiveController = SlideController(self, self.settingsmanager, self.screens,  True)
         # Create menu
         self.MenuBar = QtGui.QMenuBar(MainWindow)
         self.MenuBar.setGeometry(QtCore.QRect(0, 0, 1087, 27))
@@ -509,7 +509,6 @@
         self.songsSettingsSection = u'songs'
         self.serviceNotSaved = False
         self.settingsmanager = SettingsManager(screens)
-        self.displayManager = DisplayManager(screens)
         self.aboutForm = AboutForm(self, applicationVersion)
         self.settingsForm = SettingsForm(self.screens, self, self)
         self.recentFiles = QtCore.QStringList()
@@ -594,7 +593,6 @@
         #ThemeManager needs to call RenderManager
         self.RenderManager = RenderManager(
             self.ThemeManagerContents, self.screens)
-        self.displayManager.renderManager = self.RenderManager
         #Define the media Dock Manager
         self.mediaDockManager = MediaDockManager(self.MediaToolBox)
         log.info(u'Load Plugins')
@@ -605,7 +603,6 @@
         self.plugin_helpers[u'service'] = self.ServiceManagerContents
         self.plugin_helpers[u'settings form'] = self.settingsForm
         self.plugin_helpers[u'toolbox'] = self.mediaDockManager
-        self.plugin_helpers[u'displaymanager'] = self.displayManager
         self.plugin_helpers[u'pluginmanager'] = self.plugin_manager
         self.plugin_helpers[u'formparent'] = self
         self.plugin_manager.find_plugins(pluginpath, self.plugin_helpers)
@@ -663,9 +660,10 @@
         Show the main form, as well as the display form
         """
         QtGui.QWidget.show(self)
-        self.displayManager.setup()
-        if self.displayManager.mainDisplay.isVisible():
-            self.displayManager.mainDisplay.setFocus()
+        self.LiveController.display.setup()
+        self.PreviewController.display.setup()
+        if self.LiveController.display.isVisible():
+            self.LiveController.display.setFocus()
         self.activateWindow()
         if QtCore.QSettings().value(
             self.generalSettingsSection + u'/auto open',
@@ -746,7 +744,6 @@
         their locations
         """
         self.RenderManager.update_display()
-        self.displayManager.setup()
         self.setFocus()
         self.activateWindow()
 
@@ -792,8 +789,8 @@
         self.plugin_manager.finalise_plugins()
         # Save settings
         self.saveSettings()
-        #Close down the displays
-        self.displayManager.close()
+        #Close down the display
+        self.LiveController.display.close()
 
     def serviceChanged(self, reset=False, serviceName=None):
         """

=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py	2010-07-31 02:06:44 +0000
+++ openlp/core/ui/servicemanager.py	2010-08-24 17:30:21 +0000
@@ -317,9 +317,8 @@
         self.serviceItemEditForm.setServiceItem(
             self.serviceItems[item][u'service_item'])
         if self.serviceItemEditForm.exec_():
-            self.serviceItems[item][u'service_item'] = \
-                self.serviceItemEditForm.getServiceItem()
-            self.repaintServiceList(item, 0)
+            self.addServiceItem(self.serviceItemEditForm.getServiceItem(),
+                replace=True)
 
     def nextItem(self):
         """
@@ -873,6 +872,7 @@
                     ItemCapabilities.AllowsPreview):
                     self.parent.PreviewController.addServiceManagerItem(
                         self.serviceItems[item][u'service_item'], 0)
+                    self.parent.LiveController.PreviewListWidget.setFocus()
         else:
             QtGui.QMessageBox.critical(self,
                 translate('OpenLP.ServiceManager', 'Missing Display Handler'),

=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py	2010-08-02 19:05:40 +0000
+++ openlp/core/ui/slidecontroller.py	2010-08-24 17:30:21 +0000
@@ -25,36 +25,17 @@
 ###############################################################################
 
 import logging
-import time
 import os
 
 from PyQt4 import QtCore, QtGui
 from PyQt4.phonon import Phonon
 
-from openlp.core.ui import HideMode
+from openlp.core.ui import HideMode, MainDisplay
 from openlp.core.lib import OpenLPToolbar, Receiver, resize_image, \
     ItemCapabilities, translate
 
 log = logging.getLogger(__name__)
 
-class SlideThread(QtCore.QThread):
-    """
-    A special Qt thread class to speed up the display of text based frames.
-    This is threaded so it loads the frames in background
-    """
-    def __init__(self, parent, prefix, count):
-        QtCore.QThread.__init__(self, parent)
-        self.prefix = prefix
-        self.count = count
-
-    def run(self):
-        """
-        Run the thread.
-        """
-        time.sleep(1)
-        for i in range(0, self.count):
-            Receiver.send_message(u'%s_slide_cache' % self.prefix, i)
-
 class SlideList(QtGui.QTableWidget):
     """
     Customised version of QTableWidget which can respond to keyboard
@@ -97,7 +78,7 @@
     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, isLive=False):
+    def __init__(self, parent, settingsmanager, screens, isLive=False):
         """
         Set up the Slide Controller.
         """
@@ -105,8 +86,10 @@
         self.settingsmanager = settingsmanager
         self.isLive = isLive
         self.parent = parent
-        self.mainDisplay = self.parent.displayManager.mainDisplay
-        self.displayManager = self.parent.displayManager
+        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',
@@ -115,13 +98,14 @@
         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 = {}
-        self.canDisplay = True
         # Layout for holding panel
         self.PanelLayout = QtGui.QVBoxLayout(self.Panel)
         self.PanelLayout.setSpacing(0)
@@ -177,11 +161,11 @@
         sizeToolbarPolicy.setHeightForWidth(
             self.Toolbar.sizePolicy().hasHeightForWidth())
         self.Toolbar.setSizePolicy(sizeToolbarPolicy)
-        if self.isLive:
-            self.Toolbar.addToolbarButton(
-                u'First Slide', u':/slides/slide_first.png',
-                translate('OpenLP.SlideController', 'Move to first'),
-                self.onSlideSelectedFirst)
+#        if self.isLive:
+#            self.Toolbar.addToolbarButton(
+#                u'First Slide', u':/slides/slide_first.png',
+#                translate('OpenLP.SlideController', 'Move to first'),
+#                self.onSlideSelectedFirst)
         self.Toolbar.addToolbarButton(
             u'Previous Slide', u':/slides/slide_previous.png',
             translate('OpenLP.SlideController', 'Move to previous'),
@@ -190,11 +174,11 @@
             u'Next Slide', u':/slides/slide_next.png',
             translate('OpenLP.SlideController', 'Move to next'),
             self.onSlideSelectedNext)
-        if self.isLive:
-            self.Toolbar.addToolbarButton(
-                u'Last Slide', u':/slides/slide_last.png',
-                translate('OpenLP.SlideController', 'Move to last'),
-                self.onSlideSelectedLast)
+#        if self.isLive:
+#            self.Toolbar.addToolbarButton(
+#                u'Last Slide', u':/slides/slide_last.png',
+#                translate('OpenLP.SlideController', 'Move to last'),
+#                self.onSlideSelectedLast)
         if self.isLive:
             self.Toolbar.addToolbarSeparator(u'Close Separator')
             self.HideMenu = QtGui.QToolButton(self.Toolbar)
@@ -213,15 +197,17 @@
             self.ThemeScreen.setCheckable(True)
             QtCore.QObject.connect(self.ThemeScreen,
                 QtCore.SIGNAL("triggered(bool)"), self.onThemeDisplay)
-            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)
+            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)
-            self.HideMenu.menu().addAction(self.DesktopScreen)
+            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(
@@ -251,7 +237,7 @@
             self.DelaySpinBox.setToolTip(translate('OpenLP.SlideController',
                 'Delay between slides in seconds'))
         self.ControllerLayout.addWidget(self.Toolbar)
-        #Build a Media ToolBar
+        # Build a Media ToolBar
         self.Mediabar = OpenLPToolbar(self)
         self.Mediabar.addToolbarButton(
             u'Media Start', u':/slides/media_playback_start.png',
@@ -271,10 +257,19 @@
             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)
+            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:
@@ -322,7 +317,7 @@
         self.SlidePreview.setSizePolicy(sizePolicy)
         self.SlidePreview.setFixedSize(
             QtCore.QSize(self.settingsmanager.slidecontroller_image,
-                         self.settingsmanager.slidecontroller_image / 1.3 ))
+            self.settingsmanager.slidecontroller_image / self.ratio ))
         self.SlidePreview.setFrameShape(QtGui.QFrame.Box)
         self.SlidePreview.setFrameShadow(QtGui.QFrame.Plain)
         self.SlidePreview.setLineWidth(1)
@@ -390,17 +385,39 @@
         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)
+        QtCore.QObject.connect(Receiver.get_receiver(),
             QtCore.SIGNAL(u'%s_slide_cache' % self.typePrefix), self.slideCache)
+        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)
+        # 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)
@@ -453,8 +470,6 @@
         if item.is_media():
             self.Toolbar.setVisible(False)
             self.Mediabar.setVisible(True)
-            #self.volumeSlider.setAudioOutput(
-            #    self.mainDisplay.videoDisplay.audio)
 
     def enablePreviewToolBar(self, item):
         """
@@ -474,7 +489,7 @@
         """
         Method to update the service item if the screen has changed
         """
-        log.debug(u'refreshServiceItem')
+        log.debug(u'refreshServiceItem live = %s' % self.isLive)
         if self.serviceItem:
             if self.serviceItem.is_text() or self.serviceItem.is_image():
                 item = self.serviceItem
@@ -486,10 +501,8 @@
         Method to install the service item into the controller
         Called by plugins
         """
-        log.debug(u'addServiceItem')
-        before = time.time()
+        log.debug(u'addServiceItem live = %s' % self.isLive)
         item.render()
-        log.log(15, u'Rendering took %4s' % (time.time() - before))
         slideno = 0
         if self.songEdit:
             slideno = self.selectedRow
@@ -509,8 +522,8 @@
         request the correct toolbar for the plugin.
         Called by ServiceManager
         """
-        log.debug(u'addServiceManagerItem')
-        #If service item is the same as the current on only change slide
+        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()
@@ -522,17 +535,15 @@
         Loads a ServiceItem into the system from ServiceManager
         Display the slide number passed
         """
-        log.debug(u'processManagerItem')
+        log.debug(u'processManagerItem live = %s' % self.isLive)
         self.onStopLoop()
-        #If old item was a command tell it to stop
+        # 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 serviceItem.is_media():
-            self.onMediaStart(serviceItem)
         if self.isLive:
             blanked = self.BlankScreen.isChecked()
         else:
@@ -541,12 +552,8 @@
             [serviceItem, self.isLive, blanked, slideno])
         self.slideList = {}
         width = self.parent.ControlSplitter.sizes()[self.split]
-        #Set pointing cursor when we have somthing to point at
+        # Set pointing cursor when we have somthing to point at
         self.PreviewListWidget.setCursor(QtCore.Qt.PointingHandCursor)
-        before = time.time()
-        #Clear the old serviceItem cache to release memory
-        if self.serviceItem and self.serviceItem is not serviceItem:
-            self.serviceItem.clear_cache()
         self.serviceItem = serviceItem
         self.PreviewListWidget.clear()
         self.PreviewListWidget.setRowCount(0)
@@ -560,20 +567,19 @@
                 self.PreviewListWidget.rowCount() + 1)
             item = QtGui.QTableWidgetItem()
             slideHeight = 0
-            #It is a based Text Render
             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
-                if self.isLive and frame[u'verseTag'] is not None:
-                    if tag1 not in self.slideList:
-                        self.slideList[tag1] = framenumber
-                        self.SongMenu.menu().addAction(tag1,
-                            self.onSongBarHandler)
                 item.setText(frame[u'text'])
             else:
                 label = QtGui.QLabel()
@@ -600,15 +606,14 @@
         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])
-        if self.serviceItem.is_text():
-            st = SlideThread(
-                self, self.typePrefix, len(self.serviceItem.get_frames()))
-            st.start()
-        log.log(15, u'Display Rendering took %4s' % (time.time() - before))
 
     def onTextRequest(self):
         """
@@ -620,7 +625,7 @@
                 dataItem = {}
                 if self.serviceItem.is_text():
                     dataItem[u'tag'] = unicode(frame[u'verseTag'])
-                    dataItem[u'text'] = unicode(frame[u'text'])
+                    dataItem[u'text'] = unicode(frame[u'html'])
                 else:
                     dataItem[u'tag'] = unicode(framenumber)
                     dataItem[u'text'] = u''
@@ -630,7 +635,7 @@
         Receiver.send_message(u'slidecontroller_%s_text_response'
             % self.typePrefix, data)
 
-    #Screen event methods
+    # Screen event methods
     def onSlideSelectedFirst(self):
         """
         Go to the first slide.
@@ -664,9 +669,9 @@
         """
         Allow the main display to blank the main display at startup time
         """
-        log.debug(u'mainDisplaySetBackground')
-        if not self.mainDisplay.primary:
-            self.onBlankDisplay(True)
+        log.debug(u'mainDisplaySetBackground live = %s' % self.isLive)
+        if not self.display.primary:
+            self.onHideDisplay(True)
 
     def onSlideBlank(self):
         """
@@ -688,7 +693,8 @@
         self.HideMenu.setDefaultAction(self.BlankScreen)
         self.BlankScreen.setChecked(checked)
         self.ThemeScreen.setChecked(False)
-        self.DesktopScreen.setChecked(False)
+        if self.screens.display_count > 1:
+            self.DesktopScreen.setChecked(False)
         QtCore.QSettings().setValue(
             self.parent.generalSettingsSection + u'/screen blank',
             QtCore.QVariant(checked))
@@ -706,7 +712,8 @@
         self.HideMenu.setDefaultAction(self.ThemeScreen)
         self.BlankScreen.setChecked(False)
         self.ThemeScreen.setChecked(checked)
-        self.DesktopScreen.setChecked(False)
+        if self.screens.display_count > 1:
+            self.DesktopScreen.setChecked(False)
         if checked:
             Receiver.send_message(u'maindisplay_hide', HideMode.Theme)
         else:
@@ -721,7 +728,8 @@
         self.HideMenu.setDefaultAction(self.DesktopScreen)
         self.BlankScreen.setChecked(False)
         self.ThemeScreen.setChecked(False)
-        self.DesktopScreen.setChecked(checked)
+        if self.screens.display_count > 1:
+            self.DesktopScreen.setChecked(checked)
         if checked:
             Receiver.send_message(u'maindisplay_hide', HideMode.Screen)
         else:
@@ -778,24 +786,15 @@
             if self.serviceItem.is_command() and self.isLive:
                 self.updatePreview()
             else:
-                before = time.time()
-                frame = self.serviceItem.get_rendered_frame(row)
+                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:
-                    if isinstance(frame[u'main'], basestring):
-                        self.SlidePreview.setPixmap(
-                            QtGui.QPixmap(frame[u'main']))
-                    else:
-                        self.SlidePreview.setPixmap(
-                            QtGui.QPixmap.fromImage(frame[u'main']))
-                log.log(
-                    15, u'Slide Rendering took %4s' % (time.time() - before))
-                if self.isLive:
-                    if self.serviceItem.is_text():
-                        self.mainDisplay.frameView(frame, True)
-                    else:
-                        self.displayManager.displayImage(frame[u'main'])
+                    self.SlidePreview.setPixmap(QtGui.QPixmap(frame))
             self.selectedRow = row
         Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix,
             row)
@@ -810,8 +809,7 @@
             row)
 
     def updatePreview(self):
-        rm = self.parent.RenderManager
-        if not rm.screens.current[u'primary']:
+        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)
@@ -822,9 +820,8 @@
             self.SlidePreview.setPixmap(label.pixmap())
 
     def grabMainDisplay(self):
-        rm = self.parent.RenderManager
         winid = QtGui.QApplication.desktop().winId()
-        rect = rm.screens.current[u'size']
+        rect = self.screens.current[u'size']
         winimg = QtGui.QPixmap.grabWindow(winid, rect.x(),
             rect.y(), rect.width(), rect.height())
         self.SlidePreview.setPixmap(winimg)
@@ -941,7 +938,9 @@
         """
         log.debug(u'SlideController onMediaStart')
         if self.isLive:
-            Receiver.send_message(u'videodisplay_start', item)
+            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()
@@ -951,13 +950,21 @@
             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:
-            Receiver.send_message(u'videodisplay_pause')
+            self.display.videoPause()
         else:
             self.mediaObject.pause()
 
@@ -967,7 +974,7 @@
         """
         log.debug(u'SlideController onMediaPlay')
         if self.isLive:
-            Receiver.send_message(u'videodisplay_play')
+            self.display.videoPlay()
         else:
             self.SlidePreview.hide()
             self.video.show()
@@ -979,7 +986,7 @@
         """
         log.debug(u'SlideController onMediaStop')
         if self.isLive:
-            Receiver.send_message(u'videodisplay_stop')
+            self.display.videoStop()
         else:
             self.mediaObject.stop()
             self.video.hide()

=== modified file 'openlp/core/ui/thememanager.py'
--- openlp/core/ui/thememanager.py	2010-07-31 02:06:44 +0000
+++ openlp/core/ui/thememanager.py	2010-08-24 17:30:21 +0000
@@ -660,9 +660,8 @@
         """
         Call the RenderManager to build a Sample Image
         """
-        log.debug(u'generateImage %s ', themedata)
-        frame = self.parent.RenderManager.generate_preview(themedata)
-        return frame
+        log.debug(u'generateImage \n%s ', themedata)
+        return self.parent.RenderManager.generate_preview(themedata)
 
     def getPreviewImage(self, theme):
         """
@@ -732,8 +731,8 @@
         theme.display_slideTransition = theme.display_slideTransition
         theme.font_footer_color = theme.font_footer_color.strip()
         theme.font_footer_height = int(theme.font_footer_height.strip())
-        theme.font_footer_indentation = \
-            int(theme.font_footer_indentation.strip())
+#        theme.font_footer_indentation = \
+#            int(theme.font_footer_indentation.strip())
         theme.font_footer_italics = str_to_bool(theme.font_footer_italics)
         theme.font_footer_name = theme.font_footer_name.strip()
         #theme.font_footer_override
@@ -746,7 +745,7 @@
         theme.font_main_color = theme.font_main_color.strip()
         theme.font_main_height = int(theme.font_main_height.strip())
         theme.font_main_italics = str_to_bool(theme.font_main_italics)
-        theme.font_main_indentation = int(theme.font_main_indentation)
+#        theme.font_main_indentation = int(theme.font_main_indentation)
         theme.font_main_name = theme.font_main_name.strip()
         #theme.font_main_override
         theme.font_main_proportion = int(theme.font_main_proportion.strip())
@@ -757,3 +756,8 @@
         #theme.theme_mode
         theme.theme_name = theme.theme_name.strip()
         #theme.theme_version
+        # Remove the Transparent settings as they are not relevent
+        if theme.background_mode == u'transparent':
+            theme.background_mode = u'opaque'
+            theme.background_type = u'solid'
+            theme.background_startColor = u'#000000'

=== modified file 'openlp/plugins/alerts/alertsplugin.py'
--- openlp/plugins/alerts/alertsplugin.py	2010-07-31 22:23:48 +0000
+++ openlp/plugins/alerts/alertsplugin.py	2010-08-24 17:30:21 +0000
@@ -80,9 +80,10 @@
         log.info(u'Alerts Initialising')
         Plugin.initialise(self)
         self.toolsAlertItem.setVisible(True)
+        self.liveController.alertTab = self.alertsTab
 
     def finalise(self):
-        log.info(u'Plugin Finalise')
+        log.info(u'Alerts Finaliseing')
         Plugin.finalise(self)
         self.toolsAlertItem.setVisible(False)
 

=== modified file 'openlp/plugins/alerts/lib/alertsmanager.py'
--- openlp/plugins/alerts/lib/alertsmanager.py	2010-07-27 09:32:52 +0000
+++ openlp/plugins/alerts/lib/alertsmanager.py	2010-08-24 17:30:21 +0000
@@ -32,18 +32,9 @@
 
 log = logging.getLogger(__name__)
 
-HTMLCODE = u"""
-   <p style=\"color:%s;
-   background-color:%s;
-   font-family:%s;
-   font-size: %spt; \">
-    %s
-    </p>
-"""
-
 class AlertsManager(QtCore.QObject):
     """
-    AlertsTab is the Alerts settings tab in the settings dialog.
+    AlertsManager manages the settings of Alerts.
     """
     log.info(u'Alert Manager loaded')
 
@@ -94,10 +85,7 @@
             return
         text = self.alertList.pop(0)
         alertTab = self.parent.alertsTab
-        text = HTMLCODE % (alertTab.font_color, alertTab.bg_color,
-                           alertTab.font_face, alertTab.font_size, text)
-        self.parent.previewController.parent.displayManager.addAlert(text,
-            alertTab.location)
+        self.parent.liveController.display.alert(text)
         # check to see if we have a timer running
         if self.timer_id == 0:
             self.timer_id = self.startTimer(int(alertTab.timeout) * 1000)
@@ -111,10 +99,8 @@
 
         """
         log.debug(u'timer event')
-        alertTab = self.parent.alertsTab
         if event.timerId() == self.timer_id:
-            self.parent.previewController.parent.displayManager.addAlert(u'',
-                alertTab.location)
+            self.parent.liveController.display.alert(u'')
         self.killTimer(self.timer_id)
         self.timer_id = 0
         self.generateAlert()

=== modified file 'openlp/plugins/alerts/lib/alertstab.py'
--- openlp/plugins/alerts/lib/alertstab.py	2010-07-27 09:32:52 +0000
+++ openlp/plugins/alerts/lib/alertstab.py	2010-08-24 17:30:21 +0000
@@ -261,7 +261,7 @@
         self.font_face = unicode(settings.value(
             u'font face', QtCore.QVariant(QtGui.QFont().family())).toString())
         self.location = settings.value(
-            u'location', QtCore.QVariant(0)).toInt()[0]
+            u'location', QtCore.QVariant(1)).toInt()[0]
         settings.endGroup()
         self.FontSizeSpinBox.setValue(self.font_size)
         self.TimeoutSpinBox.setValue(self.timeout)
@@ -296,3 +296,4 @@
         self.FontPreview.setFont(font)
         self.FontPreview.setStyleSheet(u'background-color: %s; color: %s' %
             (self.bg_color, self.font_color))
+

=== modified file 'openlp/plugins/bibles/lib/mediaitem.py'
--- openlp/plugins/bibles/lib/mediaitem.py	2010-08-10 16:49:52 +0000
+++ openlp/plugins/bibles/lib/mediaitem.py	2010-08-24 17:30:21 +0000
@@ -503,16 +503,16 @@
                 dual_text = self._decodeQtObject(reference, 'dual_text')
             if self.parent.settings_tab.display_style == 1:
                 verse_text = self.formatVerse(old_chapter, chapter, verse,
-                    u'(', u')')
+                    u'{su}(', u'){/su}')
             elif self.parent.settings_tab.display_style == 2:
                 verse_text = self.formatVerse(old_chapter, chapter, verse,
-                    u'{', u'}')
+                    u'{su}{', u'}{/su}')
             elif self.parent.settings_tab.display_style == 3:
                 verse_text = self.formatVerse(old_chapter, chapter, verse,
-                    u'[', u']')
+                    u'{su}[', u']{/su}')
             else:
                 verse_text = self.formatVerse(old_chapter, chapter, verse,
-                    u'', u'')
+                    u'{su}', u'{/su}')
             old_chapter = chapter
             footer = u'%s (%s %s)' % (book, version, copyright)
             # If not found add to footer
@@ -531,7 +531,11 @@
             else:
                 # If we are 'Verse Per Line' then force a new line.
                 if self.parent.settings_tab.layout_style == 1:
-                    text = text + u'\n\n'
+                    text = text + u'\n'
+                else:
+                    # split the line but do not replace line breaks in renderer
+                    service_item.add_capability(ItemCapabilities.NoLineBreaks)
+                    text = text + u'\n'
                 bible_text = u'%s %s %s' % (bible_text, verse_text, text)
                 # If we are 'Verse Per Slide' then create a new slide.
                 if self.parent.settings_tab.layout_style == 0:

=== modified file 'openlp/plugins/custom/forms/editcustomdialog.py'
--- openlp/plugins/custom/forms/editcustomdialog.py	2010-07-27 11:42:38 +0000
+++ openlp/plugins/custom/forms/editcustomdialog.py	2010-08-24 17:30:21 +0000
@@ -27,6 +27,7 @@
 from PyQt4 import QtCore, QtGui
 
 from openlp.core.lib import build_icon, translate
+from openlp.core.ui import SpellTextEdit
 
 class Ui_CustomEditDialog(object):
     def setupUi(self, customEditDialog):
@@ -73,7 +74,7 @@
         self.editLayout3.setSpacing(8)
         self.editLayout3.setMargin(0)
         self.editLayout3.setObjectName(u'editLayout3')
-        self.verseTextEdit = QtGui.QTextEdit(self.editWidget)
+        self.verseTextEdit = SpellTextEdit(self)
         self.verseTextEdit.setObjectName(u'verseTextEdit')
         self.editLayout3.addWidget(self.verseTextEdit)
         self.buttonWidget = QtGui.QWidget(self.editWidget)

=== modified file 'openlp/plugins/images/lib/mediaitem.py'
--- openlp/plugins/images/lib/mediaitem.py	2010-07-27 09:32:52 +0000
+++ openlp/plugins/images/lib/mediaitem.py	2010-08-24 17:30:21 +0000
@@ -110,8 +110,14 @@
             u':/slides/slide_blank.png',
             translate('ImagePlugin.MediaItem', 'Replace Live Background'),
             self.onReplaceClick, False)
+        self.resetButton = self.toolbar.addToolbarButton(
+            translate('ImagePlugin.MediaItem', u'Reset Background'),
+            u':/system/system_close.png',
+            translate('ImagePlugin.MediaItem', 'Reset Live Background'),
+            self.onResetClick, False)
         # Add the song widget to the page layout
         self.pageLayout.addWidget(self.ImageWidget)
+        self.resetButton.setVisible(False)
 
     def onDeleteClick(self):
         """
@@ -169,6 +175,10 @@
         else:
             return False
 
+    def onResetClick(self):
+        self.resetButton.setVisible(False)
+        self.parent.liveController.display.resetImage()
+
     def onReplaceClick(self):
         if check_item_selected(self.listView,
             translate('ImagePlugin.MediaItem',
@@ -178,7 +188,8 @@
                 bitem = self.listView.item(item.row())
                 filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
                 frame = QtGui.QImage(unicode(filename))
-                self.parent.displayManager.displayImageWithText(frame)
+                self.parent.liveController.display.image(frame)
+        self.resetButton.setVisible(True)
 
     def onPreviewClick(self):
         MediaManagerItem.onPreviewClick(self)

=== modified file 'openlp/plugins/media/lib/mediaitem.py'
--- openlp/plugins/media/lib/mediaitem.py	2010-07-27 09:32:52 +0000
+++ openlp/plugins/media/lib/mediaitem.py	2010-08-24 17:30:21 +0000
@@ -97,8 +97,17 @@
             u':/slides/slide_blank.png',
             translate('MediaPlugin.MediaItem', 'Replace Live Background'),
                 self.onReplaceClick, False)
+        self.resetButton = self.toolbar.addToolbarButton(
+            u'Reset Background', u':/system/system_close.png',
+            translate('ImagePlugin.MediaItem', 'Reset Live Background'),
+                self.onResetClick, False)
         # Add the song widget to the page layout
         self.pageLayout.addWidget(self.ImageWidget)
+        self.resetButton.setVisible(False)
+
+    def onResetClick(self):
+        self.resetButton.setVisible(False)
+        self.parent.liveController.display.resetVideo()
 
     def onReplaceClick(self):
         if check_item_selected(self.listView,
@@ -106,7 +115,8 @@
             'You must select a media file to replace the background with.')):
             item = self.listView.currentItem()
             filename = unicode(item.data(QtCore.Qt.UserRole).toString())
-            self.parent.displayManager.displayVideo(filename)
+            self.parent.liveController.display.video(filename, 0)
+        self.resetButton.setVisible(True)
 
     def generateSlideData(self, service_item, item=None):
         if item is None:

=== modified file 'openlp/plugins/songs/forms/editsongform.py'
--- openlp/plugins/songs/forms/editsongform.py	2010-07-31 02:06:44 +0000
+++ openlp/plugins/songs/forms/editsongform.py	2010-08-24 17:30:21 +0000
@@ -314,7 +314,7 @@
             author = self.songmanager.get_object(Author, item_id)
             if author in self.song.authors:
                 QtGui.QMessageBox.warning(self,
-                    translate('SongsPlugin.EditSongForm', 'Error'), 
+                    translate('SongsPlugin.EditSongForm', 'Error'),
                     translate('SongsPlugin.EditSongForm', 'This author is '
                     'already in the list.'))
             else:
@@ -422,7 +422,9 @@
         self.VerseDeleteButton.setEnabled(True)
 
     def onVerseAddButtonClicked(self):
-        self.verse_form.setVerse(u'', True)
+        # Allow insert button as you do not know if multiple verses will
+        # be entered.
+        self.verse_form.setVerse(u'')
         if self.verse_form.exec_():
             afterText, verse, subVerse = self.verse_form.getVerse()
             data = u'%s:%s' % (verse, subVerse)

=== modified file 'openlp/plugins/songs/forms/editversedialog.py'
--- openlp/plugins/songs/forms/editversedialog.py	2010-07-27 09:32:52 +0000
+++ openlp/plugins/songs/forms/editversedialog.py	2010-08-24 17:30:21 +0000
@@ -27,6 +27,7 @@
 from PyQt4 import QtCore, QtGui
 
 from openlp.core.lib import build_icon, translate
+from openlp.core.ui import SpellTextEdit
 from openlp.plugins.songs.lib import VerseType
 
 class Ui_EditVerseDialog(object):
@@ -38,7 +39,7 @@
         self.EditVerseLayout.setSpacing(8)
         self.EditVerseLayout.setMargin(8)
         self.EditVerseLayout.setObjectName(u'EditVerseLayout')
-        self.VerseTextEdit = QtGui.QPlainTextEdit(EditVerseDialog)
+        self.VerseTextEdit = SpellTextEdit(EditVerseDialog)
         self.VerseTextEdit.setObjectName(u'VerseTextEdit')
         self.EditVerseLayout.addWidget(self.VerseTextEdit)
         self.VerseTypeLayout = QtGui.QHBoxLayout()

=== modified file 'openlp/plugins/songs/forms/editverseform.py'
--- openlp/plugins/songs/forms/editverseform.py	2010-07-27 09:32:52 +0000
+++ openlp/plugins/songs/forms/editverseform.py	2010-08-24 17:30:21 +0000
@@ -45,6 +45,9 @@
         """
         QtGui.QDialog.__init__(self, parent)
         self.setupUi(self)
+        QtCore.QObject.connect(self.VerseTextEdit,
+            QtCore.SIGNAL('customContextMenuRequested(QPoint)'),
+            self.contextMenu)
         QtCore.QObject.connect(
             self.InsertButton,
             QtCore.SIGNAL(u'clicked()'),
@@ -57,6 +60,10 @@
         )
         self.verse_regex = re.compile(r'---\[([-\w]+):([\d]+)\]---')
 
+    def contextMenu(self, point):
+        item = self.serviceManagerList.itemAt(point)
+        print item
+
     def insertVerse(self, title, num=1):
         if self.VerseTextEdit.textCursor().columnNumber() != 0:
             self.VerseTextEdit.insertPlainText(u'\n')

=== added directory 'resources/Fedora'
=== added directory 'resources/Fedora/191'
=== added file 'resources/Fedora/191/OpenLP.spec'
--- resources/Fedora/191/OpenLP.spec	1970-01-01 00:00:00 +0000
+++ resources/Fedora/191/OpenLP.spec	2010-08-24 17:30:21 +0000
@@ -0,0 +1,91 @@
+%{!?python_sitelib:%global python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
+
+Summary: Open source Church presentation and lyrics projection application
+Name: OpenLP
+Version: 1.9.1.1
+Release: 1%{?dist}
+Source0: http://downloads.sourceforge.net/openlp/openlp/%{version}/%{name}-%{version}.tar.gz
+License: GPLv2
+Group: Applications/Multimedia
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
+BuildArch: noarch
+
+URL: http://openlp.org/
+
+BuildRequires:  desktop-file-utils
+BuildRequires:  python2-devel
+BuildRequires:  python-setuptools
+
+Requires:       PyQt4
+Requires:       phonon
+Requires:       python-BeautifulSoup
+Requires:       python-chardet
+Requires:       python-lxml
+Requires:       python-sqlalchemy
+Requires:       hicolor-icon-theme
+
+%description
+OpenLP is a church presentation software, for lyrics projection software,
+used to display slides of Songs,  Bible verses, videos, images, and 
+presentations (if OpenOffice.org is installed) using a computer and projector.
+
+%prep
+%setup -q 
+
+%build
+python setup.py build
+
+%install
+rm -rf %{buildroot}
+python setup.py install --skip-build -O1 --root %{buildroot}
+
+install -m644 -p -D resources/images/openlp-logo-16x16.png \
+   %{buildroot}%{_datadir}/icons/hicolor/16x16/apps/openlp.png
+install -m644 -p -D resources/images/openlp-logo-32x32.png \
+   %{buildroot}%{_datadir}/icons/hicolor/32x32/apps/openlp.png
+install -m644 -p -D resources/images/openlp-logo-48x48.png \
+   %{buildroot}%{_datadir}/icons/hicolor/48x48/apps/openlp.png
+install -m644 -p -D resources/images/openlp-logo.svg \
+   %{buildroot}%{_datadir}/icons/hicolor/scalable/apps/openlp.svg
+
+desktop-file-install \
+  --dir %{buildroot}/%{_datadir}/applications \
+  resources/openlp.desktop 
+
+mv %{buildroot}%{_bindir}/bible-1to2-converter.py \
+   %{buildroot}%{_bindir}/bible-1to2-converter
+mv %{buildroot}%{_bindir}/openlp-1to2-converter.py \
+   %{buildroot}%{_bindir}/openlp-1to2-converter
+mv %{buildroot}%{_bindir}/openlp-remoteclient.py \
+   %{buildroot}%{_bindir}/openlp-remoteclient
+mv %{buildroot}%{_bindir}/openlp.pyw %{buildroot}%{_bindir}/openlp
+
+
+%post
+touch --no-create %{_datadir}/icons/hicolor ||:
+gtk-update-icon-cache -q %{_datadir}/icons/hicolor 2> /dev/null ||:
+
+%postun
+touch --no-create %{_datadir}/icons/hicolor ||:
+gtk-update-icon-cache -q %{_datadir}/icons/hicolor 2> /dev/null ||:
+
+
+%clean
+rm -rf %{buildroot}
+
+%files 
+%defattr(-,root,root)
+%doc copyright.txt LICENSE
+%{_bindir}/bible-1to2-converter
+%{_bindir}/openlp-1to2-converter
+%{_bindir}/openlp-remoteclient
+%{_bindir}/openlp
+%{_datadir}/applications/openlp.desktop
+%{_datadir}/icons/hicolor/*/apps/openlp.*
+%{python_sitelib}/openlp/
+%{python_sitelib}/OpenLP-%{version}*.egg-info
+%doc documentation/*.txt
+
+%changelog
+* Sun Mar 28 2010 Tim Bentley <timbentley@xxxxxxxxxx> 1.9.1.1
+- Initial build version - Alpha 1 Release

=== added directory 'resources/Fedora/192'

Follow ups