diff --git a/copyright.txt b/copyright.txt
index 0fb988622..df5563844 100644
--- a/copyright.txt
+++ b/copyright.txt
@@ -7,7 +7,7 @@
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
-# Armin Köhler, Joshua Millar, Stevan Pettit, Andreas Preikschat, Mattias #
+# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
# --------------------------------------------------------------------------- #
diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py
index e24045883..e104f4022 100644
--- a/openlp/core/lib/__init__.py
+++ b/openlp/core/lib/__init__.py
@@ -144,6 +144,59 @@ def image_to_byte(image):
# convert to base64 encoding so does not get missed!
return byte_array.toBase64()
+def create_thumb(image_path, thumb_path, return_icon=True, size=None):
+ """
+ Create a thumbnail from the given image path and depending on
+ ``return_icon`` it returns an icon from this thumb.
+
+ ``image_path``
+ The image file to create the icon from.
+
+ ``thumb_path``
+ The filename to save the thumbnail to.
+
+ ``return_icon``
+ States if an icon should be build and returned from the thumb. Defaults
+ to ``True``.
+
+ ``size``
+ Allows to state a own size to use. Defaults to ``None``, which means
+ that a default height of 88 is used.
+ """
+ ext = os.path.splitext(thumb_path)[1].lower()
+ reader = QtGui.QImageReader(image_path)
+ if size is None:
+ ratio = float(reader.size().width()) / float(reader.size().height())
+ reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88))
+ else:
+ reader.setScaledSize(size)
+ thumb = reader.read()
+ thumb.save(thumb_path, ext[1:])
+ if not return_icon:
+ return
+ if os.path.exists(thumb_path):
+ return build_icon(unicode(thumb_path))
+ # Fallback for files with animation support.
+ return build_icon(unicode(image_path))
+
+def validate_thumb(file_path, thumb_path):
+ """
+ Validates whether an file's thumb still exists and if is up to date.
+ **Note**, you must **not** call this function, before checking the
+ existence of the file.
+
+ ``file_path``
+ The path to the file. The file **must** exist!
+
+ ``thumb_path``
+ The path to the thumb.
+ """
+ if not os.path.exists(unicode(thumb_path)):
+ return False
+ image_date = os.stat(unicode(file_path)).st_mtime
+ thumb_date = os.stat(unicode(thumb_path)).st_mtime
+ return image_date <= thumb_date
+
def resize_image(image_path, width, height, background=u'#000000'):
"""
Resize an image to fit on the current screen.
@@ -158,7 +211,7 @@ def resize_image(image_path, width, height, background=u'#000000'):
The new image height.
``background``
- The background colour defaults to black.
+ The background colour. Defaults to black.
DO NOT REMOVE THE DEFAULT BACKGROUND VALUE!
"""
diff --git a/openlp/core/lib/eventreceiver.py b/openlp/core/lib/eventreceiver.py
index 7c0115f89..1b957e5df 100644
--- a/openlp/core/lib/eventreceiver.py
+++ b/openlp/core/lib/eventreceiver.py
@@ -51,9 +51,6 @@ class EventReceiver(QtCore.QObject):
``config_screen_changed``
The display monitor has been changed
- ``slidecontroller_{live|preview}_first``
- Moves to the first slide
-
``slidecontroller_{live|preview}_next``
Moves to the next slide
@@ -66,9 +63,6 @@ class EventReceiver(QtCore.QObject):
``slidecontroller_{live|preview}_previous_noloop``
Moves to the previous slide, without auto advance
- ``slidecontroller_{live|preview}_last``
- Moves to the last slide
-
``slidecontroller_{live|preview}_set``
Moves to a specific slide, by index
@@ -82,11 +76,6 @@ class EventReceiver(QtCore.QObject):
``slidecontroller_{live|preview}_changed``
Broadcasts that the slidecontroller has changed the current slide
- ``slidecontroller_{live|preview}_text_request``
- Request the text for the current item in the controller
- Returns a slidecontroller_{live|preview}_text_response with an
- array of dictionaries with the tag and verse text
-
``slidecontroller_{live|preview}_blank``
Request that the output screen is blanked
diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py
index 6f56cf8b2..8e31d8950 100644
--- a/openlp/core/lib/htmlbuilder.py
+++ b/openlp/core/lib/htmlbuilder.py
@@ -34,8 +34,8 @@ from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, \
log = logging.getLogger(__name__)
-# FIXME: Add html5 doctype. However, do not break theme gradients.
HTMLSRC = u"""
+
OpenLP Display
@@ -404,7 +404,7 @@ def build_background_css(item, width, height):
background = \
u'background: ' \
u'-webkit-gradient(linear, left top, left bottom, ' \
- 'from(%s), to(%s))' % (theme.background_start_color,
+ 'from(%s), to(%s)) fixed' % (theme.background_start_color,
theme.background_end_color)
elif theme.background_direction == \
BackgroundGradientType.to_string( \
@@ -412,7 +412,7 @@ def build_background_css(item, width, height):
background = \
u'background: ' \
u'-webkit-gradient(linear, left top, right bottom, ' \
- 'from(%s), to(%s))' % (theme.background_start_color,
+ 'from(%s), to(%s)) fixed' % (theme.background_start_color,
theme.background_end_color)
elif theme.background_direction == \
BackgroundGradientType.to_string \
@@ -420,20 +420,21 @@ def build_background_css(item, width, height):
background = \
u'background: ' \
u'-webkit-gradient(linear, left bottom, right top, ' \
- 'from(%s), to(%s))' % (theme.background_start_color,
+ 'from(%s), to(%s)) fixed' % (theme.background_start_color,
theme.background_end_color)
elif theme.background_direction == \
BackgroundGradientType.to_string \
(BackgroundGradientType.Vertical):
background = \
u'background: -webkit-gradient(linear, left top, ' \
- u'right top, from(%s), to(%s))' % \
+ u'right top, from(%s), to(%s)) fixed' % \
(theme.background_start_color, theme.background_end_color)
else:
background = \
u'background: -webkit-gradient(radial, %s 50%%, 100, %s ' \
- u'50%%, %s, from(%s), to(%s))' % (width, width, width,
- theme.background_start_color, theme.background_end_color)
+ u'50%%, %s, from(%s), to(%s)) fixed' % (width, width,
+ width, theme.background_start_color,
+ theme.background_end_color)
return background
def build_lyrics_css(item, webkitvers):
@@ -557,11 +558,15 @@ def build_lyrics_format_css(theme, width, height):
left_margin = int(theme.font_main_outline_size) * 2
else:
left_margin = 0
- lyrics = u'white-space:pre-wrap; word-wrap: break-word; ' \
+ justify = u'white-space:pre-wrap;'
+ # fix tag incompatibilities
+ if theme.display_horizontal_align == HorizontalType.Justify:
+ justify = u''
+ lyrics = u'%s word-wrap: break-word; ' \
'text-align: %s; vertical-align: %s; font-family: %s; ' \
'font-size: %spt; color: %s; line-height: %d%%; margin: 0;' \
'padding: 0; padding-left: %spx; width: %spx; height: %spx; ' % \
- (align, valign, theme.font_main_name, theme.font_main_size,
+ (justify, align, valign, theme.font_main_name, theme.font_main_size,
theme.font_main_color, 100 + int(theme.font_main_line_adjustment),
left_margin, width, height)
if theme.font_main_outline:
diff --git a/openlp/core/lib/mailto/LICENSE b/openlp/core/lib/mailto/LICENSE
deleted file mode 100644
index d8ab2d8d2..000000000
--- a/openlp/core/lib/mailto/LICENSE
+++ /dev/null
@@ -1,38 +0,0 @@
-PSF LICENSE AGREEMENT FOR PYTHON 2.7.1
-
- 1. This LICENSE AGREEMENT is between the Python Software Foundation ("PSF"),
- and the Individual or Organization ("Licensee") accessing and otherwise
- using Python 2.7.1 software in source or binary form and its associated
- documentation.
- 2. Subject to the terms and conditions of this License Agreement, PSF hereby
- grants Licensee a nonexclusive, royalty-free, world-wide license to
- reproduce, analyze, test, perform and/or display publicly, prepare
- derivative works, distribute, and otherwise use Python 2.7.1 alone or in any
- derivative version, provided, however, that PSF's License Agreement and
- PSF's notice of copyright, i.e., "Copyright (c) 2001-2010 Python Software
- Foundation; All Rights Reserved" are retained in Python 2.7.1 alone or in
- any derivative version prepared by Licensee.
- 3. In the event Licensee prepares a derivative work that is based on or
- incorporates Python 2.7.1 or any part thereof, and wants to make the
- derivative work available to others as provided herein, then Licensee hereby
- agrees to include in any such work a brief summary of the changes made to
- Python 2.7.1.
- 4. PSF is making Python 2.7.1 available to Licensee on an "AS IS" basis. PSF
- MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF
- EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION
- OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT
- THE USE OF PYTHON 2.7.1 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.
- 5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 2.7.1 FOR
- ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF
- MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.7.1, OR ANY DERIVATIVE
- THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
- 6. This License Agreement will automatically terminate upon a material breach
- of its terms and conditions.
- 7. Nothing in this License Agreement shall be deemed to create any relationship
- of agency, partnership, or joint venture between PSF and Licensee. This
- License Agreement does not grant permission to use PSF trademarks or trade
- name in a trademark sense to endorse or promote products or services of
- Licensee, or any third party.
- 8. By copying, installing or otherwise using Python 2.7.1, Licensee agrees to
- be bound by the terms and conditions of this License Agreement.
-
diff --git a/openlp/core/lib/mailto/__init__.py b/openlp/core/lib/mailto/__init__.py
deleted file mode 100644
index f05ebfdee..000000000
--- a/openlp/core/lib/mailto/__init__.py
+++ /dev/null
@@ -1,321 +0,0 @@
-# -*- coding: utf-8 -*-
-# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
-
-###############################################################################
-# Utilities for opening files or URLs in the registered default application #
-# and for sending e-mail using the user's preferred composer. #
-# --------------------------------------------------------------------------- #
-# Copyright (c) 2007 Antonio Valentino #
-# All rights reserved. #
-# --------------------------------------------------------------------------- #
-# This program offered under the PSF License as published by the Python #
-# Software Foundation. #
-# #
-# The license text can be found at http://docs.python.org/license.html #
-# #
-# This code is taken from: http://code.activestate.com/recipes/511443 #
-# Modified for use in OpenLP #
-###############################################################################
-
-__version__ = u'1.1'
-__all__ = [u'open', u'mailto']
-
-import os
-import sys
-import webbrowser
-import subprocess
-
-from email.Utils import encode_rfc2231
-
-_controllers = {}
-_open = None
-
-
-class BaseController(object):
- """
- Base class for open program controllers.
- """
-
- def __init__(self, name):
- self.name = name
-
- def open(self, filename):
- raise NotImplementedError
-
-
-class Controller(BaseController):
- """
- Controller for a generic open program.
- """
-
- def __init__(self, *args):
- super(Controller, self).__init__(os.path.basename(args[0]))
- self.args = list(args)
-
- def _invoke(self, cmdline):
- if sys.platform[:3] == u'win':
- closefds = False
- startupinfo = subprocess.STARTUPINFO()
- startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
- else:
- closefds = True
- startupinfo = None
-
- if (os.environ.get(u'DISPLAY') or sys.platform[:3] == u'win' or \
- sys.platform == u'darwin'):
- inout = file(os.devnull, u'r+')
- else:
- # for TTY programs, we need stdin/out
- inout = None
-
- # if possible, put the child precess in separate process group,
- # so keyboard interrupts don't affect child precess as well as
- # Python
- setsid = getattr(os, u'setsid', None)
- if not setsid:
- setsid = getattr(os, u'setpgrp', None)
-
- pipe = subprocess.Popen(cmdline, stdin=inout, stdout=inout,
- stderr=inout, close_fds=closefds, preexec_fn=setsid,
- startupinfo=startupinfo)
-
- # It is assumed that this kind of tools (gnome-open, kfmclient,
- # exo-open, xdg-open and open for OSX) immediately exit after lauching
- # the specific application
- returncode = pipe.wait()
- if hasattr(self, u'fixreturncode'):
- returncode = self.fixreturncode(returncode)
- return not returncode
-
- def open(self, filename):
- if isinstance(filename, basestring):
- cmdline = self.args + [filename]
- else:
- # assume it is a sequence
- cmdline = self.args + filename
- try:
- return self._invoke(cmdline)
- except OSError:
- return False
-
-
-# Platform support for Windows
-if sys.platform[:3] == u'win':
-
- class Start(BaseController):
- """
- Controller for the win32 start progam through os.startfile.
- """
-
- def open(self, filename):
- try:
- os.startfile(filename)
- except WindowsError:
- # [Error 22] No application is associated with the specified
- # file for this operation: ''
- return False
- else:
- return True
-
- _controllers[u'windows-default'] = Start(u'start')
- _open = _controllers[u'windows-default'].open
-
-
-# Platform support for MacOS
-elif sys.platform == u'darwin':
- _controllers[u'open'] = Controller(u'open')
- _open = _controllers[u'open'].open
-
-
-# Platform support for Unix
-else:
-
- import commands
-
- # @WARNING: use the private API of the webbrowser module
- from webbrowser import _iscommand
-
- class KfmClient(Controller):
- """
- Controller for the KDE kfmclient program.
- """
-
- def __init__(self, kfmclient=u'kfmclient'):
- super(KfmClient, self).__init__(kfmclient, u'exec')
- self.kde_version = self.detect_kde_version()
-
- def detect_kde_version(self):
- kde_version = None
- try:
- info = commands.getoutput(u'kfmclient --version')
-
- for line in info.splitlines():
- if line.startswith(u'KDE'):
- kde_version = line.split(u':')[-1].strip()
- break
- except (OSError, RuntimeError):
- pass
-
- return kde_version
-
- def fixreturncode(self, returncode):
- if returncode is not None and self.kde_version > u'3.5.4':
- return returncode
- else:
- return os.EX_OK
-
- def detect_desktop_environment():
- """
- Checks for known desktop environments
-
- Return the desktop environments name, lowercase (kde, gnome, xfce)
- or "generic"
- """
-
- desktop_environment = u'generic'
-
- if os.environ.get(u'KDE_FULL_SESSION') == u'true':
- desktop_environment = u'kde'
- elif os.environ.get(u'GNOME_DESKTOP_SESSION_ID'):
- desktop_environment = u'gnome'
- else:
- try:
- info = commands.getoutput(u'xprop -root _DT_SAVE_MODE')
- if u' = "xfce4"' in info:
- desktop_environment = u'xfce'
- except (OSError, RuntimeError):
- pass
-
- return desktop_environment
-
-
- def register_X_controllers():
- if _iscommand(u'kfmclient'):
- _controllers[u'kde-open'] = KfmClient()
-
- for command in (u'gnome-open', u'exo-open', u'xdg-open'):
- if _iscommand(command):
- _controllers[command] = Controller(command)
-
-
- def get():
- controllers_map = {
- u'gnome': u'gnome-open',
- u'kde': u'kde-open',
- u'xfce': u'exo-open',
- }
-
- desktop_environment = detect_desktop_environment()
-
- try:
- controller_name = controllers_map[desktop_environment]
- return _controllers[controller_name].open
-
- except KeyError:
- if _controllers.has_key(u'xdg-open'):
- return _controllers[u'xdg-open'].open
- else:
- return webbrowser.open
-
- if os.environ.get(u'DISPLAY'):
- register_X_controllers()
- _open = get()
-
-
-def open(filename):
- """
- Open a file or an URL in the registered default application.
- """
-
- return _open(filename)
-
-
-def _fix_addresses(**kwargs):
- for headername in (u'address', u'to', u'cc', u'bcc'):
- try:
- headervalue = kwargs[headername]
- if not headervalue:
- del kwargs[headername]
- continue
- elif not isinstance(headervalue, basestring):
- # assume it is a sequence
- headervalue = u','.join(headervalue)
- except KeyError:
- pass
- except TypeError:
- raise TypeError(u'string or sequence expected for "%s", %s '
- u'found' % (headername, type(headervalue).__name__))
- else:
- translation_map = {u'%': u'%25', u'&': u'%26', u'?': u'%3F'}
- for char, replacement in translation_map.items():
- headervalue = headervalue.replace(char, replacement)
- kwargs[headername] = headervalue
-
- return kwargs
-
-
-def mailto_format(**kwargs):
- """
- Compile mailto string from call parameters
- """
- # @TODO: implement utf8 option
-
- kwargs = _fix_addresses(**kwargs)
- parts = []
- for headername in (u'to', u'cc', u'bcc', u'subject', u'body', u'attach'):
- if kwargs.has_key(headername):
- headervalue = kwargs[headername]
- if not headervalue:
- continue
- if headername in (u'address', u'to', u'cc', u'bcc'):
- parts.append(u'%s=%s' % (headername, headervalue))
- else:
- headervalue = encode_rfc2231(headervalue) # @TODO: check
- parts.append(u'%s=%s' % (headername, headervalue))
-
- mailto_string = u'mailto:%s' % kwargs.get(u'address', '')
- if parts:
- mailto_string = u'%s?%s' % (mailto_string, u'&'.join(parts))
-
- return mailto_string
-
-
-def mailto(address, to=None, cc=None, bcc=None, subject=None, body=None,
- attach=None):
- """
- Send an e-mail using the user's preferred composer.
-
- Open the user's preferred e-mail composer in order to send a mail to
- address(es) that must follow the syntax of RFC822. Multiple addresses
- may be provided (for address, cc and bcc parameters) as separate
- arguments.
-
- All parameters provided are used to prefill corresponding fields in
- the user's e-mail composer. The user will have the opportunity to
- change any of this information before actually sending the e-mail.
-
- ``address``
- specify the destination recipient
-
- ``cc``
- specify a recipient to be copied on the e-mail
-
- ``bcc``
- specify a recipient to be blindly copied on the e-mail
-
- ``subject``
- specify a subject for the e-mail
-
- ``body``
- specify a body for the e-mail. Since the user will be able to make
- changes before actually sending the e-mail, this can be used to provide
- the user with a template for the e-mail text may contain linebreaks
-
- ``attach``
- specify an attachment for the e-mail. file must point to an existing
- file
- """
-
- mailto_string = mailto_format(**locals())
- return open(mailto_string)
-
diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py
index 5366f3f68..1bddb2d93 100644
--- a/openlp/core/lib/mediamanageritem.py
+++ b/openlp/core/lib/mediamanageritem.py
@@ -425,44 +425,6 @@ class MediaManagerItem(QtGui.QWidget):
count += 1
return filelist
- def validate(self, image, thumb):
- """
- Validates whether an image still exists and, if it does, is the
- thumbnail representation of the image up to date.
- """
- if not os.path.exists(unicode(image)):
- return False
- if os.path.exists(thumb):
- imageDate = os.stat(unicode(image)).st_mtime
- thumbDate = os.stat(unicode(thumb)).st_mtime
- # If image has been updated rebuild icon
- if imageDate > thumbDate:
- self.iconFromFile(image, thumb)
- else:
- self.iconFromFile(image, thumb)
- return True
-
- def iconFromFile(self, image_path, thumb_path):
- """
- Create a thumbnail icon from a given image.
-
- ``image_path``
- The image file to create the icon from.
-
- ``thumb_path``
- The filename to save the thumbnail to.
- """
- ext = os.path.splitext(thumb_path)[1].lower()
- reader = QtGui.QImageReader(image_path)
- ratio = float(reader.size().width()) / float(reader.size().height())
- reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88))
- thumb = reader.read()
- thumb.save(thumb_path, ext[1:])
- if os.path.exists(thumb_path):
- return build_icon(unicode(thumb_path))
- # Fallback for files with animation support.
- return build_icon(unicode(image_path))
-
def loadList(self, list):
raise NotImplementedError(u'MediaManagerItem.loadList needs to be '
u'defined by the plugin')
diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py
index abfd658ba..02f6970ac 100644
--- a/openlp/core/lib/renderer.py
+++ b/openlp/core/lib/renderer.py
@@ -44,6 +44,7 @@ VERSE = u'The Lord said to {r}Noah{/r}: \n' \
'Get those children out of the muddy, muddy \n' \
'{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}' \
'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
+VERSE_FOR_LINE_COUNT = u'\n'.join(map(unicode, xrange(50)))
FOOTER = [u'Arky Arky (Unknown)', u'Public Domain', u'CCLI 123456']
class Renderer(object):
@@ -56,14 +57,14 @@ class Renderer(object):
def __init__(self, imageManager, themeManager):
"""
- Initialise the render manager.
+ Initialise the renderer.
``imageManager``
- A ImageManager instance which takes care of e. g. caching and resizing
+ A imageManager instance which takes care of e. g. caching and resizing
images.
``themeManager``
- The ThemeManager instance, used to get the current theme details.
+ The themeManager instance, used to get the current theme details.
"""
log.debug(u'Initialisation started')
self.themeManager = themeManager
@@ -80,7 +81,7 @@ class Renderer(object):
def update_display(self):
"""
- Updates the render manager's information about the current screen.
+ Updates the renderer's information about the current screen.
"""
log.debug(u'Update Display')
self._calculate_default()
@@ -190,7 +191,7 @@ class Renderer(object):
serviceItem.theme = theme_data
if self.force_page:
# make big page for theme edit dialog to get line count
- serviceItem.add_from_text(u'', VERSE + VERSE + VERSE)
+ serviceItem.add_from_text(u'', VERSE_FOR_LINE_COUNT)
else:
self.imageManager.del_image(theme_data.theme_name)
serviceItem.add_from_text(u'', VERSE)
@@ -200,7 +201,8 @@ class Renderer(object):
if not self.force_page:
self.display.buildHtml(serviceItem)
raw_html = serviceItem.get_rendered_frame(0)
- preview = self.display.text(raw_html)
+ self.display.text(raw_html)
+ preview = self.display.preview()
# Reset the real screen size for subsequent render requests
self._calculate_default()
return preview
@@ -224,14 +226,10 @@ class Renderer(object):
# Bibles
if item.is_capable(ItemCapabilities.CanWordSplit):
pages = self._paginate_slide_words(text.split(u'\n'), line_end)
- else:
- # Clean up line endings.
- lines = self._lines_split(text)
- pages = self._paginate_slide(lines, line_end)
- # Songs and Custom
- if item.is_capable(ItemCapabilities.CanSoftBreak) and \
- len(pages) > 1 and u'[---]' in text:
- pages = []
+ # Songs and Custom
+ elif item.is_capable(ItemCapabilities.CanSoftBreak):
+ pages = []
+ if u'[---]' in text:
while True:
slides = text.split(u'\n[---]\n', 2)
# If there are (at least) two occurrences of [---] we use
@@ -272,6 +270,11 @@ class Renderer(object):
lines = text.strip(u'\n').split(u'\n')
pages.extend(self._paginate_slide(lines, line_end))
break
+ else:
+ # Clean up line endings.
+ pages = self._paginate_slide(text.split(u'\n'), line_end)
+ else:
+ pages = self._paginate_slide(text.split(u'\n'), line_end)
new_pages = []
for page in pages:
while page.endswith(u'
'):
@@ -302,21 +305,37 @@ class Renderer(object):
The theme to build a text block for.
"""
log.debug(u'_build_text_rectangle')
- main_rect = None
- footer_rect = None
+ main_rect = self.get_main_rectangle(theme)
+ footer_rect = self.get_footer_rectangle(theme)
+ self._set_text_rectangle(main_rect, footer_rect)
+
+ def get_main_rectangle(self, theme):
+ """
+ Calculates the placement and size of the main rectangle.
+
+ ``theme``
+ The theme information
+ """
if not theme.font_main_override:
- main_rect = QtCore.QRect(10, 0, self.width - 20, self.footer_start)
+ return QtCore.QRect(10, 0, self.width - 20, self.footer_start)
else:
- main_rect = QtCore.QRect(theme.font_main_x, theme.font_main_y,
+ return QtCore.QRect(theme.font_main_x, theme.font_main_y,
theme.font_main_width - 1, theme.font_main_height - 1)
+
+ def get_footer_rectangle(self, theme):
+ """
+ Calculates the placement and size of the footer rectangle.
+
+ ``theme``
+ The theme information
+ """
if not theme.font_footer_override:
- footer_rect = QtCore.QRect(10, self.footer_start, self.width - 20,
+ return QtCore.QRect(10, self.footer_start, self.width - 20,
self.height - self.footer_start)
else:
- footer_rect = QtCore.QRect(theme.font_footer_x,
+ return 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):
"""
@@ -585,12 +604,3 @@ class Renderer(object):
# this parse we are to be wordy
line = line.replace(u'\n', u' ')
return line.split(u' ')
-
- def _lines_split(self, text):
- """
- Split the slide up by physical line
- """
- # this parse we do not want to use this so remove it
- text = text.replace(u'\n[---]', u'')
- text = text.replace(u'[---]', u'')
- return text.split(u'\n')
diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py
index 3170e0a93..0eb0c866f 100644
--- a/openlp/core/lib/serviceitem.py
+++ b/openlp/core/lib/serviceitem.py
@@ -122,9 +122,8 @@ class ServiceItem(object):
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
+ 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())
@@ -160,9 +159,8 @@ class ServiceItem(object):
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.
- At this point all the slides are built for the given
- display size.
+ obtains the display information from the renderer. At this point all
+ slides are built for the given display size.
"""
log.debug(u'Render called')
self._display_frames = []
@@ -364,6 +362,11 @@ class ServiceItem(object):
"""
self._uuid = other._uuid
self.notes = other.notes
+ # Copy theme over if present.
+ if other.theme is not None:
+ self.theme = other.theme
+ self._new_item()
+ self.render()
if self.is_capable(ItemCapabilities.HasBackgroundAudio):
log.debug(self.background_audio)
diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py
index 3b0a62f5b..34a3b9d98 100644
--- a/openlp/core/lib/theme.py
+++ b/openlp/core/lib/theme.py
@@ -176,8 +176,9 @@ class HorizontalType(object):
Left = 0
Right = 1
Center = 2
+ Justify = 3
- Names = [u'left', u'right', u'center']
+ Names = [u'left', u'right', u'center', u'justify']
class VerticalType(object):
diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py
index e754480e0..7d2dfa0ba 100644
--- a/openlp/core/ui/__init__.py
+++ b/openlp/core/ui/__init__.py
@@ -54,6 +54,7 @@ class HideMode(object):
from firsttimeform import FirstTimeForm
from firsttimelanguageform import FirstTimeLanguageForm
+from themelayoutform import ThemeLayoutForm
from themeform import ThemeForm
from filerenameform import FileRenameForm
from starttimeform import StartTimeForm
diff --git a/openlp/core/ui/exceptionform.py b/openlp/core/ui/exceptionform.py
index 7a42d99cc..c5fc678e2 100644
--- a/openlp/core/ui/exceptionform.py
+++ b/openlp/core/ui/exceptionform.py
@@ -56,7 +56,6 @@ except ImportError:
SQLITE_VERSION = u'-'
from openlp.core.lib import translate, SettingsManager
-from openlp.core.lib.mailto import mailto
from openlp.core.lib.ui import UiStrings
from openlp.core.utils import get_application_version
@@ -159,12 +158,12 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog):
if u':' in line:
exception = line.split(u'\n')[-1].split(u':')[0]
subject = u'Bug report: %s in %s' % (exception, source)
+ mailto_url = QtCore.QUrl(u'mailto:bugs@openlp.org')
+ mailto_url.addQueryItem(u'subject', subject)
+ mailto_url.addQueryItem(u'body', body % content)
if self.fileAttachment:
- mailto(address=u'bugs@openlp.org', subject=subject,
- body=body % content, attach=self.fileAttachment)
- else:
- mailto(address=u'bugs@openlp.org', subject=subject,
- body=body % content)
+ mailto_url.addQueryItem(u'attach', self.fileAttachment)
+ QtGui.QDesktopServices.openUrl(mailto_url)
def onDescriptionUpdated(self):
count = int(20 - len(self.descriptionTextEdit.toPlainText()))
diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py
index 06afb0313..36f911df5 100644
--- a/openlp/core/ui/maindisplay.py
+++ b/openlp/core/ui/maindisplay.py
@@ -194,7 +194,6 @@ class MainDisplay(QtGui.QGraphicsView):
self.setGeometry(self.screen[u'size'])
self.frame.evaluateJavaScript(u'show_text("%s")' %
slide.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
- return self.preview()
def alert(self, text):
"""
@@ -256,7 +255,6 @@ class MainDisplay(QtGui.QGraphicsView):
image = self.imageManager.get_image_bytes(name)
self.resetVideo()
self.displayImage(image)
- return self.preview()
def displayImage(self, image):
"""
@@ -357,7 +355,7 @@ class MainDisplay(QtGui.QGraphicsView):
"""
# We request a background video but have no service Item
if isBackground and not hasattr(self, u'serviceItem'):
- return None
+ return False
if not self.mediaObject:
self.createMediaObject()
log.debug(u'video')
@@ -387,7 +385,7 @@ class MainDisplay(QtGui.QGraphicsView):
# Update the preview frame.
if self.isLive:
Receiver.send_message(u'maindisplay_active')
- return self.preview()
+ return True
def videoState(self, newState, oldState):
"""
@@ -455,9 +453,8 @@ class MainDisplay(QtGui.QGraphicsView):
self.setVisible(True)
else:
self.setVisible(True)
- preview = QtGui.QImage(self.screen[u'size'].width(),
- self.screen[u'size'].height(),
- QtGui.QImage.Format_ARGB32_Premultiplied)
+ preview = QtGui.QPixmap(self.screen[u'size'].width(),
+ self.screen[u'size'].height())
painter = QtGui.QPainter(preview)
painter.setRenderHint(QtGui.QPainter.Antialiasing)
self.frame.render(painter)
diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py
index c6ffaaccc..8ad2db9c2 100644
--- a/openlp/core/ui/servicemanager.py
+++ b/openlp/core/ui/servicemanager.py
@@ -30,6 +30,7 @@ import logging
import os
import shutil
import zipfile
+from tempfile import mkstemp
log = logging.getLogger(__name__)
@@ -467,15 +468,24 @@ class ServiceManager(QtGui.QWidget):
def saveFile(self):
"""
- Save the current Service file.
+ Save the current service file.
+
+ A temporary file is created so that we don't overwrite the existing one
+ and leave a mangled service file should there be an error when saving.
+ Audio files are also copied into the service manager directory, and
+ then packaged into the zip file.
"""
if not self.fileName():
return self.saveFileAs()
+ temp_file, temp_file_name = mkstemp(u'.osz', u'openlp_')
+ # We don't need the file handle.
+ os.close(temp_file)
+ log.debug(temp_file_name)
path_file_name = unicode(self.fileName())
path, file_name = os.path.split(path_file_name)
basename, extension = os.path.splitext(file_name)
service_file_name = '%s.osd' % basename
- log.debug(u'ServiceManager.saveFile - %s' % path_file_name)
+ log.debug(u'ServiceManager.saveFile - %s', path_file_name)
SettingsManager.set_last_dir(
self.mainwindow.servicemanagerSettingsSection,
path)
@@ -494,7 +504,8 @@ class ServiceManager(QtGui.QWidget):
if len(service_item[u'header'][u'background_audio']) > 0:
for i, filename in \
enumerate(service_item[u'header'][u'background_audio']):
- new_file = os.path.join(u'audio', item[u'service_item']._uuid,
+ new_file = os.path.join(u'audio',
+ item[u'service_item']._uuid,
os.path.split(filename)[1])
audio_files.append((filename, new_file))
service_item[u'header'][u'background_audio'][i] = new_file
@@ -545,30 +556,38 @@ class ServiceManager(QtGui.QWidget):
success = True
self.mainwindow.incrementProgressBar()
try:
- zip = zipfile.ZipFile(path_file_name, 'w', zipfile.ZIP_STORED,
+ zip = zipfile.ZipFile(temp_file_name, 'w', zipfile.ZIP_STORED,
allow_zip_64)
# First we add service contents.
# We save ALL filenames into ZIP using UTF-8.
zip.writestr(service_file_name.encode(u'utf-8'), service_content)
# Finally add all the listed media files.
- for path_from in write_list:
- zip.write(path_from, path_from.encode(u'utf-8'))
- for path_from, path_to in audio_files:
- if path_from == path_to:
- # If this file has already been saved, let's use set the
- # from path to the real location of the files
- path_from = os.path.join(self.servicePath, path_from)
- else:
- # If this file has not yet been saved, let's copy the file
- # to the service manager path
- save_file = os.path.join(self.servicePath, path_to)
- save_path = os.path.split(save_file)[0]
- if not os.path.exists(save_path):
- os.makedirs(save_path)
- shutil.copy(path_from, save_file)
- zip.write(path_from, path_to.encode(u'utf-8'))
+ for write_from in write_list:
+ zip.write(write_from, write_from.encode(u'utf-8'))
+ for audio_from, audio_to in audio_files:
+ if audio_from.startswith(u'audio'):
+ # When items are saved, they get new UUID's. Let's copy the
+ # file to the new location. Unused files can be ignored,
+ # OpenLP automatically cleans up the service manager dir on
+ # exit.
+ audio_from = os.path.join(self.servicePath, audio_from)
+ save_file = os.path.join(self.servicePath, audio_to)
+ save_path = os.path.split(save_file)[0]
+ if not os.path.exists(save_path):
+ os.makedirs(save_path)
+ if not os.path.exists(save_file):
+ shutil.copy(audio_from, save_file)
+ zip.write(audio_from, audio_to.encode(u'utf-8'))
except IOError:
- log.exception(u'Failed to save service to disk')
+ log.exception(u'Failed to save service to disk: %s', temp_file_name)
+ # Add this line in after the release to notify the user that saving
+ # their file failed. Commented out due to string freeze.
+ #Receiver.send_message(u'openlp_error_message', {
+ # u'title': translate(u'OpenLP.ServiceManager',
+ # u'Error Saving File'),
+ # u'message': translate(u'OpenLP.ServiceManager',
+ # u'There was an error saving your file.')
+ #})
success = False
finally:
if zip:
@@ -576,10 +595,13 @@ class ServiceManager(QtGui.QWidget):
self.mainwindow.finishedProgressBar()
Receiver.send_message(u'cursor_normal')
if success:
+ shutil.copy(temp_file_name, path_file_name)
self.mainwindow.addRecentFile(path_file_name)
self.setModified(False)
- else:
- delete_file(path_file_name)
+ try:
+ delete_file(temp_file_name)
+ except:
+ pass
return success
def saveFileAs(self):
@@ -623,6 +645,7 @@ class ServiceManager(QtGui.QWidget):
osfile = unicode(QtCore.QDir.toNativeSeparators(ucsfile))
if not osfile.startswith(u'audio'):
osfile = os.path.split(osfile)[1]
+ log.debug(u'Extract file: %s', osfile)
zipinfo.filename = osfile
zip.extract(zipinfo, self.servicePath)
if osfile.endswith(u'osd'):
@@ -637,14 +660,17 @@ class ServiceManager(QtGui.QWidget):
for item in items:
self.mainwindow.incrementProgressBar()
serviceItem = ServiceItem()
- serviceItem.from_service = True
serviceItem.renderer = self.mainwindow.renderer
serviceItem.set_from_service(item, self.servicePath)
self.validateItem(serviceItem)
- self.addServiceItem(serviceItem, repaint=False)
+ self.loadItem_uuid = 0
if serviceItem.is_capable(ItemCapabilities.OnLoadUpdate):
Receiver.send_message(u'%s_service_load' %
serviceItem.name.lower(), serviceItem)
+ # if the item has been processed
+ if serviceItem._uuid == self.loadItem_uuid:
+ serviceItem.edit_id = int(self.loadItem_editId)
+ self.addServiceItem(serviceItem, repaint=False)
delete_file(p_file)
self.setFileName(fileName)
self.mainwindow.addRecentFile(fileName)
@@ -1022,11 +1048,12 @@ class ServiceManager(QtGui.QWidget):
"""
Empties the servicePath of temporary files.
"""
+ log.debug(u'Cleaning up servicePath')
for file in os.listdir(self.servicePath):
file_path = os.path.join(self.servicePath, file)
delete_file(file_path)
if os.path.exists(os.path.join(self.servicePath, u'audio')):
- shutil.rmtree(os.path.join(self.servicePath, u'audio'), False)
+ shutil.rmtree(os.path.join(self.servicePath, u'audio'), True)
def onThemeComboBoxSelected(self, currentIndex):
"""
@@ -1098,12 +1125,10 @@ class ServiceManager(QtGui.QWidget):
def serviceItemUpdate(self, message):
"""
Triggered from plugins to update service items.
+ Save the values as they will be used as part of the service load
"""
- editId, uuid = message.split(u':')
- for item in self.serviceItems:
- if item[u'service_item']._uuid == uuid:
- item[u'service_item'].edit_id = int(editId)
- self.setModified()
+ editId, self.loadItem_uuid = message.split(u':')
+ self.loadItem_editId = int(editId)
def replaceServiceItem(self, newItem):
"""
@@ -1135,7 +1160,6 @@ class ServiceManager(QtGui.QWidget):
# if not passed set to config value
if expand is None:
expand = self.expandTabs
- item.render()
item.from_service = True
if replace:
sitem, child = self.findServiceItem()
@@ -1144,6 +1168,7 @@ class ServiceManager(QtGui.QWidget):
self.repaintServiceList(sitem, child)
self.mainwindow.liveController.replaceServiceManagerItem(item)
else:
+ item.render()
# nothing selected for dnd
if self.dropPosition == 0:
if isinstance(item, list):
diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py
index 6615d0e35..354cfa168 100644
--- a/openlp/core/ui/slidecontroller.py
+++ b/openlp/core/ui/slidecontroller.py
@@ -118,7 +118,7 @@ class SlideController(QtGui.QWidget):
self.previewListWidget.horizontalHeader().setVisible(False)
self.previewListWidget.setColumnWidth(0, self.controller.width())
self.previewListWidget.isLive = self.isLive
- self.previewListWidget.setObjectName(u'PreviewListWidget')
+ self.previewListWidget.setObjectName(u'previewListWidget')
self.previewListWidget.setSelectionBehavior(
QtGui.QAbstractItemView.SelectRows)
self.previewListWidget.setSelectionMode(
@@ -288,14 +288,14 @@ class SlideController(QtGui.QWidget):
QtGui.QSizePolicy.Label))
self.previewFrame.setFrameShape(QtGui.QFrame.StyledPanel)
self.previewFrame.setFrameShadow(QtGui.QFrame.Sunken)
- self.previewFrame.setObjectName(u'PreviewFrame')
+ self.previewFrame.setObjectName(u'previewFrame')
self.grid = QtGui.QGridLayout(self.previewFrame)
self.grid.setMargin(8)
self.grid.setObjectName(u'grid')
self.slideLayout = QtGui.QVBoxLayout()
self.slideLayout.setSpacing(0)
self.slideLayout.setMargin(0)
- self.slideLayout.setObjectName(u'SlideLayout')
+ self.slideLayout.setObjectName(u'slideLayout')
if not self.isLive:
self.mediaObject = Phonon.MediaObject(self)
self.video = Phonon.VideoWidget()
@@ -319,7 +319,7 @@ class SlideController(QtGui.QWidget):
self.slidePreview.setFrameShadow(QtGui.QFrame.Plain)
self.slidePreview.setLineWidth(1)
self.slidePreview.setScaledContents(True)
- self.slidePreview.setObjectName(u'SlidePreview')
+ self.slidePreview.setObjectName(u'slidePreview')
self.slideLayout.insertWidget(0, self.slidePreview)
self.grid.addLayout(self.slideLayout, 0, 0, 1, 1)
# Signals
@@ -328,8 +328,6 @@ class SlideController(QtGui.QWidget):
if self.isLive:
QtCore.QObject.connect(self.volumeSlider,
QtCore.SIGNAL(u'sliderReleased()'), self.mediaVolume)
- QtCore.QObject.connect(Receiver.get_receiver(),
- QtCore.SIGNAL(u'maindisplay_active'), self.updatePreview)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'slidecontroller_live_spin_delay'),
self.receiveSpinDelay)
@@ -351,18 +349,12 @@ class SlideController(QtGui.QWidget):
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'slidecontroller_%s_stop_loop' % self.typePrefix),
self.onStopLoop)
- QtCore.QObject.connect(Receiver.get_receiver(),
- QtCore.SIGNAL(u'slidecontroller_%s_first' % self.typePrefix),
- self.onSlideSelectedFirst)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'slidecontroller_%s_next' % self.typePrefix),
self.onSlideSelectedNext)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'slidecontroller_%s_previous' % self.typePrefix),
self.onSlideSelectedPrevious)
- QtCore.QObject.connect(Receiver.get_receiver(),
- QtCore.SIGNAL(u'slidecontroller_%s_last' % self.typePrefix),
- self.onSlideSelectedLast)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'slidecontroller_%s_change' % self.typePrefix),
self.onSlideChange)
@@ -375,9 +367,6 @@ class SlideController(QtGui.QWidget):
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'slidecontroller_%s_unblank' % self.typePrefix),
self.onSlideUnblank)
- QtCore.QObject.connect(Receiver.get_receiver(),
- QtCore.SIGNAL(u'slidecontroller_%s_text_request' % self.typePrefix),
- self.onTextRequest)
def setPreviewHotkeys(self, parent=None):
self.previousItem.setObjectName(u'previousItemPreview')
@@ -669,14 +658,14 @@ class SlideController(QtGui.QWidget):
label.setMargin(4)
label.setScaledContents(True)
if self.serviceItem.is_command():
- image = QtGui.QImage(frame[u'image'])
+ label.setPixmap(QtGui.QPixmap(frame[u'image']))
else:
# If current slide set background to image
if framenumber == slideno:
self.serviceItem.bg_image_bytes = \
self.imageManager.get_image_bytes(frame[u'title'])
image = self.imageManager.get_image(frame[u'title'])
- label.setPixmap(QtGui.QPixmap.fromImage(image))
+ label.setPixmap(QtGui.QPixmap.fromImage(image))
self.previewListWidget.setCellWidget(framenumber, 0, label)
slideHeight = width * self.parent().renderer.screen_ratio
row += 1
@@ -723,41 +712,7 @@ class SlideController(QtGui.QWidget):
else:
self.__checkUpdateSelectedSlide(slideno)
- def onTextRequest(self):
- """
- Return the text for the current item in controller
- """
- data = []
- if self.serviceItem:
- for framenumber, frame in enumerate(self.serviceItem.get_frames()):
- dataItem = {}
- if self.serviceItem.is_text():
- dataItem[u'tag'] = unicode(frame[u'verseTag'])
- dataItem[u'text'] = unicode(frame[u'html'])
- else:
- dataItem[u'tag'] = unicode(framenumber)
- dataItem[u'text'] = u''
- dataItem[u'selected'] = \
- (self.previewListWidget.currentRow() == framenumber)
- data.append(dataItem)
- Receiver.send_message(u'slidecontroller_%s_text_response'
- % self.typePrefix, data)
-
# Screen event methods
- def onSlideSelectedFirst(self):
- """
- Go to the first slide.
- """
- if not self.serviceItem:
- return
- if self.serviceItem.is_command():
- Receiver.send_message(u'%s_first' % self.serviceItem.name.lower(),
- [self.serviceItem, self.isLive])
- self.updatePreview()
- else:
- self.previewListWidget.selectRow(0)
- self.slideSelected()
-
def onSlideSelectedIndex(self, message):
"""
Go to the requested slide
@@ -936,20 +891,18 @@ class SlideController(QtGui.QWidget):
Receiver.send_message(
u'%s_slide' % self.serviceItem.name.lower(),
[self.serviceItem, self.isLive, row])
- self.updatePreview()
else:
toDisplay = self.serviceItem.get_rendered_frame(row)
if self.serviceItem.is_text():
- frame = self.display.text(toDisplay)
+ self.display.text(toDisplay)
else:
if start:
self.display.buildHtml(self.serviceItem, toDisplay)
- frame = self.display.preview()
else:
- frame = self.display.image(toDisplay)
+ self.display.image(toDisplay)
# reset the store used to display first image
self.serviceItem.bg_image_bytes = None
- self.slidePreview.setPixmap(QtGui.QPixmap.fromImage(frame))
+ self.updatePreview()
self.selectedRow = row
self.__checkUpdateSelectedSlide(row)
Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix,
@@ -977,8 +930,7 @@ class SlideController(QtGui.QWidget):
QtCore.QTimer.singleShot(0.5, self.grabMainDisplay)
QtCore.QTimer.singleShot(2.5, self.grabMainDisplay)
else:
- self.slidePreview.setPixmap(
- QtGui.QPixmap.fromImage(self.display.preview()))
+ self.slidePreview.setPixmap(self.display.preview())
def grabMainDisplay(self):
"""
@@ -1041,21 +993,6 @@ class SlideController(QtGui.QWidget):
self.previewListWidget.item(row + 1, 0))
self.previewListWidget.selectRow(row)
- def onSlideSelectedLast(self):
- """
- Go to the last slide.
- """
- if not self.serviceItem:
- return
- Receiver.send_message(u'%s_last' % self.serviceItem.name.lower(),
- [self.serviceItem, self.isLive])
- if self.serviceItem.is_command():
- self.updatePreview()
- else:
- self.previewListWidget.selectRow(
- self.previewListWidget.rowCount() - 1)
- self.slideSelected()
-
def onToggleLoop(self):
"""
Toggles the loop state.
diff --git a/openlp/core/ui/splashscreen.py b/openlp/core/ui/splashscreen.py
index 8b2ba5d95..036daf968 100644
--- a/openlp/core/ui/splashscreen.py
+++ b/openlp/core/ui/splashscreen.py
@@ -36,8 +36,7 @@ class SplashScreen(QtGui.QSplashScreen):
QtCore.SIGNAL(u'close_splash'), self.close)
def setupUi(self):
- self.setObjectName(u'splash_screen')
- self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
+ self.setObjectName(u'splashScreen')
self.setContextMenuPolicy(QtCore.Qt.PreventContextMenu)
splash_image = QtGui.QPixmap(u':/graphics/openlp-splash-screen.png')
self.setPixmap(splash_image)
diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py
index dc3c23d0d..9ccd91d08 100644
--- a/openlp/core/ui/themeform.py
+++ b/openlp/core/ui/themeform.py
@@ -33,6 +33,7 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import Receiver, translate
from openlp.core.lib.theme import BackgroundType, BackgroundGradientType
from openlp.core.lib.ui import UiStrings, critical_error_message_box
+from openlp.core.ui import ThemeLayoutForm
from openlp.core.utils import get_images_filter
from themewizard import Ui_ThemeWizard
@@ -58,6 +59,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
self.registerFields()
self.updateThemeAllowed = True
self.temp_background_filename = u''
+ self.themeLayoutForm = ThemeLayoutForm(self)
QtCore.QObject.connect(self.backgroundComboBox,
QtCore.SIGNAL(u'currentIndexChanged(int)'),
self.onBackgroundComboBoxCurrentIndexChanged)
@@ -88,6 +90,9 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
self.onShadowCheckCheckBoxStateChanged)
QtCore.QObject.connect(self.footerColorButton,
QtCore.SIGNAL(u'clicked()'), self.onFooterColorButtonClicked)
+ QtCore.QObject.connect(self,
+ QtCore.SIGNAL(u'customButtonClicked(int)'),
+ self.onCustom1ButtonClicked)
QtCore.QObject.connect(self.mainPositionCheckBox,
QtCore.SIGNAL(u'stateChanged(int)'),
self.onMainPositionCheckBoxStateChanged)
@@ -229,13 +234,36 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
"""
Detects Page changes and updates as approprate.
"""
+ if self.page(pageId) == self.areaPositionPage:
+ self.setOption(QtGui.QWizard.HaveCustomButton1, True)
+ else:
+ self.setOption(QtGui.QWizard.HaveCustomButton1, False)
if self.page(pageId) == self.previewPage:
self.updateTheme()
frame = self.thememanager.generateImage(self.theme)
- self.previewBoxLabel.setPixmap(QtGui.QPixmap.fromImage(frame))
+ self.previewBoxLabel.setPixmap(frame)
self.displayAspectRatio = float(frame.width()) / frame.height()
self.resizeEvent()
+ def onCustom1ButtonClicked(self, number):
+ """
+ Generate layout preview and display the form.
+ """
+ self.updateTheme()
+ width = self.thememanager.mainwindow.renderer.width
+ height = self.thememanager.mainwindow.renderer.height
+ pixmap = QtGui.QPixmap(width, height)
+ pixmap.fill(QtCore.Qt.white)
+ paint = QtGui.QPainter(pixmap)
+ paint.setPen(QtGui.QPen(QtCore.Qt.blue, 2))
+ paint.drawRect(self.thememanager.mainwindow.renderer.
+ get_main_rectangle(self.theme))
+ paint.setPen(QtGui.QPen(QtCore.Qt.red, 2))
+ paint.drawRect(self.thememanager.mainwindow.renderer.
+ get_footer_rectangle(self.theme))
+ paint.end()
+ self.themeLayoutForm.exec_(pixmap)
+
def onOutlineCheckCheckBoxStateChanged(self, state):
"""
Change state as Outline check box changed
diff --git a/openlp/core/ui/themelayoutdialog.py b/openlp/core/ui/themelayoutdialog.py
new file mode 100644
index 000000000..5be08ad65
--- /dev/null
+++ b/openlp/core/ui/themelayoutdialog.py
@@ -0,0 +1,75 @@
+# -*- 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, Gerald Britton, Jonathan #
+# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
+# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
+# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
+# 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 #
+###############################################################################
+
+from PyQt4 import QtCore, QtGui
+
+from openlp.core.lib import translate
+from openlp.core.lib.ui import create_accept_reject_button_box
+
+
+class Ui_ThemeLayoutDialog(object):
+ def setupUi(self, themeLayoutDialog):
+ themeLayoutDialog.setObjectName(u'themeLayoutDialogDialog')
+ #themeLayoutDialog.resize(300, 200)
+ self.previewLayout = QtGui.QVBoxLayout(themeLayoutDialog)
+ self.previewLayout.setObjectName(u'PreviewLayout')
+ self.previewArea = QtGui.QWidget(themeLayoutDialog)
+ self.previewArea.setObjectName(u'PreviewArea')
+ self.previewAreaLayout = QtGui.QGridLayout(self.previewArea)
+ self.previewAreaLayout.setMargin(0)
+ self.previewAreaLayout.setColumnStretch(0, 1)
+ self.previewAreaLayout.setRowStretch(0, 1)
+ self.previewAreaLayout.setObjectName(u'PreviewAreaLayout')
+ self.themeDisplayLabel = QtGui.QLabel(self.previewArea)
+ self.themeDisplayLabel.setFrameShape(QtGui.QFrame.Box)
+ self.themeDisplayLabel.setScaledContents(True)
+ self.themeDisplayLabel.setObjectName(u'ThemeDisplayLabel')
+ self.previewAreaLayout.addWidget(self.themeDisplayLabel)
+ self.previewLayout.addWidget(self.previewArea)
+ self.mainColourLabel = QtGui.QLabel(self.previewArea)
+ self.mainColourLabel.setObjectName(u'MainColourLabel')
+ self.previewLayout.addWidget(self.mainColourLabel)
+ self.footerColourLabel = QtGui.QLabel(self.previewArea)
+ self.footerColourLabel.setObjectName(u'FooterColourLabel')
+ self.previewLayout.addWidget(self.footerColourLabel)
+ self.buttonBox = QtGui.QDialogButtonBox(themeLayoutDialog)
+ self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok)
+ self.buttonBox.setObjectName(u'ButtonBox')
+ QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'accepted()'),
+ themeLayoutDialog.accept)
+ self.previewLayout.addWidget(self.buttonBox)
+ self.retranslateUi(themeLayoutDialog)
+ QtCore.QMetaObject.connectSlotsByName(themeLayoutDialog)
+
+ def retranslateUi(self, themeLayoutDialog):
+ themeLayoutDialog.setWindowTitle(
+ translate('OpenLP.StartTimeForm', 'Theme Layout'))
+ self.mainColourLabel.setText(translate('OpenLP.StartTimeForm',
+ 'The blue box shows the main area.'))
+ self.footerColourLabel.setText(translate('OpenLP.StartTimeForm',
+ 'The red box shows the footer.'))
+
diff --git a/openlp/core/ui/themelayoutform.py b/openlp/core/ui/themelayoutform.py
new file mode 100644
index 000000000..6f77d31da
--- /dev/null
+++ b/openlp/core/ui/themelayoutform.py
@@ -0,0 +1,55 @@
+# -*- 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, Gerald Britton, Jonathan #
+# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
+# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
+# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
+# 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 #
+###############################################################################
+
+from PyQt4 import QtGui, QtCore
+
+from themelayoutdialog import Ui_ThemeLayoutDialog
+
+from openlp.core.lib import translate
+from openlp.core.lib.ui import UiStrings, critical_error_message_box
+
+class ThemeLayoutForm(QtGui.QDialog, Ui_ThemeLayoutDialog):
+ """
+ The exception dialog
+ """
+ def __init__(self, parent):
+ QtGui.QDialog.__init__(self, parent)
+ self.setupUi(self)
+
+ def exec_(self, image):
+ """
+ Run the Dialog with correct heading.
+ """
+ pixmap = image.scaledToHeight(400, QtCore.Qt.SmoothTransformation)
+ self.themeDisplayLabel.setPixmap(image)
+ displayAspectRatio = float(image.width()) / image.height()
+ self.themeDisplayLabel.setFixedSize(400, 400 / displayAspectRatio )
+ return QtGui.QDialog.exec_(self)
+
+ def accept(self):
+ return QtGui.QDialog.accept(self)
+
diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py
index fdd0d74f3..b472ccef0 100644
--- a/openlp/core/ui/thememanager.py
+++ b/openlp/core/ui/thememanager.py
@@ -36,7 +36,7 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import OpenLPToolbar, get_text_file_string, build_icon, \
Receiver, SettingsManager, translate, check_item_selected, \
- check_directory_exists
+ check_directory_exists, create_thumb, validate_thumb
from openlp.core.lib.theme import ThemeXML, BackgroundType, VerticalType, \
BackgroundGradientType
from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
@@ -359,7 +359,7 @@ class ThemeManager(QtGui.QWidget):
The theme to delete.
"""
self.themelist.remove(theme)
- thumb = theme + u'.png'
+ thumb = u'%s.png' % theme
delete_file(os.path.join(self.path, thumb))
delete_file(os.path.join(self.thumbPath, thumb))
try:
@@ -473,15 +473,12 @@ class ThemeManager(QtGui.QWidget):
name = textName
thumb = os.path.join(self.thumbPath, u'%s.png' % textName)
item_name = QtGui.QListWidgetItem(name)
- if os.path.exists(thumb):
+ if validate_thumb(theme, thumb):
icon = build_icon(thumb)
else:
- icon = build_icon(theme)
- pixmap = icon.pixmap(QtCore.QSize(88, 50))
- pixmap.save(thumb, u'png')
+ icon = create_thumb(theme, thumb)
item_name.setIcon(icon)
- item_name.setData(QtCore.Qt.UserRole,
- QtCore.QVariant(textName))
+ item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(textName))
self.themeListWidget.addItem(item_name)
self.themelist.append(textName)
self._pushThemes()
@@ -658,9 +655,7 @@ class ThemeManager(QtGui.QWidget):
os.unlink(samplepathname)
frame.save(samplepathname, u'png')
thumb = os.path.join(self.thumbPath, u'%s.png' % name)
- icon = build_icon(frame)
- pixmap = icon.pixmap(QtCore.QSize(88, 50))
- pixmap.save(thumb, u'png')
+ create_thumb(samplepathname, thumb, False)
log.debug(u'Theme image written to %s', samplepathname)
def updatePreviewImages(self):
diff --git a/openlp/core/ui/themewizard.py b/openlp/core/ui/themewizard.py
index 6001c83d6..1135db274 100644
--- a/openlp/core/ui/themewizard.py
+++ b/openlp/core/ui/themewizard.py
@@ -38,7 +38,8 @@ class Ui_ThemeWizard(object):
themeWizard.setModal(True)
themeWizard.setWizardStyle(QtGui.QWizard.ModernStyle)
themeWizard.setOptions(QtGui.QWizard.IndependentPages |
- QtGui.QWizard.NoBackButtonOnStartPage)
+ QtGui.QWizard.NoBackButtonOnStartPage |
+ QtGui.QWizard.HaveCustomButton1)
self.spacer = QtGui.QSpacerItem(10, 0,
QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum)
# Welcome Page
@@ -246,7 +247,7 @@ class Ui_ThemeWizard(object):
self.horizontalLabel = QtGui.QLabel(self.alignmentPage)
self.horizontalLabel.setObjectName(u'HorizontalLabel')
self.horizontalComboBox = QtGui.QComboBox(self.alignmentPage)
- self.horizontalComboBox.addItems([u'', u'', u''])
+ self.horizontalComboBox.addItems([u'', u'', u'', u''])
self.horizontalComboBox.setObjectName(u'HorizontalComboBox')
self.alignmentLayout.addRow(self.horizontalLabel,
self.horizontalComboBox)
@@ -495,6 +496,8 @@ class Ui_ThemeWizard(object):
translate('OpenLP.ThemeWizard', 'Right'))
self.horizontalComboBox.setItemText(HorizontalType.Center,
translate('OpenLP.ThemeWizard', 'Center'))
+ self.horizontalComboBox.setItemText(HorizontalType.Justify,
+ translate('OpenLP.ThemeWizard', 'Justify'))
self.transitionsLabel.setText(
translate('OpenLP.ThemeWizard', 'Transitions:'))
self.areaPositionPage.setTitle(
@@ -533,6 +536,9 @@ class Ui_ThemeWizard(object):
translate('OpenLP.ThemeWizard', 'px'))
self.footerPositionCheckBox.setText(
translate('OpenLP.ThemeWizard', 'Use default location'))
+ themeWizard.setOption(QtGui.QWizard.HaveCustomButton1, False)
+ themeWizard.setButtonText(QtGui.QWizard.CustomButton1,
+ translate('OpenLP.ThemeWizard', 'Layout Preview'))
self.previewPage.setTitle(
translate('OpenLP.ThemeWizard', 'Save and Preview'))
self.previewPage.setSubTitle(
diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py
index b5478e514..7577e86a3 100644
--- a/openlp/plugins/bibles/forms/bibleimportform.py
+++ b/openlp/plugins/bibles/forms/bibleimportform.py
@@ -461,6 +461,11 @@ class BibleImportForm(OpenLPWizard):
WizardStrings.YouSpecifyFile % WizardStrings.OS)
self.openSongFileEdit.setFocus()
return False
+ elif self.field(u'source_format').toInt()[0] == \
+ BibleFormat.WebDownload:
+ self.versionNameEdit.setText(
+ self.webTranslationComboBox.currentText())
+ return True
elif self.field(u'source_format').toInt()[0] == BibleFormat.OpenLP1:
if not self.field(u'openlp1_location').toString():
critical_error_message_box(UiStrings().NFSs,
@@ -674,7 +679,7 @@ class BibleImportForm(OpenLPWizard):
elif bible_type == BibleFormat.CSV:
# Import a CSV bible.
importer = self.manager.import_bible(BibleFormat.CSV,
- name=license_version,
+ name=license_version,
booksfile=unicode(self.field(u'csv_booksfile').toString()),
versefile=unicode(self.field(u'csv_versefile').toString())
)
diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py
index e5962664b..9ec5b45b2 100644
--- a/openlp/plugins/bibles/lib/db.py
+++ b/openlp/plugins/bibles/lib/db.py
@@ -28,7 +28,6 @@
import logging
import chardet
import os
-import re
import sqlite3
from PyQt4 import QtCore
diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py
index 2d8e16c4c..228d4758c 100644
--- a/openlp/plugins/bibles/lib/http.py
+++ b/openlp/plugins/bibles/lib/http.py
@@ -82,13 +82,16 @@ class BGExtract(object):
Receiver.send_message(u'openlp_process_events')
footnotes = soup.findAll(u'sup', u'footnote')
if footnotes:
- [footnote.extract() for footnote in footnotes]
+ for footnote in footnotes:
+ footnote.extract()
crossrefs = soup.findAll(u'sup', u'xref')
if crossrefs:
- [crossref.extract() for crossref in crossrefs]
+ for crossref in crossrefs:
+ crossref.extract()
headings = soup.findAll(u'h5')
if headings:
- [heading.extract() for heading in headings]
+ for heading in headings:
+ heading.extract()
cleanup = [(re.compile('\s+'), lambda match: ' ')]
verses = BeautifulSoup(str(soup), markupMassage=cleanup)
verse_list = {}
diff --git a/openlp/plugins/images/lib/imagetab.py b/openlp/plugins/images/lib/imagetab.py
index 98fbd203f..1aa39b63c 100644
--- a/openlp/plugins/images/lib/imagetab.py
+++ b/openlp/plugins/images/lib/imagetab.py
@@ -28,7 +28,6 @@
from PyQt4 import QtCore, QtGui
from openlp.core.lib import SettingsTab, translate, Receiver
-from openlp.core.lib.ui import UiStrings, create_valign_combo
class ImageTab(SettingsTab):
"""
diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py
index cb73a132f..049e2da18 100644
--- a/openlp/plugins/images/lib/mediaitem.py
+++ b/openlp/plugins/images/lib/mediaitem.py
@@ -33,7 +33,7 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \
SettingsManager, translate, check_item_selected, check_directory_exists, \
- Receiver
+ Receiver, create_thumb, validate_thumb
from openlp.core.lib.ui import UiStrings, critical_error_message_box
from openlp.core.utils import AppLocation, delete_file, get_images_filter
@@ -127,13 +127,13 @@ class ImageMediaItem(MediaManagerItem):
self.plugin.formparent.incrementProgressBar()
filename = os.path.split(unicode(imageFile))[1]
thumb = os.path.join(self.servicePath, filename)
- if os.path.exists(thumb):
- if self.validate(imageFile, thumb):
+ if not os.path.exists(imageFile):
+ icon = build_icon(u':/general/general_delete.png')
+ else:
+ if validate_thumb(imageFile, thumb):
icon = build_icon(thumb)
else:
- icon = build_icon(u':/general/general_delete.png')
- else:
- icon = self.iconFromFile(imageFile, thumb)
+ icon = create_thumb(imageFile, thumb)
item_name = QtGui.QListWidgetItem(filename)
item_name.setIcon(icon)
item_name.setToolTip(imageFile)
diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py
index f2e0bbc06..c8f746851 100644
--- a/openlp/plugins/media/lib/mediaitem.py
+++ b/openlp/plugins/media/lib/mediaitem.py
@@ -39,7 +39,7 @@ from openlp.core.lib.ui import UiStrings, critical_error_message_box
log = logging.getLogger(__name__)
-CLAPPERBOARD = QtGui.QPixmap(u':/media/media_video.png').toImage()
+CLAPPERBOARD = QtGui.QImage(u':/media/media_video.png')
class MediaMediaItem(MediaManagerItem):
"""
@@ -95,14 +95,14 @@ class MediaMediaItem(MediaManagerItem):
def onResetClick(self):
"""
- Called to reset the Live backgound with the media selected,
+ Called to reset the Live backgound with the media selected.
"""
self.resetAction.setVisible(False)
self.plugin.liveController.display.resetVideo()
def videobackgroundReplaced(self):
"""
- Triggered by main display on change of serviceitem
+ Triggered by main display on change of serviceitem.
"""
self.resetAction.setVisible(False)
@@ -179,8 +179,7 @@ class MediaMediaItem(MediaManagerItem):
def mediaStateWait(self, mediaState):
"""
- Wait for the video to change its state
- Wait no longer than 5 seconds.
+ Wait for the video to change its state. Wait no longer than 5 seconds.
"""
start = datetime.now()
while self.mediaObject.state() != mediaState:
@@ -198,7 +197,7 @@ class MediaMediaItem(MediaManagerItem):
def onDeleteClick(self):
"""
- Remove a media item from the list
+ Remove a media item from the list.
"""
if check_item_selected(self.listView, translate('MediaPlugin.MediaItem',
'You must select a media file to delete.')):
diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py
index c6455a03a..e1dd57271 100644
--- a/openlp/plugins/presentations/lib/mediaitem.py
+++ b/openlp/plugins/presentations/lib/mediaitem.py
@@ -32,7 +32,8 @@ import locale
from PyQt4 import QtCore, QtGui
from openlp.core.lib import MediaManagerItem, build_icon, SettingsManager, \
- translate, check_item_selected, Receiver, ItemCapabilities
+ translate, check_item_selected, Receiver, ItemCapabilities, create_thumb, \
+ validate_thumb
from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
media_item_combo_box
from openlp.plugins.presentations.lib import MessageListener
@@ -193,10 +194,13 @@ class PresentationMediaItem(MediaManagerItem):
doc.load_presentation()
preview = doc.get_thumbnail_path(1, True)
doc.close_presentation()
- if preview and self.validate(preview, thumb):
- icon = build_icon(thumb)
- else:
+ if not (preview and os.path.exists(preview)):
icon = build_icon(u':/general/general_delete.png')
+ else:
+ if validate_thumb(preview, thumb):
+ icon = build_icon(thumb)
+ else:
+ icon = create_thumb(preview, thumb)
else:
if initialLoad:
icon = build_icon(u':/general/general_delete.png')
diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py
index 738974add..a9d384c81 100644
--- a/openlp/plugins/presentations/lib/presentationcontroller.py
+++ b/openlp/plugins/presentations/lib/presentationcontroller.py
@@ -31,7 +31,8 @@ import shutil
from PyQt4 import QtCore
-from openlp.core.lib import Receiver, resize_image
+from openlp.core.lib import Receiver, check_directory_exists, create_thumb, \
+ validate_thumb
from openlp.core.utils import AppLocation
log = logging.getLogger(__name__)
@@ -97,8 +98,7 @@ class PresentationDocument(object):
self.slidenumber = 0
self.controller = controller
self.filepath = name
- if not os.path.isdir(self.get_thumbnail_folder()):
- os.mkdir(self.get_thumbnail_folder())
+ check_directory_exists(self.get_thumbnail_folder())
def load_presentation(self):
"""
@@ -145,15 +145,13 @@ class PresentationDocument(object):
def check_thumbnails(self):
"""
- Returns true if the thumbnail images look to exist and are more
- recent than the powerpoint
+ Returns ``True`` if the thumbnail images exist and are more recent than
+ the powerpoint file.
"""
lastimage = self.get_thumbnail_path(self.get_slide_count(), True)
if not (lastimage and os.path.isfile(lastimage)):
return False
- imgdate = os.stat(lastimage).st_mtime
- pptdate = os.stat(self.filepath).st_mtime
- return imgdate >= pptdate
+ return validate_thumb(self.filepath, lastimage)
def close_presentation(self):
"""
@@ -246,8 +244,8 @@ class PresentationDocument(object):
if self.check_thumbnails():
return
if os.path.isfile(file):
- img = resize_image(file, 320, 240)
- img.save(self.get_thumbnail_path(idx, False))
+ thumb_path = self.get_thumbnail_path(idx, False)
+ create_thumb(file, thumb_path, False, QtCore.QSize(320, 240))
def get_thumbnail_path(self, slide_no, check_exists):
"""
@@ -387,10 +385,8 @@ class PresentationController(object):
AppLocation.get_section_data_path(self.settings_section),
u'thumbnails')
self.thumbnail_prefix = u'slide'
- if not os.path.isdir(self.thumbnail_folder):
- os.makedirs(self.thumbnail_folder)
- if not os.path.isdir(self.temp_folder):
- os.makedirs(self.temp_folder)
+ check_directory_exists(self.thumbnail_folder)
+ check_directory_exists(self.temp_folder)
def enabled(self):
"""
diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py
index a97f82159..643ad14ad 100644
--- a/openlp/plugins/presentations/presentationplugin.py
+++ b/openlp/plugins/presentations/presentationplugin.py
@@ -87,7 +87,7 @@ class PresentationPlugin(Plugin):
to close down their applications and release resources.
"""
log.info(u'Plugin Finalise')
- #Ask each controller to tidy up
+ # Ask each controller to tidy up.
for key in self.controllers:
controller = self.controllers[key]
if controller.enabled():
diff --git a/openlp/plugins/remotes/html/stage.js b/openlp/plugins/remotes/html/stage.js
index 09c82c49b..8db92b39a 100644
--- a/openlp/plugins/remotes/html/stage.js
+++ b/openlp/plugins/remotes/html/stage.js
@@ -121,11 +121,11 @@ window.OpenLP = {
$("#nextslide").html(text);
}
},
- updateClock: function() {
+ updateClock: function(data) {
var div = $("#clock");
var t = new Date();
var h = t.getHours();
- if (h > 12)
+ if (data.results.twelve && h > 12)
h = h - 12;
var m = t.getMinutes();
if (m < 10)
@@ -136,7 +136,7 @@ window.OpenLP = {
$.getJSON(
"/api/poll",
function (data, status) {
- OpenLP.updateClock();
+ OpenLP.updateClock(data);
if (OpenLP.currentItem != data.results.item) {
OpenLP.currentItem = data.results.item;
OpenLP.loadSlides();
diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py
index c81c83d92..522c354b8 100644
--- a/openlp/plugins/remotes/lib/httpserver.py
+++ b/openlp/plugins/remotes/lib/httpserver.py
@@ -315,7 +315,7 @@ class HttpConnection(object):
"""
log.debug(u'ready to read socket')
if self.socket.canReadLine():
- data = unicode(self.socket.readLine())
+ data = unicode(self.socket.readLine()).encode(u'utf-8')
log.debug(u'received: ' + data)
words = data.split(u' ')
response = None
@@ -397,7 +397,9 @@ class HttpConnection(object):
result = {
u'slide': self.parent.current_slide or 0,
u'item': self.parent.current_item._uuid \
- if self.parent.current_item else u''
+ if self.parent.current_item else u'',
+ u'twelve':QtCore.QSettings().value(
+ u'remotes/twelve hour', QtCore.QVariant(True)).toBool()
}
return HttpResponse(json.dumps({u'results': result}),
{u'Content-Type': u'application/json'})
diff --git a/openlp/plugins/remotes/lib/remotetab.py b/openlp/plugins/remotes/lib/remotetab.py
index 03781ae06..95bb27f1c 100644
--- a/openlp/plugins/remotes/lib/remotetab.py
+++ b/openlp/plugins/remotes/lib/remotetab.py
@@ -57,6 +57,9 @@ class RemoteTab(SettingsTab):
QtCore.QObject.connect(self.addressEdit,
QtCore.SIGNAL(u'textChanged(const QString&)'), self.setUrls)
self.serverSettingsLayout.addRow(self.addressLabel, self.addressEdit)
+ self.twelveHourCheckBox = QtGui.QCheckBox(self.serverSettingsGroupBox)
+ self.twelveHourCheckBox.setObjectName(u'twelveHourCheckBox')
+ self.serverSettingsLayout.addRow(self.twelveHourCheckBox)
self.portLabel = QtGui.QLabel(self.serverSettingsGroupBox)
self.portLabel.setObjectName(u'portLabel')
self.portSpinBox = QtGui.QSpinBox(self.serverSettingsGroupBox)
@@ -80,6 +83,9 @@ class RemoteTab(SettingsTab):
self.leftLayout.addWidget(self.serverSettingsGroupBox)
self.leftLayout.addStretch()
self.rightLayout.addStretch()
+ QtCore.QObject.connect(self.twelveHourCheckBox,
+ QtCore.SIGNAL(u'stateChanged(int)'),
+ self.onTwelveHourCheckBoxChanged)
def retranslateUi(self):
self.serverSettingsGroupBox.setTitle(
@@ -92,6 +98,9 @@ class RemoteTab(SettingsTab):
'Remote URL:'))
self.stageUrlLabel.setText(translate('RemotePlugin.RemoteTab',
'Stage view URL:'))
+ self.twelveHourCheckBox.setText(
+ translate('RemotePlugin.RemoteTab',
+ 'Display stage time in 12h format'))
def setUrls(self):
ipAddress = u'localhost'
@@ -123,6 +132,10 @@ class RemoteTab(SettingsTab):
self.addressEdit.setText(
QtCore.QSettings().value(self.settingsSection + u'/ip address',
QtCore.QVariant(ZERO_URL)).toString())
+ self.twelveHour = QtCore.QSettings().value(
+ self.settingsSection + u'/twelve hour',
+ QtCore.QVariant(True)).toBool()
+ self.twelveHourCheckBox.setChecked(self.twelveHour)
self.setUrls()
def save(self):
@@ -130,3 +143,11 @@ class RemoteTab(SettingsTab):
QtCore.QVariant(self.portSpinBox.value()))
QtCore.QSettings().setValue(self.settingsSection + u'/ip address',
QtCore.QVariant(self.addressEdit.text()))
+ QtCore.QSettings().setValue(self.settingsSection + u'/twelve hour',
+ QtCore.QVariant(self.twelveHour))
+
+ def onTwelveHourCheckBoxChanged(self, check_state):
+ self.twelveHour = False
+ # we have a set value convert to True/False
+ if check_state == QtCore.Qt.Checked:
+ self.twelveHour = True
diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py
index 6f09c9b8c..b79e56f7b 100644
--- a/openlp/plugins/songs/forms/songimportform.py
+++ b/openlp/plugins/songs/forms/songimportform.py
@@ -240,8 +240,9 @@ class SongImportForm(OpenLPWizard):
self.formatLabel.setText(WizardStrings.FormatLabel)
self.formatComboBox.setItemText(SongFormat.OpenLP2, UiStrings().OLPV2)
self.formatComboBox.setItemText(SongFormat.OpenLP1, UiStrings().OLPV1)
- self.formatComboBox.setItemText(
- SongFormat.OpenLyrics, WizardStrings.OL)
+ self.formatComboBox.setItemText(SongFormat.OpenLyrics,
+ translate('SongsPlugin.ImportWizardForm',
+ 'OpenLyrics or OpenLP 2.0 Exported Song'))
self.formatComboBox.setItemText(SongFormat.OpenSong, WizardStrings.OS)
self.formatComboBox.setItemText(
SongFormat.WordsOfWorship, WizardStrings.WoW)
@@ -508,7 +509,8 @@ class SongImportForm(OpenLPWizard):
Get OpenLyrics song database files
"""
self.getFiles(WizardStrings.OpenTypeFile % WizardStrings.OL,
- self.openLyricsFileListWidget)
+ self.openLyricsFileListWidget, u'%s (*.xml)' %
+ translate('SongsPlugin.ImportWizardForm', 'OpenLyrics Files'))
def onOpenLyricsRemoveButtonClicked(self):
"""
diff --git a/openlp/plugins/songs/lib/cclifileimport.py b/openlp/plugins/songs/lib/cclifileimport.py
index e15f898ab..624256290 100644
--- a/openlp/plugins/songs/lib/cclifileimport.py
+++ b/openlp/plugins/songs/lib/cclifileimport.py
@@ -333,5 +333,6 @@ class CCLIFileImport(SongImport):
if len(author_list) < 2:
author_list = song_author.split(u'|')
# Clean spaces before and after author names.
- [self.addAuthor(author_name.strip()) for author_name in author_list]
+ for author_name in author_list:
+ self.addAuthor(author_name.strip())
return self.finish()
diff --git a/openlp/plugins/songs/lib/easislidesimport.py b/openlp/plugins/songs/lib/easislidesimport.py
index 6d3bde025..a3dd553f1 100644
--- a/openlp/plugins/songs/lib/easislidesimport.py
+++ b/openlp/plugins/songs/lib/easislidesimport.py
@@ -258,7 +258,7 @@ class EasiSlidesImport(SongImport):
verses[reg][vt][vn] = {}
if not verses[reg][vt][vn].has_key(inst):
verses[reg][vt][vn][inst] = []
- words = self.tidy_text(line)
+ words = self.tidyText(line)
verses[reg][vt][vn][inst].append(words)
# done parsing
diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py
index 950eff870..d98a55c00 100644
--- a/openlp/plugins/songs/lib/mediaitem.py
+++ b/openlp/plugins/songs/lib/mediaitem.py
@@ -397,7 +397,8 @@ class SongMediaItem(MediaManagerItem):
try:
os.remove(media_file.file_name)
except:
- log.exception('Could not remove file: %s', audio)
+ log.exception('Could not remove file: %s',
+ media_file.file_name)
try:
save_path = os.path.join(AppLocation.get_section_data_path(
self.plugin.name), 'audio', str(item_id))
@@ -428,11 +429,9 @@ class SongMediaItem(MediaManagerItem):
def generateSlideData(self, service_item, item=None, xmlVersion=False,
remote=False):
- log.debug(u'generateSlideData: %s, %s, %s' % (service_item, item, self.remoteSong))
- # The ``None`` below is a workaround for bug #812289 - I think that Qt
- # deletes the item somewhere along the line because the user is taking
- # so long to update their item (or something weird like that).
- item_id = self._getIdOfItemToGenerate(None, self.remoteSong)
+ log.debug(u'generateSlideData: %s, %s, %s' %
+ (service_item, item, self.remoteSong))
+ item_id = self._getIdOfItemToGenerate(item, self.remoteSong)
service_item.add_capability(ItemCapabilities.CanEdit)
service_item.add_capability(ItemCapabilities.CanPreview)
service_item.add_capability(ItemCapabilities.CanLoop)
@@ -512,7 +511,8 @@ class SongMediaItem(MediaManagerItem):
# Add the audio file to the service item.
if len(song.media_files) > 0:
service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
- service_item.background_audio = [m.file_name for m in song.media_files]
+ service_item.background_audio = \
+ [m.file_name for m in song.media_files]
return True
def serviceLoad(self, item):
diff --git a/openlp/plugins/songs/lib/openlyricsexport.py b/openlp/plugins/songs/lib/openlyricsexport.py
index ec5677ea4..515674618 100644
--- a/openlp/plugins/songs/lib/openlyricsexport.py
+++ b/openlp/plugins/songs/lib/openlyricsexport.py
@@ -30,7 +30,6 @@ songs from the database to the OpenLyrics format.
"""
import logging
import os
-import re
from lxml import etree
diff --git a/openlp/plugins/songs/lib/sofimport.py b/openlp/plugins/songs/lib/sofimport.py
index 6294f211e..5f310dba0 100644
--- a/openlp/plugins/songs/lib/sofimport.py
+++ b/openlp/plugins/songs/lib/sofimport.py
@@ -194,7 +194,7 @@ class SofImport(OooImport):
into line
"""
text = textportion.getString()
- text = self.tidy_text(text)
+ text = self.tidyText(text)
if text.strip() == u'':
return text
if textportion.CharWeight == BOLD:
diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py
index fae3400c2..21988c267 100644
--- a/openlp/plugins/songs/lib/upgrade.py
+++ b/openlp/plugins/songs/lib/upgrade.py
@@ -29,7 +29,7 @@ The :mod:`upgrade` module provides a way for the database and schema that is the
backend for the Songs plugin
"""
-from sqlalchemy import Column, ForeignKey, Table, types
+from sqlalchemy import Column, Table, types
from sqlalchemy.sql.expression import func
from migrate import changeset
from migrate.changeset.constraint import ForeignKeyConstraint
diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py
index 5b36a7cb9..aaf82b395 100644
--- a/openlp/plugins/songs/lib/xml.py
+++ b/openlp/plugins/songs/lib/xml.py
@@ -60,7 +60,7 @@ The XML of an `OpenLyrics `_ song looks like this::
"""
-
+import cgi
import logging
import re
@@ -257,11 +257,12 @@ class OpenLyrics(object):
"""
IMPLEMENTED_VERSION = u'0.8'
+ START_TAGS_REGEX = re.compile(r'\{(\w+)\}')
+ END_TAGS_REGEX = re.compile(r'\{\/(\w+)\}')
+ VERSE_NUMBER_REGEX = re.compile(u'[a-zA-Z]*')
def __init__(self, manager):
self.manager = manager
- self.start_tags_regex = re.compile(r'\{(\w+)\}') # {abc} -> abc
- self.end_tags_regex = re.compile(r'\{\/(\w+)\}') # {/abc} -> abc
def song_to_xml(self, song):
"""
@@ -334,7 +335,8 @@ class OpenLyrics(object):
if u'lang' in verse[0]:
verse_element.set(u'lang', verse[0][u'lang'])
# Create a list with all "virtual" verses.
- virtual_verses = verse[1].split(u'[---]')
+ virtual_verses = cgi.escape(verse[1])
+ virtual_verses = virtual_verses.split(u'[---]')
for index, virtual_verse in enumerate(virtual_verses):
# Add formatting tags to text
lines_element = self._add_text_with_tags_to_lines(verse_element,
@@ -402,32 +404,32 @@ class OpenLyrics(object):
def _add_tag_to_formatting(self, tag_name, tags_element):
"""
- Add new formatting tag to the element ````
- if the tag is not present yet.
+ Add new formatting tag to the element ```` if the tag is not
+ present yet.
"""
available_tags = FormattingTags.get_html_tags()
start_tag = '{%s}' % tag_name
- for t in available_tags:
- if t[u'start tag'] == start_tag:
+ for tag in available_tags:
+ if tag[u'start tag'] == start_tag:
# Create new formatting tag in openlyrics xml.
- el = self._add_text_to_element(u'tag', tags_element)
- el.set(u'name', tag_name)
- el_open = self._add_text_to_element(u'open', el)
- el_open.text = etree.CDATA(t[u'start html'])
+ element = self._add_text_to_element(u'tag', tags_element)
+ element.set(u'name', tag_name)
+ element_open = self._add_text_to_element(u'open', element)
+ element_open.text = etree.CDATA(tag[u'start html'])
# Check if formatting tag contains end tag. Some formatting
# tags e.g. {br} has only start tag. If no end tag is present
# element has not to be in OpenLyrics xml.
- if t['end tag']:
- el_close = self._add_text_to_element(u'close', el)
- el_close.text = etree.CDATA(t[u'end html'])
+ if tag['end tag']:
+ element_close = self._add_text_to_element(u'close', element)
+ element_close.text = etree.CDATA(tag[u'end html'])
def _add_text_with_tags_to_lines(self, verse_element, text, tags_element):
"""
Convert text with formatting tags from OpenLP format to OpenLyrics
format and append it to element ````.
"""
- start_tags = self.start_tags_regex.findall(text)
- end_tags = self.end_tags_regex.findall(text)
+ start_tags = OpenLyrics.START_TAGS_REGEX.findall(text)
+ end_tags = OpenLyrics.END_TAGS_REGEX.findall(text)
# Replace start tags with xml syntax.
for tag in start_tags:
# Tags already converted to xml structure.
@@ -442,12 +444,11 @@ class OpenLyrics(object):
if tag not in xml_tags:
self._add_tag_to_formatting(tag, tags_element)
# Replace end tags.
- for t in end_tags:
- text = text.replace(u'{/%s}' % t, u'')
+ for tag in end_tags:
+ text = text.replace(u'{/%s}' % tag, u'')
# Replace \n with
.
text = text.replace(u'\n', u'
')
- text = u'' + text + u''
- element = etree.XML(text)
+ element = etree.XML(u'%s' % text)
verse_element.append(element)
return element
@@ -692,7 +693,7 @@ class OpenLyrics(object):
verse_tag = verse_def[0]
else:
verse_tag = VerseType.Tags[VerseType.Other]
- verse_number = re.compile(u'[a-zA-Z]*').sub(u'', verse_def)
+ verse_number = OpenLyrics.VERSE_NUMBER_REGEX.sub(u'', verse_def)
# OpenLyrics allows e. g. "c", but we need "c1". However, this does
# not correct the verse order.
if not verse_number:
diff --git a/openlp/plugins/songusage/forms/songusagedeletedialog.py b/openlp/plugins/songusage/forms/songusagedeletedialog.py
index 28f18d578..a6f7fb2b0 100644
--- a/openlp/plugins/songusage/forms/songusagedeletedialog.py
+++ b/openlp/plugins/songusage/forms/songusagedeletedialog.py
@@ -28,7 +28,6 @@
from PyQt4 import QtCore, QtGui
from openlp.core.lib import translate
-from openlp.core.lib.ui import create_accept_reject_button_box
class Ui_SongUsageDeleteDialog(object):
def setupUi(self, songUsageDeleteDialog):
diff --git a/resources/forms/themelayout.ui b/resources/forms/themelayout.ui
new file mode 100644
index 000000000..2152fc5d0
--- /dev/null
+++ b/resources/forms/themelayout.ui
@@ -0,0 +1,81 @@
+
+
+ ThemeLayout
+
+
+
+ 0
+ 0
+ 400
+ 300
+
+
+
+ Theme Layout
+
+
+
+
+ 50
+ 260
+ 341
+ 32
+
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+ 20
+ 10
+ 361
+ 231
+
+
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ ThemeLayout
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ ThemeLayout
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc
index fff1f75b8..c12b67002 100644
--- a/resources/images/openlp-2.qrc
+++ b/resources/images/openlp-2.qrc
@@ -60,8 +60,6 @@
slide_close.png
- slide_first.png
- slide_last.png
slide_next.png
slide_blank.png
slide_desktop.png
diff --git a/resources/images/slide_first.png b/resources/images/slide_first.png
deleted file mode 100644
index a9d66123e..000000000
Binary files a/resources/images/slide_first.png and /dev/null differ
diff --git a/resources/images/slide_last.png b/resources/images/slide_last.png
deleted file mode 100644
index 2702a3ddd..000000000
Binary files a/resources/images/slide_last.png and /dev/null differ
diff --git a/resources/windows/OpenLP-2.0.iss b/resources/windows/OpenLP-2.0.iss
index 9d2e6bfaa..d773a22fd 100644
--- a/resources/windows/OpenLP-2.0.iss
+++ b/resources/windows/OpenLP-2.0.iss
@@ -87,7 +87,7 @@ Root: HKCR; Subkey: "OpenLP\DefaultIcon"; ValueType: string; ValueName: ""; Valu
Root: HKCR; Subkey: "OpenLP\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\OpenLP.exe"" ""%1"""
[Code]
-function IsModuleLoaded(modulename: String ): Boolean;
+function IsModuleLoaded(modulename: AnsiString ): Boolean;
external 'IsModuleLoaded@files:psvince.dll stdcall';
function GetUninstallString(): String;
diff --git a/scripts/check_dependencies.py b/scripts/check_dependencies.py
index 14d27fb81..dd2907ba9 100755
--- a/scripts/check_dependencies.py
+++ b/scripts/check_dependencies.py
@@ -8,7 +8,7 @@
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
-# Armin Köhler, Joshua Millar, Stevan Pettit, Andreas Preikschat, Mattias #
+# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
# --------------------------------------------------------------------------- #
diff --git a/scripts/generate_resources.sh b/scripts/generate_resources.sh
index cf6f8f2f8..5778f771a 100755
--- a/scripts/generate_resources.sh
+++ b/scripts/generate_resources.sh
@@ -5,11 +5,11 @@
# 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, #
-# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode #
-# Woldsund #
+# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
+# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
+# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
+# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
+# 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 #
diff --git a/testing/conftest.py b/testing/conftest.py
index f38018c17..001f8979a 100644
--- a/testing/conftest.py
+++ b/testing/conftest.py
@@ -8,7 +8,7 @@
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
-# Armin Köhler, Joshua Millar, Stevan Pettit, Andreas Preikschat, Mattias #
+# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
# --------------------------------------------------------------------------- #
diff --git a/testing/test_app.py b/testing/test_app.py
index 00cd744ba..3ff053479 100644
--- a/testing/test_app.py
+++ b/testing/test_app.py
@@ -8,7 +8,7 @@
# Copyright (c) 2008-2011 Raoul Snyman #
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
-# Armin Köhler, Joshua Millar, Stevan Pettit, Andreas Preikschat, Mattias #
+# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
# --------------------------------------------------------------------------- #