← Back to team overview

openlp-core team mailing list archive

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

 

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

Requested reviews:
  Raoul Snyman (raoul-snyman)
  Jonathan Corwin (j-corwin)

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

Refactored RenderManager and Renderer into one class and cleaned up the tree.

Added a 2nd pass for Songs and Custom if one screen does not fit and split at [###.
Added a 2nd pass for bibles which splits by words.  Tested on Esther 8:7-10 at 46 and 83pt.

UI changes to come.
-- 
https://code.launchpad.net/~trb143/openlp/beta1/+merge/59118
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/lib/__init__.py'
--- openlp/core/lib/__init__.py	2011-04-14 13:53:02 +0000
+++ openlp/core/lib/__init__.py	2011-04-26 19:12:28 +0000
@@ -289,6 +289,5 @@
 from toolbar import OpenLPToolbar
 from dockwidget import OpenLPDockWidget
 from renderer import Renderer
-from rendermanager import RenderManager
 from mediamanageritem import MediaManagerItem
 from openlp.core.utils.actions import ActionList

=== modified file 'openlp/core/lib/plugin.py'
--- openlp/core/lib/plugin.py	2011-04-15 21:43:59 +0000
+++ openlp/core/lib/plugin.py	2011-04-26 19:12:28 +0000
@@ -161,7 +161,7 @@
         self.log = logging.getLogger(self.name)
         self.previewController = plugin_helpers[u'preview']
         self.liveController = plugin_helpers[u'live']
-        self.renderManager = plugin_helpers[u'render']
+        self.renderer = plugin_helpers[u'renderer']
         self.serviceManager = plugin_helpers[u'service']
         self.settingsForm = plugin_helpers[u'settings form']
         self.mediadock = plugin_helpers[u'toolbox']
@@ -359,4 +359,4 @@
         use of the singular name of the plugin object so must only be called
         after this has been set.
         """
-        self.textStrings[name] = {u'title': title, u'tooltip': tooltip}
\ No newline at end of file
+        self.textStrings[name] = {u'title': title, u'tooltip': tooltip}

=== removed file 'openlp/core/lib/renderer.py'
--- openlp/core/lib/renderer.py	2011-03-24 19:04:02 +0000
+++ openlp/core/lib/renderer.py	1970-01-01 00:00:00 +0000
@@ -1,147 +0,0 @@
-# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
-
-###############################################################################
-# OpenLP - Open Source Lyrics Projection                                      #
-# --------------------------------------------------------------------------- #
-# Copyright (c) 2008-2011 Raoul Snyman                                        #
-# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael      #
-# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler,        #
-# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout,      #
-# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund             #
-# --------------------------------------------------------------------------- #
-# This program is free software; you can redistribute it and/or modify it     #
-# under the terms of the GNU General Public License as published by the Free  #
-# Software Foundation; version 2 of the License.                              #
-#                                                                             #
-# This program is distributed in the hope that it will be useful, but WITHOUT #
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       #
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    #
-# more details.                                                               #
-#                                                                             #
-# You should have received a copy of the GNU General Public License along     #
-# with this program; if not, write to the Free Software Foundation, Inc., 59  #
-# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
-###############################################################################
-"""
-The :mod:`renderer` module enables OpenLP to take the input from plugins and
-format it for the output display.
-"""
-import logging
-
-from PyQt4 import QtWebKit
-
-from openlp.core.lib import expand_tags, build_lyrics_format_css, \
-    build_lyrics_outline_css, Receiver
-
-log = logging.getLogger(__name__)
-
-class Renderer(object):
-    """
-    Genarates a pixmap image of a array of text. The Text is formatted to
-    make sure it fits on the screen and if not extra frames are generated.
-    """
-    log.info(u'Renderer Loaded')
-
-    def __init__(self):
-        """
-        Initialise the renderer.
-        """
-        self._rect = None
-        self.theme_name = None
-        self._theme = None
-
-    def set_theme(self, theme):
-        """
-        Set the theme to be used.
-
-        ``theme``
-            The theme to be used.
-        """
-        log.debug(u'set theme')
-        self._theme = theme
-        self.theme_name = theme.theme_name
-
-    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
-        self.page_width = self._rect.width()
-        self.page_height = self._rect.height()
-        if self._theme.font_main_shadow:
-            self.page_width -= int(self._theme.font_main_shadow_size)
-            self.page_height -= int(self._theme.font_main_shadow_size)
-        self.web = QtWebKit.QWebView()
-        self.web.setVisible(False)
-        self.web.resize(self.page_width, self.page_height)
-        self.web_frame = self.web.page().mainFrame()
-        # Adjust width and height to account for shadow. outline done in css
-        self.page_shell = u'<html><head><style>' \
-            u'*{margin: 0; padding: 0; border: 0;} '\
-            u'#main {position:absolute; top:0px; %s %s}</style><body>' \
-            u'<div id="main">' % \
-            (build_lyrics_format_css(self._theme, self.page_width,
-            self.page_height), build_lyrics_outline_css(self._theme))
-
-    def format_slide(self, words, line_break, force_page=False):
-        """
-        Figure out how much text can appear on a slide, using the current
-        theme settings.
-
-        ``words``
-            The words to be fitted on the slide.
-
-        ``line_break``
-            Add line endings after each line of text used for bibles.
-
-        ``force_page``
-            Flag to tell message lines in page.
-
-        """
-        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 = []
-        for verse in verses_text:
-            lines = verse.split(u'\n')
-            for line in lines:
-                text.append(line)
-        formatted = []
-        html_text = u''
-        styled_text = u''
-        line_count = 0
-        for line in text:
-            if line_count != -1:
-                line_count += 1
-            styled_line = expand_tags(line) + line_end
-            styled_text += styled_line
-            html = self.page_shell + styled_text + u'</div></body></html>'
-            self.web.setHtml(html)
-            # Text too long so go to next page
-            if self.web_frame.contentsSize().height() > self.page_height:
-                if force_page and line_count > 0:
-                    Receiver.send_message(u'theme_line_count', line_count)
-                line_count = -1
-                if html_text.endswith(u'<br>'):
-                    html_text = html_text[:len(html_text)-4]
-                formatted.append(html_text)
-                html_text = u''
-                styled_text = styled_line
-            html_text += line + line_end
-        if html_text.endswith(u'<br>'):
-            html_text = html_text[:len(html_text)-4]
-        formatted.append(html_text)
-        log.debug(u'format_slide - End')
-        return formatted

=== renamed file 'openlp/core/lib/rendermanager.py' => 'openlp/core/lib/renderer.py'
--- openlp/core/lib/rendermanager.py	2011-03-27 16:39:04 +0000
+++ openlp/core/lib/renderer.py	2011-04-26 19:12:28 +0000
@@ -26,9 +26,11 @@
 
 import logging
 
-from PyQt4 import QtCore
+from PyQt4 import QtCore, QtWebKit
 
-from openlp.core.lib import Renderer, ServiceItem, ImageManager
+from openlp.core.lib import ServiceItem, ImageManager, expand_tags, \
+    build_lyrics_format_css, build_lyrics_outline_css, Receiver, \
+    ItemCapabilities
 from openlp.core.lib.theme import ThemeLevel
 from openlp.core.ui import MainDisplay
 
@@ -43,7 +45,9 @@
     'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
 FOOTER = [u'Arky Arky (Unknown)', u'Public Domain', u'CCLI 123456']
 
-class RenderManager(object):
+HTML_END = u'</div></body></html>'
+
+class Renderer(object):
     """
     Class to pull all Renderer interactions into one place. The plugins will
     call helper methods to do the rendering but this class will provide
@@ -58,7 +62,7 @@
     ``screen_number``
         Defaults to *0*. The index of the output/display screen.
     """
-    log.info(u'RenderManager Loaded')
+    log.info(u'Renderer Loaded')
 
     def __init__(self, theme_manager, screens):
         """
@@ -70,9 +74,6 @@
         self.display = MainDisplay(self, screens, False)
         self.display.imageManager = self.image_manager
         self.theme_manager = theme_manager
-        self.renderer = Renderer()
-        self.calculate_default(self.screens.current[u'size'])
-        self.theme = u''
         self.service_theme = u''
         self.theme_level = u''
         self.override_background = None
@@ -84,11 +85,11 @@
         Updates the render manager's information about the current screen.
         """
         log.debug(u'Update Display')
-        self.calculate_default(self.screens.current[u'size'])
+        self._calculate_default(self.screens.current[u'size'])
         self.display = MainDisplay(self, self.screens, False)
         self.display.imageManager = self.image_manager
         self.display.setup()
-        self.renderer.bg_frame = None
+        self.bg_frame = None
         self.theme_data = None
         self.image_manager.update_display(self.width, self.height)
 
@@ -120,7 +121,7 @@
         self.service_theme = service_theme
         self.theme_data = None
 
-    def set_override_theme(self, theme, overrideLevels=False):
+    def set_override_theme(self, override_theme, override_levels=False):
         """
         Set the appropriate theme depending on the theme level.
         Called by the service item when building a display frame
@@ -129,72 +130,44 @@
             The name of the song-level theme. None means the service
             item wants to use the given value.
 
-        ``overrideLevels``
+        ``override_levels``
             Used to force the theme data passed in to be used.
 
         """
-        log.debug(u'set override theme to %s', theme)
+        log.debug(u'set override theme to %s', override_theme)
         theme_level = self.theme_level
-        if overrideLevels:
+        if override_levels:
             theme_level = ThemeLevel.Song
         if theme_level == ThemeLevel.Global:
-            self.theme = self.global_theme
+            theme = self.global_theme
         elif theme_level == ThemeLevel.Service:
             if self.service_theme == u'':
-                self.theme = self.global_theme
+                theme = self.global_theme
             else:
-                self.theme = self.service_theme
+                theme = self.service_theme
         else:
             # Images have a theme of -1
-            if theme and theme != -1:
-                self.theme = theme
+            if override_theme and override_theme != -1:
+                theme = override_theme
             elif theme_level == ThemeLevel.Song or \
                 theme_level == ThemeLevel.Service:
                 if self.service_theme == u'':
-                    self.theme = self.global_theme
+                    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.theme_data is None \
-            or overrideLevels:
-            log.debug(u'theme is now %s', self.theme)
-            # Force the theme to be the one passed in.
-            if overrideLevels:
-                self.theme_data = theme
-            else:
-                self.theme_data = self.theme_manager.getThemeData(self.theme)
-            self.calculate_default(self.screens.current[u'size'])
-            self.renderer.set_theme(self.theme_data)
-            self.build_text_rectangle(self.theme_data)
-            self.image_manager.add_image(self.theme_data.theme_name,
-                self.theme_data.background_filename)
-        return self.renderer._rect, self.renderer._rect_footer
-
-    def build_text_rectangle(self, theme):
-        """
-        Builds a text block using the settings in ``theme``
-        and the size of the display screen.height.
-
-        ``theme``
-            The theme to build a text block for.
-        """
-        log.debug(u'build_text_rectangle')
-        main_rect = None
-        footer_rect = None
-        if not theme.font_main_override:
-            main_rect = QtCore.QRect(10, 0, self.width - 20, self.footer_start)
-        else:
-            main_rect = QtCore.QRect(theme.font_main_x, theme.font_main_y,
-                theme.font_main_width - 1, theme.font_main_height - 1)
-        if not theme.font_footer_override:
-            footer_rect = QtCore.QRect(10, self.footer_start, self.width - 20,
-                self.height - self.footer_start)
-        else:
-            footer_rect = QtCore.QRect(theme.font_footer_x,
-                theme.font_footer_y, theme.font_footer_width - 1,
-                theme.font_footer_height - 1)
-        self.renderer.set_text_rectangle(main_rect, footer_rect)
+                    theme = self.service_theme
+            else:
+                theme = self.global_theme
+        log.debug(u'theme is now %s', theme)
+        # Force the theme to be the one passed in.
+        if override_levels:
+            self.theme_data = override_theme
+        else:
+            self.theme_data = self.theme_manager.getThemeData(theme)
+        self._calculate_default(self.screens.current[u'size'])
+        self._build_text_rectangle(self.theme_data)
+        self.image_manager.add_image(self.theme_data.theme_name,
+            self.theme_data.background_filename)
+        return self._rect, self._rect_footer
 
     def generate_preview(self, theme_data, force_page=False):
         """
@@ -210,7 +183,7 @@
         # save value for use in format_slide
         self.force_page = force_page
         # set the default image size for previews
-        self.calculate_default(self.screens.preview[u'size'])
+        self._calculate_default(self.screens.preview[u'size'])
         # build a service item to generate preview
         serviceItem = ServiceItem()
         serviceItem.theme = theme_data
@@ -220,7 +193,7 @@
         else:
             self.image_manager.del_image(theme_data.theme_name)
             serviceItem.add_from_text(u'', VERSE, FOOTER)
-        serviceItem.render_manager = self
+        serviceItem.renderer = self
         serviceItem.raw_footer = FOOTER
         serviceItem.render(True)
         if not self.force_page:
@@ -228,23 +201,41 @@
             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'])
+            self._calculate_default(self.screens.current[u'size'])
             return preview
 
-    def format_slide(self, words, line_break):
+    def format_slide(self, text, line_break, item):
         """
         Calculate how much text can fit on a slide.
 
-        ``words``
+        ``text``
             The words to go on the slides.
 
         ``line_break``
             Add line endings after each line of text used for bibles.
         """
         log.debug(u'format slide')
-        return self.renderer.format_slide(words, line_break, self.force_page)
+        # clean up line endings
+        lines = self._lines_split(text)
+        # Songs and Custom
+        if item.is_capable(ItemCapabilities.AllowsVirtualSplit):
+            # Do not forget the line breaks !
+            slides = text.split(u'\n[---]\n')
+            pages = []
+            for slide in slides:
+                lines = self._lines(slide)
+                new_pages = self._paginate_slide(lines, line_break,
+                    self.force_page)
+                pages.extend([page for page in new_pages])
+        # Bibles
+        elif item.is_capable(ItemCapabilities.AllowsWordSplit):
+            pages = self._paginate_slide_words(text, line_break)
+        # Theme preview "service items".
+        else:
+            pages = self._paginate_slide(lines, line_break, self.force_page)
+        return pages
 
-    def calculate_default(self, screen):
+    def _calculate_default(self, screen):
         """
         Calculate the default dimentions of the screen.
 
@@ -259,3 +250,199 @@
             self.width, self.height, self.screen_ratio)
         # 90% is start of footer
         self.footer_start = int(self.height * 0.90)
+
+    def _build_text_rectangle(self, theme):
+        """
+        Builds a text block using the settings in ``theme``
+        and the size of the display screen.height.
+        Note the system has a 10 pixel border round the screen
+
+        ``theme``
+            The theme to build a text block for.
+        """
+        log.debug(u'_build_text_rectangle')
+        main_rect = None
+        footer_rect = None
+        if not theme.font_main_override:
+            main_rect = QtCore.QRect(10, 0, self.width - 20, self.footer_start)
+        else:
+            main_rect = QtCore.QRect(theme.font_main_x, theme.font_main_y,
+                theme.font_main_width - 1, theme.font_main_height - 1)
+        if not theme.font_footer_override:
+            footer_rect = QtCore.QRect(10, self.footer_start, self.width - 20,
+                self.height - self.footer_start)
+        else:
+            footer_rect = QtCore.QRect(theme.font_footer_x,
+                theme.font_footer_y, theme.font_footer_width - 1,
+                theme.font_footer_height - 1)
+        self._set_text_rectangle(main_rect, footer_rect)
+
+    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
+        self.page_width = self._rect.width()
+        self.page_height = self._rect.height()
+        if self.theme_data.font_main_shadow:
+            self.page_width -= int(self.theme_data.font_main_shadow_size)
+            self.page_height -= int(self.theme_data.font_main_shadow_size)
+        self.web = QtWebKit.QWebView()
+        self.web.setVisible(False)
+        self.web.resize(self.page_width, self.page_height)
+        self.web_frame = self.web.page().mainFrame()
+        # Adjust width and height to account for shadow. outline done in css
+        self.page_shell = u'<html><head><style>' \
+            u'*{margin: 0; padding: 0; border: 0;} '\
+            u'#main {position:absolute; top:0px; %s %s}</style><body>' \
+            u'<div id="main">' % \
+            (build_lyrics_format_css(self.theme_data, self.page_width,
+            self.page_height), build_lyrics_outline_css(self.theme_data))
+
+    def _paginate_slide(self, lines, line_break, force_page=False):
+        """
+        Figure out how much text can appear on a slide, using the current
+        theme settings.
+
+        ``lines``
+            The words to be fitted on the slide split into lines.
+
+        ``line_break``
+            Add line endings after each line of text (used for bibles).
+
+        ``force_page``
+            Flag to tell message lines in page.
+
+        """
+        log.debug(u'_paginate_slide - Start')
+        line_end = u''
+        if line_break:
+            line_end = u'<br>'
+        formatted = []
+        html_text = u''
+        styled_text = u''
+        line_count = 0
+        for line in lines:
+            if line_count != -1:
+                line_count += 1
+            styled_line = expand_tags(line) + line_end
+            styled_text += styled_line
+            html = self.page_shell + styled_text + HTML_END
+            self.web.setHtml(html)
+            # Text too long so go to next page
+            if self.web_frame.contentsSize().height() > self.page_height:
+                if force_page and line_count > 0:
+                    Receiver.send_message(u'theme_line_count', line_count)
+                line_count = -1
+                html_text = html_text.rstrip(u'<br>')
+                formatted.append(html_text)
+                html_text = u''
+                styled_text = styled_line
+            html_text += line + line_end
+        html_text = html_text.rstrip(u'<br>')
+        formatted.append(html_text)
+        log.debug(u'_paginate_slide - End')
+        return formatted
+
+    def _paginate_slide_words(self, text, line_break):
+        """
+        Figure out how much text can appear on a slide, using the current
+        theme settings. This version is to handle text which needs to be split
+        into words to get it to fit.
+
+        ``text``
+            The words to be fitted on the slide split into lines.
+
+        ``line_break``
+            Add line endings after each line of text used for bibles.
+
+        """
+        # In the beginning ...
+        # <space> <!-- here we could have added the second verse -->
+        # <new slide>
+        # Verse 2
+        log.debug(u'_paginate_slide_words - Start')
+        line_end = u''
+        if line_break:
+            line_end = u'<br>'
+        formatted = []
+        previous_html = u''
+        previous_raw = u''
+        lines = self._lines(text, u'[---]')
+        for line in lines:
+            styled_line = expand_tags(line)
+            html = self.page_shell + previous_html + styled_line + HTML_END
+            self.web.setHtml(html)
+            # Text too long so go to next page
+            if self.web_frame.contentsSize().height() > self.page_height:
+                words = self._words_split(line)
+                for word in words:
+                    styled_word = expand_tags(word)
+                    html = self.page_shell + previous_html + styled_word + \
+                        HTML_END
+                    self.web.setHtml(html)
+                    # Text too long so go to next page
+                    if self.web_frame.contentsSize().height() > \
+                        self.page_height:
+                        previous_raw = previous_raw.rstrip(u'<br>')
+                        formatted.append(previous_raw)
+                        previous_html = u''
+                        previous_raw = u''
+                    previous_html += styled_word
+                    previous_raw += word
+                previous_html += line_end
+                previous_raw += line_end
+            else:
+                previous_html += styled_line + line_end
+                previous_raw += line + line_end
+        previous_raw = previous_raw.rstrip(u'<br>')
+        formatted.append(previous_raw)
+        log.debug(u'_paginate_slide_words - End')
+        return formatted
+
+    def _lines(self, text, split=u'\n[---]\n'):
+        """
+        Split the slide up by physical line
+        """
+        # this parse we do not want to use this so remove it
+        verses_text = text.split(u'\n')
+        text = []
+        for verse in verses_text:
+            lines = verse.split(u'\n')
+            text.extend([line for line in lines])
+
+        return text
+
+    def _words_split(self, line):
+        """
+        Split the slide up by word so can wrap better
+        """
+        # this parse we are to be wordy
+        line = line.replace(u'\n', u' ')
+        verses_text = line.split(u' ')
+        text = []
+        for verse in verses_text:
+            lines = verse.split(u' ')
+            text.extend([line + u' ' for line in lines])
+        return text
+
+    def _lines_split(self, text):
+        """
+        Split the slide up by physical line
+        """
+        # this parse we do not want to use this so remove it
+        lines = text.split(u'\n')
+        real_lines = []
+        for line in lines:
+            line = line.replace(u' [---]', u'[---]')
+            sub_lines = line.split(u'\n')
+            real_lines.extend([sub_line for sub_line in sub_lines])
+        return real_lines

=== modified file 'openlp/core/lib/serviceitem.py'
--- openlp/core/lib/serviceitem.py	2011-04-15 21:43:59 +0000
+++ openlp/core/lib/serviceitem.py	2011-04-26 19:12:28 +0000
@@ -63,6 +63,8 @@
     ProvidesOwnDisplay = 10
     AllowsDetailedTitleDisplay = 11
     AllowsVariableStartTime = 12
+    AllowsVirtualSplit = 13
+    AllowsWordSplit = 14
 
 
 class ServiceItem(object):
@@ -81,7 +83,7 @@
             The plugin that this service item belongs to.
         """
         if plugin:
-            self.render_manager = plugin.renderManager
+            self.renderer = plugin.renderer
             self.name = plugin.name
         self.title = u''
         self.shortname = u''
@@ -151,7 +153,7 @@
         self.icon = icon
         self.iconic_representation = build_icon(icon)
 
-    def render(self, useOverride=False):
+    def render(self, use_override=False):
         """
         The render method is what generates the frames for the screen and
         obtains the display information from the renderemanager.
@@ -161,24 +163,23 @@
         log.debug(u'Render called')
         self._display_frames = []
         self.bg_image_bytes = None
-        line_break = True
-        if self.is_capable(ItemCapabilities.NoLineBreaks):
-            line_break = False
+        line_break = not self.is_capable(ItemCapabilities.NoLineBreaks)
         theme = self.theme if self.theme else None
         self.main, self.footer = \
-            self.render_manager.set_override_theme(theme, useOverride)
-        self.themedata = self.render_manager.renderer._theme
+            self.renderer.set_override_theme(theme, use_override)
+        self.themedata = self.renderer.theme_data
         if self.service_item_type == ServiceItemType.Text:
             log.debug(u'Formatting slides')
             for slide in self._raw_frames:
-                formatted = self.render_manager \
-                    .format_slide(slide[u'raw_slide'], line_break)
+                formatted = self.renderer \
+                    .format_slide(slide[u'raw_slide'], line_break, self)
                 for page in formatted:
-                    self._display_frames.append(
-                        {u'title': clean_tags(page),
+                    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'] })
+                        u'verseTag': slide[u'verseTag']
+                    })
         elif self.service_item_type == ServiceItemType.Image or \
             self.service_item_type == ServiceItemType.Command:
             pass
@@ -205,7 +206,7 @@
         """
         self.service_item_type = ServiceItemType.Image
         self._raw_frames.append({u'title': title, u'path': path})
-        self.render_manager.image_manager.add_image(title, path)
+        self.renderer.image_manager.add_image(title, path)
         self._new_item()
 
     def add_from_text(self, title, raw_slide, verse_tag=None):
@@ -453,4 +454,5 @@
         elif not start and end:
             return end
         else:
-            return u'%s : %s' % (start, end)
\ No newline at end of file
+            return u'%s : %s' % (start, end)
+

=== modified file 'openlp/core/ui/mainwindow.py'
--- openlp/core/ui/mainwindow.py	2011-04-22 06:30:07 +0000
+++ openlp/core/ui/mainwindow.py	2011-04-26 19:12:28 +0000
@@ -30,7 +30,7 @@
 
 from PyQt4 import QtCore, QtGui
 
-from openlp.core.lib import RenderManager, build_icon, OpenLPDockWidget, \
+from openlp.core.lib import Renderer, build_icon, OpenLPDockWidget, \
     SettingsManager, PluginManager, Receiver, translate
 from openlp.core.lib.ui import UiStrings, base_action, checkable_action, \
     icon_action, shortcut_action
@@ -545,9 +545,9 @@
             QtCore.SIGNAL(u'openlp_information_message'),
             self.onInformationMessage)
         # warning cyclic dependency
