forked from openlp/openlp
bugfixing and sync
This commit is contained in:
commit
2d870bd90c
@ -7,7 +7,7 @@
|
||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||
# Armin Köhler, Joshua Millar, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
@ -144,6 +144,59 @@ def image_to_byte(image):
|
||||
# convert to base64 encoding so does not get missed!
|
||||
return byte_array.toBase64()
|
||||
|
||||
def create_thumb(image_path, thumb_path, return_icon=True, size=None):
|
||||
"""
|
||||
Create a thumbnail from the given image path and depending on
|
||||
``return_icon`` it returns an icon from this thumb.
|
||||
|
||||
``image_path``
|
||||
The image file to create the icon from.
|
||||
|
||||
``thumb_path``
|
||||
The filename to save the thumbnail to.
|
||||
|
||||
``return_icon``
|
||||
States if an icon should be build and returned from the thumb. Defaults
|
||||
to ``True``.
|
||||
|
||||
``size``
|
||||
Allows to state a own size to use. Defaults to ``None``, which means
|
||||
that a default height of 88 is used.
|
||||
"""
|
||||
ext = os.path.splitext(thumb_path)[1].lower()
|
||||
reader = QtGui.QImageReader(image_path)
|
||||
if size is None:
|
||||
ratio = float(reader.size().width()) / float(reader.size().height())
|
||||
reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88))
|
||||
else:
|
||||
reader.setScaledSize(size)
|
||||
thumb = reader.read()
|
||||
thumb.save(thumb_path, ext[1:])
|
||||
if not return_icon:
|
||||
return
|
||||
if os.path.exists(thumb_path):
|
||||
return build_icon(unicode(thumb_path))
|
||||
# Fallback for files with animation support.
|
||||
return build_icon(unicode(image_path))
|
||||
|
||||
def validate_thumb(file_path, thumb_path):
|
||||
"""
|
||||
Validates whether an file's thumb still exists and if is up to date.
|
||||
**Note**, you must **not** call this function, before checking the
|
||||
existence of the file.
|
||||
|
||||
``file_path``
|
||||
The path to the file. The file **must** exist!
|
||||
|
||||
``thumb_path``
|
||||
The path to the thumb.
|
||||
"""
|
||||
if not os.path.exists(unicode(thumb_path)):
|
||||
return False
|
||||
image_date = os.stat(unicode(file_path)).st_mtime
|
||||
thumb_date = os.stat(unicode(thumb_path)).st_mtime
|
||||
return image_date <= thumb_date
|
||||
|
||||
def resize_image(image_path, width, height, background=u'#000000'):
|
||||
"""
|
||||
Resize an image to fit on the current screen.
|
||||
@ -158,7 +211,7 @@ def resize_image(image_path, width, height, background=u'#000000'):
|
||||
The new image height.
|
||||
|
||||
``background``
|
||||
The background colour defaults to black.
|
||||
The background colour. Defaults to black.
|
||||
|
||||
DO NOT REMOVE THE DEFAULT BACKGROUND VALUE!
|
||||
"""
|
||||
|
@ -51,9 +51,6 @@ class EventReceiver(QtCore.QObject):
|
||||
``config_screen_changed``
|
||||
The display monitor has been changed
|
||||
|
||||
``slidecontroller_{live|preview}_first``
|
||||
Moves to the first slide
|
||||
|
||||
``slidecontroller_{live|preview}_next``
|
||||
Moves to the next slide
|
||||
|
||||
@ -66,9 +63,6 @@ class EventReceiver(QtCore.QObject):
|
||||
``slidecontroller_{live|preview}_previous_noloop``
|
||||
Moves to the previous slide, without auto advance
|
||||
|
||||
``slidecontroller_{live|preview}_last``
|
||||
Moves to the last slide
|
||||
|
||||
``slidecontroller_{live|preview}_set``
|
||||
Moves to a specific slide, by index
|
||||
|
||||
@ -82,11 +76,6 @@ class EventReceiver(QtCore.QObject):
|
||||
``slidecontroller_{live|preview}_changed``
|
||||
Broadcasts that the slidecontroller has changed the current slide
|
||||
|
||||
``slidecontroller_{live|preview}_text_request``
|
||||
Request the text for the current item in the controller
|
||||
Returns a slidecontroller_{live|preview}_text_response with an
|
||||
array of dictionaries with the tag and verse text
|
||||
|
||||
``slidecontroller_{live|preview}_blank``
|
||||
Request that the output screen is blanked
|
||||
|
||||
|
@ -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
|
||||
return filelist
|
||||
|
||||
def validate(self, image, thumb):
|
||||
"""
|
||||
Validates whether an image still exists and, if it does, is the
|
||||
thumbnail representation of the image up to date.
|
||||
"""
|
||||
if not os.path.exists(unicode(image)):
|
||||
return False
|
||||
if os.path.exists(thumb):
|
||||
imageDate = os.stat(unicode(image)).st_mtime
|
||||
thumbDate = os.stat(unicode(thumb)).st_mtime
|
||||
# If image has been updated rebuild icon
|
||||
if imageDate > thumbDate:
|
||||
self.iconFromFile(image, thumb)
|
||||
else:
|
||||
self.iconFromFile(image, thumb)
|
||||
return True
|
||||
|
||||
def iconFromFile(self, image_path, thumb_path):
|
||||
"""
|
||||
Create a thumbnail icon from a given image.
|
||||
|
||||
``image_path``
|
||||
The image file to create the icon from.
|
||||
|
||||
``thumb_path``
|
||||
The filename to save the thumbnail to.
|
||||
"""
|
||||
ext = os.path.splitext(thumb_path)[1].lower()
|
||||
reader = QtGui.QImageReader(image_path)
|
||||
ratio = float(reader.size().width()) / float(reader.size().height())
|
||||
reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88))
|
||||
thumb = reader.read()
|
||||
thumb.save(thumb_path, ext[1:])
|
||||
if os.path.exists(thumb_path):
|
||||
return build_icon(unicode(thumb_path))
|
||||
# Fallback for files with animation support.
|
||||
return build_icon(unicode(image_path))
|
||||
|
||||
def loadList(self, list):
|
||||
raise NotImplementedError(u'MediaManagerItem.loadList needs to be '
|
||||
u'defined by the plugin')
|
||||
|
@ -57,14 +57,14 @@ class Renderer(object):
|
||||
|
||||
def __init__(self, imageManager, themeManager, plugins):
|
||||
"""
|
||||
Initialise the render manager.
|
||||
Initialise the renderer.
|
||||
|
||||
``imageManager``
|
||||
A ImageManager instance which takes care of e. g. caching and resizing
|
||||
A imageManager instance which takes care of e. g. caching and resizing
|
||||
images.
|
||||
|
||||
``themeManager``
|
||||
The ThemeManager instance, used to get the current theme details.
|
||||
The themeManager instance, used to get the current theme details.
|
||||
"""
|
||||
log.debug(u'Initialisation started')
|
||||
self.themeManager = themeManager
|
||||
@ -83,7 +83,7 @@ class Renderer(object):
|
||||
|
||||
def update_display(self):
|
||||
"""
|
||||
Updates the render manager's information about the current screen.
|
||||
Updates the renderer's information about the current screen.
|
||||
"""
|
||||
log.debug(u'Update Display')
|
||||
self._calculate_default()
|
||||
@ -204,7 +204,8 @@ class Renderer(object):
|
||||
if not self.force_page:
|
||||
self.display.buildHtml(serviceItem)
|
||||
raw_html = serviceItem.get_rendered_frame(0)
|
||||
preview = self.display.text(raw_html)
|
||||
self.display.text(raw_html)
|
||||
preview = self.display.preview()
|
||||
# Reset the real screen size for subsequent render requests
|
||||
self._calculate_default()
|
||||
return preview
|
||||
@ -307,21 +308,37 @@ class Renderer(object):
|
||||
The theme to build a text block for.
|
||||
"""
|
||||
log.debug(u'_build_text_rectangle')
|
||||
main_rect = None
|
||||
footer_rect = None
|
||||
main_rect = self.get_main_rectangle(theme)
|
||||
footer_rect = self.get_footer_rectangle(theme)
|
||||
self._set_text_rectangle(main_rect, footer_rect)
|
||||
|
||||
def get_main_rectangle(self, theme):
|
||||
"""
|
||||
Calculates the placement and size of the main rectangle.
|
||||
|
||||
``theme``
|
||||
The theme information
|
||||
"""
|
||||
if not theme.font_main_override:
|
||||
main_rect = QtCore.QRect(10, 0, self.width - 20, self.footer_start)
|
||||
return QtCore.QRect(10, 0, self.width - 20, self.footer_start)
|
||||
else:
|
||||
main_rect = QtCore.QRect(theme.font_main_x, theme.font_main_y,
|
||||
return QtCore.QRect(theme.font_main_x, theme.font_main_y,
|
||||
theme.font_main_width - 1, theme.font_main_height - 1)
|
||||
|
||||
def get_footer_rectangle(self, theme):
|
||||
"""
|
||||
Calculates the placement and size of the footer rectangle.
|
||||
|
||||
``theme``
|
||||
The theme information
|
||||
"""
|
||||
if not theme.font_footer_override:
|
||||
footer_rect = QtCore.QRect(10, self.footer_start, self.width - 20,
|
||||
return QtCore.QRect(10, self.footer_start, self.width - 20,
|
||||
self.height - self.footer_start)
|
||||
else:
|
||||
footer_rect = QtCore.QRect(theme.font_footer_x,
|
||||
return QtCore.QRect(theme.font_footer_x,
|
||||
theme.font_footer_y, theme.font_footer_width - 1,
|
||||
theme.font_footer_height - 1)
|
||||
self._set_text_rectangle(main_rect, footer_rect)
|
||||
|
||||
def _set_text_rectangle(self, rect_main, rect_footer):
|
||||
"""
|
||||
|
@ -122,9 +122,8 @@ class ServiceItem(object):
|
||||
|
||||
def _new_item(self):
|
||||
"""
|
||||
Method to set the internal id of the item
|
||||
This is used to compare service items to see if they are
|
||||
the same
|
||||
Method to set the internal id of the item. This is used to compare
|
||||
service items to see if they are the same.
|
||||
"""
|
||||
self._uuid = unicode(uuid.uuid1())
|
||||
|
||||
@ -160,9 +159,8 @@ class ServiceItem(object):
|
||||
def render(self, use_override=False):
|
||||
"""
|
||||
The render method is what generates the frames for the screen and
|
||||
obtains the display information from the renderemanager.
|
||||
At this point all the slides are built for the given
|
||||
display size.
|
||||
obtains the display information from the renderer. At this point all
|
||||
slides are built for the given display size.
|
||||
"""
|
||||
log.debug(u'Render called')
|
||||
self._display_frames = []
|
||||
@ -364,6 +362,11 @@ class ServiceItem(object):
|
||||
"""
|
||||
self._uuid = other._uuid
|
||||
self.notes = other.notes
|
||||
# Copy theme over if present.
|
||||
if other.theme is not None:
|
||||
self.theme = other.theme
|
||||
self._new_item()
|
||||
self.render()
|
||||
if self.is_capable(ItemCapabilities.HasBackgroundAudio):
|
||||
log.debug(self.background_audio)
|
||||
|
||||
|
@ -54,6 +54,7 @@ class HideMode(object):
|
||||
|
||||
from firsttimeform import FirstTimeForm
|
||||
from firsttimelanguageform import FirstTimeLanguageForm
|
||||
from themelayoutform import ThemeLayoutForm
|
||||
from themeform import ThemeForm
|
||||
from filerenameform import FileRenameForm
|
||||
from starttimeform import StartTimeForm
|
||||
|
@ -56,7 +56,6 @@ except ImportError:
|
||||
SQLITE_VERSION = u'-'
|
||||
|
||||
from openlp.core.lib import translate, SettingsManager
|
||||
from openlp.core.lib.mailto import mailto
|
||||
from openlp.core.lib.ui import UiStrings
|
||||
from openlp.core.utils import get_application_version
|
||||
|
||||
@ -159,12 +158,12 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog):
|
||||
if u':' in line:
|
||||
exception = line.split(u'\n')[-1].split(u':')[0]
|
||||
subject = u'Bug report: %s in %s' % (exception, source)
|
||||
mailto_url = QtCore.QUrl(u'mailto:bugs@openlp.org')
|
||||
mailto_url.addQueryItem(u'subject', subject)
|
||||
mailto_url.addQueryItem(u'body', body % content)
|
||||
if self.fileAttachment:
|
||||
mailto(address=u'bugs@openlp.org', subject=subject,
|
||||
body=body % content, attach=self.fileAttachment)
|
||||
else:
|
||||
mailto(address=u'bugs@openlp.org', subject=subject,
|
||||
body=body % content)
|
||||
mailto_url.addQueryItem(u'attach', self.fileAttachment)
|
||||
QtGui.QDesktopServices.openUrl(mailto_url)
|
||||
|
||||
def onDescriptionUpdated(self):
|
||||
count = int(20 - len(self.descriptionTextEdit.toPlainText()))
|
||||
|
@ -200,7 +200,6 @@ class MainDisplay(Display):
|
||||
self.setGeometry(self.screen[u'size'])
|
||||
self.frame.evaluateJavaScript(u'show_text("%s")' %
|
||||
slide.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
|
||||
return self.preview()
|
||||
|
||||
def alert(self, text):
|
||||
"""
|
||||
@ -258,7 +257,6 @@ class MainDisplay(Display):
|
||||
image = self.imageManager.get_image_bytes(name)
|
||||
self.controller.mediaController.video_reset(self.controller)
|
||||
self.displayImage(image)
|
||||
return self.preview()
|
||||
|
||||
def displayImage(self, image):
|
||||
"""
|
||||
@ -329,9 +327,8 @@ class MainDisplay(Display):
|
||||
self.setVisible(True)
|
||||
else:
|
||||
self.setVisible(True)
|
||||
preview = QtGui.QImage(self.screen[u'size'].width(),
|
||||
self.screen[u'size'].height(),
|
||||
QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
preview = QtGui.QPixmap(self.screen[u'size'].width(),
|
||||
self.screen[u'size'].height())
|
||||
painter = QtGui.QPainter(preview)
|
||||
painter.setRenderHint(QtGui.QPainter.Antialiasing)
|
||||
self.frame.render(painter)
|
||||
|
@ -191,16 +191,20 @@ class MediaController(object):
|
||||
# Build the seekSlider.
|
||||
controller.seekSlider = QtGui.QSlider(QtCore.Qt.Horizontal)
|
||||
controller.seekSlider.setMaximum(1000)
|
||||
controller.seekSlider.setToolTip(translate(
|
||||
'OpenLP.SlideController', 'Video position.'))
|
||||
controller.seekSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
|
||||
controller.seekSlider.setObjectName(u'seekSlider')
|
||||
controller.mediabar.addToolbarWidget(u'Seek Slider', controller.seekSlider)
|
||||
# Build the volumeSlider.
|
||||
controller.volumeSlider = QtGui.QSlider(QtCore.Qt.Horizontal)
|
||||
controller.volumeSlider.setTickInterval(10)
|
||||
controller.volumeSlider.setTickPosition(QtGui.QSlider.TicksAbove)
|
||||
controller.volumeSlider.setMinimum(0)
|
||||
controller.volumeSlider.setMaximum(100)
|
||||
controller.volumeSlider.setToolTip(translate(
|
||||
'OpenLP.SlideController', 'Audio Volume.'))
|
||||
controller.volumeSlider.setValue(controller.media_info.volume)
|
||||
controller.seekSlider.setGeometry(QtCore.QRect(90, 260, 221, 24))
|
||||
controller.seekSlider.setObjectName(u'seekSlider')
|
||||
controller.mediabar.addToolbarWidget(u'Seek Slider', controller.seekSlider)
|
||||
controller.volumeSlider.setGeometry(QtCore.QRect(90, 160, 221, 24))
|
||||
controller.volumeSlider.setObjectName(u'volumeSlider')
|
||||
controller.mediabar.addToolbarWidget(u'Audio Volume', controller.volumeSlider)
|
||||
|
@ -1160,7 +1160,6 @@ class ServiceManager(QtGui.QWidget):
|
||||
# if not passed set to config value
|
||||
if expand is None:
|
||||
expand = self.expandTabs
|
||||
item.render()
|
||||
item.from_service = True
|
||||
if replace:
|
||||
sitem, child = self.findServiceItem()
|
||||
@ -1169,6 +1168,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
self.repaintServiceList(sitem, child)
|
||||
self.mainwindow.liveController.replaceServiceManagerItem(item)
|
||||
else:
|
||||
item.render()
|
||||
# nothing selected for dnd
|
||||
if self.dropPosition == 0:
|
||||
if isinstance(item, list):
|
||||
|
@ -137,7 +137,7 @@ class SlideController(Controller):
|
||||
self.previewListWidget.horizontalHeader().setVisible(False)
|
||||
self.previewListWidget.setColumnWidth(0, self.controller.width())
|
||||
self.previewListWidget.isLive = self.isLive
|
||||
self.previewListWidget.setObjectName(u'PreviewListWidget')
|
||||
self.previewListWidget.setObjectName(u'previewListWidget')
|
||||
self.previewListWidget.setSelectionBehavior(
|
||||
QtGui.QAbstractItemView.SelectRows)
|
||||
self.previewListWidget.setSelectionMode(
|
||||
@ -278,7 +278,7 @@ class SlideController(Controller):
|
||||
QtGui.QSizePolicy.Label))
|
||||
self.previewFrame.setFrameShape(QtGui.QFrame.StyledPanel)
|
||||
self.previewFrame.setFrameShadow(QtGui.QFrame.Sunken)
|
||||
self.previewFrame.setObjectName(u'PreviewFrame')
|
||||
self.previewFrame.setObjectName(u'previewFrame')
|
||||
self.grid = QtGui.QGridLayout(self.previewFrame)
|
||||
self.grid.setMargin(8)
|
||||
self.grid.setObjectName(u'grid')
|
||||
@ -303,15 +303,13 @@ class SlideController(Controller):
|
||||
self.slidePreview.setFrameShadow(QtGui.QFrame.Plain)
|
||||
self.slidePreview.setLineWidth(1)
|
||||
self.slidePreview.setScaledContents(True)
|
||||
self.slidePreview.setObjectName(u'SlidePreview')
|
||||
self.slidePreview.setObjectName(u'slidePreview')
|
||||
self.slideLayout.insertWidget(0, self.slidePreview)
|
||||
self.grid.addLayout(self.slideLayout, 0, 0, 1, 1)
|
||||
# Signals
|
||||
QtCore.QObject.connect(self.previewListWidget,
|
||||
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected)
|
||||
if self.isLive:
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'maindisplay_active'), self.updatePreview)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'slidecontroller_live_spin_delay'),
|
||||
self.receiveSpinDelay)
|
||||
@ -332,18 +330,12 @@ class SlideController(Controller):
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'slidecontroller_%s_stop_loop' % self.typePrefix),
|
||||
self.onStopLoop)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'slidecontroller_%s_first' % self.typePrefix),
|
||||
self.onSlideSelectedFirst)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'slidecontroller_%s_next' % self.typePrefix),
|
||||
self.onSlideSelectedNext)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'slidecontroller_%s_previous' % self.typePrefix),
|
||||
self.onSlideSelectedPrevious)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'slidecontroller_%s_last' % self.typePrefix),
|
||||
self.onSlideSelectedLast)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'slidecontroller_%s_change' % self.typePrefix),
|
||||
self.onSlideChange)
|
||||
@ -356,9 +348,6 @@ class SlideController(Controller):
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'slidecontroller_%s_unblank' % self.typePrefix),
|
||||
self.onSlideUnblank)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'slidecontroller_%s_text_request' % self.typePrefix),
|
||||
self.onTextRequest)
|
||||
|
||||
def setPreviewHotkeys(self, parent=None):
|
||||
self.previousItem.setObjectName(u'previousItemPreview')
|
||||
@ -664,14 +653,14 @@ class SlideController(Controller):
|
||||
label.setMargin(4)
|
||||
label.setScaledContents(True)
|
||||
if self.serviceItem.is_command():
|
||||
image = QtGui.QImage(frame[u'image'])
|
||||
label.setPixmap(QtGui.QPixmap(frame[u'image']))
|
||||
else:
|
||||
# If current slide set background to image
|
||||
if framenumber == slideno:
|
||||
self.serviceItem.bg_image_bytes = \
|
||||
self.imageManager.get_image_bytes(frame[u'title'])
|
||||
image = self.imageManager.get_image(frame[u'title'])
|
||||
label.setPixmap(QtGui.QPixmap.fromImage(image))
|
||||
label.setPixmap(QtGui.QPixmap.fromImage(image))
|
||||
self.previewListWidget.setCellWidget(framenumber, 0, label)
|
||||
slideHeight = width * self.parent().renderer.screen_ratio
|
||||
row += 1
|
||||
@ -718,41 +707,7 @@ class SlideController(Controller):
|
||||
else:
|
||||
self.__checkUpdateSelectedSlide(slideno)
|
||||
|
||||
def onTextRequest(self):
|
||||
"""
|
||||
Return the text for the current item in controller
|
||||
"""
|
||||
data = []
|
||||
if self.serviceItem:
|
||||
for framenumber, frame in enumerate(self.serviceItem.get_frames()):
|
||||
dataItem = {}
|
||||
if self.serviceItem.is_text():
|
||||
dataItem[u'tag'] = unicode(frame[u'verseTag'])
|
||||
dataItem[u'text'] = unicode(frame[u'html'])
|
||||
else:
|
||||
dataItem[u'tag'] = unicode(framenumber)
|
||||
dataItem[u'text'] = u''
|
||||
dataItem[u'selected'] = \
|
||||
(self.previewListWidget.currentRow() == framenumber)
|
||||
data.append(dataItem)
|
||||
Receiver.send_message(u'slidecontroller_%s_text_response'
|
||||
% self.typePrefix, data)
|
||||
|
||||
# Screen event methods
|
||||
def onSlideSelectedFirst(self):
|
||||
"""
|
||||
Go to the first slide.
|
||||
"""
|
||||
if not self.serviceItem:
|
||||
return
|
||||
if self.serviceItem.is_command():
|
||||
Receiver.send_message(u'%s_first' % self.serviceItem.name.lower(),
|
||||
[self.serviceItem, self.isLive])
|
||||
self.updatePreview()
|
||||
else:
|
||||
self.previewListWidget.selectRow(0)
|
||||
self.slideSelected()
|
||||
|
||||
def onSlideSelectedIndex(self, message):
|
||||
"""
|
||||
Go to the requested slide
|
||||
@ -931,20 +886,18 @@ class SlideController(Controller):
|
||||
Receiver.send_message(
|
||||
u'%s_slide' % self.serviceItem.name.lower(),
|
||||
[self.serviceItem, self.isLive, row])
|
||||
self.updatePreview()
|
||||
else:
|
||||
toDisplay = self.serviceItem.get_rendered_frame(row)
|
||||
if self.serviceItem.is_text():
|
||||
frame = self.display.text(toDisplay)
|
||||
self.display.text(toDisplay)
|
||||
else:
|
||||
if start:
|
||||
self.display.buildHtml(self.serviceItem, toDisplay)
|
||||
frame = self.display.preview()
|
||||
else:
|
||||
frame = self.display.image(toDisplay)
|
||||
self.display.image(toDisplay)
|
||||
# reset the store used to display first image
|
||||
self.serviceItem.bg_image_bytes = None
|
||||
self.slidePreview.setPixmap(QtGui.QPixmap.fromImage(frame))
|
||||
self.updatePreview()
|
||||
self.selectedRow = row
|
||||
self.__checkUpdateSelectedSlide(row)
|
||||
Receiver.send_message(u'slidecontroller_%s_changed' % self.typePrefix,
|
||||
@ -972,8 +925,7 @@ class SlideController(Controller):
|
||||
QtCore.QTimer.singleShot(0.5, self.grabMainDisplay)
|
||||
QtCore.QTimer.singleShot(2.5, self.grabMainDisplay)
|
||||
else:
|
||||
self.slidePreview.setPixmap(
|
||||
QtGui.QPixmap.fromImage(self.display.preview()))
|
||||
self.slidePreview.setPixmap(self.display.preview())
|
||||
|
||||
def grabMainDisplay(self):
|
||||
"""
|
||||
@ -1036,21 +988,6 @@ class SlideController(Controller):
|
||||
self.previewListWidget.item(row + 1, 0))
|
||||
self.previewListWidget.selectRow(row)
|
||||
|
||||
def onSlideSelectedLast(self):
|
||||
"""
|
||||
Go to the last slide.
|
||||
"""
|
||||
if not self.serviceItem:
|
||||
return
|
||||
Receiver.send_message(u'%s_last' % self.serviceItem.name.lower(),
|
||||
[self.serviceItem, self.isLive])
|
||||
if self.serviceItem.is_command():
|
||||
self.updatePreview()
|
||||
else:
|
||||
self.previewListWidget.selectRow(
|
||||
self.previewListWidget.rowCount() - 1)
|
||||
self.slideSelected()
|
||||
|
||||
def onToggleLoop(self):
|
||||
"""
|
||||
Toggles the loop state.
|
||||
|
@ -36,8 +36,7 @@ class SplashScreen(QtGui.QSplashScreen):
|
||||
QtCore.SIGNAL(u'close_splash'), self.close)
|
||||
|
||||
def setupUi(self):
|
||||
self.setObjectName(u'splash_screen')
|
||||
self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
|
||||
self.setObjectName(u'splashScreen')
|
||||
self.setContextMenuPolicy(QtCore.Qt.PreventContextMenu)
|
||||
splash_image = QtGui.QPixmap(u':/graphics/openlp-splash-screen.png')
|
||||
self.setPixmap(splash_image)
|
||||
|
@ -33,6 +33,7 @@ from PyQt4 import QtCore, QtGui
|
||||
from openlp.core.lib import Receiver, translate
|
||||
from openlp.core.lib.theme import BackgroundType, BackgroundGradientType
|
||||
from openlp.core.lib.ui import UiStrings, critical_error_message_box
|
||||
from openlp.core.ui import ThemeLayoutForm
|
||||
from openlp.core.utils import get_images_filter
|
||||
from themewizard import Ui_ThemeWizard
|
||||
|
||||
@ -58,6 +59,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
||||
self.registerFields()
|
||||
self.updateThemeAllowed = True
|
||||
self.temp_background_filename = u''
|
||||
self.themeLayoutForm = ThemeLayoutForm(self)
|
||||
QtCore.QObject.connect(self.backgroundComboBox,
|
||||
QtCore.SIGNAL(u'currentIndexChanged(int)'),
|
||||
self.onBackgroundComboBoxCurrentIndexChanged)
|
||||
@ -88,6 +90,9 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
||||
self.onShadowCheckCheckBoxStateChanged)
|
||||
QtCore.QObject.connect(self.footerColorButton,
|
||||
QtCore.SIGNAL(u'clicked()'), self.onFooterColorButtonClicked)
|
||||
QtCore.QObject.connect(self,
|
||||
QtCore.SIGNAL(u'customButtonClicked(int)'),
|
||||
self.onCustom1ButtonClicked)
|
||||
QtCore.QObject.connect(self.mainPositionCheckBox,
|
||||
QtCore.SIGNAL(u'stateChanged(int)'),
|
||||
self.onMainPositionCheckBoxStateChanged)
|
||||
@ -229,13 +234,36 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
||||
"""
|
||||
Detects Page changes and updates as approprate.
|
||||
"""
|
||||
if self.page(pageId) == self.areaPositionPage:
|
||||
self.setOption(QtGui.QWizard.HaveCustomButton1, True)
|
||||
else:
|
||||
self.setOption(QtGui.QWizard.HaveCustomButton1, False)
|
||||
if self.page(pageId) == self.previewPage:
|
||||
self.updateTheme()
|
||||
frame = self.thememanager.generateImage(self.theme)
|
||||
self.previewBoxLabel.setPixmap(QtGui.QPixmap.fromImage(frame))
|
||||
self.previewBoxLabel.setPixmap(frame)
|
||||
self.displayAspectRatio = float(frame.width()) / frame.height()
|
||||
self.resizeEvent()
|
||||
|
||||
def onCustom1ButtonClicked(self, number):
|
||||
"""
|
||||
Generate layout preview and display the form.
|
||||
"""
|
||||
self.updateTheme()
|
||||
width = self.thememanager.mainwindow.renderer.width
|
||||
height = self.thememanager.mainwindow.renderer.height
|
||||
pixmap = QtGui.QPixmap(width, height)
|
||||
pixmap.fill(QtCore.Qt.white)
|
||||
paint = QtGui.QPainter(pixmap)
|
||||
paint.setPen(QtGui.QPen(QtCore.Qt.blue, 2))
|
||||
paint.drawRect(self.thememanager.mainwindow.renderer.
|
||||
get_main_rectangle(self.theme))
|
||||
paint.setPen(QtGui.QPen(QtCore.Qt.red, 2))
|
||||
paint.drawRect(self.thememanager.mainwindow.renderer.
|
||||
get_footer_rectangle(self.theme))
|
||||
paint.end()
|
||||
self.themeLayoutForm.exec_(pixmap)
|
||||
|
||||
def onOutlineCheckCheckBoxStateChanged(self, state):
|
||||
"""
|
||||
Change state as Outline check box changed
|
||||
|
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, \
|
||||
Receiver, SettingsManager, translate, check_item_selected, \
|
||||
check_directory_exists
|
||||
check_directory_exists, create_thumb, validate_thumb
|
||||
from openlp.core.lib.theme import ThemeXML, BackgroundType, VerticalType, \
|
||||
BackgroundGradientType
|
||||
from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
|
||||
@ -359,7 +359,7 @@ class ThemeManager(QtGui.QWidget):
|
||||
The theme to delete.
|
||||
"""
|
||||
self.themelist.remove(theme)
|
||||
thumb = theme + u'.png'
|
||||
thumb = u'%s.png' % theme
|
||||
delete_file(os.path.join(self.path, thumb))
|
||||
delete_file(os.path.join(self.thumbPath, thumb))
|
||||
try:
|
||||
@ -473,15 +473,12 @@ class ThemeManager(QtGui.QWidget):
|
||||
name = textName
|
||||
thumb = os.path.join(self.thumbPath, u'%s.png' % textName)
|
||||
item_name = QtGui.QListWidgetItem(name)
|
||||
if os.path.exists(thumb):
|
||||
if validate_thumb(theme, thumb):
|
||||
icon = build_icon(thumb)
|
||||
else:
|
||||
icon = build_icon(theme)
|
||||
pixmap = icon.pixmap(QtCore.QSize(88, 50))
|
||||
pixmap.save(thumb, u'png')
|
||||
icon = create_thumb(theme, thumb)
|
||||
item_name.setIcon(icon)
|
||||
item_name.setData(QtCore.Qt.UserRole,
|
||||
QtCore.QVariant(textName))
|
||||
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(textName))
|
||||
self.themeListWidget.addItem(item_name)
|
||||
self.themelist.append(textName)
|
||||
self._pushThemes()
|
||||
@ -658,9 +655,7 @@ class ThemeManager(QtGui.QWidget):
|
||||
os.unlink(samplepathname)
|
||||
frame.save(samplepathname, u'png')
|
||||
thumb = os.path.join(self.thumbPath, u'%s.png' % name)
|
||||
icon = build_icon(frame)
|
||||
pixmap = icon.pixmap(QtCore.QSize(88, 50))
|
||||
pixmap.save(thumb, u'png')
|
||||
create_thumb(samplepathname, thumb, False)
|
||||
log.debug(u'Theme image written to %s', samplepathname)
|
||||
|
||||
def updatePreviewImages(self):
|
||||
|
@ -38,7 +38,8 @@ class Ui_ThemeWizard(object):
|
||||
themeWizard.setModal(True)
|
||||
themeWizard.setWizardStyle(QtGui.QWizard.ModernStyle)
|
||||
themeWizard.setOptions(QtGui.QWizard.IndependentPages |
|
||||
QtGui.QWizard.NoBackButtonOnStartPage)
|
||||
QtGui.QWizard.NoBackButtonOnStartPage |
|
||||
QtGui.QWizard.HaveCustomButton1)
|
||||
self.spacer = QtGui.QSpacerItem(10, 0,
|
||||
QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Minimum)
|
||||
# Welcome Page
|
||||
@ -535,6 +536,9 @@ class Ui_ThemeWizard(object):
|
||||
translate('OpenLP.ThemeWizard', 'px'))
|
||||
self.footerPositionCheckBox.setText(
|
||||
translate('OpenLP.ThemeWizard', 'Use default location'))
|
||||
themeWizard.setOption(QtGui.QWizard.HaveCustomButton1, False)
|
||||
themeWizard.setButtonText(QtGui.QWizard.CustomButton1,
|
||||
translate('OpenLP.ThemeWizard', 'Layout Preview'))
|
||||
self.previewPage.setTitle(
|
||||
translate('OpenLP.ThemeWizard', 'Save and Preview'))
|
||||
self.previewPage.setSubTitle(
|
||||
|
@ -461,6 +461,11 @@ class BibleImportForm(OpenLPWizard):
|
||||
WizardStrings.YouSpecifyFile % WizardStrings.OS)
|
||||
self.openSongFileEdit.setFocus()
|
||||
return False
|
||||
elif self.field(u'source_format').toInt()[0] == \
|
||||
BibleFormat.WebDownload:
|
||||
self.versionNameEdit.setText(
|
||||
self.webTranslationComboBox.currentText())
|
||||
return True
|
||||
elif self.field(u'source_format').toInt()[0] == BibleFormat.OpenLP1:
|
||||
if not self.field(u'openlp1_location').toString():
|
||||
critical_error_message_box(UiStrings().NFSs,
|
||||
@ -674,7 +679,7 @@ class BibleImportForm(OpenLPWizard):
|
||||
elif bible_type == BibleFormat.CSV:
|
||||
# Import a CSV bible.
|
||||
importer = self.manager.import_bible(BibleFormat.CSV,
|
||||
name=license_version,
|
||||
name=license_version,
|
||||
booksfile=unicode(self.field(u'csv_booksfile').toString()),
|
||||
versefile=unicode(self.field(u'csv_versefile').toString())
|
||||
)
|
||||
|
@ -28,7 +28,6 @@
|
||||
import logging
|
||||
import chardet
|
||||
import os
|
||||
import re
|
||||
import sqlite3
|
||||
|
||||
from PyQt4 import QtCore
|
||||
|
@ -82,13 +82,16 @@ class BGExtract(object):
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
footnotes = soup.findAll(u'sup', u'footnote')
|
||||
if footnotes:
|
||||
[footnote.extract() for footnote in footnotes]
|
||||
for footnote in footnotes:
|
||||
footnote.extract()
|
||||
crossrefs = soup.findAll(u'sup', u'xref')
|
||||
if crossrefs:
|
||||
[crossref.extract() for crossref in crossrefs]
|
||||
for crossref in crossrefs:
|
||||
crossref.extract()
|
||||
headings = soup.findAll(u'h5')
|
||||
if headings:
|
||||
[heading.extract() for heading in headings]
|
||||
for heading in headings:
|
||||
heading.extract()
|
||||
cleanup = [(re.compile('\s+'), lambda match: ' ')]
|
||||
verses = BeautifulSoup(str(soup), markupMassage=cleanup)
|
||||
verse_list = {}
|
||||
|
@ -28,7 +28,6 @@
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import SettingsTab, translate, Receiver
|
||||
from openlp.core.lib.ui import UiStrings, create_valign_combo
|
||||
|
||||
class ImageTab(SettingsTab):
|
||||
"""
|
||||
|
@ -33,7 +33,7 @@ from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \
|
||||
SettingsManager, translate, check_item_selected, check_directory_exists, \
|
||||
Receiver
|
||||
Receiver, create_thumb, validate_thumb
|
||||
from openlp.core.lib.ui import UiStrings, critical_error_message_box
|
||||
from openlp.core.utils import AppLocation, delete_file, get_images_filter
|
||||
|
||||
@ -127,13 +127,13 @@ class ImageMediaItem(MediaManagerItem):
|
||||
self.plugin.formparent.incrementProgressBar()
|
||||
filename = os.path.split(unicode(imageFile))[1]
|
||||
thumb = os.path.join(self.servicePath, filename)
|
||||
if os.path.exists(thumb):
|
||||
if self.validate(imageFile, thumb):
|
||||
if not os.path.exists(imageFile):
|
||||
icon = build_icon(u':/general/general_delete.png')
|
||||
else:
|
||||
if validate_thumb(imageFile, thumb):
|
||||
icon = build_icon(thumb)
|
||||
else:
|
||||
icon = build_icon(u':/general/general_delete.png')
|
||||
else:
|
||||
icon = self.iconFromFile(imageFile, thumb)
|
||||
icon = create_thumb(imageFile, thumb)
|
||||
item_name = QtGui.QListWidgetItem(filename)
|
||||
item_name.setIcon(icon)
|
||||
item_name.setToolTip(imageFile)
|
||||
|
@ -41,7 +41,7 @@ from openlp.plugins.media.forms import MediaOpenForm
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
CLAPPERBOARD = QtGui.QPixmap(u':/media/media_video.png').toImage()
|
||||
CLAPPERBOARD = QtGui.QImage(u':/media/media_video.png')
|
||||
|
||||
class MediaMediaItem(MediaManagerItem):
|
||||
"""
|
||||
@ -163,7 +163,7 @@ class MediaMediaItem(MediaManagerItem):
|
||||
|
||||
def videobackgroundReplaced(self):
|
||||
"""
|
||||
Triggered by main display on change of serviceitem
|
||||
Triggered by main display on change of serviceitem.
|
||||
"""
|
||||
self.resetAction.setVisible(False)
|
||||
|
||||
@ -263,7 +263,7 @@ class MediaMediaItem(MediaManagerItem):
|
||||
|
||||
def onDeleteClick(self):
|
||||
"""
|
||||
Remove a media item from the list
|
||||
Remove a media item from the list.
|
||||
"""
|
||||
if check_item_selected(self.listView, translate('MediaPlugin.MediaItem',
|
||||
'You must select a media file to delete.')):
|
||||
|
@ -32,7 +32,8 @@ import locale
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import MediaManagerItem, build_icon, SettingsManager, \
|
||||
translate, check_item_selected, Receiver, ItemCapabilities
|
||||
translate, check_item_selected, Receiver, ItemCapabilities, create_thumb, \
|
||||
validate_thumb
|
||||
from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
|
||||
media_item_combo_box
|
||||
from openlp.plugins.presentations.lib import MessageListener
|
||||
@ -193,10 +194,13 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
doc.load_presentation()
|
||||
preview = doc.get_thumbnail_path(1, True)
|
||||
doc.close_presentation()
|
||||
if preview and self.validate(preview, thumb):
|
||||
icon = build_icon(thumb)
|
||||
else:
|
||||
if not (preview and os.path.exists(preview)):
|
||||
icon = build_icon(u':/general/general_delete.png')
|
||||
else:
|
||||
if validate_thumb(preview, thumb):
|
||||
icon = build_icon(thumb)
|
||||
else:
|
||||
icon = create_thumb(preview, thumb)
|
||||
else:
|
||||
if initialLoad:
|
||||
icon = build_icon(u':/general/general_delete.png')
|
||||
|
@ -31,7 +31,8 @@ import shutil
|
||||
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.lib import Receiver, resize_image
|
||||
from openlp.core.lib import Receiver, check_directory_exists, create_thumb, \
|
||||
validate_thumb
|
||||
from openlp.core.utils import AppLocation
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -97,8 +98,7 @@ class PresentationDocument(object):
|
||||
self.slidenumber = 0
|
||||
self.controller = controller
|
||||
self.filepath = name
|
||||
if not os.path.isdir(self.get_thumbnail_folder()):
|
||||
os.mkdir(self.get_thumbnail_folder())
|
||||
check_directory_exists(self.get_thumbnail_folder())
|
||||
|
||||
def load_presentation(self):
|
||||
"""
|
||||
@ -145,15 +145,13 @@ class PresentationDocument(object):
|
||||
|
||||
def check_thumbnails(self):
|
||||
"""
|
||||
Returns true if the thumbnail images look to exist and are more
|
||||
recent than the powerpoint
|
||||
Returns ``True`` if the thumbnail images exist and are more recent than
|
||||
the powerpoint file.
|
||||
"""
|
||||
lastimage = self.get_thumbnail_path(self.get_slide_count(), True)
|
||||
if not (lastimage and os.path.isfile(lastimage)):
|
||||
return False
|
||||
imgdate = os.stat(lastimage).st_mtime
|
||||
pptdate = os.stat(self.filepath).st_mtime
|
||||
return imgdate >= pptdate
|
||||
return validate_thumb(self.filepath, lastimage)
|
||||
|
||||
def close_presentation(self):
|
||||
"""
|
||||
@ -246,8 +244,8 @@ class PresentationDocument(object):
|
||||
if self.check_thumbnails():
|
||||
return
|
||||
if os.path.isfile(file):
|
||||
img = resize_image(file, 320, 240)
|
||||
img.save(self.get_thumbnail_path(idx, False))
|
||||
thumb_path = self.get_thumbnail_path(idx, False)
|
||||
create_thumb(file, thumb_path, False, QtCore.QSize(320, 240))
|
||||
|
||||
def get_thumbnail_path(self, slide_no, check_exists):
|
||||
"""
|
||||
@ -387,10 +385,8 @@ class PresentationController(object):
|
||||
AppLocation.get_section_data_path(self.settings_section),
|
||||
u'thumbnails')
|
||||
self.thumbnail_prefix = u'slide'
|
||||
if not os.path.isdir(self.thumbnail_folder):
|
||||
os.makedirs(self.thumbnail_folder)
|
||||
if not os.path.isdir(self.temp_folder):
|
||||
os.makedirs(self.temp_folder)
|
||||
check_directory_exists(self.thumbnail_folder)
|
||||
check_directory_exists(self.temp_folder)
|
||||
|
||||
def enabled(self):
|
||||
"""
|
||||
|
@ -87,7 +87,7 @@ class PresentationPlugin(Plugin):
|
||||
to close down their applications and release resources.
|
||||
"""
|
||||
log.info(u'Plugin Finalise')
|
||||
#Ask each controller to tidy up
|
||||
# Ask each controller to tidy up.
|
||||
for key in self.controllers:
|
||||
controller = self.controllers[key]
|
||||
if controller.enabled():
|
||||
|
@ -121,11 +121,11 @@ window.OpenLP = {
|
||||
$("#nextslide").html(text);
|
||||
}
|
||||
},
|
||||
updateClock: function() {
|
||||
updateClock: function(data) {
|
||||
var div = $("#clock");
|
||||
var t = new Date();
|
||||
var h = t.getHours();
|
||||
if (h > 12)
|
||||
if (data.results.twelve && h > 12)
|
||||
h = h - 12;
|
||||
var m = t.getMinutes();
|
||||
if (m < 10)
|
||||
@ -136,7 +136,7 @@ window.OpenLP = {
|
||||
$.getJSON(
|
||||
"/api/poll",
|
||||
function (data, status) {
|
||||
OpenLP.updateClock();
|
||||
OpenLP.updateClock(data);
|
||||
if (OpenLP.currentItem != data.results.item) {
|
||||
OpenLP.currentItem = data.results.item;
|
||||
OpenLP.loadSlides();
|
||||
|
@ -315,7 +315,7 @@ class HttpConnection(object):
|
||||
"""
|
||||
log.debug(u'ready to read socket')
|
||||
if self.socket.canReadLine():
|
||||
data = unicode(self.socket.readLine())
|
||||
data = unicode(self.socket.readLine()).encode(u'utf-8')
|
||||
log.debug(u'received: ' + data)
|
||||
words = data.split(u' ')
|
||||
response = None
|
||||
@ -397,7 +397,9 @@ class HttpConnection(object):
|
||||
result = {
|
||||
u'slide': self.parent.current_slide or 0,
|
||||
u'item': self.parent.current_item._uuid \
|
||||
if self.parent.current_item else u''
|
||||
if self.parent.current_item else u'',
|
||||
u'twelve':QtCore.QSettings().value(
|
||||
u'remotes/twelve hour', QtCore.QVariant(True)).toBool()
|
||||
}
|
||||
return HttpResponse(json.dumps({u'results': result}),
|
||||
{u'Content-Type': u'application/json'})
|
||||
|
@ -57,6 +57,9 @@ class RemoteTab(SettingsTab):
|
||||
QtCore.QObject.connect(self.addressEdit,
|
||||
QtCore.SIGNAL(u'textChanged(const QString&)'), self.setUrls)
|
||||
self.serverSettingsLayout.addRow(self.addressLabel, self.addressEdit)
|
||||
self.twelveHourCheckBox = QtGui.QCheckBox(self.serverSettingsGroupBox)
|
||||
self.twelveHourCheckBox.setObjectName(u'twelveHourCheckBox')
|
||||
self.serverSettingsLayout.addRow(self.twelveHourCheckBox)
|
||||
self.portLabel = QtGui.QLabel(self.serverSettingsGroupBox)
|
||||
self.portLabel.setObjectName(u'portLabel')
|
||||
self.portSpinBox = QtGui.QSpinBox(self.serverSettingsGroupBox)
|
||||
@ -80,6 +83,9 @@ class RemoteTab(SettingsTab):
|
||||
self.leftLayout.addWidget(self.serverSettingsGroupBox)
|
||||
self.leftLayout.addStretch()
|
||||
self.rightLayout.addStretch()
|
||||
QtCore.QObject.connect(self.twelveHourCheckBox,
|
||||
QtCore.SIGNAL(u'stateChanged(int)'),
|
||||
self.onTwelveHourCheckBoxChanged)
|
||||
|
||||
def retranslateUi(self):
|
||||
self.serverSettingsGroupBox.setTitle(
|
||||
@ -92,6 +98,9 @@ class RemoteTab(SettingsTab):
|
||||
'Remote URL:'))
|
||||
self.stageUrlLabel.setText(translate('RemotePlugin.RemoteTab',
|
||||
'Stage view URL:'))
|
||||
self.twelveHourCheckBox.setText(
|
||||
translate('RemotePlugin.RemoteTab',
|
||||
'Display stage time in 12h format'))
|
||||
|
||||
def setUrls(self):
|
||||
ipAddress = u'localhost'
|
||||
@ -123,6 +132,10 @@ class RemoteTab(SettingsTab):
|
||||
self.addressEdit.setText(
|
||||
QtCore.QSettings().value(self.settingsSection + u'/ip address',
|
||||
QtCore.QVariant(ZERO_URL)).toString())
|
||||
self.twelveHour = QtCore.QSettings().value(
|
||||
self.settingsSection + u'/twelve hour',
|
||||
QtCore.QVariant(True)).toBool()
|
||||
self.twelveHourCheckBox.setChecked(self.twelveHour)
|
||||
self.setUrls()
|
||||
|
||||
def save(self):
|
||||
@ -130,3 +143,11 @@ class RemoteTab(SettingsTab):
|
||||
QtCore.QVariant(self.portSpinBox.value()))
|
||||
QtCore.QSettings().setValue(self.settingsSection + u'/ip address',
|
||||
QtCore.QVariant(self.addressEdit.text()))
|
||||
QtCore.QSettings().setValue(self.settingsSection + u'/twelve hour',
|
||||
QtCore.QVariant(self.twelveHour))
|
||||
|
||||
def onTwelveHourCheckBoxChanged(self, check_state):
|
||||
self.twelveHour = False
|
||||
# we have a set value convert to True/False
|
||||
if check_state == QtCore.Qt.Checked:
|
||||
self.twelveHour = True
|
||||
|
@ -240,8 +240,9 @@ class SongImportForm(OpenLPWizard):
|
||||
self.formatLabel.setText(WizardStrings.FormatLabel)
|
||||
self.formatComboBox.setItemText(SongFormat.OpenLP2, UiStrings().OLPV2)
|
||||
self.formatComboBox.setItemText(SongFormat.OpenLP1, UiStrings().OLPV1)
|
||||
self.formatComboBox.setItemText(
|
||||
SongFormat.OpenLyrics, WizardStrings.OL)
|
||||
self.formatComboBox.setItemText(SongFormat.OpenLyrics,
|
||||
translate('SongsPlugin.ImportWizardForm',
|
||||
'OpenLyrics or OpenLP 2.0 Exported Song'))
|
||||
self.formatComboBox.setItemText(SongFormat.OpenSong, WizardStrings.OS)
|
||||
self.formatComboBox.setItemText(
|
||||
SongFormat.WordsOfWorship, WizardStrings.WoW)
|
||||
@ -508,7 +509,8 @@ class SongImportForm(OpenLPWizard):
|
||||
Get OpenLyrics song database files
|
||||
"""
|
||||
self.getFiles(WizardStrings.OpenTypeFile % WizardStrings.OL,
|
||||
self.openLyricsFileListWidget)
|
||||
self.openLyricsFileListWidget, u'%s (*.xml)' %
|
||||
translate('SongsPlugin.ImportWizardForm', 'OpenLyrics Files'))
|
||||
|
||||
def onOpenLyricsRemoveButtonClicked(self):
|
||||
"""
|
||||
|
@ -333,5 +333,6 @@ class CCLIFileImport(SongImport):
|
||||
if len(author_list) < 2:
|
||||
author_list = song_author.split(u'|')
|
||||
# Clean spaces before and after author names.
|
||||
[self.addAuthor(author_name.strip()) for author_name in author_list]
|
||||
for author_name in author_list:
|
||||
self.addAuthor(author_name.strip())
|
||||
return self.finish()
|
||||
|
@ -258,7 +258,7 @@ class EasiSlidesImport(SongImport):
|
||||
verses[reg][vt][vn] = {}
|
||||
if not verses[reg][vt][vn].has_key(inst):
|
||||
verses[reg][vt][vn][inst] = []
|
||||
words = self.tidy_text(line)
|
||||
words = self.tidyText(line)
|
||||
verses[reg][vt][vn][inst].append(words)
|
||||
# done parsing
|
||||
|
||||
|
@ -397,7 +397,8 @@ class SongMediaItem(MediaManagerItem):
|
||||
try:
|
||||
os.remove(media_file.file_name)
|
||||
except:
|
||||
log.exception('Could not remove file: %s', audio)
|
||||
log.exception('Could not remove file: %s',
|
||||
media_file.file_name)
|
||||
try:
|
||||
save_path = os.path.join(AppLocation.get_section_data_path(
|
||||
self.plugin.name), 'audio', str(item_id))
|
||||
|
@ -30,7 +30,6 @@ songs from the database to the OpenLyrics format.
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
||||
from lxml import etree
|
||||
|
||||
|
@ -194,7 +194,7 @@ class SofImport(OooImport):
|
||||
into line
|
||||
"""
|
||||
text = textportion.getString()
|
||||
text = self.tidy_text(text)
|
||||
text = self.tidyText(text)
|
||||
if text.strip() == u'':
|
||||
return text
|
||||
if textportion.CharWeight == BOLD:
|
||||
|
@ -29,7 +29,7 @@ The :mod:`upgrade` module provides a way for the database and schema that is the
|
||||
backend for the Songs plugin
|
||||
"""
|
||||
|
||||
from sqlalchemy import Column, ForeignKey, Table, types
|
||||
from sqlalchemy import Column, Table, types
|
||||
from sqlalchemy.sql.expression import func
|
||||
from migrate import changeset
|
||||
from migrate.changeset.constraint import ForeignKeyConstraint
|
||||
|
@ -60,7 +60,7 @@ The XML of an `OpenLyrics <http://openlyrics.info/>`_ song looks like this::
|
||||
</lyrics>
|
||||
</song>
|
||||
"""
|
||||
|
||||
import cgi
|
||||
import logging
|
||||
import re
|
||||
|
||||
@ -257,11 +257,12 @@ class OpenLyrics(object):
|
||||
|
||||
"""
|
||||
IMPLEMENTED_VERSION = u'0.8'
|
||||
START_TAGS_REGEX = re.compile(r'\{(\w+)\}')
|
||||
END_TAGS_REGEX = re.compile(r'\{\/(\w+)\}')
|
||||
VERSE_NUMBER_REGEX = re.compile(u'[a-zA-Z]*')
|
||||
|
||||
def __init__(self, manager):
|
||||
self.manager = manager
|
||||
self.start_tags_regex = re.compile(r'\{(\w+)\}') # {abc} -> abc
|
||||
self.end_tags_regex = re.compile(r'\{\/(\w+)\}') # {/abc} -> abc
|
||||
|
||||
def song_to_xml(self, song):
|
||||
"""
|
||||
@ -334,7 +335,8 @@ class OpenLyrics(object):
|
||||
if u'lang' in verse[0]:
|
||||
verse_element.set(u'lang', verse[0][u'lang'])
|
||||
# Create a list with all "virtual" verses.
|
||||
virtual_verses = verse[1].split(u'[---]')
|
||||
virtual_verses = cgi.escape(verse[1])
|
||||
virtual_verses = virtual_verses.split(u'[---]')
|
||||
for index, virtual_verse in enumerate(virtual_verses):
|
||||
# Add formatting tags to text
|
||||
lines_element = self._add_text_with_tags_to_lines(verse_element,
|
||||
@ -402,32 +404,32 @@ class OpenLyrics(object):
|
||||
|
||||
def _add_tag_to_formatting(self, tag_name, tags_element):
|
||||
"""
|
||||
Add new formatting tag to the element ``<format>``
|
||||
if the tag is not present yet.
|
||||
Add new formatting tag to the element ``<format>`` if the tag is not
|
||||
present yet.
|
||||
"""
|
||||
available_tags = FormattingTags.get_html_tags()
|
||||
start_tag = '{%s}' % tag_name
|
||||
for t in available_tags:
|
||||
if t[u'start tag'] == start_tag:
|
||||
for tag in available_tags:
|
||||
if tag[u'start tag'] == start_tag:
|
||||
# Create new formatting tag in openlyrics xml.
|
||||
el = self._add_text_to_element(u'tag', tags_element)
|
||||
el.set(u'name', tag_name)
|
||||
el_open = self._add_text_to_element(u'open', el)
|
||||
el_open.text = etree.CDATA(t[u'start html'])
|
||||
element = self._add_text_to_element(u'tag', tags_element)
|
||||
element.set(u'name', tag_name)
|
||||
element_open = self._add_text_to_element(u'open', element)
|
||||
element_open.text = etree.CDATA(tag[u'start html'])
|
||||
# Check if formatting tag contains end tag. Some formatting
|
||||
# tags e.g. {br} has only start tag. If no end tag is present
|
||||
# <close> element has not to be in OpenLyrics xml.
|
||||
if t['end tag']:
|
||||
el_close = self._add_text_to_element(u'close', el)
|
||||
el_close.text = etree.CDATA(t[u'end html'])
|
||||
if tag['end tag']:
|
||||
element_close = self._add_text_to_element(u'close', element)
|
||||
element_close.text = etree.CDATA(tag[u'end html'])
|
||||
|
||||
def _add_text_with_tags_to_lines(self, verse_element, text, tags_element):
|
||||
"""
|
||||
Convert text with formatting tags from OpenLP format to OpenLyrics
|
||||
format and append it to element ``<lines>``.
|
||||
"""
|
||||
start_tags = self.start_tags_regex.findall(text)
|
||||
end_tags = self.end_tags_regex.findall(text)
|
||||
start_tags = OpenLyrics.START_TAGS_REGEX.findall(text)
|
||||
end_tags = OpenLyrics.END_TAGS_REGEX.findall(text)
|
||||
# Replace start tags with xml syntax.
|
||||
for tag in start_tags:
|
||||
# Tags already converted to xml structure.
|
||||
@ -442,12 +444,11 @@ class OpenLyrics(object):
|
||||
if tag not in xml_tags:
|
||||
self._add_tag_to_formatting(tag, tags_element)
|
||||
# Replace end tags.
|
||||
for t in end_tags:
|
||||
text = text.replace(u'{/%s}' % t, u'</tag>')
|
||||
for tag in end_tags:
|
||||
text = text.replace(u'{/%s}' % tag, u'</tag>')
|
||||
# Replace \n with <br/>.
|
||||
text = text.replace(u'\n', u'<br/>')
|
||||
text = u'<lines>' + text + u'</lines>'
|
||||
element = etree.XML(text)
|
||||
element = etree.XML(u'<lines>%s</lines>' % text)
|
||||
verse_element.append(element)
|
||||
return element
|
||||
|
||||
@ -692,7 +693,7 @@ class OpenLyrics(object):
|
||||
verse_tag = verse_def[0]
|
||||
else:
|
||||
verse_tag = VerseType.Tags[VerseType.Other]
|
||||
verse_number = re.compile(u'[a-zA-Z]*').sub(u'', verse_def)
|
||||
verse_number = OpenLyrics.VERSE_NUMBER_REGEX.sub(u'', verse_def)
|
||||
# OpenLyrics allows e. g. "c", but we need "c1". However, this does
|
||||
# not correct the verse order.
|
||||
if not verse_number:
|
||||
|
@ -28,7 +28,6 @@
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import translate
|
||||
from openlp.core.lib.ui import create_accept_reject_button_box
|
||||
|
||||
class Ui_SongUsageDeleteDialog(object):
|
||||
def setupUi(self, songUsageDeleteDialog):
|
||||
|
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 prefix="slides">
|
||||
<file>slide_close.png</file>
|
||||
<file>slide_first.png</file>
|
||||
<file>slide_last.png</file>
|
||||
<file>slide_next.png</file>
|
||||
<file>slide_blank.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 |
30
resources/pyinstaller/hook-openlp.media.py
Normal file
30
resources/pyinstaller/hook-openlp.media.py
Normal file
@ -0,0 +1,30 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
|
||||
# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, #
|
||||
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
|
||||
# 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 #
|
||||
###############################################################################
|
||||
|
||||
hiddenimports = ['openlp.plugins.presentations.lib.impresscontroller',
|
||||
'openlp.plugins.presentations.lib.powerpointcontroller',
|
||||
'openlp.plugins.presentations.lib.pptviewcontroller']
|
@ -87,7 +87,7 @@ Root: HKCR; Subkey: "OpenLP\DefaultIcon"; ValueType: string; ValueName: ""; Valu
|
||||
Root: HKCR; Subkey: "OpenLP\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\OpenLP.exe"" ""%1"""
|
||||
|
||||
[Code]
|
||||
function IsModuleLoaded(modulename: String ): Boolean;
|
||||
function IsModuleLoaded(modulename: AnsiString ): Boolean;
|
||||
external 'IsModuleLoaded@files:psvince.dll stdcall';
|
||||
|
||||
function GetUninstallString(): String;
|
||||
|
@ -8,7 +8,7 @@
|
||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||
# Armin Köhler, Joshua Millar, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
@ -5,11 +5,11 @@
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
|
||||
# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, #
|
||||
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
|
||||
# Jeffrey Smith, Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode #
|
||||
# Woldsund #
|
||||
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
|
@ -8,7 +8,7 @@
|
||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||
# Armin Köhler, Joshua Millar, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
@ -8,7 +8,7 @@
|
||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||
# Armin Köhler, Joshua Millar, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
Loading…
Reference in New Issue
Block a user