forked from openlp/openlp
r1778
This commit is contained in:
commit
3c0ec6d7b3
@ -7,7 +7,7 @@
|
|||||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||||
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
# 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, #
|
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
|
@ -144,6 +144,59 @@ def image_to_byte(image):
|
|||||||
# convert to base64 encoding so does not get missed!
|
# convert to base64 encoding so does not get missed!
|
||||||
return byte_array.toBase64()
|
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'):
|
def resize_image(image_path, width, height, background=u'#000000'):
|
||||||
"""
|
"""
|
||||||
Resize an image to fit on the current screen.
|
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.
|
The new image height.
|
||||||
|
|
||||||
``background``
|
``background``
|
||||||
The background colour defaults to black.
|
The background colour. Defaults to black.
|
||||||
|
|
||||||
DO NOT REMOVE THE DEFAULT BACKGROUND VALUE!
|
DO NOT REMOVE THE DEFAULT BACKGROUND VALUE!
|
||||||
"""
|
"""
|
||||||
|
@ -51,9 +51,6 @@ class EventReceiver(QtCore.QObject):
|
|||||||
``config_screen_changed``
|
``config_screen_changed``
|
||||||
The display monitor has been changed
|
The display monitor has been changed
|
||||||
|
|
||||||
``slidecontroller_{live|preview}_first``
|
|
||||||
Moves to the first slide
|
|
||||||
|
|
||||||
``slidecontroller_{live|preview}_next``
|
``slidecontroller_{live|preview}_next``
|
||||||
Moves to the next slide
|
Moves to the next slide
|
||||||
|
|
||||||
@ -66,9 +63,6 @@ class EventReceiver(QtCore.QObject):
|
|||||||
``slidecontroller_{live|preview}_previous_noloop``
|
``slidecontroller_{live|preview}_previous_noloop``
|
||||||
Moves to the previous slide, without auto advance
|
Moves to the previous slide, without auto advance
|
||||||
|
|
||||||
``slidecontroller_{live|preview}_last``
|
|
||||||
Moves to the last slide
|
|
||||||
|
|
||||||
``slidecontroller_{live|preview}_set``
|
``slidecontroller_{live|preview}_set``
|
||||||
Moves to a specific slide, by index
|
Moves to a specific slide, by index
|
||||||
|
|
||||||
@ -82,11 +76,6 @@ class EventReceiver(QtCore.QObject):
|
|||||||
``slidecontroller_{live|preview}_changed``
|
``slidecontroller_{live|preview}_changed``
|
||||||
Broadcasts that the slidecontroller has changed the current slide
|
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``
|
``slidecontroller_{live|preview}_blank``
|
||||||
Request that the output screen is blanked
|
Request that the output screen is blanked
|
||||||
|
|
||||||
|
@ -34,8 +34,8 @@ from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, \
|
|||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
# FIXME: Add html5 doctype. However, do not break theme gradients.
|
|
||||||
HTMLSRC = u"""
|
HTMLSRC = u"""
|
||||||
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>OpenLP Display</title>
|
<title>OpenLP Display</title>
|
||||||
@ -404,7 +404,7 @@ def build_background_css(item, width, height):
|
|||||||
background = \
|
background = \
|
||||||
u'background: ' \
|
u'background: ' \
|
||||||
u'-webkit-gradient(linear, left top, left bottom, ' \
|
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)
|
theme.background_end_color)
|
||||||
elif theme.background_direction == \
|
elif theme.background_direction == \
|
||||||
BackgroundGradientType.to_string( \
|
BackgroundGradientType.to_string( \
|
||||||
@ -412,7 +412,7 @@ def build_background_css(item, width, height):
|
|||||||
background = \
|
background = \
|
||||||
u'background: ' \
|
u'background: ' \
|
||||||
u'-webkit-gradient(linear, left top, right bottom, ' \
|
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)
|
theme.background_end_color)
|
||||||
elif theme.background_direction == \
|
elif theme.background_direction == \
|
||||||
BackgroundGradientType.to_string \
|
BackgroundGradientType.to_string \
|
||||||
@ -420,20 +420,21 @@ def build_background_css(item, width, height):
|
|||||||
background = \
|
background = \
|
||||||
u'background: ' \
|
u'background: ' \
|
||||||
u'-webkit-gradient(linear, left bottom, right top, ' \
|
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)
|
theme.background_end_color)
|
||||||
elif theme.background_direction == \
|
elif theme.background_direction == \
|
||||||
BackgroundGradientType.to_string \
|
BackgroundGradientType.to_string \
|
||||||
(BackgroundGradientType.Vertical):
|
(BackgroundGradientType.Vertical):
|
||||||
background = \
|
background = \
|
||||||
u'background: -webkit-gradient(linear, left top, ' \
|
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)
|
(theme.background_start_color, theme.background_end_color)
|
||||||
else:
|
else:
|
||||||
background = \
|
background = \
|
||||||
u'background: -webkit-gradient(radial, %s 50%%, 100, %s ' \
|
u'background: -webkit-gradient(radial, %s 50%%, 100, %s ' \
|
||||||
u'50%%, %s, from(%s), to(%s))' % (width, width, width,
|
u'50%%, %s, from(%s), to(%s)) fixed' % (width, width,
|
||||||
theme.background_start_color, theme.background_end_color)
|
width, theme.background_start_color,
|
||||||
|
theme.background_end_color)
|
||||||
return background
|
return background
|
||||||
|
|
||||||
def build_lyrics_css(item, webkitvers):
|
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
|
left_margin = int(theme.font_main_outline_size) * 2
|
||||||
else:
|
else:
|
||||||
left_margin = 0
|
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; ' \
|
'text-align: %s; vertical-align: %s; font-family: %s; ' \
|
||||||
'font-size: %spt; color: %s; line-height: %d%%; margin: 0;' \
|
'font-size: %spt; color: %s; line-height: %d%%; margin: 0;' \
|
||||||
'padding: 0; padding-left: %spx; width: %spx; height: %spx; ' % \
|
'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),
|
theme.font_main_color, 100 + int(theme.font_main_line_adjustment),
|
||||||
left_margin, width, height)
|
left_margin, width, height)
|
||||||
if theme.font_main_outline:
|
if theme.font_main_outline:
|
||||||
|
@ -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.
|
|
||||||
|
|
@ -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: '<URL>'
|
|
||||||
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)
|
|
||||||
|
|
@ -425,44 +425,6 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
count += 1
|
count += 1
|
||||||
return filelist
|
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):
|
def loadList(self, list):
|
||||||
raise NotImplementedError(u'MediaManagerItem.loadList needs to be '
|
raise NotImplementedError(u'MediaManagerItem.loadList needs to be '
|
||||||
u'defined by the plugin')
|
u'defined by the plugin')
|
||||||
|
@ -44,6 +44,7 @@ VERSE = u'The Lord said to {r}Noah{/r}: \n' \
|
|||||||
'Get those children out of the muddy, muddy \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}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'
|
'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']
|
FOOTER = [u'Arky Arky (Unknown)', u'Public Domain', u'CCLI 123456']
|
||||||
|
|
||||||
class Renderer(object):
|
class Renderer(object):
|
||||||
@ -56,14 +57,14 @@ class Renderer(object):
|
|||||||
|
|
||||||
def __init__(self, imageManager, themeManager):
|
def __init__(self, imageManager, themeManager):
|
||||||
"""
|
"""
|
||||||
Initialise the render manager.
|
Initialise the renderer.
|
||||||
|
|
||||||
``imageManager``
|
``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.
|
images.
|
||||||
|
|
||||||
``themeManager``
|
``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')
|
log.debug(u'Initialisation started')
|
||||||
self.themeManager = themeManager
|
self.themeManager = themeManager
|
||||||
@ -80,7 +81,7 @@ class Renderer(object):
|
|||||||
|
|
||||||
def update_display(self):
|
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')
|
log.debug(u'Update Display')
|
||||||
self._calculate_default()
|
self._calculate_default()
|
||||||
@ -190,7 +191,7 @@ class Renderer(object):
|
|||||||
serviceItem.theme = theme_data
|
serviceItem.theme = theme_data
|
||||||
if self.force_page:
|
if self.force_page:
|
||||||
# make big page for theme edit dialog to get line count
|
# 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:
|
else:
|
||||||
self.imageManager.del_image(theme_data.theme_name)
|
self.imageManager.del_image(theme_data.theme_name)
|
||||||
serviceItem.add_from_text(u'', VERSE)
|
serviceItem.add_from_text(u'', VERSE)
|
||||||
@ -200,7 +201,8 @@ class Renderer(object):
|
|||||||
if not self.force_page:
|
if not self.force_page:
|
||||||
self.display.buildHtml(serviceItem)
|
self.display.buildHtml(serviceItem)
|
||||||
raw_html = serviceItem.get_rendered_frame(0)
|
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
|
# Reset the real screen size for subsequent render requests
|
||||||
self._calculate_default()
|
self._calculate_default()
|
||||||
return preview
|
return preview
|
||||||
@ -224,14 +226,10 @@ class Renderer(object):
|
|||||||
# Bibles
|
# Bibles
|
||||||
if item.is_capable(ItemCapabilities.CanWordSplit):
|
if item.is_capable(ItemCapabilities.CanWordSplit):
|
||||||
pages = self._paginate_slide_words(text.split(u'\n'), line_end)
|
pages = self._paginate_slide_words(text.split(u'\n'), line_end)
|
||||||
else:
|
# Songs and Custom
|
||||||
# Clean up line endings.
|
elif item.is_capable(ItemCapabilities.CanSoftBreak):
|
||||||
lines = self._lines_split(text)
|
pages = []
|
||||||
pages = self._paginate_slide(lines, line_end)
|
if u'[---]' in text:
|
||||||
# Songs and Custom
|
|
||||||
if item.is_capable(ItemCapabilities.CanSoftBreak) and \
|
|
||||||
len(pages) > 1 and u'[---]' in text:
|
|
||||||
pages = []
|
|
||||||
while True:
|
while True:
|
||||||
slides = text.split(u'\n[---]\n', 2)
|
slides = text.split(u'\n[---]\n', 2)
|
||||||
# If there are (at least) two occurrences of [---] we use
|
# 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')
|
lines = text.strip(u'\n').split(u'\n')
|
||||||
pages.extend(self._paginate_slide(lines, line_end))
|
pages.extend(self._paginate_slide(lines, line_end))
|
||||||
break
|
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 = []
|
new_pages = []
|
||||||
for page in pages:
|
for page in pages:
|
||||||
while page.endswith(u'<br>'):
|
while page.endswith(u'<br>'):
|
||||||
@ -302,21 +305,37 @@ class Renderer(object):
|
|||||||
The theme to build a text block for.
|
The theme to build a text block for.
|
||||||
"""
|
"""
|
||||||
log.debug(u'_build_text_rectangle')
|
log.debug(u'_build_text_rectangle')
|
||||||
main_rect = None
|
main_rect = self.get_main_rectangle(theme)
|
||||||
footer_rect = None
|
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:
|
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:
|
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)
|
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:
|
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)
|
self.height - self.footer_start)
|
||||||
else:
|
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_y, theme.font_footer_width - 1,
|
||||||
theme.font_footer_height - 1)
|
theme.font_footer_height - 1)
|
||||||
self._set_text_rectangle(main_rect, footer_rect)
|
|
||||||
|
|
||||||
def _set_text_rectangle(self, rect_main, rect_footer):
|
def _set_text_rectangle(self, rect_main, rect_footer):
|
||||||
"""
|
"""
|
||||||
@ -585,12 +604,3 @@ class Renderer(object):
|
|||||||
# this parse we are to be wordy
|
# this parse we are to be wordy
|
||||||
line = line.replace(u'\n', u' ')
|
line = line.replace(u'\n', u' ')
|
||||||
return line.split(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')
|
|
||||||
|
@ -122,9 +122,8 @@ class ServiceItem(object):
|
|||||||
|
|
||||||
def _new_item(self):
|
def _new_item(self):
|
||||||
"""
|
"""
|
||||||
Method to set the internal id of the item
|
Method to set the internal id of the item. This is used to compare
|
||||||
This is used to compare service items to see if they are
|
service items to see if they are the same.
|
||||||
the same
|
|
||||||
"""
|
"""
|
||||||
self._uuid = unicode(uuid.uuid1())
|
self._uuid = unicode(uuid.uuid1())
|
||||||
|
|
||||||
@ -160,9 +159,8 @@ class ServiceItem(object):
|
|||||||
def render(self, use_override=False):
|
def render(self, use_override=False):
|
||||||
"""
|
"""
|
||||||
The render method is what generates the frames for the screen and
|
The render method is what generates the frames for the screen and
|
||||||
obtains the display information from the renderemanager.
|
obtains the display information from the renderer. At this point all
|
||||||
At this point all the slides are built for the given
|
slides are built for the given display size.
|
||||||
display size.
|
|
||||||
"""
|
"""
|
||||||
log.debug(u'Render called')
|
log.debug(u'Render called')
|
||||||
self._display_frames = []
|
self._display_frames = []
|
||||||
@ -364,6 +362,11 @@ class ServiceItem(object):
|
|||||||
"""
|
"""
|
||||||
self._uuid = other._uuid
|
self._uuid = other._uuid
|
||||||
self.notes = other.notes
|
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):
|
if self.is_capable(ItemCapabilities.HasBackgroundAudio):
|
||||||
log.debug(self.background_audio)
|
log.debug(self.background_audio)
|
||||||
|
|
||||||
|
@ -176,8 +176,9 @@ class HorizontalType(object):
|
|||||||
Left = 0
|
Left = 0
|
||||||
Right = 1
|
Right = 1
|
||||||
Center = 2
|
Center = 2
|
||||||
|
Justify = 3
|
||||||
|
|
||||||
Names = [u'left', u'right', u'center']
|
Names = [u'left', u'right', u'center', u'justify']
|
||||||
|
|
||||||
|
|
||||||
class VerticalType(object):
|
class VerticalType(object):
|
||||||
|
@ -54,6 +54,7 @@ class HideMode(object):
|
|||||||
|
|
||||||
from firsttimeform import FirstTimeForm
|
from firsttimeform import FirstTimeForm
|
||||||
from firsttimelanguageform import FirstTimeLanguageForm
|
from firsttimelanguageform import FirstTimeLanguageForm
|
||||||
|
from themelayoutform import ThemeLayoutForm
|
||||||
from themeform import ThemeForm
|
from themeform import ThemeForm
|
||||||
from filerenameform import FileRenameForm
|
from filerenameform import FileRenameForm
|
||||||
from starttimeform import StartTimeForm
|
from starttimeform import StartTimeForm
|
||||||
|
@ -56,7 +56,6 @@ except ImportError:
|
|||||||
SQLITE_VERSION = u'-'
|
SQLITE_VERSION = u'-'
|
||||||
|
|
||||||
from openlp.core.lib import translate, SettingsManager
|
from openlp.core.lib import translate, SettingsManager
|
||||||
from openlp.core.lib.mailto import mailto
|
|
||||||
from openlp.core.lib.ui import UiStrings
|
from openlp.core.lib.ui import UiStrings
|
||||||
from openlp.core.utils import get_application_version
|
from openlp.core.utils import get_application_version
|
||||||
|
|
||||||
@ -159,12 +158,12 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog):
|
|||||||
if u':' in line:
|
if u':' in line:
|
||||||
exception = line.split(u'\n')[-1].split(u':')[0]
|
exception = line.split(u'\n')[-1].split(u':')[0]
|
||||||
subject = u'Bug report: %s in %s' % (exception, source)
|
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:
|
if self.fileAttachment:
|
||||||
mailto(address=u'bugs@openlp.org', subject=subject,
|
mailto_url.addQueryItem(u'attach', self.fileAttachment)
|
||||||
body=body % content, attach=self.fileAttachment)
|
QtGui.QDesktopServices.openUrl(mailto_url)
|
||||||
else:
|
|
||||||
mailto(address=u'bugs@openlp.org', subject=subject,
|
|
||||||
body=body % content)
|
|
||||||
|
|
||||||
def onDescriptionUpdated(self):
|
def onDescriptionUpdated(self):
|
||||||
count = int(20 - len(self.descriptionTextEdit.toPlainText()))
|
count = int(20 - len(self.descriptionTextEdit.toPlainText()))
|
||||||
|
@ -194,7 +194,6 @@ class MainDisplay(QtGui.QGraphicsView):
|
|||||||
self.setGeometry(self.screen[u'size'])
|
self.setGeometry(self.screen[u'size'])
|
||||||
self.frame.evaluateJavaScript(u'show_text("%s")' %
|
self.frame.evaluateJavaScript(u'show_text("%s")' %
|
||||||
slide.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
|
slide.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
|
||||||
return self.preview()
|
|
||||||
|
|
||||||
def alert(self, text):
|
def alert(self, text):
|
||||||
"""
|
"""
|
||||||
@ -256,7 +255,6 @@ class MainDisplay(QtGui.QGraphicsView):
|
|||||||
image = self.imageManager.get_image_bytes(name)
|
image = self.imageManager.get_image_bytes(name)
|
||||||
self.resetVideo()
|
self.resetVideo()
|
||||||
self.displayImage(image)
|
self.displayImage(image)
|
||||||
return self.preview()
|
|
||||||
|
|
||||||
def displayImage(self, image):
|
def displayImage(self, image):
|
||||||
"""
|
"""
|
||||||
@ -357,7 +355,7 @@ class MainDisplay(QtGui.QGraphicsView):
|
|||||||
"""
|
"""
|
||||||
# We request a background video but have no service Item
|
# We request a background video but have no service Item
|
||||||
if isBackground and not hasattr(self, u'serviceItem'):
|
if isBackground and not hasattr(self, u'serviceItem'):
|
||||||
return None
|
return False
|
||||||
if not self.mediaObject:
|
if not self.mediaObject:
|
||||||
self.createMediaObject()
|
self.createMediaObject()
|
||||||
log.debug(u'video')
|
log.debug(u'video')
|
||||||
@ -387,7 +385,7 @@ class MainDisplay(QtGui.QGraphicsView):
|
|||||||
# Update the preview frame.
|
# Update the preview frame.
|
||||||
if self.isLive:
|
if self.isLive:
|
||||||
Receiver.send_message(u'maindisplay_active')
|
Receiver.send_message(u'maindisplay_active')
|
||||||
return self.preview()
|
return True
|
||||||
|
|
||||||
def videoState(self, newState, oldState):
|
def videoState(self, newState, oldState):
|
||||||
"""
|
"""
|
||||||
@ -455,9 +453,8 @@ class MainDisplay(QtGui.QGraphicsView):
|
|||||||
self.setVisible(True)
|
self.setVisible(True)
|
||||||
else:
|
else:
|
||||||
self.setVisible(True)
|
self.setVisible(True)
|
||||||
preview = QtGui.QImage(self.screen[u'size'].width(),
|
preview = QtGui.QPixmap(self.screen[u'size'].width(),
|
||||||
self.screen[u'size'].height(),
|
self.screen[u'size'].height())
|
||||||
QtGui.QImage.Format_ARGB32_Premultiplied)
|
|
||||||
painter = QtGui.QPainter(preview)
|
painter = QtGui.QPainter(preview)
|
||||||
painter.setRenderHint(QtGui.QPainter.Antialiasing)
|
painter.setRenderHint(QtGui.QPainter.Antialiasing)
|
||||||
self.frame.render(painter)
|
self.frame.render(painter)
|
||||||
|
@ -30,6 +30,7 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
import zipfile
|
import zipfile
|
||||||
|
from tempfile import mkstemp
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -467,15 +468,24 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
|
|
||||||
def saveFile(self):
|
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():
|
if not self.fileName():
|
||||||
return self.saveFileAs()
|
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 = unicode(self.fileName())
|
||||||
path, file_name = os.path.split(path_file_name)
|
path, file_name = os.path.split(path_file_name)
|
||||||
basename, extension = os.path.splitext(file_name)
|
basename, extension = os.path.splitext(file_name)
|
||||||
service_file_name = '%s.osd' % basename
|
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(
|
SettingsManager.set_last_dir(
|
||||||
self.mainwindow.servicemanagerSettingsSection,
|
self.mainwindow.servicemanagerSettingsSection,
|
||||||
path)
|
path)
|
||||||
@ -494,7 +504,8 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
if len(service_item[u'header'][u'background_audio']) > 0:
|
if len(service_item[u'header'][u'background_audio']) > 0:
|
||||||
for i, filename in \
|
for i, filename in \
|
||||||
enumerate(service_item[u'header'][u'background_audio']):
|
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])
|
os.path.split(filename)[1])
|
||||||
audio_files.append((filename, new_file))
|
audio_files.append((filename, new_file))
|
||||||
service_item[u'header'][u'background_audio'][i] = new_file
|
service_item[u'header'][u'background_audio'][i] = new_file
|
||||||
@ -545,30 +556,38 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
success = True
|
success = True
|
||||||
self.mainwindow.incrementProgressBar()
|
self.mainwindow.incrementProgressBar()
|
||||||
try:
|
try:
|
||||||
zip = zipfile.ZipFile(path_file_name, 'w', zipfile.ZIP_STORED,
|
zip = zipfile.ZipFile(temp_file_name, 'w', zipfile.ZIP_STORED,
|
||||||
allow_zip_64)
|
allow_zip_64)
|
||||||
# First we add service contents.
|
# First we add service contents.
|
||||||
# We save ALL filenames into ZIP using UTF-8.
|
# We save ALL filenames into ZIP using UTF-8.
|
||||||
zip.writestr(service_file_name.encode(u'utf-8'), service_content)
|
zip.writestr(service_file_name.encode(u'utf-8'), service_content)
|
||||||
# Finally add all the listed media files.
|
# Finally add all the listed media files.
|
||||||
for path_from in write_list:
|
for write_from in write_list:
|
||||||
zip.write(path_from, path_from.encode(u'utf-8'))
|
zip.write(write_from, write_from.encode(u'utf-8'))
|
||||||
for path_from, path_to in audio_files:
|
for audio_from, audio_to in audio_files:
|
||||||
if path_from == path_to:
|
if audio_from.startswith(u'audio'):
|
||||||
# If this file has already been saved, let's use set the
|
# When items are saved, they get new UUID's. Let's copy the
|
||||||
# from path to the real location of the files
|
# file to the new location. Unused files can be ignored,
|
||||||
path_from = os.path.join(self.servicePath, path_from)
|
# OpenLP automatically cleans up the service manager dir on
|
||||||
else:
|
# exit.
|
||||||
# If this file has not yet been saved, let's copy the file
|
audio_from = os.path.join(self.servicePath, audio_from)
|
||||||
# to the service manager path
|
save_file = os.path.join(self.servicePath, audio_to)
|
||||||
save_file = os.path.join(self.servicePath, path_to)
|
save_path = os.path.split(save_file)[0]
|
||||||
save_path = os.path.split(save_file)[0]
|
if not os.path.exists(save_path):
|
||||||
if not os.path.exists(save_path):
|
os.makedirs(save_path)
|
||||||
os.makedirs(save_path)
|
if not os.path.exists(save_file):
|
||||||
shutil.copy(path_from, save_file)
|
shutil.copy(audio_from, save_file)
|
||||||
zip.write(path_from, path_to.encode(u'utf-8'))
|
zip.write(audio_from, audio_to.encode(u'utf-8'))
|
||||||
except IOError:
|
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
|
success = False
|
||||||
finally:
|
finally:
|
||||||
if zip:
|
if zip:
|
||||||
@ -576,10 +595,13 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
self.mainwindow.finishedProgressBar()
|
self.mainwindow.finishedProgressBar()
|
||||||
Receiver.send_message(u'cursor_normal')
|
Receiver.send_message(u'cursor_normal')
|
||||||
if success:
|
if success:
|
||||||
|
shutil.copy(temp_file_name, path_file_name)
|
||||||
self.mainwindow.addRecentFile(path_file_name)
|
self.mainwindow.addRecentFile(path_file_name)
|
||||||
self.setModified(False)
|
self.setModified(False)
|
||||||
else:
|
try:
|
||||||
delete_file(path_file_name)
|
delete_file(temp_file_name)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
return success
|
return success
|
||||||
|
|
||||||
def saveFileAs(self):
|
def saveFileAs(self):
|
||||||
@ -623,6 +645,7 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
osfile = unicode(QtCore.QDir.toNativeSeparators(ucsfile))
|
osfile = unicode(QtCore.QDir.toNativeSeparators(ucsfile))
|
||||||
if not osfile.startswith(u'audio'):
|
if not osfile.startswith(u'audio'):
|
||||||
osfile = os.path.split(osfile)[1]
|
osfile = os.path.split(osfile)[1]
|
||||||
|
log.debug(u'Extract file: %s', osfile)
|
||||||
zipinfo.filename = osfile
|
zipinfo.filename = osfile
|
||||||
zip.extract(zipinfo, self.servicePath)
|
zip.extract(zipinfo, self.servicePath)
|
||||||
if osfile.endswith(u'osd'):
|
if osfile.endswith(u'osd'):
|
||||||
@ -637,14 +660,17 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
for item in items:
|
for item in items:
|
||||||
self.mainwindow.incrementProgressBar()
|
self.mainwindow.incrementProgressBar()
|
||||||
serviceItem = ServiceItem()
|
serviceItem = ServiceItem()
|
||||||
serviceItem.from_service = True
|
|
||||||
serviceItem.renderer = self.mainwindow.renderer
|
serviceItem.renderer = self.mainwindow.renderer
|
||||||
serviceItem.set_from_service(item, self.servicePath)
|
serviceItem.set_from_service(item, self.servicePath)
|
||||||
self.validateItem(serviceItem)
|
self.validateItem(serviceItem)
|
||||||
self.addServiceItem(serviceItem, repaint=False)
|
self.loadItem_uuid = 0
|
||||||
if serviceItem.is_capable(ItemCapabilities.OnLoadUpdate):
|
if serviceItem.is_capable(ItemCapabilities.OnLoadUpdate):
|
||||||
Receiver.send_message(u'%s_service_load' %
|
Receiver.send_message(u'%s_service_load' %
|
||||||
serviceItem.name.lower(), serviceItem)
|
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)
|
delete_file(p_file)
|
||||||
self.setFileName(fileName)
|
self.setFileName(fileName)
|
||||||
self.mainwindow.addRecentFile(fileName)
|
self.mainwindow.addRecentFile(fileName)
|
||||||
@ -1022,11 +1048,12 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
"""
|
"""
|
||||||
Empties the servicePath of temporary files.
|
Empties the servicePath of temporary files.
|
||||||
"""
|
"""
|
||||||
|
log.debug(u'Cleaning up servicePath')
|
||||||
for file in os.listdir(self.servicePath):
|
for file in os.listdir(self.servicePath):
|
||||||
file_path = os.path.join(self.servicePath, file)
|
file_path = os.path.join(self.servicePath, file)
|
||||||
delete_file(file_path)
|
delete_file(file_path)
|
||||||
if os.path.exists(os.path.join(self.servicePath, u'audio')):
|
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):
|
def onThemeComboBoxSelected(self, currentIndex):
|
||||||
"""
|
"""
|
||||||
@ -1098,12 +1125,10 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
def serviceItemUpdate(self, message):
|
def serviceItemUpdate(self, message):
|
||||||
"""
|
"""
|
||||||
Triggered from plugins to update service items.
|
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':')
|
editId, self.loadItem_uuid = message.split(u':')
|
||||||
for item in self.serviceItems:
|
self.loadItem_editId = int(editId)
|
||||||
if item[u'service_item']._uuid == uuid:
|
|
||||||
item[u'service_item'].edit_id = int(editId)
|
|
||||||
self.setModified()
|
|
||||||
|
|
||||||
def replaceServiceItem(self, newItem):
|
def replaceServiceItem(self, newItem):
|
||||||
"""
|
"""
|
||||||
@ -1135,7 +1160,6 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
# if not passed set to config value
|
# if not passed set to config value
|
||||||
if expand is None:
|
if expand is None:
|
||||||
expand = self.expandTabs
|
expand = self.expandTabs
|
||||||
item.render()
|
|
||||||
item.from_service = True
|
item.from_service = True
|
||||||
if replace:
|
if replace:
|
||||||
sitem, child = self.findServiceItem()
|
sitem, child = self.findServiceItem()
|
||||||
@ -1144,6 +1168,7 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
self.repaintServiceList(sitem, child)
|
self.repaintServiceList(sitem, child)
|
||||||
self.mainwindow.liveController.replaceServiceManagerItem(item)
|
self.mainwindow.liveController.replaceServiceManagerItem(item)
|
||||||
else:
|
else:
|
||||||
|
item.render()
|
||||||
# nothing selected for dnd
|
# nothing selected for dnd
|
||||||
if self.dropPosition == 0:
|
if self.dropPosition == 0:
|
||||||
if isinstance(item, list):
|
if isinstance(item, list):
|
||||||
|
@ -118,7 +118,7 @@ class SlideController(QtGui.QWidget):
|
|||||||
self.previewListWidget.horizontalHeader().setVisible(False)
|
self.previewListWidget.horizontalHeader().setVisible(False)
|
||||||
self.previewListWidget.setColumnWidth(0, self.controller.width())
|
self.previewListWidget.setColumnWidth(0, self.controller.width())
|
||||||
self.previewListWidget.isLive = self.isLive
|
self.previewListWidget.isLive = self.isLive
|
||||||
self.previewListWidget.setObjectName(u'PreviewListWidget')
|
self.previewListWidget.setObjectName(u'previewListWidget')
|
||||||
self.previewListWidget.setSelectionBehavior(
|
self.previewListWidget.setSelectionBehavior(
|
||||||
QtGui.QAbstractItemView.SelectRows)
|
QtGui.QAbstractItemView.SelectRows)
|
||||||
self.previewListWidget.setSelectionMode(
|
self.previewListWidget.setSelectionMode(
|
||||||
@ -288,14 +288,14 @@ class SlideController(QtGui.QWidget):
|
|||||||
QtGui.QSizePolicy.Label))
|
QtGui.QSizePolicy.Label))
|
||||||
self.previewFrame.setFrameShape(QtGui.QFrame.StyledPanel)
|
self.previewFrame.setFrameShape(QtGui.QFrame.StyledPanel)
|
||||||
self.previewFrame.setFrameShadow(QtGui.QFrame.Sunken)
|
self.previewFrame.setFrameShadow(QtGui.QFrame.Sunken)
|
||||||
self.previewFrame.setObjectName(u'PreviewFrame')
|
self.previewFrame.setObjectName(u'previewFrame')
|
||||||
self.grid = QtGui.QGridLayout(self.previewFrame)
|
self.grid = QtGui.QGridLayout(self.previewFrame)
|
||||||
self.grid.setMargin(8)
|
self.grid.setMargin(8)
|
||||||
self.grid.setObjectName(u'grid')
|
self.grid.setObjectName(u'grid')
|
||||||
self.slideLayout = QtGui.QVBoxLayout()
|
self.slideLayout = QtGui.QVBoxLayout()
|
||||||
self.slideLayout.setSpacing(0)
|
self.slideLayout.setSpacing(0)
|
||||||
self.slideLayout.setMargin(0)
|
self.slideLayout.setMargin(0)
|
||||||
self.slideLayout.setObjectName(u'SlideLayout')
|
self.slideLayout.setObjectName(u'slideLayout')
|
||||||
if not self.isLive:
|
if not self.isLive:
|
||||||
self.mediaObject = Phonon.MediaObject(self)
|
self.mediaObject = Phonon.MediaObject(self)
|
||||||
self.video = Phonon.VideoWidget()
|
self.video = Phonon.VideoWidget()
|
||||||
@ -319,7 +319,7 @@ class SlideController(QtGui.QWidget):
|
|||||||
self.slidePreview.setFrameShadow(QtGui.QFrame.Plain)
|
self.slidePreview.setFrameShadow(QtGui.QFrame.Plain)
|
||||||
self.slidePreview.setLineWidth(1)
|
self.slidePreview.setLineWidth(1)
|
||||||
self.slidePreview.setScaledContents(True)
|
self.slidePreview.setScaledContents(True)
|
||||||
self.slidePreview.setObjectName(u'SlidePreview')
|
self.slidePreview.setObjectName(u'slidePreview')
|
||||||
self.slideLayout.insertWidget(0, self.slidePreview)
|
self.slideLayout.insertWidget(0, self.slidePreview)
|
||||||
self.grid.addLayout(self.slideLayout, 0, 0, 1, 1)
|
self.grid.addLayout(self.slideLayout, 0, 0, 1, 1)
|
||||||
# Signals
|
# Signals
|
||||||
@ -328,8 +328,6 @@ class SlideController(QtGui.QWidget):
|
|||||||
if self.isLive:
|
if self.isLive:
|
||||||
QtCore.QObject.connect(self.volumeSlider,
|
QtCore.QObject.connect(self.volumeSlider,
|
||||||
QtCore.SIGNAL(u'sliderReleased()'), self.mediaVolume)
|
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.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'slidecontroller_live_spin_delay'),
|
QtCore.SIGNAL(u'slidecontroller_live_spin_delay'),
|
||||||
self.receiveSpinDelay)
|
self.receiveSpinDelay)
|
||||||
@ -351,18 +349,12 @@ class SlideController(QtGui.QWidget):
|
|||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'slidecontroller_%s_stop_loop' % self.typePrefix),
|
QtCore.SIGNAL(u'slidecontroller_%s_stop_loop' % self.typePrefix),
|
||||||
self.onStopLoop)
|
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.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'slidecontroller_%s_next' % self.typePrefix),
|
QtCore.SIGNAL(u'slidecontroller_%s_next' % self.typePrefix),
|
||||||
self.onSlideSelectedNext)
|
self.onSlideSelectedNext)
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'slidecontroller_%s_previous' % self.typePrefix),
|
QtCore.SIGNAL(u'slidecontroller_%s_previous' % self.typePrefix),
|
||||||
self.onSlideSelectedPrevious)
|
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.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'slidecontroller_%s_change' % self.typePrefix),
|
QtCore.SIGNAL(u'slidecontroller_%s_change' % self.typePrefix),
|
||||||
self.onSlideChange)
|
self.onSlideChange)
|
||||||
@ -375,9 +367,6 @@ class SlideController(QtGui.QWidget):
|
|||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'slidecontroller_%s_unblank' % self.typePrefix),
|
QtCore.SIGNAL(u'slidecontroller_%s_unblank' % self.typePrefix),
|
||||||
self.onSlideUnblank)
|
self.onSlideUnblank)
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
|
||||||
QtCore.SIGNAL(u'slidecontroller_%s_text_request' % self.typePrefix),
|
|
||||||
self.onTextRequest)
|
|
||||||
|
|
||||||
def setPreviewHotkeys(self, parent=None):
|
def setPreviewHotkeys(self, parent=None):
|
||||||
self.previousItem.setObjectName(u'previousItemPreview')
|
self.previousItem.setObjectName(u'previousItemPreview')
|
||||||
@ -669,14 +658,14 @@ class SlideController(QtGui.QWidget):
|
|||||||
label.setMargin(4)
|
label.setMargin(4)
|
||||||
label.setScaledContents(True)
|
label.setScaledContents(True)
|
||||||
if self.serviceItem.is_command():
|
if self.serviceItem.is_command():
|
||||||
image = QtGui.QImage(frame[u'image'])
|
label.setPixmap(QtGui.QPixmap(frame[u'image']))
|
||||||
else:
|
else:
|
||||||
# If current slide set background to image
|
# If current slide set background to image
|
||||||
if framenumber == slideno:
|
if framenumber == slideno:
|
||||||
self.serviceItem.bg_image_bytes = \
|
self.serviceItem.bg_image_bytes = \
|
||||||
self.imageManager.get_image_bytes(frame[u'title'])
|
self.imageManager.get_image_bytes(frame[u'title'])
|
||||||
image = self.imageManager.get_image(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)
|
self.previewListWidget.setCellWidget(framenumber, 0, label)
|
||||||
slideHeight = width * self.parent().renderer.screen_ratio
|
slideHeight = width * self.parent().renderer.screen_ratio
|
||||||
row += 1
|
row += 1
|
||||||
@ -723,41 +712,7 @@ class SlideController(QtGui.QWidget):
|
|||||||
else:
|
else:
|
||||||
self.__checkUpdateSelectedSlide(slideno)
|
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
|
# 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):
|
def onSlideSelectedIndex(self, message):
|
||||||
"""
|
"""
|
||||||
Go to the requested slide
|
Go to the requested slide
|
||||||
@ -936,20 +891,18 @@ class SlideController(QtGui.QWidget):
|
|||||||
Receiver.send_message(
|
Receiver.send_message(
|
||||||
u'%s_slide' % self.serviceItem.name.lower(),
|
u'%s_slide' % self.serviceItem.name.lower(),
|
||||||
[self.serviceItem, self.isLive, row])
|
[self.serviceItem, self.isLive, row])
|
||||||
self.updatePreview()
|
|
||||||
else:
|
else:
|
||||||
toDisplay = self.serviceItem.get_rendered_frame(row)
|
toDisplay = self.serviceItem.get_rendered_frame(row)
|
||||||
if self.serviceItem.is_text():
|
if self.serviceItem.is_text():
|
||||||
frame = self.display.text(toDisplay)
|
self.display.text(toDisplay)
|
||||||
else:
|
else:
|
||||||
if start:
|
if start:
|
||||||
self.display.buildHtml(self.serviceItem, toDisplay)
|
self.display.buildHtml(self.serviceItem, toDisplay)
|
||||||
frame = self.display.preview()
|
|
||||||
else:
|
else:
|
||||||
frame = self.display.image(toDisplay)
|
self.display.image(toDisplay)
|
||||||
# reset the store used to display first image
|
# reset the store used to display first image
|
||||||
self.serviceItem.bg_image_bytes = None
|
self.serviceItem.bg_image_bytes = None
|
||||||
self.slidePreview.setPixmap(QtGui.QPixmap.fromImage(frame))
|
self.updatePreview()
|
||||||
self.selectedRow = row
|
self.selectedRow = row
|
||||||
self.__checkUpdateSelectedSlide(row)
|
self.__checkUpdateSelectedSlide(row)
|
||||||
Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix,
|
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(0.5, self.grabMainDisplay)
|
||||||
QtCore.QTimer.singleShot(2.5, self.grabMainDisplay)
|
QtCore.QTimer.singleShot(2.5, self.grabMainDisplay)
|
||||||
else:
|
else:
|
||||||
self.slidePreview.setPixmap(
|
self.slidePreview.setPixmap(self.display.preview())
|
||||||
QtGui.QPixmap.fromImage(self.display.preview()))
|
|
||||||
|
|
||||||
def grabMainDisplay(self):
|
def grabMainDisplay(self):
|
||||||
"""
|
"""
|
||||||
@ -1041,21 +993,6 @@ class SlideController(QtGui.QWidget):
|
|||||||
self.previewListWidget.item(row + 1, 0))
|
self.previewListWidget.item(row + 1, 0))
|
||||||
self.previewListWidget.selectRow(row)
|
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):
|
def onToggleLoop(self):
|
||||||
"""
|
"""
|
||||||
Toggles the loop state.
|
Toggles the loop state.
|
||||||
|
@ -36,8 +36,7 @@ class SplashScreen(QtGui.QSplashScreen):
|
|||||||
QtCore.SIGNAL(u'close_splash'), self.close)
|
QtCore.SIGNAL(u'close_splash'), self.close)
|
||||||
|
|
||||||
def setupUi(self):
|
def setupUi(self):
|
||||||
self.setObjectName(u'splash_screen')
|
self.setObjectName(u'splashScreen')
|
||||||
self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
|
|
||||||
self.setContextMenuPolicy(QtCore.Qt.PreventContextMenu)
|
self.setContextMenuPolicy(QtCore.Qt.PreventContextMenu)
|
||||||
splash_image = QtGui.QPixmap(u':/graphics/openlp-splash-screen.png')
|
splash_image = QtGui.QPixmap(u':/graphics/openlp-splash-screen.png')
|
||||||
self.setPixmap(splash_image)
|
self.setPixmap(splash_image)
|
||||||
|
@ -33,6 +33,7 @@ from PyQt4 import QtCore, QtGui
|
|||||||
from openlp.core.lib import Receiver, translate
|
from openlp.core.lib import Receiver, translate
|
||||||
from openlp.core.lib.theme import BackgroundType, BackgroundGradientType
|
from openlp.core.lib.theme import BackgroundType, BackgroundGradientType
|
||||||
from openlp.core.lib.ui import UiStrings, critical_error_message_box
|
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 openlp.core.utils import get_images_filter
|
||||||
from themewizard import Ui_ThemeWizard
|
from themewizard import Ui_ThemeWizard
|
||||||
|
|
||||||
@ -58,6 +59,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
|||||||
self.registerFields()
|
self.registerFields()
|
||||||
self.updateThemeAllowed = True
|
self.updateThemeAllowed = True
|
||||||
self.temp_background_filename = u''
|
self.temp_background_filename = u''
|
||||||
|
self.themeLayoutForm = ThemeLayoutForm(self)
|
||||||
QtCore.QObject.connect(self.backgroundComboBox,
|
QtCore.QObject.connect(self.backgroundComboBox,
|
||||||
QtCore.SIGNAL(u'currentIndexChanged(int)'),
|
QtCore.SIGNAL(u'currentIndexChanged(int)'),
|
||||||
self.onBackgroundComboBoxCurrentIndexChanged)
|
self.onBackgroundComboBoxCurrentIndexChanged)
|
||||||
@ -88,6 +90,9 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
|||||||
self.onShadowCheckCheckBoxStateChanged)
|
self.onShadowCheckCheckBoxStateChanged)
|
||||||
QtCore.QObject.connect(self.footerColorButton,
|
QtCore.QObject.connect(self.footerColorButton,
|
||||||
QtCore.SIGNAL(u'clicked()'), self.onFooterColorButtonClicked)
|
QtCore.SIGNAL(u'clicked()'), self.onFooterColorButtonClicked)
|
||||||
|
QtCore.QObject.connect(self,
|
||||||
|
QtCore.SIGNAL(u'customButtonClicked(int)'),
|
||||||
|
self.onCustom1ButtonClicked)
|
||||||
QtCore.QObject.connect(self.mainPositionCheckBox,
|
QtCore.QObject.connect(self.mainPositionCheckBox,
|
||||||
QtCore.SIGNAL(u'stateChanged(int)'),
|
QtCore.SIGNAL(u'stateChanged(int)'),
|
||||||
self.onMainPositionCheckBoxStateChanged)
|
self.onMainPositionCheckBoxStateChanged)
|
||||||
@ -229,13 +234,36 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
|||||||
"""
|
"""
|
||||||
Detects Page changes and updates as approprate.
|
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:
|
if self.page(pageId) == self.previewPage:
|
||||||
self.updateTheme()
|
self.updateTheme()
|
||||||
frame = self.thememanager.generateImage(self.theme)
|
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.displayAspectRatio = float(frame.width()) / frame.height()
|
||||||
self.resizeEvent()
|
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):
|
def onOutlineCheckCheckBoxStateChanged(self, state):
|
||||||
"""
|
"""
|
||||||
Change state as Outline check box changed
|
Change state as Outline check box changed
|
||||||
|
75
openlp/core/ui/themelayoutdialog.py
Normal file
75
openlp/core/ui/themelayoutdialog.py
Normal file
@ -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.'))
|
||||||
|
|
55
openlp/core/ui/themelayoutform.py
Normal file
55
openlp/core/ui/themelayoutform.py
Normal file
@ -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)
|
||||||
|
|
@ -36,7 +36,7 @@ from PyQt4 import QtCore, QtGui
|
|||||||
|
|
||||||
from openlp.core.lib import OpenLPToolbar, get_text_file_string, build_icon, \
|
from openlp.core.lib import OpenLPToolbar, get_text_file_string, build_icon, \
|
||||||
Receiver, SettingsManager, translate, check_item_selected, \
|
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, \
|
from openlp.core.lib.theme import ThemeXML, BackgroundType, VerticalType, \
|
||||||
BackgroundGradientType
|
BackgroundGradientType
|
||||||
from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
|
from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
|
||||||
@ -359,7 +359,7 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
The theme to delete.
|
The theme to delete.
|
||||||
"""
|
"""
|
||||||
self.themelist.remove(theme)
|
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.path, thumb))
|
||||||
delete_file(os.path.join(self.thumbPath, thumb))
|
delete_file(os.path.join(self.thumbPath, thumb))
|
||||||
try:
|
try:
|
||||||
@ -473,15 +473,12 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
name = textName
|
name = textName
|
||||||
thumb = os.path.join(self.thumbPath, u'%s.png' % textName)
|
thumb = os.path.join(self.thumbPath, u'%s.png' % textName)
|
||||||
item_name = QtGui.QListWidgetItem(name)
|
item_name = QtGui.QListWidgetItem(name)
|
||||||
if os.path.exists(thumb):
|
if validate_thumb(theme, thumb):
|
||||||
icon = build_icon(thumb)
|
icon = build_icon(thumb)
|
||||||
else:
|
else:
|
||||||
icon = build_icon(theme)
|
icon = create_thumb(theme, thumb)
|
||||||
pixmap = icon.pixmap(QtCore.QSize(88, 50))
|
|
||||||
pixmap.save(thumb, u'png')
|
|
||||||
item_name.setIcon(icon)
|
item_name.setIcon(icon)
|
||||||
item_name.setData(QtCore.Qt.UserRole,
|
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(textName))
|
||||||
QtCore.QVariant(textName))
|
|
||||||
self.themeListWidget.addItem(item_name)
|
self.themeListWidget.addItem(item_name)
|
||||||
self.themelist.append(textName)
|
self.themelist.append(textName)
|
||||||
self._pushThemes()
|
self._pushThemes()
|
||||||
@ -658,9 +655,7 @@ class ThemeManager(QtGui.QWidget):
|
|||||||
os.unlink(samplepathname)
|
os.unlink(samplepathname)
|
||||||
frame.save(samplepathname, u'png')
|
frame.save(samplepathname, u'png')
|
||||||
thumb = os.path.join(self.thumbPath, u'%s.png' % name)
|
thumb = os.path.join(self.thumbPath, u'%s.png' % name)
|
||||||
icon = build_icon(frame)
|
create_thumb(samplepathname, thumb, False)
|
||||||
pixmap = icon.pixmap(QtCore.QSize(88, 50))
|
|
||||||
pixmap.save(thumb, u'png')
|
|
||||||
log.debug(u'Theme image written to %s', samplepathname)
|
log.debug(u'Theme image written to %s', samplepathname)
|
||||||
|
|
||||||
def updatePreviewImages(self):
|
def updatePreviewImages(self):
|
||||||
|
@ -38,7 +38,8 @@ class Ui_ThemeWizard(object):
|
|||||||
themeWizard.setModal(True)
|
themeWizard.setModal(True)
|
||||||
themeWizard.setWizardStyle(QtGui.QWizard.ModernStyle)
|
themeWizard.setWizardStyle(QtGui.QWizard.ModernStyle)
|
||||||
themeWizard.setOptions(QtGui.QWizard.IndependentPages |
|
themeWizard.setOptions(QtGui.QWizard.IndependentPages |
|
||||||
QtGui.QWizard.NoBackButtonOnStartPage)
|
QtGui.QWizard.NoBackButtonOnStartPage |
|
||||||
|
QtGui.QWizard.HaveCustomButton1)
|
||||||
self.spacer = QtGui.QSpacerItem(10, 0,
|
self.spacer = QtGui.QSpacerItem(10, 0,
|
||||||
QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum)
|
QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum)
|
||||||
# Welcome Page
|
# Welcome Page
|
||||||
@ -246,7 +247,7 @@ class Ui_ThemeWizard(object):
|
|||||||
self.horizontalLabel = QtGui.QLabel(self.alignmentPage)
|
self.horizontalLabel = QtGui.QLabel(self.alignmentPage)
|
||||||
self.horizontalLabel.setObjectName(u'HorizontalLabel')
|
self.horizontalLabel.setObjectName(u'HorizontalLabel')
|
||||||
self.horizontalComboBox = QtGui.QComboBox(self.alignmentPage)
|
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.horizontalComboBox.setObjectName(u'HorizontalComboBox')
|
||||||
self.alignmentLayout.addRow(self.horizontalLabel,
|
self.alignmentLayout.addRow(self.horizontalLabel,
|
||||||
self.horizontalComboBox)
|
self.horizontalComboBox)
|
||||||
@ -495,6 +496,8 @@ class Ui_ThemeWizard(object):
|
|||||||
translate('OpenLP.ThemeWizard', 'Right'))
|
translate('OpenLP.ThemeWizard', 'Right'))
|
||||||
self.horizontalComboBox.setItemText(HorizontalType.Center,
|
self.horizontalComboBox.setItemText(HorizontalType.Center,
|
||||||
translate('OpenLP.ThemeWizard', 'Center'))
|
translate('OpenLP.ThemeWizard', 'Center'))
|
||||||
|
self.horizontalComboBox.setItemText(HorizontalType.Justify,
|
||||||
|
translate('OpenLP.ThemeWizard', 'Justify'))
|
||||||
self.transitionsLabel.setText(
|
self.transitionsLabel.setText(
|
||||||
translate('OpenLP.ThemeWizard', 'Transitions:'))
|
translate('OpenLP.ThemeWizard', 'Transitions:'))
|
||||||
self.areaPositionPage.setTitle(
|
self.areaPositionPage.setTitle(
|
||||||
@ -533,6 +536,9 @@ class Ui_ThemeWizard(object):
|
|||||||
translate('OpenLP.ThemeWizard', 'px'))
|
translate('OpenLP.ThemeWizard', 'px'))
|
||||||
self.footerPositionCheckBox.setText(
|
self.footerPositionCheckBox.setText(
|
||||||
translate('OpenLP.ThemeWizard', 'Use default location'))
|
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(
|
self.previewPage.setTitle(
|
||||||
translate('OpenLP.ThemeWizard', 'Save and Preview'))
|
translate('OpenLP.ThemeWizard', 'Save and Preview'))
|
||||||
self.previewPage.setSubTitle(
|
self.previewPage.setSubTitle(
|
||||||
|
@ -461,6 +461,11 @@ class BibleImportForm(OpenLPWizard):
|
|||||||
WizardStrings.YouSpecifyFile % WizardStrings.OS)
|
WizardStrings.YouSpecifyFile % WizardStrings.OS)
|
||||||
self.openSongFileEdit.setFocus()
|
self.openSongFileEdit.setFocus()
|
||||||
return False
|
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:
|
elif self.field(u'source_format').toInt()[0] == BibleFormat.OpenLP1:
|
||||||
if not self.field(u'openlp1_location').toString():
|
if not self.field(u'openlp1_location').toString():
|
||||||
critical_error_message_box(UiStrings().NFSs,
|
critical_error_message_box(UiStrings().NFSs,
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
import logging
|
import logging
|
||||||
import chardet
|
import chardet
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from PyQt4 import QtCore
|
from PyQt4 import QtCore
|
||||||
|
@ -82,13 +82,16 @@ class BGExtract(object):
|
|||||||
Receiver.send_message(u'openlp_process_events')
|
Receiver.send_message(u'openlp_process_events')
|
||||||
footnotes = soup.findAll(u'sup', u'footnote')
|
footnotes = soup.findAll(u'sup', u'footnote')
|
||||||
if footnotes:
|
if footnotes:
|
||||||
[footnote.extract() for footnote in footnotes]
|
for footnote in footnotes:
|
||||||
|
footnote.extract()
|
||||||
crossrefs = soup.findAll(u'sup', u'xref')
|
crossrefs = soup.findAll(u'sup', u'xref')
|
||||||
if crossrefs:
|
if crossrefs:
|
||||||
[crossref.extract() for crossref in crossrefs]
|
for crossref in crossrefs:
|
||||||
|
crossref.extract()
|
||||||
headings = soup.findAll(u'h5')
|
headings = soup.findAll(u'h5')
|
||||||
if headings:
|
if headings:
|
||||||
[heading.extract() for heading in headings]
|
for heading in headings:
|
||||||
|
heading.extract()
|
||||||
cleanup = [(re.compile('\s+'), lambda match: ' ')]
|
cleanup = [(re.compile('\s+'), lambda match: ' ')]
|
||||||
verses = BeautifulSoup(str(soup), markupMassage=cleanup)
|
verses = BeautifulSoup(str(soup), markupMassage=cleanup)
|
||||||
verse_list = {}
|
verse_list = {}
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.lib import SettingsTab, translate, Receiver
|
from openlp.core.lib import SettingsTab, translate, Receiver
|
||||||
from openlp.core.lib.ui import UiStrings, create_valign_combo
|
|
||||||
|
|
||||||
class ImageTab(SettingsTab):
|
class ImageTab(SettingsTab):
|
||||||
"""
|
"""
|
||||||
|
@ -33,7 +33,7 @@ from PyQt4 import QtCore, QtGui
|
|||||||
|
|
||||||
from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \
|
from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \
|
||||||
SettingsManager, translate, check_item_selected, check_directory_exists, \
|
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.lib.ui import UiStrings, critical_error_message_box
|
||||||
from openlp.core.utils import AppLocation, delete_file, get_images_filter
|
from openlp.core.utils import AppLocation, delete_file, get_images_filter
|
||||||
|
|
||||||
@ -127,13 +127,13 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
self.plugin.formparent.incrementProgressBar()
|
self.plugin.formparent.incrementProgressBar()
|
||||||
filename = os.path.split(unicode(imageFile))[1]
|
filename = os.path.split(unicode(imageFile))[1]
|
||||||
thumb = os.path.join(self.servicePath, filename)
|
thumb = os.path.join(self.servicePath, filename)
|
||||||
if os.path.exists(thumb):
|
if not os.path.exists(imageFile):
|
||||||
if self.validate(imageFile, thumb):
|
icon = build_icon(u':/general/general_delete.png')
|
||||||
|
else:
|
||||||
|
if validate_thumb(imageFile, thumb):
|
||||||
icon = build_icon(thumb)
|
icon = build_icon(thumb)
|
||||||
else:
|
else:
|
||||||
icon = build_icon(u':/general/general_delete.png')
|
icon = create_thumb(imageFile, thumb)
|
||||||
else:
|
|
||||||
icon = self.iconFromFile(imageFile, thumb)
|
|
||||||
item_name = QtGui.QListWidgetItem(filename)
|
item_name = QtGui.QListWidgetItem(filename)
|
||||||
item_name.setIcon(icon)
|
item_name.setIcon(icon)
|
||||||
item_name.setToolTip(imageFile)
|
item_name.setToolTip(imageFile)
|
||||||
|
@ -39,7 +39,7 @@ from openlp.core.lib.ui import UiStrings, critical_error_message_box
|
|||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
CLAPPERBOARD = QtGui.QPixmap(u':/media/media_video.png').toImage()
|
CLAPPERBOARD = QtGui.QImage(u':/media/media_video.png')
|
||||||
|
|
||||||
class MediaMediaItem(MediaManagerItem):
|
class MediaMediaItem(MediaManagerItem):
|
||||||
"""
|
"""
|
||||||
@ -95,14 +95,14 @@ class MediaMediaItem(MediaManagerItem):
|
|||||||
|
|
||||||
def onResetClick(self):
|
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.resetAction.setVisible(False)
|
||||||
self.plugin.liveController.display.resetVideo()
|
self.plugin.liveController.display.resetVideo()
|
||||||
|
|
||||||
def videobackgroundReplaced(self):
|
def videobackgroundReplaced(self):
|
||||||
"""
|
"""
|
||||||
Triggered by main display on change of serviceitem
|
Triggered by main display on change of serviceitem.
|
||||||
"""
|
"""
|
||||||
self.resetAction.setVisible(False)
|
self.resetAction.setVisible(False)
|
||||||
|
|
||||||
@ -179,8 +179,7 @@ class MediaMediaItem(MediaManagerItem):
|
|||||||
|
|
||||||
def mediaStateWait(self, mediaState):
|
def mediaStateWait(self, mediaState):
|
||||||
"""
|
"""
|
||||||
Wait for the video to change its state
|
Wait for the video to change its state. Wait no longer than 5 seconds.
|
||||||
Wait no longer than 5 seconds.
|
|
||||||
"""
|
"""
|
||||||
start = datetime.now()
|
start = datetime.now()
|
||||||
while self.mediaObject.state() != mediaState:
|
while self.mediaObject.state() != mediaState:
|
||||||
@ -198,7 +197,7 @@ class MediaMediaItem(MediaManagerItem):
|
|||||||
|
|
||||||
def onDeleteClick(self):
|
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',
|
if check_item_selected(self.listView, translate('MediaPlugin.MediaItem',
|
||||||
'You must select a media file to delete.')):
|
'You must select a media file to delete.')):
|
||||||
|
@ -32,7 +32,8 @@ import locale
|
|||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.lib import MediaManagerItem, build_icon, SettingsManager, \
|
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, \
|
from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
|
||||||
media_item_combo_box
|
media_item_combo_box
|
||||||
from openlp.plugins.presentations.lib import MessageListener
|
from openlp.plugins.presentations.lib import MessageListener
|
||||||
@ -193,10 +194,13 @@ class PresentationMediaItem(MediaManagerItem):
|
|||||||
doc.load_presentation()
|
doc.load_presentation()
|
||||||
preview = doc.get_thumbnail_path(1, True)
|
preview = doc.get_thumbnail_path(1, True)
|
||||||
doc.close_presentation()
|
doc.close_presentation()
|
||||||
if preview and self.validate(preview, thumb):
|
if not (preview and os.path.exists(preview)):
|
||||||
icon = build_icon(thumb)
|
|
||||||
else:
|
|
||||||
icon = build_icon(u':/general/general_delete.png')
|
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:
|
else:
|
||||||
if initialLoad:
|
if initialLoad:
|
||||||
icon = build_icon(u':/general/general_delete.png')
|
icon = build_icon(u':/general/general_delete.png')
|
||||||
|
@ -31,7 +31,8 @@ import shutil
|
|||||||
|
|
||||||
from PyQt4 import QtCore
|
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
|
from openlp.core.utils import AppLocation
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -97,8 +98,7 @@ class PresentationDocument(object):
|
|||||||
self.slidenumber = 0
|
self.slidenumber = 0
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.filepath = name
|
self.filepath = name
|
||||||
if not os.path.isdir(self.get_thumbnail_folder()):
|
check_directory_exists(self.get_thumbnail_folder())
|
||||||
os.mkdir(self.get_thumbnail_folder())
|
|
||||||
|
|
||||||
def load_presentation(self):
|
def load_presentation(self):
|
||||||
"""
|
"""
|
||||||
@ -145,15 +145,13 @@ class PresentationDocument(object):
|
|||||||
|
|
||||||
def check_thumbnails(self):
|
def check_thumbnails(self):
|
||||||
"""
|
"""
|
||||||
Returns true if the thumbnail images look to exist and are more
|
Returns ``True`` if the thumbnail images exist and are more recent than
|
||||||
recent than the powerpoint
|
the powerpoint file.
|
||||||
"""
|
"""
|
||||||
lastimage = self.get_thumbnail_path(self.get_slide_count(), True)
|
lastimage = self.get_thumbnail_path(self.get_slide_count(), True)
|
||||||
if not (lastimage and os.path.isfile(lastimage)):
|
if not (lastimage and os.path.isfile(lastimage)):
|
||||||
return False
|
return False
|
||||||
imgdate = os.stat(lastimage).st_mtime
|
return validate_thumb(self.filepath, lastimage)
|
||||||
pptdate = os.stat(self.filepath).st_mtime
|
|
||||||
return imgdate >= pptdate
|
|
||||||
|
|
||||||
def close_presentation(self):
|
def close_presentation(self):
|
||||||
"""
|
"""
|
||||||
@ -246,8 +244,8 @@ class PresentationDocument(object):
|
|||||||
if self.check_thumbnails():
|
if self.check_thumbnails():
|
||||||
return
|
return
|
||||||
if os.path.isfile(file):
|
if os.path.isfile(file):
|
||||||
img = resize_image(file, 320, 240)
|
thumb_path = self.get_thumbnail_path(idx, False)
|
||||||
img.save(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):
|
def get_thumbnail_path(self, slide_no, check_exists):
|
||||||
"""
|
"""
|
||||||
@ -387,10 +385,8 @@ class PresentationController(object):
|
|||||||
AppLocation.get_section_data_path(self.settings_section),
|
AppLocation.get_section_data_path(self.settings_section),
|
||||||
u'thumbnails')
|
u'thumbnails')
|
||||||
self.thumbnail_prefix = u'slide'
|
self.thumbnail_prefix = u'slide'
|
||||||
if not os.path.isdir(self.thumbnail_folder):
|
check_directory_exists(self.thumbnail_folder)
|
||||||
os.makedirs(self.thumbnail_folder)
|
check_directory_exists(self.temp_folder)
|
||||||
if not os.path.isdir(self.temp_folder):
|
|
||||||
os.makedirs(self.temp_folder)
|
|
||||||
|
|
||||||
def enabled(self):
|
def enabled(self):
|
||||||
"""
|
"""
|
||||||
|
@ -87,7 +87,7 @@ class PresentationPlugin(Plugin):
|
|||||||
to close down their applications and release resources.
|
to close down their applications and release resources.
|
||||||
"""
|
"""
|
||||||
log.info(u'Plugin Finalise')
|
log.info(u'Plugin Finalise')
|
||||||
#Ask each controller to tidy up
|
# Ask each controller to tidy up.
|
||||||
for key in self.controllers:
|
for key in self.controllers:
|
||||||
controller = self.controllers[key]
|
controller = self.controllers[key]
|
||||||
if controller.enabled():
|
if controller.enabled():
|
||||||
|
@ -121,11 +121,11 @@ window.OpenLP = {
|
|||||||
$("#nextslide").html(text);
|
$("#nextslide").html(text);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateClock: function() {
|
updateClock: function(data) {
|
||||||
var div = $("#clock");
|
var div = $("#clock");
|
||||||
var t = new Date();
|
var t = new Date();
|
||||||
var h = t.getHours();
|
var h = t.getHours();
|
||||||
if (h > 12)
|
if (data.results.twelve && h > 12)
|
||||||
h = h - 12;
|
h = h - 12;
|
||||||
var m = t.getMinutes();
|
var m = t.getMinutes();
|
||||||
if (m < 10)
|
if (m < 10)
|
||||||
@ -136,7 +136,7 @@ window.OpenLP = {
|
|||||||
$.getJSON(
|
$.getJSON(
|
||||||
"/api/poll",
|
"/api/poll",
|
||||||
function (data, status) {
|
function (data, status) {
|
||||||
OpenLP.updateClock();
|
OpenLP.updateClock(data);
|
||||||
if (OpenLP.currentItem != data.results.item) {
|
if (OpenLP.currentItem != data.results.item) {
|
||||||
OpenLP.currentItem = data.results.item;
|
OpenLP.currentItem = data.results.item;
|
||||||
OpenLP.loadSlides();
|
OpenLP.loadSlides();
|
||||||
|
@ -315,7 +315,7 @@ class HttpConnection(object):
|
|||||||
"""
|
"""
|
||||||
log.debug(u'ready to read socket')
|
log.debug(u'ready to read socket')
|
||||||
if self.socket.canReadLine():
|
if self.socket.canReadLine():
|
||||||
data = unicode(self.socket.readLine())
|
data = unicode(self.socket.readLine()).encode(u'utf-8')
|
||||||
log.debug(u'received: ' + data)
|
log.debug(u'received: ' + data)
|
||||||
words = data.split(u' ')
|
words = data.split(u' ')
|
||||||
response = None
|
response = None
|
||||||
@ -397,7 +397,9 @@ class HttpConnection(object):
|
|||||||
result = {
|
result = {
|
||||||
u'slide': self.parent.current_slide or 0,
|
u'slide': self.parent.current_slide or 0,
|
||||||
u'item': self.parent.current_item._uuid \
|
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}),
|
return HttpResponse(json.dumps({u'results': result}),
|
||||||
{u'Content-Type': u'application/json'})
|
{u'Content-Type': u'application/json'})
|
||||||
|
@ -57,6 +57,9 @@ class RemoteTab(SettingsTab):
|
|||||||
QtCore.QObject.connect(self.addressEdit,
|
QtCore.QObject.connect(self.addressEdit,
|
||||||
QtCore.SIGNAL(u'textChanged(const QString&)'), self.setUrls)
|
QtCore.SIGNAL(u'textChanged(const QString&)'), self.setUrls)
|
||||||
self.serverSettingsLayout.addRow(self.addressLabel, self.addressEdit)
|
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 = QtGui.QLabel(self.serverSettingsGroupBox)
|
||||||
self.portLabel.setObjectName(u'portLabel')
|
self.portLabel.setObjectName(u'portLabel')
|
||||||
self.portSpinBox = QtGui.QSpinBox(self.serverSettingsGroupBox)
|
self.portSpinBox = QtGui.QSpinBox(self.serverSettingsGroupBox)
|
||||||
@ -80,6 +83,9 @@ class RemoteTab(SettingsTab):
|
|||||||
self.leftLayout.addWidget(self.serverSettingsGroupBox)
|
self.leftLayout.addWidget(self.serverSettingsGroupBox)
|
||||||
self.leftLayout.addStretch()
|
self.leftLayout.addStretch()
|
||||||
self.rightLayout.addStretch()
|
self.rightLayout.addStretch()
|
||||||
|
QtCore.QObject.connect(self.twelveHourCheckBox,
|
||||||
|
QtCore.SIGNAL(u'stateChanged(int)'),
|
||||||
|
self.onTwelveHourCheckBoxChanged)
|
||||||
|
|
||||||
def retranslateUi(self):
|
def retranslateUi(self):
|
||||||
self.serverSettingsGroupBox.setTitle(
|
self.serverSettingsGroupBox.setTitle(
|
||||||
@ -92,6 +98,9 @@ class RemoteTab(SettingsTab):
|
|||||||
'Remote URL:'))
|
'Remote URL:'))
|
||||||
self.stageUrlLabel.setText(translate('RemotePlugin.RemoteTab',
|
self.stageUrlLabel.setText(translate('RemotePlugin.RemoteTab',
|
||||||
'Stage view URL:'))
|
'Stage view URL:'))
|
||||||
|
self.twelveHourCheckBox.setText(
|
||||||
|
translate('RemotePlugin.RemoteTab',
|
||||||
|
'Display stage time in 12h format'))
|
||||||
|
|
||||||
def setUrls(self):
|
def setUrls(self):
|
||||||
ipAddress = u'localhost'
|
ipAddress = u'localhost'
|
||||||
@ -123,6 +132,10 @@ class RemoteTab(SettingsTab):
|
|||||||
self.addressEdit.setText(
|
self.addressEdit.setText(
|
||||||
QtCore.QSettings().value(self.settingsSection + u'/ip address',
|
QtCore.QSettings().value(self.settingsSection + u'/ip address',
|
||||||
QtCore.QVariant(ZERO_URL)).toString())
|
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()
|
self.setUrls()
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
@ -130,3 +143,11 @@ class RemoteTab(SettingsTab):
|
|||||||
QtCore.QVariant(self.portSpinBox.value()))
|
QtCore.QVariant(self.portSpinBox.value()))
|
||||||
QtCore.QSettings().setValue(self.settingsSection + u'/ip address',
|
QtCore.QSettings().setValue(self.settingsSection + u'/ip address',
|
||||||
QtCore.QVariant(self.addressEdit.text()))
|
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
|
||||||
|
@ -240,8 +240,9 @@ class SongImportForm(OpenLPWizard):
|
|||||||
self.formatLabel.setText(WizardStrings.FormatLabel)
|
self.formatLabel.setText(WizardStrings.FormatLabel)
|
||||||
self.formatComboBox.setItemText(SongFormat.OpenLP2, UiStrings().OLPV2)
|
self.formatComboBox.setItemText(SongFormat.OpenLP2, UiStrings().OLPV2)
|
||||||
self.formatComboBox.setItemText(SongFormat.OpenLP1, UiStrings().OLPV1)
|
self.formatComboBox.setItemText(SongFormat.OpenLP1, UiStrings().OLPV1)
|
||||||
self.formatComboBox.setItemText(
|
self.formatComboBox.setItemText(SongFormat.OpenLyrics,
|
||||||
SongFormat.OpenLyrics, WizardStrings.OL)
|
translate('SongsPlugin.ImportWizardForm',
|
||||||
|
'OpenLyrics or OpenLP 2.0 Exported Song'))
|
||||||
self.formatComboBox.setItemText(SongFormat.OpenSong, WizardStrings.OS)
|
self.formatComboBox.setItemText(SongFormat.OpenSong, WizardStrings.OS)
|
||||||
self.formatComboBox.setItemText(
|
self.formatComboBox.setItemText(
|
||||||
SongFormat.WordsOfWorship, WizardStrings.WoW)
|
SongFormat.WordsOfWorship, WizardStrings.WoW)
|
||||||
@ -508,7 +509,8 @@ class SongImportForm(OpenLPWizard):
|
|||||||
Get OpenLyrics song database files
|
Get OpenLyrics song database files
|
||||||
"""
|
"""
|
||||||
self.getFiles(WizardStrings.OpenTypeFile % WizardStrings.OL,
|
self.getFiles(WizardStrings.OpenTypeFile % WizardStrings.OL,
|
||||||
self.openLyricsFileListWidget)
|
self.openLyricsFileListWidget, u'%s (*.xml)' %
|
||||||
|
translate('SongsPlugin.ImportWizardForm', 'OpenLyrics Files'))
|
||||||
|
|
||||||
def onOpenLyricsRemoveButtonClicked(self):
|
def onOpenLyricsRemoveButtonClicked(self):
|
||||||
"""
|
"""
|
||||||
|
@ -333,5 +333,6 @@ class CCLIFileImport(SongImport):
|
|||||||
if len(author_list) < 2:
|
if len(author_list) < 2:
|
||||||
author_list = song_author.split(u'|')
|
author_list = song_author.split(u'|')
|
||||||
# Clean spaces before and after author names.
|
# 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()
|
return self.finish()
|
||||||
|
@ -258,7 +258,7 @@ class EasiSlidesImport(SongImport):
|
|||||||
verses[reg][vt][vn] = {}
|
verses[reg][vt][vn] = {}
|
||||||
if not verses[reg][vt][vn].has_key(inst):
|
if not verses[reg][vt][vn].has_key(inst):
|
||||||
verses[reg][vt][vn][inst] = []
|
verses[reg][vt][vn][inst] = []
|
||||||
words = self.tidy_text(line)
|
words = self.tidyText(line)
|
||||||
verses[reg][vt][vn][inst].append(words)
|
verses[reg][vt][vn][inst].append(words)
|
||||||
# done parsing
|
# done parsing
|
||||||
|
|
||||||
|
@ -397,7 +397,8 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
try:
|
try:
|
||||||
os.remove(media_file.file_name)
|
os.remove(media_file.file_name)
|
||||||
except:
|
except:
|
||||||
log.exception('Could not remove file: %s', audio)
|
log.exception('Could not remove file: %s',
|
||||||
|
media_file.file_name)
|
||||||
try:
|
try:
|
||||||
save_path = os.path.join(AppLocation.get_section_data_path(
|
save_path = os.path.join(AppLocation.get_section_data_path(
|
||||||
self.plugin.name), 'audio', str(item_id))
|
self.plugin.name), 'audio', str(item_id))
|
||||||
@ -428,11 +429,9 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
|
|
||||||
def generateSlideData(self, service_item, item=None, xmlVersion=False,
|
def generateSlideData(self, service_item, item=None, xmlVersion=False,
|
||||||
remote=False):
|
remote=False):
|
||||||
log.debug(u'generateSlideData: %s, %s, %s' % (service_item, item, self.remoteSong))
|
log.debug(u'generateSlideData: %s, %s, %s' %
|
||||||
# The ``None`` below is a workaround for bug #812289 - I think that Qt
|
(service_item, item, self.remoteSong))
|
||||||
# deletes the item somewhere along the line because the user is taking
|
item_id = self._getIdOfItemToGenerate(item, self.remoteSong)
|
||||||
# so long to update their item (or something weird like that).
|
|
||||||
item_id = self._getIdOfItemToGenerate(None, self.remoteSong)
|
|
||||||
service_item.add_capability(ItemCapabilities.CanEdit)
|
service_item.add_capability(ItemCapabilities.CanEdit)
|
||||||
service_item.add_capability(ItemCapabilities.CanPreview)
|
service_item.add_capability(ItemCapabilities.CanPreview)
|
||||||
service_item.add_capability(ItemCapabilities.CanLoop)
|
service_item.add_capability(ItemCapabilities.CanLoop)
|
||||||
@ -512,7 +511,8 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
# Add the audio file to the service item.
|
# Add the audio file to the service item.
|
||||||
if len(song.media_files) > 0:
|
if len(song.media_files) > 0:
|
||||||
service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
|
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
|
return True
|
||||||
|
|
||||||
def serviceLoad(self, item):
|
def serviceLoad(self, item):
|
||||||
|
@ -30,7 +30,6 @@ songs from the database to the OpenLyrics format.
|
|||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ class SofImport(OooImport):
|
|||||||
into line
|
into line
|
||||||
"""
|
"""
|
||||||
text = textportion.getString()
|
text = textportion.getString()
|
||||||
text = self.tidy_text(text)
|
text = self.tidyText(text)
|
||||||
if text.strip() == u'':
|
if text.strip() == u'':
|
||||||
return text
|
return text
|
||||||
if textportion.CharWeight == BOLD:
|
if textportion.CharWeight == BOLD:
|
||||||
|
@ -29,7 +29,7 @@ The :mod:`upgrade` module provides a way for the database and schema that is the
|
|||||||
backend for the Songs plugin
|
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 sqlalchemy.sql.expression import func
|
||||||
from migrate import changeset
|
from migrate import changeset
|
||||||
from migrate.changeset.constraint import ForeignKeyConstraint
|
from migrate.changeset.constraint import ForeignKeyConstraint
|
||||||
|
@ -60,7 +60,7 @@ The XML of an `OpenLyrics <http://openlyrics.info/>`_ song looks like this::
|
|||||||
</lyrics>
|
</lyrics>
|
||||||
</song>
|
</song>
|
||||||
"""
|
"""
|
||||||
|
import cgi
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
@ -257,11 +257,12 @@ class OpenLyrics(object):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
IMPLEMENTED_VERSION = u'0.8'
|
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):
|
def __init__(self, manager):
|
||||||
self.manager = 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):
|
def song_to_xml(self, song):
|
||||||
"""
|
"""
|
||||||
@ -334,7 +335,8 @@ class OpenLyrics(object):
|
|||||||
if u'lang' in verse[0]:
|
if u'lang' in verse[0]:
|
||||||
verse_element.set(u'lang', verse[0][u'lang'])
|
verse_element.set(u'lang', verse[0][u'lang'])
|
||||||
# Create a list with all "virtual" verses.
|
# 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):
|
for index, virtual_verse in enumerate(virtual_verses):
|
||||||
# Add formatting tags to text
|
# Add formatting tags to text
|
||||||
lines_element = self._add_text_with_tags_to_lines(verse_element,
|
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):
|
def _add_tag_to_formatting(self, tag_name, tags_element):
|
||||||
"""
|
"""
|
||||||
Add new formatting tag to the element ``<format>``
|
Add new formatting tag to the element ``<format>`` if the tag is not
|
||||||
if the tag is not present yet.
|
present yet.
|
||||||
"""
|
"""
|
||||||
available_tags = FormattingTags.get_html_tags()
|
available_tags = FormattingTags.get_html_tags()
|
||||||
start_tag = '{%s}' % tag_name
|
start_tag = '{%s}' % tag_name
|
||||||
for t in available_tags:
|
for tag in available_tags:
|
||||||
if t[u'start tag'] == start_tag:
|
if tag[u'start tag'] == start_tag:
|
||||||
# Create new formatting tag in openlyrics xml.
|
# Create new formatting tag in openlyrics xml.
|
||||||
el = self._add_text_to_element(u'tag', tags_element)
|
element = self._add_text_to_element(u'tag', tags_element)
|
||||||
el.set(u'name', tag_name)
|
element.set(u'name', tag_name)
|
||||||
el_open = self._add_text_to_element(u'open', el)
|
element_open = self._add_text_to_element(u'open', element)
|
||||||
el_open.text = etree.CDATA(t[u'start html'])
|
element_open.text = etree.CDATA(tag[u'start html'])
|
||||||
# Check if formatting tag contains end tag. Some formatting
|
# Check if formatting tag contains end tag. Some formatting
|
||||||
# tags e.g. {br} has only start tag. If no end tag is present
|
# tags e.g. {br} has only start tag. If no end tag is present
|
||||||
# <close> element has not to be in OpenLyrics xml.
|
# <close> element has not to be in OpenLyrics xml.
|
||||||
if t['end tag']:
|
if tag['end tag']:
|
||||||
el_close = self._add_text_to_element(u'close', el)
|
element_close = self._add_text_to_element(u'close', element)
|
||||||
el_close.text = etree.CDATA(t[u'end html'])
|
element_close.text = etree.CDATA(tag[u'end html'])
|
||||||
|
|
||||||
def _add_text_with_tags_to_lines(self, verse_element, text, tags_element):
|
def _add_text_with_tags_to_lines(self, verse_element, text, tags_element):
|
||||||
"""
|
"""
|
||||||
Convert text with formatting tags from OpenLP format to OpenLyrics
|
Convert text with formatting tags from OpenLP format to OpenLyrics
|
||||||
format and append it to element ``<lines>``.
|
format and append it to element ``<lines>``.
|
||||||
"""
|
"""
|
||||||
start_tags = self.start_tags_regex.findall(text)
|
start_tags = OpenLyrics.START_TAGS_REGEX.findall(text)
|
||||||
end_tags = self.end_tags_regex.findall(text)
|
end_tags = OpenLyrics.END_TAGS_REGEX.findall(text)
|
||||||
# Replace start tags with xml syntax.
|
# Replace start tags with xml syntax.
|
||||||
for tag in start_tags:
|
for tag in start_tags:
|
||||||
# Tags already converted to xml structure.
|
# Tags already converted to xml structure.
|
||||||
@ -442,12 +444,11 @@ class OpenLyrics(object):
|
|||||||
if tag not in xml_tags:
|
if tag not in xml_tags:
|
||||||
self._add_tag_to_formatting(tag, tags_element)
|
self._add_tag_to_formatting(tag, tags_element)
|
||||||
# Replace end tags.
|
# Replace end tags.
|
||||||
for t in end_tags:
|
for tag in end_tags:
|
||||||
text = text.replace(u'{/%s}' % t, u'</tag>')
|
text = text.replace(u'{/%s}' % tag, u'</tag>')
|
||||||
# Replace \n with <br/>.
|
# Replace \n with <br/>.
|
||||||
text = text.replace(u'\n', u'<br/>')
|
text = text.replace(u'\n', u'<br/>')
|
||||||
text = u'<lines>' + text + u'</lines>'
|
element = etree.XML(u'<lines>%s</lines>' % text)
|
||||||
element = etree.XML(text)
|
|
||||||
verse_element.append(element)
|
verse_element.append(element)
|
||||||
return element
|
return element
|
||||||
|
|
||||||
@ -692,7 +693,7 @@ class OpenLyrics(object):
|
|||||||
verse_tag = verse_def[0]
|
verse_tag = verse_def[0]
|
||||||
else:
|
else:
|
||||||
verse_tag = VerseType.Tags[VerseType.Other]
|
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
|
# OpenLyrics allows e. g. "c", but we need "c1". However, this does
|
||||||
# not correct the verse order.
|
# not correct the verse order.
|
||||||
if not verse_number:
|
if not verse_number:
|
||||||
|
@ -28,7 +28,6 @@
|
|||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.lib import translate
|
from openlp.core.lib import translate
|
||||||
from openlp.core.lib.ui import create_accept_reject_button_box
|
|
||||||
|
|
||||||
class Ui_SongUsageDeleteDialog(object):
|
class Ui_SongUsageDeleteDialog(object):
|
||||||
def setupUi(self, songUsageDeleteDialog):
|
def setupUi(self, songUsageDeleteDialog):
|
||||||
|
81
resources/forms/themelayout.ui
Normal file
81
resources/forms/themelayout.ui
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>ThemeLayout</class>
|
||||||
|
<widget class="QDialog" name="ThemeLayout">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Theme Layout</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>50</x>
|
||||||
|
<y>260</y>
|
||||||
|
<width>341</width>
|
||||||
|
<height>32</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
<widget class="QLabel" name="themeDisplayLabel">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>20</x>
|
||||||
|
<y>10</y>
|
||||||
|
<width>361</width>
|
||||||
|
<height>231</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>ThemeLayout</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>ThemeLayout</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
@ -60,8 +60,6 @@
|
|||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="slides">
|
<qresource prefix="slides">
|
||||||
<file>slide_close.png</file>
|
<file>slide_close.png</file>
|
||||||
<file>slide_first.png</file>
|
|
||||||
<file>slide_last.png</file>
|
|
||||||
<file>slide_next.png</file>
|
<file>slide_next.png</file>
|
||||||
<file>slide_blank.png</file>
|
<file>slide_blank.png</file>
|
||||||
<file>slide_desktop.png</file>
|
<file>slide_desktop.png</file>
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 653 B |
Binary file not shown.
Before Width: | Height: | Size: 666 B |
@ -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"""
|
Root: HKCR; Subkey: "OpenLP\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\OpenLP.exe"" ""%1"""
|
||||||
|
|
||||||
[Code]
|
[Code]
|
||||||
function IsModuleLoaded(modulename: String ): Boolean;
|
function IsModuleLoaded(modulename: AnsiString ): Boolean;
|
||||||
external 'IsModuleLoaded@files:psvince.dll stdcall';
|
external 'IsModuleLoaded@files:psvince.dll stdcall';
|
||||||
|
|
||||||
function GetUninstallString(): String;
|
function GetUninstallString(): String;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||||
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
# 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, #
|
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
|
@ -5,11 +5,11 @@
|
|||||||
# OpenLP - Open Source Lyrics Projection #
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||||
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
|
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode #
|
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
# Woldsund #
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
# This program is free software; you can redistribute it and/or modify it #
|
# 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 #
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||||
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
# 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, #
|
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||||
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
# 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, #
|
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||||
# --------------------------------------------------------------------------- #
|
# --------------------------------------------------------------------------- #
|
||||||
|
Loading…
Reference in New Issue
Block a user