-        # RenderManager needs to call ThemeManager and
-        # ThemeManager needs to call RenderManager
-        self.renderManager = RenderManager(
+        # renderer needs to call ThemeManager and
+        # ThemeManager needs to call Renderer
+        self.renderer = Renderer(
             self.themeManagerContents, self.screens)
         # Define the media Dock Manager
         self.mediaDockManager = MediaDockManager(self.MediaToolBox)
@@ -555,7 +555,7 @@
         # make the controllers available to the plugins
         self.pluginHelpers[u'preview'] = self.previewController
         self.pluginHelpers[u'live'] = self.liveController
-        self.pluginHelpers[u'render'] = self.renderManager
+        self.pluginHelpers[u'renderer'] = self.renderer
         self.pluginHelpers[u'service'] = self.ServiceManagerContents
         self.pluginHelpers[u'settings form'] = self.settingsForm
         self.pluginHelpers[u'toolbox'] = self.mediaDockManager
@@ -781,7 +781,7 @@
         their locations
         """
         log.debug(u'screenChanged')
-        self.renderManager.update_display()
+        self.renderer.update_display()
         self.setFocus()
         self.activateWindow()
 

=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py	2011-04-25 08:35:48 +0000
+++ openlp/core/ui/servicemanager.py	2011-04-26 19:12:28 +0000
@@ -579,7 +579,7 @@
                 for item in items:
                     serviceItem = ServiceItem()
                     serviceItem.from_service = True
-                    serviceItem.render_manager = self.mainwindow.renderManager
+                    serviceItem.renderer = self.mainwindow.renderer
                     serviceItem.set_from_service(item, self.servicePath)
                     self.validateItem(serviceItem)
                     self.addServiceItem(serviceItem)
@@ -989,7 +989,7 @@
         """
         log.debug(u'onThemeComboBoxSelected')
         self.service_theme = unicode(self.themeComboBox.currentText())
-        self.mainwindow.renderManager.set_service_theme(self.service_theme)
+        self.mainwindow.renderer.set_service_theme(self.service_theme)
         QtCore.QSettings().setValue(
             self.mainwindow.serviceSettingsSection + u'/service theme',
             QtCore.QVariant(self.service_theme))
@@ -1001,7 +1001,7 @@
         sure the theme combo box is in the correct state.
         """
         log.debug(u'themeChange')
-        if self.mainwindow.renderManager.theme_level == ThemeLevel.Global:
+        if self.mainwindow.renderer.theme_level == ThemeLevel.Global:
             self.toolbar.actions[u'ThemeLabel'].setVisible(False)
             self.toolbar.actions[u'ThemeWidget'].setVisible(False)
         else:
@@ -1016,7 +1016,7 @@
         Receiver.send_message(u'cursor_busy')
         log.debug(u'regenerateServiceItems')
         # force reset of renderer as theme data has changed
-        self.mainwindow.renderManager.themedata = None
+        self.mainwindow.renderer.themedata = None
         if self.serviceItems:
             tempServiceItems = self.serviceItems
             self.serviceManagerList.clear()
@@ -1278,7 +1278,7 @@
                 self.onThemeChangeAction, context=QtCore.Qt.WidgetShortcut)
             self.themeMenu.addAction(action)
         find_and_set_in_combo_box(self.themeComboBox, self.service_theme)
-        self.mainwindow.renderManager.set_service_theme(self.service_theme)
+        self.mainwindow.renderer.set_service_theme(self.service_theme)
         self.regenerateServiceItems()
 
     def onThemeChangeAction(self):

=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py	2011-04-15 21:43:59 +0000
+++ openlp/core/ui/slidecontroller.py	2011-04-26 19:12:28 +0000
@@ -412,7 +412,7 @@
         """
         # rebuild display as screen size changed
         self.display = MainDisplay(self, self.screens, self.isLive)
-        self.display.imageManager = self.parent.renderManager.image_manager
+        self.display.imageManager = self.parent.renderer.image_manager
         self.display.alertTab = self.alertTab
         self.display.setup()
         if self.isLive:
@@ -614,19 +614,19 @@
                 label.setScaledContents(True)
                 if self.serviceItem.is_command():
                     image = resize_image(frame[u'image'],
-                        self.parent.renderManager.width,
-                        self.parent.renderManager.height)
+                        self.parent.renderer.width,
+                        self.parent.renderer.height)
                 else:
                     # If current slide set background to image
                     if framenumber == slideno:
                         self.serviceItem.bg_image_bytes = \
-                            self.parent.renderManager.image_manager. \
+                            self.parent.renderer.image_manager. \
                             get_image_bytes(frame[u'title'])
-                    image = self.parent.renderManager.image_manager. \
+                    image = self.parent.renderer.image_manager. \
                         get_image(frame[u'title'])
                 label.setPixmap(QtGui.QPixmap.fromImage(image))
                 self.previewListWidget.setCellWidget(framenumber, 0, label)
-                slideHeight = width * self.parent.renderManager.screen_ratio
+                slideHeight = width * self.parent.renderer.screen_ratio
                 row += 1
             text.append(unicode(row))
             self.previewListWidget.setItem(framenumber, 0, item)
@@ -1150,4 +1150,4 @@
         elif self.desktopScreen.isChecked():
             return HideMode.Screen
         else:
-            return None
\ No newline at end of file
+            return None

=== modified file 'openlp/core/ui/thememanager.py'
--- openlp/core/ui/thememanager.py	2011-04-17 17:57:47 +0000
+++ openlp/core/ui/thememanager.py	2011-04-26 19:12:28 +0000
@@ -660,7 +660,7 @@
 
     def generateImage(self, themeData, forcePage=False):
         """
-        Call the RenderManager to build a Sample Image
+        Call the renderer to build a Sample Image
 
         ``themeData``
             The theme to generated a preview for.
@@ -669,7 +669,7 @@
             Flag to tell message lines per page need to be generated.
         """
         log.debug(u'generateImage \n%s ', themeData)
-        return self.mainwindow.renderManager.generate_preview(
+        return self.mainwindow.renderer.generate_preview(
             themeData, forcePage)
 
     def getPreviewImage(self, theme):

=== modified file 'openlp/core/ui/themestab.py'
--- openlp/core/ui/themestab.py	2011-04-15 21:43:59 +0000
+++ openlp/core/ui/themestab.py	2011-04-26 19:12:28 +0000
@@ -149,7 +149,7 @@
         settings.setValue(u'global theme',
             QtCore.QVariant(self.global_theme))
         settings.endGroup()
-        self.mainwindow.renderManager.set_global_theme(
+        self.mainwindow.renderer.set_global_theme(
             self.global_theme, self.theme_level)
         Receiver.send_message(u'theme_update_global', self.global_theme)
 
@@ -167,7 +167,7 @@
 
     def onDefaultComboBoxChanged(self, value):
         self.global_theme = unicode(self.DefaultComboBox.currentText())
-        self.mainwindow.renderManager.set_global_theme(
+        self.mainwindow.renderer.set_global_theme(
             self.global_theme, self.theme_level)
         self.__previewGlobalTheme()
 
@@ -188,7 +188,7 @@
         for theme in theme_list:
             self.DefaultComboBox.addItem(theme)
         find_and_set_in_combo_box(self.DefaultComboBox, self.global_theme)
-        self.mainwindow.renderManager.set_global_theme(
+        self.mainwindow.renderer.set_global_theme(
             self.global_theme, self.theme_level)
         if self.global_theme is not u'':
             self.__previewGlobalTheme()
@@ -203,4 +203,4 @@
         if not preview.isNull():
             preview = preview.scaled(300, 255, QtCore.Qt.KeepAspectRatio,
                 QtCore.Qt.SmoothTransformation)
-        self.DefaultListView.setPixmap(preview)
\ No newline at end of file
+        self.DefaultListView.setPixmap(preview)

=== modified file 'openlp/plugins/bibles/lib/mediaitem.py'
--- openlp/plugins/bibles/lib/mediaitem.py	2011-04-15 21:43:59 +0000
+++ openlp/plugins/bibles/lib/mediaitem.py	2011-04-26 19:12:28 +0000
@@ -723,6 +723,7 @@
             service_item.add_capability(ItemCapabilities.NoLineBreaks)
         service_item.add_capability(ItemCapabilities.AllowsPreview)
         service_item.add_capability(ItemCapabilities.AllowsLoop)
+        service_item.add_capability(ItemCapabilities.AllowsWordSplit)
         # Service Item: Title
         service_item.title = u', '.join(raw_title)
         # Service Item: Theme

=== modified file 'openlp/plugins/custom/forms/editcustomform.py'
--- openlp/plugins/custom/forms/editcustomform.py	2011-04-10 18:54:26 +0000
+++ openlp/plugins/custom/forms/editcustomform.py	2011-04-26 19:12:28 +0000
@@ -169,7 +169,7 @@
             item = self.slideListView.item(row)
             slide_list += item.text()
             if row != self.slideListView.count() - 1:
-                slide_list += u'\n[---]\n'
+                slide_list += u'\n[===]\n'
         self.editSlideForm.setText(slide_list)
         if self.editSlideForm.exec_():
             self.updateSlideList(self.editSlideForm.getText(), True)

=== modified file 'openlp/plugins/custom/forms/editcustomslideform.py'
--- openlp/plugins/custom/forms/editcustomslideform.py	2011-03-27 17:00:34 +0000
+++ openlp/plugins/custom/forms/editcustomslideform.py	2011-04-26 19:12:28 +0000
@@ -63,7 +63,7 @@
         """
         Returns a list with all slides.
         """
-        return self.slideTextEdit.toPlainText().split(u'\n[---]\n')
+        return self.slideTextEdit.toPlainText().split(u'\n[===]\n')
 
     def onSplitButtonPressed(self):
         """
@@ -71,5 +71,5 @@
         """
         if self.slideTextEdit.textCursor().columnNumber() != 0:
             self.slideTextEdit.insertPlainText(u'\n')
-        self.slideTextEdit.insertPlainText(u'[---]\n')
+        self.slideTextEdit.insertPlainText(u'[===]\n')
         self.slideTextEdit.setFocus()

=== modified file 'openlp/plugins/custom/lib/mediaitem.py'
--- openlp/plugins/custom/lib/mediaitem.py	2011-04-15 21:43:59 +0000
+++ openlp/plugins/custom/lib/mediaitem.py	2011-04-26 19:12:28 +0000
@@ -140,6 +140,7 @@
         service_item.add_capability(ItemCapabilities.AllowsEdit)
         service_item.add_capability(ItemCapabilities.AllowsPreview)
         service_item.add_capability(ItemCapabilities.AllowsLoop)
+        service_item.add_capability(ItemCapabilities.AllowsVirtualSplit)
         customSlide = self.parent.manager.get_object(CustomSlide, item_id)
         title = customSlide.title
         credit = customSlide.credits

=== modified file 'openlp/plugins/presentations/lib/impresscontroller.py'
--- openlp/plugins/presentations/lib/impresscontroller.py	2011-03-24 19:04:02 +0000
+++ openlp/plugins/presentations/lib/impresscontroller.py	2011-04-26 19:12:28 +0000
@@ -244,7 +244,7 @@
             return False
         self.presentation = self.document.getPresentation()
         self.presentation.Display = \
-            self.controller.plugin.renderManager.screens.current_display + 1
+            self.controller.plugin.renderer.screens.current_display + 1
         self.control = None
         self.create_thumbnails()
         return True
@@ -463,4 +463,4 @@
             shape = page.getByIndex(idx)
             if shape.supportsService("com.sun.star.drawing.Text"):
                 text += shape.getString() + '\n'
-        return text
+        return text
\ No newline at end of file

=== modified file 'openlp/plugins/presentations/lib/powerpointcontroller.py'
--- openlp/plugins/presentations/lib/powerpointcontroller.py	2011-04-07 22:57:12 +0000
+++ openlp/plugins/presentations/lib/powerpointcontroller.py	2011-04-26 19:12:28 +0000
@@ -251,14 +251,15 @@
                         win32ui.GetForegroundWindow().GetDC().GetDeviceCaps(88)
                 except win32ui.error:
                     dpi = 96
-            rendermanager = self.controller.plugin.renderManager
-            rect = rendermanager.screens.current[u'size']
+            renderer = self.controller.plugin.renderer
+            rect = renderer.screens.current[u'size']
             ppt_window = self.presentation.SlideShowSettings.Run()
             ppt_window.Top = rect.y() * 72 / dpi
             ppt_window.Height = rect.height() * 72 / dpi
             ppt_window.Left = rect.x() * 72 / dpi
             ppt_window.Width = rect.width() * 72 / dpi
 
+
     def get_slide_number(self):
         """
         Returns the current slide number.

=== modified file 'openlp/plugins/presentations/lib/pptviewcontroller.py'
--- openlp/plugins/presentations/lib/pptviewcontroller.py	2011-03-24 19:04:02 +0000
+++ openlp/plugins/presentations/lib/pptviewcontroller.py	2011-04-26 19:12:28 +0000
@@ -121,8 +121,8 @@
         The file name of the presentations to run.
         """
         log.debug(u'LoadPresentation')
-        rendermanager = self.controller.plugin.renderManager
-        rect = rendermanager.screens.current[u'size']
+        renderer = self.controller.plugin.renderer
+        rect = renderer.screens.current[u'size']
         rect = RECT(rect.x(), rect.y(), rect.right(), rect.bottom())
         filepath = str(self.filepath.replace(u'/', u'\\'))
         if not os.path.isdir(self.get_temp_folder()):
@@ -244,4 +244,4 @@
         """
         Triggers the previous slide on the running presentation
         """
-        self.controller.process.PrevStep(self.pptid)
+        self.controller.process.PrevStep(self.pptid)
\ No newline at end of file

=== modified file 'openlp/plugins/songs/forms/editsongform.py'
--- openlp/plugins/songs/forms/editsongform.py	2011-04-15 21:43:59 +0000
+++ openlp/plugins/songs/forms/editsongform.py	2011-04-26 19:12:28 +0000
@@ -89,7 +89,7 @@
             self.onVerseListViewPressed)
         QtCore.QObject.connect(self.themeAddButton,
             QtCore.SIGNAL(u'clicked()'),
-            self.parent.parent.renderManager.theme_manager.onAddTheme)
+            self.parent.parent.renderer.theme_manager.onAddTheme)
         QtCore.QObject.connect(self.maintenanceButton,
             QtCore.SIGNAL(u'clicked()'), self.onMaintenanceButtonClicked)
         QtCore.QObject.connect(Receiver.get_receiver(),

=== modified file 'openlp/plugins/songs/lib/mediaitem.py'
--- openlp/plugins/songs/lib/mediaitem.py	2011-04-15 21:43:59 +0000
+++ openlp/plugins/songs/lib/mediaitem.py	2011-04-26 19:12:28 +0000
@@ -347,6 +347,7 @@
         service_item.add_capability(ItemCapabilities.AllowsLoop)
         service_item.add_capability(ItemCapabilities.OnLoadUpdate)
         service_item.add_capability(ItemCapabilities.AddIfNewItem)
+        service_item.add_capability(ItemCapabilities.AllowsVirtualSplit)
         song = self.parent.manager.get_object(Song, item_id)
         service_item.theme = song.theme_name
         service_item.edit_id = item_id


Follow ups