sync
|
@ -20,3 +20,4 @@ _eric4project
|
||||||
openlp/core/resources.py.old
|
openlp/core/resources.py.old
|
||||||
*.qm
|
*.qm
|
||||||
resources/windows/warnOpenLP.txt
|
resources/windows/warnOpenLP.txt
|
||||||
|
openlp.cfg
|
||||||
|
|
10
MANIFEST.in
|
@ -4,13 +4,11 @@ recursive-include openlp *.csv
|
||||||
recursive-include openlp *.html
|
recursive-include openlp *.html
|
||||||
recursive-include openlp *.js
|
recursive-include openlp *.js
|
||||||
recursive-include openlp *.css
|
recursive-include openlp *.css
|
||||||
recursive-include openlp *.qm
|
recursive-include openlp *.png
|
||||||
recursive-include documentation *
|
recursive-include documentation *
|
||||||
recursive-include resources/forms *
|
recursive-include resources *
|
||||||
recursive-include resources/i18n *
|
recursive-include scripts *
|
||||||
recursive-include resources/images *
|
|
||||||
recursive-include scripts *.py
|
|
||||||
include resources/*.desktop
|
|
||||||
include copyright.txt
|
include copyright.txt
|
||||||
include LICENSE
|
include LICENSE
|
||||||
|
include README.txt
|
||||||
include openlp/.version
|
include openlp/.version
|
||||||
|
|
|
@ -8,12 +8,7 @@ page on the web site::
|
||||||
http://openlp.org/en/download.html
|
http://openlp.org/en/download.html
|
||||||
|
|
||||||
If you're looking for how to contribute to OpenLP, then please look at the
|
If you're looking for how to contribute to OpenLP, then please look at the
|
||||||
contribution page on the web site::
|
OpenLP wiki::
|
||||||
|
|
||||||
http://openlp.org/en/documentation/introduction/contributing.html
|
|
||||||
|
|
||||||
If you've looked at that page, and are wanting to help develop, test or
|
|
||||||
translate OpenLP, have a look at the OpenLP wiki::
|
|
||||||
|
|
||||||
http://wiki.openlp.org/
|
http://wiki.openlp.org/
|
||||||
|
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
openlp.org 2.x Plugin Developer's Guide
|
|
||||||
========================================================================
|
|
||||||
|
|
||||||
Introduction
|
|
||||||
------------
|
|
||||||
This document will show you how to write your own module for openlp.org.
|
|
||||||
openlp.org has been written in plugins so that you can add your own
|
|
||||||
functionality to openlp.org.
|
|
|
@ -1,88 +0,0 @@
|
||||||
# Makefile for Sphinx documentation
|
|
||||||
#
|
|
||||||
|
|
||||||
# You can set these variables from the command line.
|
|
||||||
SPHINXOPTS =
|
|
||||||
SPHINXBUILD = sphinx-build
|
|
||||||
PAPER =
|
|
||||||
|
|
||||||
# Internal variables.
|
|
||||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
|
||||||
PAPEROPT_letter = -D latex_paper_size=letter
|
|
||||||
ALLSPHINXOPTS = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
|
||||||
|
|
||||||
.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
|
|
||||||
|
|
||||||
help:
|
|
||||||
@echo "Please use \`make <target>' where <target> is one of"
|
|
||||||
@echo " html to make standalone HTML files"
|
|
||||||
@echo " dirhtml to make HTML files named index.html in directories"
|
|
||||||
@echo " pickle to make pickle files"
|
|
||||||
@echo " json to make JSON files"
|
|
||||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
|
||||||
@echo " qthelp to make HTML files and a qthelp project"
|
|
||||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
|
||||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
|
||||||
@echo " linkcheck to check all external links for integrity"
|
|
||||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
|
||||||
|
|
||||||
clean:
|
|
||||||
-rm -rf build/*
|
|
||||||
|
|
||||||
html:
|
|
||||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The HTML pages are in build/html."
|
|
||||||
|
|
||||||
dirhtml:
|
|
||||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) build/dirhtml
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The HTML pages are in build/dirhtml."
|
|
||||||
|
|
||||||
pickle:
|
|
||||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can process the pickle files."
|
|
||||||
|
|
||||||
json:
|
|
||||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) build/json
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can process the JSON files."
|
|
||||||
|
|
||||||
htmlhelp:
|
|
||||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
|
||||||
".hhp project file in build/htmlhelp."
|
|
||||||
|
|
||||||
qthelp:
|
|
||||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) build/qthelp
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
|
||||||
".qhcp project file in build/qthelp, like this:"
|
|
||||||
@echo "# qcollectiongenerator build/qthelp/OpenLP.qhcp"
|
|
||||||
@echo "To view the help file:"
|
|
||||||
@echo "# assistant -collectionFile build/qthelp/OpenLP.qhc"
|
|
||||||
|
|
||||||
latex:
|
|
||||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; the LaTeX files are in build/latex."
|
|
||||||
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
|
|
||||||
"run these through (pdf)latex."
|
|
||||||
|
|
||||||
changes:
|
|
||||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes
|
|
||||||
@echo
|
|
||||||
@echo "The overview file is in build/changes."
|
|
||||||
|
|
||||||
linkcheck:
|
|
||||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck
|
|
||||||
@echo
|
|
||||||
@echo "Link check complete; look for any errors in the above output " \
|
|
||||||
"or in build/linkcheck/output.txt."
|
|
||||||
|
|
||||||
doctest:
|
|
||||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) build/doctest
|
|
||||||
@echo "Testing of doctests in the sources finished, look at the " \
|
|
||||||
"results in build/doctest/output.txt."
|
|
|
@ -1,112 +0,0 @@
|
||||||
@ECHO OFF
|
|
||||||
|
|
||||||
REM Command file for Sphinx documentation
|
|
||||||
|
|
||||||
set SPHINXBUILD=sphinx-build
|
|
||||||
set ALLSPHINXOPTS=-d build/doctrees %SPHINXOPTS% source
|
|
||||||
if NOT "%PAPER%" == "" (
|
|
||||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "" goto help
|
|
||||||
|
|
||||||
if "%1" == "help" (
|
|
||||||
:help
|
|
||||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
|
||||||
echo. html to make standalone HTML files
|
|
||||||
echo. dirhtml to make HTML files named index.html in directories
|
|
||||||
echo. pickle to make pickle files
|
|
||||||
echo. json to make JSON files
|
|
||||||
echo. htmlhelp to make HTML files and a HTML help project
|
|
||||||
echo. qthelp to make HTML files and a qthelp project
|
|
||||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
|
||||||
echo. changes to make an overview over all changed/added/deprecated items
|
|
||||||
echo. linkcheck to check all external links for integrity
|
|
||||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "clean" (
|
|
||||||
for /d %%i in (build\*) do rmdir /q /s %%i
|
|
||||||
del /q /s build\*
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "html" (
|
|
||||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% build/html
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The HTML pages are in build/html.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "dirhtml" (
|
|
||||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% build/dirhtml
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The HTML pages are in build/dirhtml.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "pickle" (
|
|
||||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% build/pickle
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can process the pickle files.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "json" (
|
|
||||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% build/json
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can process the JSON files.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "htmlhelp" (
|
|
||||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% build/htmlhelp
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
|
||||||
.hhp project file in build/htmlhelp.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "qthelp" (
|
|
||||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% build/qthelp
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
|
||||||
.qhcp project file in build/qthelp, like this:
|
|
||||||
echo.^> qcollectiongenerator build\qthelp\OpenLP.qhcp
|
|
||||||
echo.To view the help file:
|
|
||||||
echo.^> assistant -collectionFile build\qthelp\OpenLP.ghc
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "latex" (
|
|
||||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% build/latex
|
|
||||||
echo.
|
|
||||||
echo.Build finished; the LaTeX files are in build/latex.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "changes" (
|
|
||||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% build/changes
|
|
||||||
echo.
|
|
||||||
echo.The overview file is in build/changes.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "linkcheck" (
|
|
||||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% build/linkcheck
|
|
||||||
echo.
|
|
||||||
echo.Link check complete; look for any errors in the above output ^
|
|
||||||
or in build/linkcheck/output.txt.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "doctest" (
|
|
||||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% build/doctest
|
|
||||||
echo.
|
|
||||||
echo.Testing of doctests in the sources finished, look at the ^
|
|
||||||
results in build/doctest/output.txt.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
:end
|
|
|
@ -1,202 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# OpenLP documentation build configuration file, created by
|
|
||||||
# sphinx-quickstart on Fri Jul 10 17:20:40 2009.
|
|
||||||
#
|
|
||||||
# This file is execfile()d with the current directory set to its containing dir.
|
|
||||||
#
|
|
||||||
# Note that not all possible configuration values are present in this
|
|
||||||
# autogenerated file.
|
|
||||||
#
|
|
||||||
# All configuration values have a default; values that are commented out
|
|
||||||
# serve to show the default.
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
|
||||||
sys.path.insert(0, os.path.abspath(os.path.join('..', '..', '..')))
|
|
||||||
|
|
||||||
# -- General configuration -----------------------------------------------------
|
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
|
||||||
extensions = ['sphinx.ext.autodoc']
|
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
|
||||||
templates_path = ['_templates']
|
|
||||||
|
|
||||||
# The suffix of source filenames.
|
|
||||||
source_suffix = '.rst'
|
|
||||||
|
|
||||||
# The encoding of source files.
|
|
||||||
source_encoding = 'utf-8'
|
|
||||||
|
|
||||||
# The master toctree document.
|
|
||||||
master_doc = 'index'
|
|
||||||
|
|
||||||
# General information about the project.
|
|
||||||
project = u'OpenLP'
|
|
||||||
copyright = u'2004-2010, Raoul Snyman'
|
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
|
||||||
# |version| and |release|, also used in various other places throughout the
|
|
||||||
# built documents.
|
|
||||||
#
|
|
||||||
# The short X.Y version.
|
|
||||||
version = '2.0'
|
|
||||||
# The full version, including alpha/beta/rc tags.
|
|
||||||
release = '1.9.3'
|
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
|
||||||
# for a list of supported languages.
|
|
||||||
#language = None
|
|
||||||
|
|
||||||
# There are two options for replacing |today|: either, you set today to some
|
|
||||||
# non-false value, then it is used:
|
|
||||||
#today = ''
|
|
||||||
# Else, today_fmt is used as the format for a strftime call.
|
|
||||||
#today_fmt = '%B %d, %Y'
|
|
||||||
|
|
||||||
# List of documents that shouldn't be included in the build.
|
|
||||||
#unused_docs = []
|
|
||||||
|
|
||||||
# List of directories, relative to source directory, that shouldn't be searched
|
|
||||||
# for source files.
|
|
||||||
exclude_trees = []
|
|
||||||
|
|
||||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
|
||||||
#default_role = None
|
|
||||||
|
|
||||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
|
||||||
#add_function_parentheses = True
|
|
||||||
|
|
||||||
# If true, the current module name will be prepended to all description
|
|
||||||
# unit titles (such as .. function::).
|
|
||||||
add_module_names = False
|
|
||||||
|
|
||||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
|
||||||
# output. They are ignored by default.
|
|
||||||
#show_authors = False
|
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
|
||||||
pygments_style = 'sphinx'
|
|
||||||
|
|
||||||
# A list of ignored prefixes for module index sorting.
|
|
||||||
#modindex_common_prefix = []
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output ---------------------------------------------------
|
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
|
||||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
|
||||||
html_theme = 'default'
|
|
||||||
|
|
||||||
# Theme options are theme-specific and customize the look and feel of a theme
|
|
||||||
# further. For a list of options available for each theme, see the
|
|
||||||
# documentation.
|
|
||||||
html_theme_options = {
|
|
||||||
'sidebarbgcolor': '#3a60a9',
|
|
||||||
'relbarbgcolor': '#203b6f',
|
|
||||||
'footerbgcolor': '#26437c',
|
|
||||||
'headtextcolor': '#203b6f',
|
|
||||||
'linkcolor': '#26437c',
|
|
||||||
'sidebarlinkcolor': '#ceceff'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Add any paths that contain custom themes here, relative to this directory.
|
|
||||||
#html_theme_path = []
|
|
||||||
|
|
||||||
# The name for this set of Sphinx documents. If None, it defaults to
|
|
||||||
# "<project> v<release> documentation".
|
|
||||||
html_title = 'OpenLP 2.0 Developer API'
|
|
||||||
|
|
||||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
|
||||||
#html_short_title = None
|
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top
|
|
||||||
# of the sidebar.
|
|
||||||
#html_logo = None
|
|
||||||
|
|
||||||
# The name of an image file (within the static path) to use as favicon of the
|
|
||||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
|
||||||
# pixels large.
|
|
||||||
#html_favicon = None
|
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
|
||||||
html_static_path = ['_static']
|
|
||||||
|
|
||||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
|
||||||
# using the given strftime format.
|
|
||||||
#html_last_updated_fmt = '%b %d, %Y'
|
|
||||||
|
|
||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
|
||||||
# typographically correct entities.
|
|
||||||
#html_use_smartypants = True
|
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
|
||||||
#html_sidebars = {}
|
|
||||||
|
|
||||||
# Additional templates that should be rendered to pages, maps page names to
|
|
||||||
# template names.
|
|
||||||
#html_additional_pages = {}
|
|
||||||
|
|
||||||
# If false, no module index is generated.
|
|
||||||
#html_use_modindex = True
|
|
||||||
|
|
||||||
# If false, no index is generated.
|
|
||||||
#html_use_index = True
|
|
||||||
|
|
||||||
# If true, the index is split into individual pages for each letter.
|
|
||||||
#html_split_index = False
|
|
||||||
|
|
||||||
# If true, links to the reST sources are added to the pages.
|
|
||||||
#html_show_sourcelink = True
|
|
||||||
|
|
||||||
# If true, an OpenSearch description file will be output, and all pages will
|
|
||||||
# contain a <link> tag referring to it. The value of this option must be the
|
|
||||||
# base URL from which the finished HTML is served.
|
|
||||||
#html_use_opensearch = ''
|
|
||||||
|
|
||||||
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
|
||||||
#html_file_suffix = ''
|
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
|
||||||
htmlhelp_basename = 'OpenLP-2.0-api'
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for LaTeX output --------------------------------------------------
|
|
||||||
|
|
||||||
# The paper size ('letter' or 'a4').
|
|
||||||
#latex_paper_size = 'letter'
|
|
||||||
|
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
|
||||||
#latex_font_size = '10pt'
|
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
|
||||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
|
||||||
latex_documents = [
|
|
||||||
('index', 'OpenLP.tex', u'OpenLP 2.0 Developer API',
|
|
||||||
u'Raoul Snyman', 'manual'),
|
|
||||||
]
|
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top of
|
|
||||||
# the title page.
|
|
||||||
#latex_logo = None
|
|
||||||
|
|
||||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
|
||||||
# not chapters.
|
|
||||||
#latex_use_parts = False
|
|
||||||
|
|
||||||
# Additional stuff for the LaTeX preamble.
|
|
||||||
#latex_preamble = ''
|
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
|
||||||
#latex_appendices = []
|
|
||||||
|
|
||||||
# If false, no module index is generated.
|
|
||||||
#latex_use_modindex = True
|
|
|
@ -1,15 +0,0 @@
|
||||||
.. _core-index:
|
|
||||||
|
|
||||||
:mod:`core` Module
|
|
||||||
==================
|
|
||||||
|
|
||||||
.. automodule:: openlp.core
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
lib
|
|
||||||
theme
|
|
||||||
ui
|
|
||||||
utils
|
|
|
@ -1,67 +0,0 @@
|
||||||
.. _core-lib:
|
|
||||||
|
|
||||||
Object Library
|
|
||||||
==============
|
|
||||||
|
|
||||||
.. automodule:: openlp.core.lib
|
|
||||||
:members:
|
|
||||||
|
|
||||||
:mod:`EventReceiver`
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.core.lib.eventreceiver.EventReceiver
|
|
||||||
:members:
|
|
||||||
|
|
||||||
:mod:`ListWidgetWithDnD`
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.core.lib.listwidgetwithdnd.ListWidgetWithDnD
|
|
||||||
:members:
|
|
||||||
|
|
||||||
:mod:`MediaManagerItem`
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.core.lib.mediamanageritem.MediaManagerItem
|
|
||||||
:members:
|
|
||||||
|
|
||||||
:mod:`Plugin`
|
|
||||||
-------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.core.lib.plugin.Plugin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
:mod:`PluginManager`
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.core.lib.pluginmanager.PluginManager
|
|
||||||
:members:
|
|
||||||
|
|
||||||
:mod:`Renderer`
|
|
||||||
---------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.core.lib.renderer.Renderer
|
|
||||||
:members:
|
|
||||||
|
|
||||||
:mod:`RenderManager`
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.core.lib.rendermanager.RenderManager
|
|
||||||
:members:
|
|
||||||
|
|
||||||
:mod:`ServiceItem`
|
|
||||||
------------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.core.lib.serviceitem.ServiceItem
|
|
||||||
:members:
|
|
||||||
|
|
||||||
:mod:`SettingsTab`
|
|
||||||
------------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.core.lib.settingstab.SettingsTab
|
|
||||||
:members:
|
|
||||||
|
|
||||||
:mod:`OpenLPToolbar`
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.core.lib.toolbar.OpenLPToolbar
|
|
||||||
:members:
|
|
|
@ -1,10 +0,0 @@
|
||||||
.. _core-theme:
|
|
||||||
|
|
||||||
Theme Function Library
|
|
||||||
======================
|
|
||||||
|
|
||||||
.. automodule:: openlp.core.theme
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.core.theme.theme.Theme
|
|
||||||
:members:
|
|
|
@ -1,27 +0,0 @@
|
||||||
.. _core-ui:
|
|
||||||
|
|
||||||
User Interface
|
|
||||||
==============
|
|
||||||
|
|
||||||
.. automodule:: openlp.core.ui
|
|
||||||
|
|
||||||
Main Windows
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.core.ui.mainwindow.MainWindow
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.core.ui.maindisplay.MainDisplay
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Managers
|
|
||||||
--------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.core.ui.servicemanager.ServiceManager
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.core.ui.mediadockmanager.MediaDockManager
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.core.ui.thememanager.ThemeManager
|
|
||||||
:members:
|
|
|
@ -1,7 +0,0 @@
|
||||||
.. _core-utils:
|
|
||||||
|
|
||||||
Utilities
|
|
||||||
=========
|
|
||||||
|
|
||||||
.. automodule:: openlp.core.utils
|
|
||||||
:members:
|
|
|
@ -1,27 +0,0 @@
|
||||||
.. OpenLP documentation master file, created by
|
|
||||||
sphinx-quickstart on Fri Jul 10 17:20:40 2009.
|
|
||||||
You can adapt this file completely to your liking, but it should at least
|
|
||||||
contain the root `toctree` directive.
|
|
||||||
|
|
||||||
Welcome
|
|
||||||
=======
|
|
||||||
|
|
||||||
Welcome to the OpenLP 2.0 API Documentation! In here you will find all
|
|
||||||
information relating to OpenLP's core classes, core plugins, and anything else
|
|
||||||
deemed necessary or interesting by the developers.
|
|
||||||
|
|
||||||
Contents:
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
core/index
|
|
||||||
plugins/index
|
|
||||||
|
|
||||||
Indices and tables
|
|
||||||
==================
|
|
||||||
|
|
||||||
* :ref:`genindex`
|
|
||||||
* :ref:`modindex`
|
|
||||||
* :ref:`search`
|
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
.. _plugins-alerts:
|
|
||||||
|
|
||||||
Alerts Plugin
|
|
||||||
=============
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.alerts
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Plugin Class
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.alerts.alertsplugin.AlertsPlugin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Forms
|
|
||||||
-----
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.alerts.forms
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.alerts.forms.alertform.AlertForm
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Helper Classes & Functions
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.alerts.lib
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.alerts.lib.db
|
|
||||||
:members:
|
|
|
@ -1,55 +0,0 @@
|
||||||
.. _plugins-bibles:
|
|
||||||
|
|
||||||
Bibles Plugin
|
|
||||||
=============
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.bibles
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Plugin Class
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.bibles.bibleplugin.BiblePlugin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Forms
|
|
||||||
-----
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.bibles.forms
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.bibles.forms.bibleimportform.BibleImportForm
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Helper Classes & Functions
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.bibles.lib
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.bibles.lib.db
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.bibles.lib.biblestab
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.bibles.lib.manager
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.bibles.lib.mediaitem
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Bible Importers
|
|
||||||
---------------
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.bibles.lib.csvbible
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.bibles.lib.http
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.bibles.lib.osis
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.bibles.lib.opensong
|
|
||||||
:members:
|
|
|
@ -1,34 +0,0 @@
|
||||||
.. _plugins-custom:
|
|
||||||
|
|
||||||
Custom Slides Plugin
|
|
||||||
====================
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.custom
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Plugin Class
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.custom.customplugin.CustomPlugin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Forms
|
|
||||||
-----
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.custom.forms
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.custom.forms.editcustomform.EditCustomForm
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.custom.forms.editcustomslideform.EditCustomSlideForm
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Helper Classes & Functions
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.custom.lib
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.custom.lib.mediaitem
|
|
||||||
:members:
|
|
|
@ -1,22 +0,0 @@
|
||||||
.. _plugins-images:
|
|
||||||
|
|
||||||
Images Plugin
|
|
||||||
=============
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.images
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Plugin Class
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.images.imageplugin.ImagePlugin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Helper Classes & Functions
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.images.lib
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.images.lib.mediaitem
|
|
||||||
:members:
|
|
|
@ -1,20 +0,0 @@
|
||||||
.. _plugins-index:
|
|
||||||
|
|
||||||
Plugins
|
|
||||||
=======
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
songs
|
|
||||||
bibles
|
|
||||||
presentations
|
|
||||||
media
|
|
||||||
images
|
|
||||||
custom
|
|
||||||
remotes
|
|
||||||
songusage
|
|
||||||
alerts
|
|
|
@ -1,22 +0,0 @@
|
||||||
.. _plugins-media:
|
|
||||||
|
|
||||||
Media Plugin
|
|
||||||
============
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.media
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Plugin Class
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.media.mediaplugin.MediaPlugin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Helper Classes & Functions
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.media.lib
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.media.lib.mediaitem
|
|
||||||
:members:
|
|
|
@ -1,40 +0,0 @@
|
||||||
.. _plugins-presentations:
|
|
||||||
|
|
||||||
Presentations Plugin
|
|
||||||
====================
|
|
||||||
|
|
||||||
Plugin Class
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.presentations.presentationplugin.PresentationPlugin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Helper Classes & Functions
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.presentations.lib
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.presentations.lib.mediaitem
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.presentations.lib.presentationtab
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.presentations.lib.messagelistener
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.presentations.lib.presentationcontroller
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Presentation Application Controllers
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.presentations.lib.impresscontroller
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.presentations.lib.pptviewcontroller
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.presentations.lib.powerpointcontroller
|
|
||||||
:members:
|
|
|
@ -1,19 +0,0 @@
|
||||||
.. _plugins-remotes:
|
|
||||||
|
|
||||||
Remotes Plugin
|
|
||||||
==============
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.remotes
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Plugin Class
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.remotes.remoteplugin.RemotesPlugin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Helper Classes & Functions
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.remotes.lib
|
|
||||||
:members:
|
|
|
@ -1,97 +0,0 @@
|
||||||
.. _plugins-songs:
|
|
||||||
|
|
||||||
Songs Plugin
|
|
||||||
============
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Plugin Class
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.songs.songsplugin.SongsPlugin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Forms
|
|
||||||
-----
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs.forms
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.songs.forms.authorsform.AuthorsForm
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.songs.forms.editsongform.EditSongForm
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.songs.forms.editverseform.EditVerseForm
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.songs.forms.songbookform.SongBookForm
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.songs.forms.songimportform.SongImportForm
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.songs.forms.songmaintenanceform.SongMaintenanceForm
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.songs.forms.topicsform.TopicsForm
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Helper Classes & Functions
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs.lib
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs.lib.db
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs.lib.importer
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs.lib.mediaitem
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs.lib.songimport
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs.lib.songstab
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs.lib.xml
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Song Importers
|
|
||||||
--------------
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs.lib.cclifileimport
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs.lib.ewimport
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.songs.lib.ewimport.FieldDescEntry
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs.lib.olp1import
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs.lib.olpimport
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs.lib.oooimport
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs.lib.opensongimport
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs.lib.sofimport
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs.lib.songbeamerimport
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songs.lib.wowimport
|
|
||||||
:members:
|
|
|
@ -1,34 +0,0 @@
|
||||||
.. _plugins-songusage:
|
|
||||||
|
|
||||||
Song Usage Plugin
|
|
||||||
=================
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songusage
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Plugin Class
|
|
||||||
------------
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.songusage.songusageplugin.SongUsagePlugin
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Forms
|
|
||||||
-----
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songusage.forms
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.songusage.forms.songusagedeleteform.SongUsageDeleteForm
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. autoclass:: openlp.plugins.songusage.forms.songusagedetailform.SongUsageDetailForm
|
|
||||||
:members:
|
|
||||||
|
|
||||||
Helper Classes & Functions
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songusage.lib
|
|
||||||
:members:
|
|
||||||
|
|
||||||
.. automodule:: openlp.plugins.songusage.lib.db
|
|
||||||
:members:
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
OpenLP Manual
|
||||||
|
=============
|
||||||
|
|
||||||
|
If you're reading this file, you're probably looking for the OpenLP manual. The
|
||||||
|
manual is hosted online at http://manual.openlp.org/. If you want to help with
|
||||||
|
the manual, contact the OpenLP team via IRC in the #openlp.org channel on the
|
||||||
|
Freenode network.
|
|
@ -1,88 +0,0 @@
|
||||||
# Makefile for Sphinx documentation
|
|
||||||
#
|
|
||||||
|
|
||||||
# You can set these variables from the command line.
|
|
||||||
SPHINXOPTS =
|
|
||||||
SPHINXBUILD = sphinx-build
|
|
||||||
PAPER =
|
|
||||||
|
|
||||||
# Internal variables.
|
|
||||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
|
||||||
PAPEROPT_letter = -D latex_paper_size=letter
|
|
||||||
ALLSPHINXOPTS = -d build/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
|
|
||||||
|
|
||||||
.PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest
|
|
||||||
|
|
||||||
help:
|
|
||||||
@echo "Please use \`make <target>' where <target> is one of"
|
|
||||||
@echo " html to make standalone HTML files"
|
|
||||||
@echo " dirhtml to make HTML files named index.html in directories"
|
|
||||||
@echo " pickle to make pickle files"
|
|
||||||
@echo " json to make JSON files"
|
|
||||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
|
||||||
@echo " qthelp to make HTML files and a qthelp project"
|
|
||||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
|
||||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
|
||||||
@echo " linkcheck to check all external links for integrity"
|
|
||||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
|
||||||
|
|
||||||
clean:
|
|
||||||
-rm -rf build/*
|
|
||||||
|
|
||||||
html:
|
|
||||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) build/html
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The HTML pages are in build/html."
|
|
||||||
|
|
||||||
dirhtml:
|
|
||||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) build/dirhtml
|
|
||||||
@echo
|
|
||||||
@echo "Build finished. The HTML pages are in build/dirhtml."
|
|
||||||
|
|
||||||
pickle:
|
|
||||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) build/pickle
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can process the pickle files."
|
|
||||||
|
|
||||||
json:
|
|
||||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) build/json
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can process the JSON files."
|
|
||||||
|
|
||||||
htmlhelp:
|
|
||||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) build/htmlhelp
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
|
||||||
".hhp project file in build/htmlhelp."
|
|
||||||
|
|
||||||
qthelp:
|
|
||||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) build/qthelp
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
|
||||||
".qhcp project file in build/qthelp, like this:"
|
|
||||||
@echo "# qcollectiongenerator build/qthelp/OpenLP.qhcp"
|
|
||||||
@echo "To view the help file:"
|
|
||||||
@echo "# assistant -collectionFile build/qthelp/OpenLP.qhc"
|
|
||||||
|
|
||||||
latex:
|
|
||||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) build/latex
|
|
||||||
@echo
|
|
||||||
@echo "Build finished; the LaTeX files are in build/latex."
|
|
||||||
@echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \
|
|
||||||
"run these through (pdf)latex."
|
|
||||||
|
|
||||||
changes:
|
|
||||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) build/changes
|
|
||||||
@echo
|
|
||||||
@echo "The overview file is in build/changes."
|
|
||||||
|
|
||||||
linkcheck:
|
|
||||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) build/linkcheck
|
|
||||||
@echo
|
|
||||||
@echo "Link check complete; look for any errors in the above output " \
|
|
||||||
"or in build/linkcheck/output.txt."
|
|
||||||
|
|
||||||
doctest:
|
|
||||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) build/doctest
|
|
||||||
@echo "Testing of doctests in the sources finished, look at the " \
|
|
||||||
"results in build/doctest/output.txt."
|
|
|
@ -1,112 +0,0 @@
|
||||||
@ECHO OFF
|
|
||||||
|
|
||||||
REM Command file for Sphinx documentation
|
|
||||||
|
|
||||||
set SPHINXBUILD=sphinx-build
|
|
||||||
set ALLSPHINXOPTS=-d build/doctrees %SPHINXOPTS% source
|
|
||||||
if NOT "%PAPER%" == "" (
|
|
||||||
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "" goto help
|
|
||||||
|
|
||||||
if "%1" == "help" (
|
|
||||||
:help
|
|
||||||
echo.Please use `make ^<target^>` where ^<target^> is one of
|
|
||||||
echo. html to make standalone HTML files
|
|
||||||
echo. dirhtml to make HTML files named index.html in directories
|
|
||||||
echo. pickle to make pickle files
|
|
||||||
echo. json to make JSON files
|
|
||||||
echo. htmlhelp to make HTML files and a HTML help project
|
|
||||||
echo. qthelp to make HTML files and a qthelp project
|
|
||||||
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
|
|
||||||
echo. changes to make an overview over all changed/added/deprecated items
|
|
||||||
echo. linkcheck to check all external links for integrity
|
|
||||||
echo. doctest to run all doctests embedded in the documentation if enabled
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "clean" (
|
|
||||||
for /d %%i in (build\*) do rmdir /q /s %%i
|
|
||||||
del /q /s build\*
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "html" (
|
|
||||||
%SPHINXBUILD% -b html %ALLSPHINXOPTS% build/html
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The HTML pages are in build/html.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "dirhtml" (
|
|
||||||
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% build/dirhtml
|
|
||||||
echo.
|
|
||||||
echo.Build finished. The HTML pages are in build/dirhtml.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "pickle" (
|
|
||||||
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% build/pickle
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can process the pickle files.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "json" (
|
|
||||||
%SPHINXBUILD% -b json %ALLSPHINXOPTS% build/json
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can process the JSON files.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "htmlhelp" (
|
|
||||||
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% build/htmlhelp
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can run HTML Help Workshop with the ^
|
|
||||||
.hhp project file in build/htmlhelp.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "qthelp" (
|
|
||||||
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% build/qthelp
|
|
||||||
echo.
|
|
||||||
echo.Build finished; now you can run "qcollectiongenerator" with the ^
|
|
||||||
.qhcp project file in build/qthelp, like this:
|
|
||||||
echo.^> qcollectiongenerator build\qthelp\OpenLP.qhcp
|
|
||||||
echo.To view the help file:
|
|
||||||
echo.^> assistant -collectionFile build\qthelp\OpenLP.ghc
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "latex" (
|
|
||||||
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% build/latex
|
|
||||||
echo.
|
|
||||||
echo.Build finished; the LaTeX files are in build/latex.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "changes" (
|
|
||||||
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% build/changes
|
|
||||||
echo.
|
|
||||||
echo.The overview file is in build/changes.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "linkcheck" (
|
|
||||||
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% build/linkcheck
|
|
||||||
echo.
|
|
||||||
echo.Link check complete; look for any errors in the above output ^
|
|
||||||
or in build/linkcheck/output.txt.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
if "%1" == "doctest" (
|
|
||||||
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% build/doctest
|
|
||||||
echo.
|
|
||||||
echo.Testing of doctests in the sources finished, look at the ^
|
|
||||||
results in build/doctest/output.txt.
|
|
||||||
goto end
|
|
||||||
)
|
|
||||||
|
|
||||||
:end
|
|
|
@ -1,210 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
#
|
|
||||||
# OpenLP documentation build configuration file, created by
|
|
||||||
# sphinx-quickstart on Fri Jul 10 17:20:40 2009.
|
|
||||||
#
|
|
||||||
# This file is execfile()d with the current directory set to its containing dir.
|
|
||||||
#
|
|
||||||
# Note that not all possible configuration values are present in this
|
|
||||||
# autogenerated file.
|
|
||||||
#
|
|
||||||
# All configuration values have a default; values that are commented out
|
|
||||||
# serve to show the default.
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
|
||||||
#sys.path.insert(0, os.path.abspath(os.path.join('..', '..')))
|
|
||||||
|
|
||||||
# -- General configuration -----------------------------------------------------
|
|
||||||
|
|
||||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
|
||||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
|
||||||
#extensions = ['sphinx.ext.autodoc']
|
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
|
||||||
templates_path = ['_templates']
|
|
||||||
|
|
||||||
# The suffix of source filenames.
|
|
||||||
source_suffix = '.rst'
|
|
||||||
|
|
||||||
# The encoding of source files.
|
|
||||||
source_encoding = 'utf-8'
|
|
||||||
|
|
||||||
# The master toctree document.
|
|
||||||
master_doc = 'index'
|
|
||||||
|
|
||||||
# General information about the project.
|
|
||||||
project = u'OpenLP'
|
|
||||||
copyright = u'2004-2010 Raoul Snyman'
|
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
|
||||||
# |version| and |release|, also used in various other places throughout the
|
|
||||||
# built documents.
|
|
||||||
#
|
|
||||||
# The short X.Y version.
|
|
||||||
version = '2.0'
|
|
||||||
# The full version, including alpha/beta/rc tags.
|
|
||||||
release = '1.9.5'
|
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
|
||||||
# for a list of supported languages.
|
|
||||||
#language = None
|
|
||||||
|
|
||||||
# There are two options for replacing |today|: either, you set today to some
|
|
||||||
# non-false value, then it is used:
|
|
||||||
#today = ''
|
|
||||||
# Else, today_fmt is used as the format for a strftime call.
|
|
||||||
#today_fmt = '%B %d, %Y'
|
|
||||||
|
|
||||||
# List of documents that shouldn't be included in the build.
|
|
||||||
#unused_docs = []
|
|
||||||
|
|
||||||
# List of directories, relative to source directory, that shouldn't be searched
|
|
||||||
# for source files.
|
|
||||||
exclude_trees = []
|
|
||||||
|
|
||||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
|
||||||
#default_role = None
|
|
||||||
|
|
||||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
|
||||||
#add_function_parentheses = True
|
|
||||||
|
|
||||||
# If true, the current module name will be prepended to all description
|
|
||||||
# unit titles (such as .. function::).
|
|
||||||
add_module_names = False
|
|
||||||
|
|
||||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
|
||||||
# output. They are ignored by default.
|
|
||||||
#show_authors = False
|
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
|
||||||
pygments_style = 'sphinx'
|
|
||||||
|
|
||||||
# A list of ignored prefixes for module index sorting.
|
|
||||||
#modindex_common_prefix = []
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for HTML output ---------------------------------------------------
|
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
|
||||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
|
||||||
#html_theme = 'openlp_qthelp'
|
|
||||||
html_theme = 'default'
|
|
||||||
|
|
||||||
# Theme options are theme-specific and customize the look and feel of a theme
|
|
||||||
# further. For a list of options available for each theme, see the
|
|
||||||
# documentation.
|
|
||||||
if html_theme == 'default':
|
|
||||||
html_theme_options = {
|
|
||||||
'sidebarbgcolor': '#3a60a9',
|
|
||||||
'relbarbgcolor': '#203b6f',
|
|
||||||
'footerbgcolor': '#26437c',
|
|
||||||
'headtextcolor': '#203b6f',
|
|
||||||
'linkcolor': '#26437c',
|
|
||||||
'sidebarlinkcolor': '#ceceff'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Add any paths that contain custom themes here, relative to this directory.
|
|
||||||
html_theme_path = [u'../themes']
|
|
||||||
|
|
||||||
# The name for this set of Sphinx documents. If None, it defaults to
|
|
||||||
# "<project> v<release> documentation".
|
|
||||||
html_title = u'OpenLP 2.0 User Manual'
|
|
||||||
|
|
||||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
|
||||||
#html_short_title = None
|
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top
|
|
||||||
# of the sidebar.
|
|
||||||
#html_logo = None
|
|
||||||
|
|
||||||
# The name of an image file (within the static path) to use as favicon of the
|
|
||||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
|
||||||
# pixels large.
|
|
||||||
#html_favicon = None
|
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
|
||||||
html_static_path = ['_static']
|
|
||||||
|
|
||||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
|
||||||
# using the given strftime format.
|
|
||||||
#html_last_updated_fmt = '%b %d, %Y'
|
|
||||||
|
|
||||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
|
||||||
# typographically correct entities.
|
|
||||||
#html_use_smartypants = True
|
|
||||||
|
|
||||||
# Custom sidebar templates, maps document names to template names.
|
|
||||||
#html_sidebars = {}
|
|
||||||
|
|
||||||
# Additional templates that should be rendered to pages, maps page names to
|
|
||||||
# template names.
|
|
||||||
#html_additional_pages = {}
|
|
||||||
|
|
||||||
# If false, no module index is generated.
|
|
||||||
#html_use_modindex = True
|
|
||||||
|
|
||||||
# If false, no index is generated.
|
|
||||||
#html_use_index = True
|
|
||||||
|
|
||||||
# If true, the index is split into individual pages for each letter.
|
|
||||||
#html_split_index = False
|
|
||||||
|
|
||||||
# If true, links to the reST sources are added to the pages.
|
|
||||||
#html_show_sourcelink = True
|
|
||||||
|
|
||||||
# If true, an OpenSearch description file will be output, and all pages will
|
|
||||||
# contain a <link> tag referring to it. The value of this option must be the
|
|
||||||
# base URL from which the finished HTML is served.
|
|
||||||
#html_use_opensearch = ''
|
|
||||||
|
|
||||||
# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml").
|
|
||||||
#html_file_suffix = ''
|
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
|
||||||
htmlhelp_basename = 'OpenLP-2.0-manual'
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for LaTeX output --------------------------------------------------
|
|
||||||
|
|
||||||
# The paper size ('letter' or 'a4').
|
|
||||||
#latex_paper_size = 'letter'
|
|
||||||
|
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
|
||||||
#latex_font_size = '10pt'
|
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
|
||||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
|
||||||
latex_documents = [
|
|
||||||
('index', 'OpenLP.tex', u'OpenLP 2.0 User Manual',
|
|
||||||
u'Raoul Snyman', 'manual'),
|
|
||||||
]
|
|
||||||
|
|
||||||
# The name of an image file (relative to this directory) to place at the top of
|
|
||||||
# the title page.
|
|
||||||
#latex_logo = None
|
|
||||||
|
|
||||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
|
||||||
# not chapters.
|
|
||||||
#latex_use_parts = False
|
|
||||||
|
|
||||||
# Additional stuff for the LaTeX preamble.
|
|
||||||
#latex_preamble = ''
|
|
||||||
|
|
||||||
# Documents to append as an appendix to all manuals.
|
|
||||||
#latex_appendices = []
|
|
||||||
|
|
||||||
# If false, no module index is generated.
|
|
||||||
#latex_use_modindex = True
|
|
||||||
|
|
||||||
# A dictionary that contains LaTeX snippets that override those Sphinx usually
|
|
||||||
# puts into the generated .tex files.
|
|
||||||
latex_elements = {
|
|
||||||
'fontpkg': '\\usepackage{helvet}'
|
|
||||||
}
|
|
|
@ -1,180 +0,0 @@
|
||||||
==================
|
|
||||||
Dual Monitor Setup
|
|
||||||
==================
|
|
||||||
|
|
||||||
The first step in getting OpenLP working on your system is to setup your
|
|
||||||
computer properly for dual monitors. This is not very difficult, but the steps
|
|
||||||
do vary depending on operating system.
|
|
||||||
|
|
||||||
Most modern computers do have the ability for dual monitors. To be certain
|
|
||||||
check your computer's documentation. A typical desktop computer capable of dual
|
|
||||||
monitors will have two of, or a combination of the two connectors below.
|
|
||||||
|
|
||||||
**VGA**
|
|
||||||
|
|
||||||
.. image:: pics/vga.png
|
|
||||||
|
|
||||||
**DVI**
|
|
||||||
|
|
||||||
.. image:: pics/dvi.png
|
|
||||||
|
|
||||||
A laptop computer setup only varies slightly, generally you will need only one
|
|
||||||
of outputs pictured above since your laptops screen serves as one of the
|
|
||||||
monitors. Sometimes with older laptops a key stroke generally involving the
|
|
||||||
:kbd:`Fn` key and another key is required to enable the second monitor on
|
|
||||||
laptops.
|
|
||||||
|
|
||||||
Some computers also incorporate the use of :abbr:`S-Video (Separate Video)` or
|
|
||||||
:abbr:`HDMI (High-Definition Multimedia Interface)` connections.
|
|
||||||
|
|
||||||
A typical OpenLP set up consist of your normal single monitor setup, with your
|
|
||||||
projector setup as the second monitor. With the option of extending your
|
|
||||||
desktop across the second monitor, or your operating system's equivalent.
|
|
||||||
|
|
||||||
Microsoft Windows
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Dual monitor setup is similar among all the currently supported Windows
|
|
||||||
releases (XP, Vista, Windows 7), but does vary slightly from one release to the
|
|
||||||
next.
|
|
||||||
|
|
||||||
Windows 7
|
|
||||||
^^^^^^^^^
|
|
||||||
|
|
||||||
Windows 7 has using a projector in mind. Simply connect your projector and
|
|
||||||
press :kbd:`Windows+P`.
|
|
||||||
|
|
||||||
The more traditional way is also fairly straight forward. Go to
|
|
||||||
:guilabel:`Control Panel` and click on :guilabel:`Display`. This will open up
|
|
||||||
the :guilabel:`Display` dialog. You can also bypass this step by right click on
|
|
||||||
a blank area on your desktop and selecting :guilabel:`Resolution`.
|
|
||||||
|
|
||||||
.. image:: pics/winsevendisplay.png
|
|
||||||
|
|
||||||
Then click on the :guilabel:`Adjust resolution` link in the left pane. Enable
|
|
||||||
your projector and make sure that the selected value for :guilabel:`Multiple
|
|
||||||
displays` is :guilabel:`Extend these displays`.
|
|
||||||
|
|
||||||
.. image:: pics/winsevenresolution.png
|
|
||||||
|
|
||||||
Windows Vista
|
|
||||||
^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
From :guilabel:`Control Panel` click on :guilabel:`Personalize`, or right click
|
|
||||||
a blank place on the desktop and click :guilabel:`Personalization`.
|
|
||||||
|
|
||||||
.. image:: pics/vistapersonalize.png
|
|
||||||
|
|
||||||
From the :guilabel:`Personalization` window click on :guilabel:`Display
|
|
||||||
Settings`. Then enable the montior that represents your projector and make sure
|
|
||||||
you have checked :guilabel:`Extend the desktop onto this monitor`.
|
|
||||||
|
|
||||||
.. image:: pics/vistadisplaysettings.png
|
|
||||||
|
|
||||||
Windows XP
|
|
||||||
^^^^^^^^^^
|
|
||||||
|
|
||||||
From :guilabel:`Control Panel` select :guilabel:`Display`, or right click on a
|
|
||||||
blank area of the desktop and select :guilabel:`Properties`. From the
|
|
||||||
:guilabel:`Display Properties` window click on the :guilabel:`Settings` tab.
|
|
||||||
Then click on the monitor that represents your projector and make sure you have
|
|
||||||
checked :guilabel:`Extend my Windows desktop onto this monitor`.
|
|
||||||
|
|
||||||
.. image:: pics/xpdisplaysettings.png
|
|
||||||
|
|
||||||
Linux
|
|
||||||
-----
|
|
||||||
|
|
||||||
Due to the vast varieties of hardware, distributions, desktops, and drivers
|
|
||||||
this is not an exhaustive guide to dual monitor setup on Linux. This guide
|
|
||||||
assumes that you have properly set up any proprietary drivers if needed. You
|
|
||||||
should seek out your distributions documentation if this general guide does not
|
|
||||||
work.
|
|
||||||
|
|
||||||
GNOME
|
|
||||||
^^^^^
|
|
||||||
|
|
||||||
This guide is for users of the GNOME desktop who do not use proprietary drivers.
|
|
||||||
From most distros go to :menuselection:`System --> Preferences --> Display
|
|
||||||
Settings (Monitors)`. Set up your projector with the correct resolution and make
|
|
||||||
sure that :guilabel:`Same image on all monitors` is **unchecked**.
|
|
||||||
|
|
||||||
.. image:: pics/gnome.png
|
|
||||||
|
|
||||||
KDE
|
|
||||||
^^^
|
|
||||||
|
|
||||||
This guide is for users of the KDE desktop who do not use proprietary drivers.
|
|
||||||
From most distros click the Kick Off menu and navigate to
|
|
||||||
:guilabel:`System Settings`
|
|
||||||
|
|
||||||
.. image:: pics/kdesystemsettings.png
|
|
||||||
|
|
||||||
Click on the display and monitor icon.
|
|
||||||
|
|
||||||
.. image:: pics/kdedisplay.png
|
|
||||||
|
|
||||||
From here you will need to set up your projector with the appropriate
|
|
||||||
resolution, and position. OpenLP works best projecting to the monitor on the
|
|
||||||
right.
|
|
||||||
|
|
||||||
Linux Systems Using nVidia Drivers
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
This guide is for users of the proprietary nVidia driver on Linux Distributions.
|
|
||||||
It is assumed that you have properly setup your drivers according to your
|
|
||||||
distribution's documentation, and you have a working ``xorg.conf`` file in place.
|
|
||||||
|
|
||||||
If you wish to make the changes permanent in setting up your system for dual
|
|
||||||
monitors it will be necessary to modify your ``xorg.conf`` file. It is always a
|
|
||||||
good idea to make a backup of any critical file before making changes::
|
|
||||||
|
|
||||||
user@linux:~ $ sudo cp /etc/X11/xorg.conf /etc/X11/xorg.conf.old
|
|
||||||
|
|
||||||
Or for those using systems that use the root user instead of sudo, change to
|
|
||||||
root and enter::
|
|
||||||
|
|
||||||
root@linux: # cp /etc/X11/xorg.conf /etc/X11/xorg.conf.old
|
|
||||||
|
|
||||||
The exact location of the ``xorg.conf`` file can vary so check your
|
|
||||||
distribution's documentation.
|
|
||||||
|
|
||||||
If you want to make your changes permanent run nVidia settings from the
|
|
||||||
terminal::
|
|
||||||
|
|
||||||
user@linux:~ $ sudo nvidia-settings
|
|
||||||
|
|
||||||
Or, as root::
|
|
||||||
|
|
||||||
root@linux: # nividia-settings
|
|
||||||
|
|
||||||
If you do not want to write the changes to your ``xorg.conf`` file simply run
|
|
||||||
the nVidia Settings program (:command:`nvidia-settings`) from your desktop's
|
|
||||||
menu, usually in an administration or system menu, or from the terminal as a
|
|
||||||
normal user run::
|
|
||||||
|
|
||||||
user@linux:~ $ nvidia-settings
|
|
||||||
|
|
||||||
Once you have opened nVidia Settings, click on :guilabel:`X Server Display
|
|
||||||
Configuration`. Then select the monitor you are wanting to use as your second
|
|
||||||
monitor and click :guilabel:`Configure`.
|
|
||||||
|
|
||||||
.. image:: pics/nvlinux1.png
|
|
||||||
|
|
||||||
After clicking :guilabel:`Configure`, select :guilabel:`TwinView`. Then click
|
|
||||||
:guilabel:`OK`.
|
|
||||||
|
|
||||||
.. image:: pics/twinview.png
|
|
||||||
|
|
||||||
Then click :guilabel:`Apply` and if you are happy with the way things look click
|
|
||||||
:guilabel:`Keep` to keep your new settings. Don't worry if all goes wrong the
|
|
||||||
settings will return back to the previous settings in 15 seconds without any
|
|
||||||
action. nVidia Settings should take care of selecting your optimum resolution
|
|
||||||
etc, but that can be changed as needed. When you are happy with everything click
|
|
||||||
on :guilabel:`Save to X Configuration File`.
|
|
||||||
|
|
||||||
.. image:: pics/xorgwrite.png
|
|
||||||
|
|
||||||
Then click :guilabel:`Save` and you should be set. You may want to restart X or
|
|
||||||
your machine just to make sure all the settings carry over the next time you log
|
|
||||||
in.
|
|
|
@ -1,70 +0,0 @@
|
||||||
========
|
|
||||||
Glossary
|
|
||||||
========
|
|
||||||
|
|
||||||
The developers of OpenLP have strived to make it a straightforward and easy to
|
|
||||||
use application. However, it is good to be familiar with a few terms that will
|
|
||||||
be used throughout this documentation, and when seeking support.
|
|
||||||
|
|
||||||
Main Window
|
|
||||||
-----------
|
|
||||||
|
|
||||||
The Main Window is what you will see when you first open OpenLP
|
|
||||||
|
|
||||||
.. image:: pics/mainwindow.png
|
|
||||||
|
|
||||||
The Main Window contains all the tools and plugins that make OpenLP function
|
|
||||||
|
|
||||||
Media Manager
|
|
||||||
-------------
|
|
||||||
|
|
||||||
The Media Manager contains a number of tabs that plugins supply to OpenLP.
|
|
||||||
Each tab in the Media Manager is called a **Media Item**
|
|
||||||
|
|
||||||
.. image:: pics/mediamanager.png
|
|
||||||
|
|
||||||
From the Media Manager you can send Media Items to the Preview or Live screens.
|
|
||||||
|
|
||||||
Preview
|
|
||||||
-------
|
|
||||||
|
|
||||||
The preview pane is a section to preview your media items before you go live
|
|
||||||
with them.
|
|
||||||
|
|
||||||
.. image:: pics/preview.png
|
|
||||||
|
|
||||||
Service File
|
|
||||||
------------
|
|
||||||
|
|
||||||
A service file, is the file that is created when you save your work on OpenLP.
|
|
||||||
The service file consist of **Service Items**
|
|
||||||
|
|
||||||
Service Item
|
|
||||||
------------
|
|
||||||
|
|
||||||
A service item are the **media items** that are in the **service manager**
|
|
||||||
|
|
||||||
Service Manger
|
|
||||||
--------------
|
|
||||||
|
|
||||||
The service manager contains the media items in your service file. This is the
|
|
||||||
area from which your media items go live, and you can also save, open, and edit
|
|
||||||
services files.
|
|
||||||
|
|
||||||
.. image:: pics/servicemanager.png
|
|
||||||
|
|
||||||
Slide Controller
|
|
||||||
----------------
|
|
||||||
|
|
||||||
The Slide Controller controls which slide from a **Service Item** is currently
|
|
||||||
being displayed, and moving between the various slides.
|
|
||||||
|
|
||||||
.. image:: pics/slidecontroller.png
|
|
||||||
|
|
||||||
Theme Manager
|
|
||||||
-------------
|
|
||||||
|
|
||||||
The theme manager is where themes are created and edited. Themes are the text
|
|
||||||
styles backgrounds that you use to personalize your services.
|
|
||||||
|
|
||||||
.. image:: pics/thememanager.png
|
|
|
@ -1,19 +0,0 @@
|
||||||
.. OpenLP documentation master file, created by
|
|
||||||
sphinx-quickstart on Thu Sep 30 21:24:54 2010.
|
|
||||||
You can adapt this file completely to your liking, but it should at least
|
|
||||||
contain the root `toctree` directive.
|
|
||||||
|
|
||||||
Welcome to the OpenLP 2.0 User Manual
|
|
||||||
=====================================
|
|
||||||
|
|
||||||
Contents:
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
introduction
|
|
||||||
glossary
|
|
||||||
dualmonitors
|
|
||||||
mediamanager
|
|
||||||
songs
|
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
=============
|
|
||||||
Introduction
|
|
||||||
=============
|
|
||||||
|
|
||||||
About
|
|
||||||
-----
|
|
||||||
|
|
||||||
OpenLP is an open source lyrics projection application developed specifically
|
|
||||||
for churches. It is licensed under the GNU Generic Public License, which means
|
|
||||||
that it is free to use and distribute, and it stays free.
|
|
||||||
|
|
||||||
Lyrics Projection
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
OpenLP's purpose is to project the lyrics of songs and Bible verses using a
|
|
||||||
computer and a data projector. OpenLP also has the ability to project videos,
|
|
||||||
images, and also play audio. OpenLP also is highly customizable providing users
|
|
||||||
with the ability to set up a wide variety of themes, including themes with
|
|
||||||
video backgrounds.
|
|
||||||
|
|
||||||
Open Source
|
|
||||||
-----------
|
|
||||||
|
|
||||||
OpenLP is open source software. This means that the source code (the
|
|
||||||
programming instructions the developers write) is open to anyone who wants to
|
|
||||||
look at it. This gives you, the end user, a few freedoms.
|
|
||||||
|
|
||||||
From a developer's perspective, it gives you the freedom to inspect the code
|
|
||||||
and make sure that it is not malicious. Also, it gives you the freedom to
|
|
||||||
change the code and the freedom to "fork" the project and make it your own.
|
|
||||||
|
|
||||||
For end users open source software gives you the freedom to use software as
|
|
||||||
you wish. You are not required to pay for the software and you are free to
|
|
||||||
make copies and distribute it to anyone you want.
|
|
||||||
|
|
||||||
GNU General Public License
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
The GNU General Public License was specifically chosen because it ensures the
|
|
||||||
above mentioned freedoms. It specifically states that you are not allowed
|
|
||||||
to charge for the software, and that you have to distribute the source code as
|
|
||||||
well.
|
|
||||||
|
|
||||||
You can find a copy of the GNU General Public License from the Help menu
|
|
||||||
selecting about OpenLP or on-line
|
|
||||||
at: http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
|
|
@ -1,26 +0,0 @@
|
||||||
=============
|
|
||||||
Media Manager
|
|
||||||
=============
|
|
||||||
|
|
||||||
Once you get your system set up for OpenLP you will be ready to add content to
|
|
||||||
your setup. This will all happen through the **Media Manager**. The
|
|
||||||
`Media Manager` contains all the bibles, songs, presentations, media, and
|
|
||||||
everything else that you will project through OpenLP.
|
|
||||||
|
|
||||||
Enabling the Plugins
|
|
||||||
--------------------
|
|
||||||
|
|
||||||
You may need to enable the plugins that came with OpenLP. As you can see below
|
|
||||||
this is what the `Media Manager` looks like with all the plugins enabled.
|
|
||||||
|
|
||||||
.. image:: pics/mediamanager.png
|
|
||||||
|
|
||||||
To enable the plugins navigate to :menuselection:`Settings --> Plugins` or
|
|
||||||
press `F7`. You will then want to click on the plugin to the left that you want
|
|
||||||
to enable and select **active** from the drop down box to the right.
|
|
||||||
|
|
||||||
.. image:: pics/plugins.png
|
|
||||||
|
|
||||||
|
|
||||||
Now you should be ready to add content to OpenLP check out the section of this
|
|
||||||
guide on the individual plugins.
|
|
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 123 KiB |
Before Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 140 KiB |
Before Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 63 KiB |
Before Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 90 KiB |
Before Width: | Height: | Size: 138 KiB |
Before Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 206 KiB |
Before Width: | Height: | Size: 160 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 139 KiB |
Before Width: | Height: | Size: 155 KiB |
Before Width: | Height: | Size: 141 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 24 KiB |
|
@ -1,100 +0,0 @@
|
||||||
=====
|
|
||||||
Songs
|
|
||||||
=====
|
|
||||||
|
|
||||||
Managing your songs in OpenLP is a relatively simple process. There are also
|
|
||||||
converters provided to get data from other formats into OpenLP.
|
|
||||||
|
|
||||||
Song Importer
|
|
||||||
=============
|
|
||||||
|
|
||||||
If you are using an earlier version of OpenLP or come from another software
|
|
||||||
package, you may be able to convert your existing database to work in OpenLP
|
|
||||||
2.0. To access the Song Importer :menuselection:`File --> Import --> Song`.
|
|
||||||
You will then see the Song Importer window, then click :guilabel:`Next`.
|
|
||||||
|
|
||||||
.. image:: pics/songimporter.png
|
|
||||||
|
|
||||||
After choosing :guilabel:`Next` you can then select from the various types of
|
|
||||||
software that OpenLP will convert songs from.
|
|
||||||
|
|
||||||
.. image:: pics/songimporterchoices.png
|
|
||||||
|
|
||||||
Then click on the file folder icon to choose the file of the song database you
|
|
||||||
want to import. See the following sections for information on the different
|
|
||||||
formats that OpenLP will import.
|
|
||||||
|
|
||||||
Importing from OpenLP Version 1
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Converting from OpenLP Version 1 is a pretty simple process. You will first
|
|
||||||
need to locate your version 1 database file.
|
|
||||||
|
|
||||||
Windows XP::
|
|
||||||
|
|
||||||
C:\Documents and Settings\All Users\Application Data\openlp.org\Data\songs.olp
|
|
||||||
|
|
||||||
Windows Vista / Windows 7::
|
|
||||||
|
|
||||||
C:\ProgramData\openlp.org\Data\songs.olp
|
|
||||||
|
|
||||||
After clicking :guilabel:`Next` your conversion should be complete.
|
|
||||||
|
|
||||||
.. image:: pics/finishedimport.png
|
|
||||||
|
|
||||||
Then press :guilabel:`Finish` and you should now be ready to use your OpenLP
|
|
||||||
version one songs.
|
|
||||||
|
|
||||||
Importing from OpenSong
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
Converting from OpenSong you will need to locate your songs database. In the
|
|
||||||
later versions of OpenSong you are asked to define the location of this. The
|
|
||||||
songs will be located in a folder named :guilabel:`Songs`. This folder should
|
|
||||||
contain files with all your songs in them without a file extension. (file.xxx).
|
|
||||||
When you have located this folder you will then need to select the songs from
|
|
||||||
the folder.
|
|
||||||
|
|
||||||
.. image:: pics/selectsongs.png
|
|
||||||
|
|
||||||
On most operating systems to select all the songs, first select the first song
|
|
||||||
in the lest then press shift and select the last song in the list. After this
|
|
||||||
press :guilabel:`Next` and you should see that your import has been successful.
|
|
||||||
|
|
||||||
.. image:: pics/finishedimport.png
|
|
||||||
|
|
||||||
Press :guilabel:`Finish` and you will now be ready to use your songs imported
|
|
||||||
from OpenSong.
|
|
||||||
|
|
||||||
Importing from CCLI Song Select
|
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
To import from CCLI Song Select you must be a CCLI Subscriber and also a
|
|
||||||
subscriber of the Song Select service. For info on that go to:
|
|
||||||
http://www.ccli.com
|
|
||||||
|
|
||||||
The first step for importing from CCLI Song Select is to log into your account.
|
|
||||||
Then search for your desired song. For this example we will be adding the song
|
|
||||||
"Amazing Grace".
|
|
||||||
|
|
||||||
.. image:: pics/songselectsongsearch.png
|
|
||||||
|
|
||||||
For the song you are searching for select `lyrics` This should take you to a
|
|
||||||
page displaying the lyrics and copyright info for your song.
|
|
||||||
|
|
||||||
.. image:: pics/songselectlyrics.png
|
|
||||||
|
|
||||||
Next, hover over the :guilabel:`Lyrics` menu from the upper right corner. Then
|
|
||||||
choose either the .txt or .usr file. You will then be asked to chose a download
|
|
||||||
location if your browser does not automatically select that for you. Select
|
|
||||||
this file from the OpenLP import window and then click :guilabel:`Next` You can
|
|
||||||
also select multiple songs for import at once on most operating systems by
|
|
||||||
selecting the first item in the list then holding shift select the last item in
|
|
||||||
the list. When finished you should see that your import has completed.
|
|
||||||
|
|
||||||
.. image:: pics/finishedimport.png
|
|
||||||
|
|
||||||
Press :guilabel:`Finish` and you will now be ready to use your songs imported
|
|
||||||
from CCLI SongSelect.
|
|
||||||
|
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
{#
|
|
||||||
openlp_qthelp/layout.html
|
|
||||||
~~~~~~~~~~~~~~~~~
|
|
||||||
|
|
||||||
Sphinx layout template for the openlp_qthelp theme.
|
|
||||||
|
|
||||||
:copyright: Copyright 2004-2010 Raoul Snyman.
|
|
||||||
:license: GPL
|
|
||||||
#}
|
|
||||||
{% extends "basic/layout.html" %}
|
|
||||||
{% set script_files = script_files + ['_static/theme_extras.js'] %}
|
|
||||||
{% set css_files = css_files + ['_static/print.css'] %}
|
|
||||||
|
|
||||||
{# do not display relbars #}
|
|
||||||
{% block relbar1 %}{% endblock %}
|
|
||||||
{% block relbar2 %}{% endblock %}
|
|
||||||
|
|
||||||
{% macro nav() %}
|
|
||||||
<p>
|
|
||||||
{%- block openlp_qthelprel1 %}
|
|
||||||
{%- endblock %}
|
|
||||||
{%- if prev %}
|
|
||||||
«  <a href="{{ prev.link|e }}">{{ prev.title }}</a>
|
|
||||||
  ::  
|
|
||||||
{%- endif %}
|
|
||||||
<a class="uplink" href="{{ pathto(master_doc) }}">{{ _('Contents') }}</a>
|
|
||||||
{%- if next %}
|
|
||||||
  ::  
|
|
||||||
<a href="{{ next.link|e }}">{{ next.title }}</a>  »
|
|
||||||
{%- endif %}
|
|
||||||
{%- block openlp_qthelprel2 %}
|
|
||||||
{%- endblock %}
|
|
||||||
</p>
|
|
||||||
{% endmacro %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<!-- div class="header">
|
|
||||||
{%- block openlp_qthelpheader %}
|
|
||||||
{%- if theme_full_logo != "false" %}
|
|
||||||
<a href="{{ pathto('index') }}">
|
|
||||||
<img class="logo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
|
|
||||||
</a>
|
|
||||||
{%- else %}
|
|
||||||
{%- if logo -%}
|
|
||||||
<img class="rightlogo" src="{{ pathto('_static/' + logo, 1) }}" alt="Logo"/>
|
|
||||||
{%- endif -%}
|
|
||||||
<h1 class="heading"><a href="{{ pathto('index') }}">
|
|
||||||
<span>{{ shorttitle|e }}</span></a></h1>
|
|
||||||
<h2 class="heading"><span>{{ title|striptags|e }}</span></h2>
|
|
||||||
{%- endif %}
|
|
||||||
{%- endblock %}
|
|
||||||
</div -->
|
|
||||||
<div class="topnav">
|
|
||||||
{{ nav() }}
|
|
||||||
</div>
|
|
||||||
<div class="content">
|
|
||||||
{#{%- if display_toc %}
|
|
||||||
<div id="toc">
|
|
||||||
<h3>Table Of Contents</h3>
|
|
||||||
{{ toc }}
|
|
||||||
</div>
|
|
||||||
{%- endif %}#}
|
|
||||||
{% block body %}{% endblock %}
|
|
||||||
</div>
|
|
||||||
<div class="bottomnav">
|
|
||||||
{{ nav() }}
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 164 B |
Before Width: | Height: | Size: 365 B |
|
@ -1,372 +0,0 @@
|
||||||
/*
|
|
||||||
* openlp_qthelp.css_t
|
|
||||||
* ~~~~~~~~~~~
|
|
||||||
*
|
|
||||||
* Sphinx stylesheet -- openlp_qthelp theme.
|
|
||||||
*
|
|
||||||
* Adapted from http://openlp_qthelp-os.org/docs/Haiku-doc.css.
|
|
||||||
* Original copyright message:
|
|
||||||
*
|
|
||||||
* Copyright 2008-2009, Haiku. All rights reserved.
|
|
||||||
* Distributed under the terms of the MIT License.
|
|
||||||
*
|
|
||||||
* Authors:
|
|
||||||
* Francois Revol <revol@free.fr>
|
|
||||||
* Stephan Assmus <superstippi@gmx.de>
|
|
||||||
* Braden Ewing <brewin@gmail.com>
|
|
||||||
* Humdinger <humdingerb@gmail.com>
|
|
||||||
*
|
|
||||||
* :copyright: Copyright 2007-2010 by the Sphinx team, see AUTHORS.
|
|
||||||
* :license: BSD, see LICENSE for details.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
@import url("basic.css");
|
|
||||||
|
|
||||||
html {
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
background-color: #fff;
|
|
||||||
background-image: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
line-height: 1.5;
|
|
||||||
margin: auto;
|
|
||||||
padding: 0px;
|
|
||||||
font-family: "DejaVu Sans", Arial, Helvetica, sans-serif;
|
|
||||||
min-width: 59em;
|
|
||||||
max-width: 70em;
|
|
||||||
color: {{ theme_textcolor }};
|
|
||||||
}
|
|
||||||
|
|
||||||
div.footer {
|
|
||||||
padding: 8px;
|
|
||||||
font-size: 11px;
|
|
||||||
text-align: center;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* link colors and text decoration */
|
|
||||||
|
|
||||||
a:link {
|
|
||||||
font-weight: bold;
|
|
||||||
text-decoration: none;
|
|
||||||
color: {{ theme_linkcolor }};
|
|
||||||
}
|
|
||||||
|
|
||||||
a:visited {
|
|
||||||
font-weight: bold;
|
|
||||||
text-decoration: none;
|
|
||||||
color: {{ theme_visitedlinkcolor }};
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover, a:active {
|
|
||||||
text-decoration: underline;
|
|
||||||
color: {{ theme_hoverlinkcolor }};
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Some headers act as anchors, don't give them a hover effect */
|
|
||||||
|
|
||||||
h1 a:hover, a:active {
|
|
||||||
text-decoration: none;
|
|
||||||
color: {{ theme_headingcolor }};
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 a:hover, a:active {
|
|
||||||
text-decoration: none;
|
|
||||||
color: {{ theme_headingcolor }};
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 a:hover, a:active {
|
|
||||||
text-decoration: none;
|
|
||||||
color: {{ theme_headingcolor }};
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 a:hover, a:active {
|
|
||||||
text-decoration: none;
|
|
||||||
color: {{ theme_headingcolor }};
|
|
||||||
}
|
|
||||||
|
|
||||||
a.headerlink {
|
|
||||||
color: #a7ce38;
|
|
||||||
padding-left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.headerlink:hover {
|
|
||||||
color: #a7ce38;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* basic text elements */
|
|
||||||
|
|
||||||
div.content {
|
|
||||||
margin-top: 20px;
|
|
||||||
margin-left: 40px;
|
|
||||||
margin-right: 40px;
|
|
||||||
margin-bottom: 50px;
|
|
||||||
font-size: 0.9em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* heading and navigation */
|
|
||||||
|
|
||||||
div.header {
|
|
||||||
position: relative;
|
|
||||||
left: 0px;
|
|
||||||
top: 0px;
|
|
||||||
height: 85px;
|
|
||||||
/* background: #eeeeee; */
|
|
||||||
padding: 0 40px;
|
|
||||||
}
|
|
||||||
div.header h1 {
|
|
||||||
font-size: 1.6em;
|
|
||||||
font-weight: normal;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
color: {{ theme_headingcolor }};
|
|
||||||
border: 0;
|
|
||||||
margin: 0;
|
|
||||||
padding-top: 15px;
|
|
||||||
}
|
|
||||||
div.header h1 a {
|
|
||||||
font-weight: normal;
|
|
||||||
color: {{ theme_headingcolor }};
|
|
||||||
}
|
|
||||||
div.header h2 {
|
|
||||||
font-size: 1.3em;
|
|
||||||
font-weight: normal;
|
|
||||||
letter-spacing: 1px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
color: #aaa;
|
|
||||||
border: 0;
|
|
||||||
margin-top: -3px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.header img.rightlogo {
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
div.title {
|
|
||||||
font-size: 1.3em;
|
|
||||||
font-weight: bold;
|
|
||||||
color: {{ theme_headingcolor }};
|
|
||||||
border-bottom: dotted thin #e0e0e0;
|
|
||||||
margin-bottom: 25px;
|
|
||||||
}
|
|
||||||
div.topnav {
|
|
||||||
/* background: #e0e0e0; */
|
|
||||||
}
|
|
||||||
div.topnav p {
|
|
||||||
margin-top: 0;
|
|
||||||
margin-left: 40px;
|
|
||||||
margin-right: 40px;
|
|
||||||
margin-bottom: 0px;
|
|
||||||
text-align: right;
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
div.bottomnav {
|
|
||||||
background: #eeeeee;
|
|
||||||
}
|
|
||||||
div.bottomnav p {
|
|
||||||
margin-right: 40px;
|
|
||||||
text-align: right;
|
|
||||||
font-size: 0.8em;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.uplink {
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* contents box */
|
|
||||||
|
|
||||||
table.index {
|
|
||||||
margin: 0px 0px 30px 30px;
|
|
||||||
padding: 1px;
|
|
||||||
border-width: 1px;
|
|
||||||
border-style: dotted;
|
|
||||||
border-color: #e0e0e0;
|
|
||||||
}
|
|
||||||
table.index tr.heading {
|
|
||||||
background-color: #e0e0e0;
|
|
||||||
text-align: center;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 1.1em;
|
|
||||||
}
|
|
||||||
table.index tr.index {
|
|
||||||
background-color: #eeeeee;
|
|
||||||
}
|
|
||||||
table.index td {
|
|
||||||
padding: 5px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.index a:link, table.index a:visited {
|
|
||||||
font-weight: normal;
|
|
||||||
text-decoration: none;
|
|
||||||
color: {{ theme_linkcolor }};
|
|
||||||
}
|
|
||||||
table.index a:hover, table.index a:active {
|
|
||||||
text-decoration: underline;
|
|
||||||
color: {{ theme_hoverlinkcolor }};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Haiku User Guide styles and layout */
|
|
||||||
|
|
||||||
/* Rounded corner boxes */
|
|
||||||
/* Common declarations */
|
|
||||||
div.admonition {
|
|
||||||
-webkit-border-radius: 10px;
|
|
||||||
-khtml-border-radius: 10px;
|
|
||||||
-moz-border-radius: 10px;
|
|
||||||
border-radius: 10px;
|
|
||||||
border-style: dotted;
|
|
||||||
border-width: thin;
|
|
||||||
border-color: #dcdcdc;
|
|
||||||
padding: 10px 15px 10px 15px;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
div.note {
|
|
||||||
padding: 10px 15px 10px 80px;
|
|
||||||
background: #e4ffde url(alert_info_32.png) 15px 15px no-repeat;
|
|
||||||
min-height: 42px;
|
|
||||||
}
|
|
||||||
div.warning {
|
|
||||||
padding: 10px 15px 10px 80px;
|
|
||||||
background: #fffbc6 url(alert_warning_32.png) 15px 15px no-repeat;
|
|
||||||
min-height: 42px;
|
|
||||||
}
|
|
||||||
div.seealso {
|
|
||||||
background: #e4ffde;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* More layout and styles */
|
|
||||||
h1 {
|
|
||||||
font-size: 1.3em;
|
|
||||||
font-weight: bold;
|
|
||||||
color: {{ theme_headingcolor }};
|
|
||||||
border-bottom: dotted thin #e0e0e0;
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 1.2em;
|
|
||||||
font-weight: normal;
|
|
||||||
color: {{ theme_headingcolor }};
|
|
||||||
border-bottom: dotted thin #e0e0e0;
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3 {
|
|
||||||
font-size: 1.1em;
|
|
||||||
font-weight: normal;
|
|
||||||
color: {{ theme_headingcolor }};
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-size: 1.0em;
|
|
||||||
font-weight: normal;
|
|
||||||
color: {{ theme_headingcolor }};
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
|
||||||
text-align: justify;
|
|
||||||
}
|
|
||||||
|
|
||||||
p.last {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol {
|
|
||||||
padding-left: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ul {
|
|
||||||
padding-left: 5px;
|
|
||||||
margin-top: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.content ul > li {
|
|
||||||
-moz-background-clip:border;
|
|
||||||
-moz-background-inline-policy:continuous;
|
|
||||||
-moz-background-origin:padding;
|
|
||||||
background: transparent url(bullet_orange.png) no-repeat scroll left 0.45em;
|
|
||||||
list-style-image: none;
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 0 0 0 1.666em;
|
|
||||||
margin-bottom: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
td {
|
|
||||||
vertical-align: top;
|
|
||||||
}
|
|
||||||
|
|
||||||
tt {
|
|
||||||
background-color: #e2e2e2;
|
|
||||||
font-size: 1.0em;
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre {
|
|
||||||
border-color: #0c3762;
|
|
||||||
border-style: dotted;
|
|
||||||
border-width: thin;
|
|
||||||
margin: 0 0 12px 0;
|
|
||||||
padding: 0.8em;
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
hr {
|
|
||||||
border-top: 1px solid #ccc;
|
|
||||||
border-bottom: 0;
|
|
||||||
border-right: 0;
|
|
||||||
border-left: 0;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* printer only pretty stuff */
|
|
||||||
@media print {
|
|
||||||
.noprint {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
/* for acronyms we want their definitions inlined at print time */
|
|
||||||
acronym[title]:after {
|
|
||||||
font-size: small;
|
|
||||||
content: " (" attr(title) ")";
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
/* and not have mozilla dotted underline */
|
|
||||||
acronym {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
div.topnav, div.bottomnav, div.header, table.index {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
div.content {
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
}
|
|
||||||
html {
|
|
||||||
background: #FFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.viewcode-back {
|
|
||||||
font-family: "DejaVu Sans", Arial, Helvetica, sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.viewcode-block:target {
|
|
||||||
background-color: #f4debf;
|
|
||||||
border-top: 1px solid #ac9;
|
|
||||||
border-bottom: 1px solid #ac9;
|
|
||||||
margin: -1px -12px;
|
|
||||||
padding: 0 12px;
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
[theme]
|
|
||||||
inherit = basic
|
|
||||||
stylesheet = openlp_qthelp.css
|
|
||||||
pygments_style = autumn
|
|
||||||
|
|
||||||
[options]
|
|
||||||
full_logo = false
|
|
||||||
textcolor = #333333
|
|
||||||
headingcolor = #203b6f
|
|
||||||
linkcolor = #26437c
|
|
||||||
visitedlinkcolor = #26437c
|
|
||||||
hoverlinkcolor = #26437c
|
|
|
@ -1,97 +0,0 @@
|
||||||
A plugin architecture for openlp 2.0
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
Why?
|
|
||||||
----
|
|
||||||
To allow easy development of new "things to display". Examples:
|
|
||||||
* Bible passages
|
|
||||||
* Video
|
|
||||||
* Powerpoint/Openoffice Impress
|
|
||||||
* Lyrics :) (with chords, rich text, etc...)
|
|
||||||
* Musical score
|
|
||||||
* Midi files (hmmm, that's not a thing to display, but feels like it should be
|
|
||||||
there...)
|
|
||||||
* Audio files, CDs (hmmm again)
|
|
||||||
* Collections of pictures
|
|
||||||
* Alerts to members of the congregation
|
|
||||||
... etc.
|
|
||||||
|
|
||||||
The scope of these plugins is "things for display purposes", so each needs to
|
|
||||||
be able to:
|
|
||||||
* Render their display (on the projection screen and in a "shrunken form"
|
|
||||||
for preview purposes)
|
|
||||||
|
|
||||||
These plugins need to be part of a service. This means they need to
|
|
||||||
* Be able to tell the service manager code what to put in the service for their
|
|
||||||
"bit"
|
|
||||||
* Have a "tab" in the media manager, which they can render on request
|
|
||||||
to allow bits to be added to the service (or indeed shown live)
|
|
||||||
|
|
||||||
In addition, some plugins need to be able to show
|
|
||||||
* How their multiple screens of data are split (eg verses)
|
|
||||||
* be told which screen to display
|
|
||||||
|
|
||||||
Some plugins will also have things to configure, so will need
|
|
||||||
* to tell the core app that they need a preferences page
|
|
||||||
* and be able to render it and handle those prefs
|
|
||||||
|
|
||||||
Some plugins may need to define
|
|
||||||
* Hot keys for their display actions. The core will have to pass these on...
|
|
||||||
|
|
||||||
Other basic things all plugins will need:
|
|
||||||
* A name
|
|
||||||
* A version number
|
|
||||||
* Helpfile?
|
|
||||||
|
|
||||||
Funnily enough, the core lyrics engine fits those requirements, so could
|
|
||||||
actually form a plugin...
|
|
||||||
|
|
||||||
Each service entry may be made up of multiple plugins (to do text on video), so
|
|
||||||
each plugin that contributes to a service item will need a "layering"
|
|
||||||
priority.
|
|
||||||
|
|
||||||
Plugin management
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Plugins will be packages within the plugins/ directory. The plugin manager
|
|
||||||
will scan this directory when openlp loads for any class which is based on the
|
|
||||||
base Plugin class (or should we call it the DisplayPlugin class to allow for
|
|
||||||
other sorts??)
|
|
||||||
|
|
||||||
These plugins are then queried for their capabilities/requirements and
|
|
||||||
spaces made in the prefs UI as required, and in the media manager.
|
|
||||||
|
|
||||||
The service manager can find out what plugins it has available (we need to
|
|
||||||
report missing plugins when a service is loaded).
|
|
||||||
|
|
||||||
The display manager will get a ref to a/some plugin(s) from the service
|
|
||||||
manager when each service item is made live, and can then call on each to
|
|
||||||
render their display.
|
|
||||||
|
|
||||||
Each plugin will have basic attributes for
|
|
||||||
* name
|
|
||||||
* version number
|
|
||||||
* capabilities/requirements
|
|
||||||
(eg:
|
|
||||||
needs a prefs page,
|
|
||||||
needs key presses forwarding
|
|
||||||
has multiple screensful?
|
|
||||||
)
|
|
||||||
|
|
||||||
and a set of API functions for
|
|
||||||
* media manager rendering and handling
|
|
||||||
* creating service data
|
|
||||||
* being told service data
|
|
||||||
* set paint context
|
|
||||||
* render
|
|
||||||
* selecting a screen to display
|
|
||||||
* handling preferences
|
|
||||||
* shortcut key handling...
|
|
||||||
|
|
||||||
|
|
||||||
Other things to add:
|
|
||||||
Multiple monitors
|
|
||||||
Openoffice like plugins - external rendering.
|
|
||||||
update hook for rendering?
|
|
||||||
Event hook from app
|
|
||||||
Event hook to app (MIDI events....)
|
|
277
openlp.pyw
|
@ -6,10 +6,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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -24,271 +25,15 @@
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import logging
|
|
||||||
from optparse import OptionParser
|
|
||||||
from traceback import format_exception
|
|
||||||
from subprocess import Popen, PIPE
|
|
||||||
|
|
||||||
from PyQt4 import QtCore, QtGui
|
# Import uuid now, to avoid the rare bug described in the support system:
|
||||||
|
# http://support.openlp.org/issues/102
|
||||||
|
# If https://bugs.gentoo.org/show_bug.cgi?id=317557 is fixed, the import can be
|
||||||
|
# removed.
|
||||||
|
import uuid
|
||||||
|
|
||||||
from openlp.core.lib import Receiver, check_directory_exists
|
from openlp.core import main
|
||||||
from openlp.core.resources import qInitResources
|
|
||||||
from openlp.core.ui.mainwindow import MainWindow
|
|
||||||
from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm
|
|
||||||
from openlp.core.ui.firsttimeform import FirstTimeForm
|
|
||||||
from openlp.core.ui.exceptionform import ExceptionForm
|
|
||||||
from openlp.core.ui import SplashScreen, ScreenList
|
|
||||||
from openlp.core.utils import AppLocation, LanguageManager, VersionThread
|
|
||||||
|
|
||||||
log = logging.getLogger()
|
|
||||||
|
|
||||||
application_stylesheet = u"""
|
|
||||||
QMainWindow::separator
|
|
||||||
{
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
QDockWidget::title
|
|
||||||
{
|
|
||||||
border: 1px solid palette(dark);
|
|
||||||
padding-left: 5px;
|
|
||||||
padding-top: 2px;
|
|
||||||
margin: 1px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
QToolBar
|
|
||||||
{
|
|
||||||
border: none;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
class OpenLP(QtGui.QApplication):
|
|
||||||
"""
|
|
||||||
The core application class. This class inherits from Qt's QApplication
|
|
||||||
class in order to provide the core of the application.
|
|
||||||
"""
|
|
||||||
log.info(u'OpenLP Application Loaded')
|
|
||||||
|
|
||||||
def _get_version(self):
|
|
||||||
"""
|
|
||||||
Load and store current Application Version
|
|
||||||
"""
|
|
||||||
if u'--dev-version' in sys.argv or u'-d' in sys.argv:
|
|
||||||
# If we're running the dev version, let's use bzr to get the version
|
|
||||||
try:
|
|
||||||
# If bzrlib is availble, use it
|
|
||||||
from bzrlib.branch import Branch
|
|
||||||
b = Branch.open_containing('.')[0]
|
|
||||||
b.lock_read()
|
|
||||||
try:
|
|
||||||
# Get the branch's latest revision number.
|
|
||||||
revno = b.revno()
|
|
||||||
# Convert said revision number into a bzr revision id.
|
|
||||||
revision_id = b.dotted_revno_to_revision_id((revno,))
|
|
||||||
# Get a dict of tags, with the revision id as the key.
|
|
||||||
tags = b.tags.get_reverse_tag_dict()
|
|
||||||
# Check if the latest
|
|
||||||
if revision_id in tags:
|
|
||||||
full_version = u'%s' % tags[revision_id][0]
|
|
||||||
else:
|
|
||||||
full_version = '%s-bzr%s' % \
|
|
||||||
(sorted(b.tags.get_tag_dict().keys())[-1], revno)
|
|
||||||
finally:
|
|
||||||
b.unlock()
|
|
||||||
except:
|
|
||||||
# Otherwise run the command line bzr client
|
|
||||||
bzr = Popen((u'bzr', u'tags', u'--sort', u'time'), stdout=PIPE)
|
|
||||||
output, error = bzr.communicate()
|
|
||||||
code = bzr.wait()
|
|
||||||
if code != 0:
|
|
||||||
raise Exception(u'Error running bzr tags')
|
|
||||||
lines = output.splitlines()
|
|
||||||
if len(lines) == 0:
|
|
||||||
tag = u'0.0.0'
|
|
||||||
revision = u'0'
|
|
||||||
else:
|
|
||||||
tag, revision = lines[-1].split()
|
|
||||||
bzr = Popen((u'bzr', u'log', u'--line', u'-r', u'-1'),
|
|
||||||
stdout=PIPE)
|
|
||||||
output, error = bzr.communicate()
|
|
||||||
code = bzr.wait()
|
|
||||||
if code != 0:
|
|
||||||
raise Exception(u'Error running bzr log')
|
|
||||||
latest = output.split(u':')[0]
|
|
||||||
full_version = latest == revision and tag or \
|
|
||||||
u'%s-bzr%s' % (tag, latest)
|
|
||||||
else:
|
|
||||||
# We're not running the development version, let's use the file
|
|
||||||
filepath = AppLocation.get_directory(AppLocation.VersionDir)
|
|
||||||
filepath = os.path.join(filepath, u'.version')
|
|
||||||
fversion = None
|
|
||||||
try:
|
|
||||||
fversion = open(filepath, u'r')
|
|
||||||
full_version = unicode(fversion.read()).rstrip()
|
|
||||||
except IOError:
|
|
||||||
log.exception('Error in version file.')
|
|
||||||
full_version = u'0.0.0-bzr000'
|
|
||||||
finally:
|
|
||||||
if fversion:
|
|
||||||
fversion.close()
|
|
||||||
bits = full_version.split(u'-')
|
|
||||||
app_version = {
|
|
||||||
u'full': full_version,
|
|
||||||
u'version': bits[0],
|
|
||||||
u'build': bits[1] if len(bits) > 1 else None
|
|
||||||
}
|
|
||||||
if app_version[u'build']:
|
|
||||||
log.info(
|
|
||||||
u'Openlp version %s build %s',
|
|
||||||
app_version[u'version'],
|
|
||||||
app_version[u'build']
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
log.info(u'Openlp version %s' % app_version[u'version'])
|
|
||||||
return app_version
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
"""
|
|
||||||
Run the OpenLP application.
|
|
||||||
"""
|
|
||||||
app_version = self._get_version()
|
|
||||||
# provide a listener for widgets to reqest a screen update.
|
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
|
||||||
QtCore.SIGNAL(u'openlp_process_events'), self.processEvents)
|
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
|
||||||
QtCore.SIGNAL(u'cursor_busy'), self.setBusyCursor)
|
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
|
||||||
QtCore.SIGNAL(u'cursor_normal'), self.setNormalCursor)
|
|
||||||
self.setOrganizationName(u'OpenLP')
|
|
||||||
self.setOrganizationDomain(u'openlp.org')
|
|
||||||
self.setApplicationName(u'OpenLP')
|
|
||||||
self.setApplicationVersion(app_version[u'version'])
|
|
||||||
# Decide how many screens we have and their size
|
|
||||||
screens = ScreenList(self.desktop())
|
|
||||||
# First time checks in settings
|
|
||||||
firstTime = QtCore.QSettings().value(
|
|
||||||
u'general/first time', QtCore.QVariant(True)).toBool()
|
|
||||||
if firstTime:
|
|
||||||
FirstTimeForm(screens).exec_()
|
|
||||||
if os.name == u'nt':
|
|
||||||
self.setStyleSheet(application_stylesheet)
|
|
||||||
show_splash = QtCore.QSettings().value(
|
|
||||||
u'general/show splash', QtCore.QVariant(True)).toBool()
|
|
||||||
if show_splash:
|
|
||||||
self.splash = SplashScreen()
|
|
||||||
self.splash.show()
|
|
||||||
# make sure Qt really display the splash screen
|
|
||||||
self.processEvents()
|
|
||||||
# start the main app window
|
|
||||||
self.appClipboard = self.clipboard()
|
|
||||||
self.mainWindow = MainWindow(screens, app_version, self.appClipboard,
|
|
||||||
firstTime)
|
|
||||||
self.mainWindow.show()
|
|
||||||
if show_splash:
|
|
||||||
# now kill the splashscreen
|
|
||||||
self.splash.finish(self.mainWindow)
|
|
||||||
self.mainWindow.repaint()
|
|
||||||
update_check = QtCore.QSettings().value(
|
|
||||||
u'general/update check', QtCore.QVariant(True)).toBool()
|
|
||||||
if update_check:
|
|
||||||
VersionThread(self.mainWindow, app_version).start()
|
|
||||||
return self.exec_()
|
|
||||||
|
|
||||||
def hookException(self, exctype, value, traceback):
|
|
||||||
if not hasattr(self, u'mainWindow'):
|
|
||||||
log.exception(''.join(format_exception(exctype, value, traceback)))
|
|
||||||
return
|
|
||||||
if not hasattr(self, u'exceptionForm'):
|
|
||||||
self.exceptionForm = ExceptionForm(self.mainWindow)
|
|
||||||
self.exceptionForm.exceptionTextEdit.setPlainText(
|
|
||||||
''.join(format_exception(exctype, value, traceback)))
|
|
||||||
self.setNormalCursor()
|
|
||||||
self.exceptionForm.exec_()
|
|
||||||
|
|
||||||
def setBusyCursor(self):
|
|
||||||
"""
|
|
||||||
Sets the Busy Cursor for the Application
|
|
||||||
"""
|
|
||||||
self.setOverrideCursor(QtCore.Qt.BusyCursor)
|
|
||||||
self.processEvents()
|
|
||||||
|
|
||||||
def setNormalCursor(self):
|
|
||||||
"""
|
|
||||||
Sets the Normal Cursor for the Application
|
|
||||||
"""
|
|
||||||
self.restoreOverrideCursor()
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""
|
|
||||||
The main function which parses command line options and then runs
|
|
||||||
the PyQt4 Application.
|
|
||||||
"""
|
|
||||||
# Set up command line options.
|
|
||||||
usage = 'Usage: %prog [options] [qt-options]'
|
|
||||||
parser = OptionParser(usage=usage)
|
|
||||||
parser.add_option('-e', '--no-error-form', dest='no_error_form',
|
|
||||||
action='store_true', help='Disable the error notification form.')
|
|
||||||
parser.add_option('-l', '--log-level', dest='loglevel',
|
|
||||||
default='warning', metavar='LEVEL', help='Set logging to LEVEL '
|
|
||||||
'level. Valid values are "debug", "info", "warning".')
|
|
||||||
parser.add_option('-p', '--portable', dest='portable',
|
|
||||||
action='store_true', help='Specify if this should be run as a '
|
|
||||||
'portable app, off a USB flash drive (not implemented).')
|
|
||||||
parser.add_option('-d', '--dev-version', dest='dev_version',
|
|
||||||
action='store_true', help='Ignore the version file and pull the '
|
|
||||||
'version directly from Bazaar')
|
|
||||||
parser.add_option('-s', '--style', dest='style',
|
|
||||||
help='Set the Qt4 style (passed directly to Qt4).')
|
|
||||||
# Set up logging
|
|
||||||
log_path = AppLocation.get_directory(AppLocation.CacheDir)
|
|
||||||
check_directory_exists(log_path)
|
|
||||||
filename = os.path.join(log_path, u'openlp.log')
|
|
||||||
logfile = logging.FileHandler(filename, u'w')
|
|
||||||
logfile.setFormatter(logging.Formatter(
|
|
||||||
u'%(asctime)s %(name)-55s %(levelname)-8s %(message)s'))
|
|
||||||
log.addHandler(logfile)
|
|
||||||
logging.addLevelName(15, u'Timer')
|
|
||||||
# Parse command line options and deal with them.
|
|
||||||
(options, args) = parser.parse_args()
|
|
||||||
qt_args = []
|
|
||||||
if options.loglevel.lower() in ['d', 'debug']:
|
|
||||||
log.setLevel(logging.DEBUG)
|
|
||||||
print 'Logging to:', filename
|
|
||||||
elif options.loglevel.lower() in ['w', 'warning']:
|
|
||||||
log.setLevel(logging.WARNING)
|
|
||||||
else:
|
|
||||||
log.setLevel(logging.INFO)
|
|
||||||
if options.style:
|
|
||||||
qt_args.extend(['-style', options.style])
|
|
||||||
# Throw the rest of the arguments at Qt, just in case.
|
|
||||||
qt_args.extend(args)
|
|
||||||
# Initialise the resources
|
|
||||||
qInitResources()
|
|
||||||
# Now create and actually run the application.
|
|
||||||
app = OpenLP(qt_args)
|
|
||||||
# Define the settings environment
|
|
||||||
QtCore.QSettings(u'OpenLP', u'OpenLP')
|
|
||||||
# First time checks in settings
|
|
||||||
# Use explicit reference as not inside a QT environment yet
|
|
||||||
if QtCore.QSettings(u'OpenLP', u'OpenLP').value(
|
|
||||||
u'general/first time', QtCore.QVariant(True)).toBool():
|
|
||||||
if not FirstTimeLanguageForm().exec_():
|
|
||||||
# if cancel then stop processing
|
|
||||||
sys.exit()
|
|
||||||
if sys.platform == u'darwin':
|
|
||||||
OpenLP.addLibraryPath(QtGui.QApplication.applicationDirPath()
|
|
||||||
+ "/qt4_plugins")
|
|
||||||
# i18n Set Language
|
|
||||||
language = LanguageManager.get_language()
|
|
||||||
appTranslator = LanguageManager.get_translator(language)
|
|
||||||
app.installTranslator(appTranslator)
|
|
||||||
if not options.no_error_form:
|
|
||||||
sys.excepthook = app.hookException
|
|
||||||
sys.exit(app.run())
|
|
||||||
|
|
||||||
if __name__ == u'__main__':
|
if __name__ == u'__main__':
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
1.9.2-bzr987
|
1.9.5-bzr1421
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -26,3 +27,9 @@
|
||||||
"""
|
"""
|
||||||
The :mod:`openlp` module contains all the project produced OpenLP functionality
|
The :mod:`openlp` module contains all the project produced OpenLP functionality
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import core
|
||||||
|
import plugins
|
||||||
|
|
||||||
|
__all__ = [u'core', u'plugins']
|
||||||
|
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -23,9 +24,268 @@
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The :mod:`core` module provides all core application functions
|
The :mod:`core` module provides all core application functions
|
||||||
|
|
||||||
All the core functions of the OpenLP application including the GUI, settings,
|
All the core functions of the OpenLP application including the GUI, settings,
|
||||||
logging and a plugin framework are contained within the openlp.core module.
|
logging and a plugin framework are contained within the openlp.core module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
from optparse import OptionParser
|
||||||
|
from traceback import format_exception
|
||||||
|
|
||||||
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
|
from openlp.core.lib import Receiver, check_directory_exists
|
||||||
|
from openlp.core.lib.ui import UiStrings
|
||||||
|
from openlp.core.resources import qInitResources
|
||||||
|
from openlp.core.ui.mainwindow import MainWindow
|
||||||
|
from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm
|
||||||
|
from openlp.core.ui.firsttimeform import FirstTimeForm
|
||||||
|
from openlp.core.ui.exceptionform import ExceptionForm
|
||||||
|
from openlp.core.ui import SplashScreen, ScreenList
|
||||||
|
from openlp.core.utils import AppLocation, LanguageManager, VersionThread, \
|
||||||
|
get_application_version, DelayStartThread
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [u'OpenLP', u'main']
|
||||||
|
|
||||||
|
|
||||||
|
log = logging.getLogger()
|
||||||
|
application_stylesheet = u"""
|
||||||
|
QMainWindow::separator
|
||||||
|
{
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDockWidget::title
|
||||||
|
{
|
||||||
|
border: 1px solid palette(dark);
|
||||||
|
padding-left: 5px;
|
||||||
|
padding-top: 2px;
|
||||||
|
margin: 1px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QToolBar
|
||||||
|
{
|
||||||
|
border: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class OpenLP(QtGui.QApplication):
|
||||||
|
"""
|
||||||
|
The core application class. This class inherits from Qt's QApplication
|
||||||
|
class in order to provide the core of the application.
|
||||||
|
"""
|
||||||
|
|
||||||
|
args = []
|
||||||
|
|
||||||
|
def exec_(self):
|
||||||
|
"""
|
||||||
|
Override exec method to allow the shared memory to be released on exit
|
||||||
|
"""
|
||||||
|
QtGui.QApplication.exec_()
|
||||||
|
self.sharedMemory.detach()
|
||||||
|
|
||||||
|
def run(self, args, testing=False):
|
||||||
|
"""
|
||||||
|
Run the OpenLP application.
|
||||||
|
"""
|
||||||
|
# On Windows, the args passed into the constructor are
|
||||||
|
# ignored. Not very handy, so set the ones we want to use.
|
||||||
|
self.args.extend(args)
|
||||||
|
# provide a listener for widgets to reqest a screen update.
|
||||||
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
|
QtCore.SIGNAL(u'openlp_process_events'), self.processEvents)
|
||||||
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
|
QtCore.SIGNAL(u'cursor_busy'), self.setBusyCursor)
|
||||||
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
|
QtCore.SIGNAL(u'cursor_normal'), self.setNormalCursor)
|
||||||
|
# Decide how many screens we have and their size
|
||||||
|
screens = ScreenList(self.desktop())
|
||||||
|
# First time checks in settings
|
||||||
|
has_run_wizard = QtCore.QSettings().value(
|
||||||
|
u'general/has run wizard', QtCore.QVariant(False)).toBool()
|
||||||
|
if not has_run_wizard:
|
||||||
|
if FirstTimeForm(screens).exec_() == QtGui.QDialog.Accepted:
|
||||||
|
QtCore.QSettings().setValue(u'general/has run wizard',
|
||||||
|
QtCore.QVariant(True))
|
||||||
|
if os.name == u'nt':
|
||||||
|
self.setStyleSheet(application_stylesheet)
|
||||||
|
show_splash = QtCore.QSettings().value(
|
||||||
|
u'general/show splash', QtCore.QVariant(True)).toBool()
|
||||||
|
if show_splash:
|
||||||
|
self.splash = SplashScreen()
|
||||||
|
self.splash.show()
|
||||||
|
# make sure Qt really display the splash screen
|
||||||
|
self.processEvents()
|
||||||
|
# start the main app window
|
||||||
|
self.mainWindow = MainWindow(self.clipboard(), self.args)
|
||||||
|
self.mainWindow.show()
|
||||||
|
if show_splash:
|
||||||
|
# now kill the splashscreen
|
||||||
|
self.splash.finish(self.mainWindow)
|
||||||
|
log.debug(u'Splashscreen closed')
|
||||||
|
# make sure Qt really display the splash screen
|
||||||
|
self.processEvents()
|
||||||
|
self.mainWindow.repaint()
|
||||||
|
self.processEvents()
|
||||||
|
if not has_run_wizard:
|
||||||
|
self.mainWindow.firstTime()
|
||||||
|
update_check = QtCore.QSettings().value(
|
||||||
|
u'general/update check', QtCore.QVariant(True)).toBool()
|
||||||
|
if update_check:
|
||||||
|
VersionThread(self.mainWindow).start()
|
||||||
|
Receiver.send_message(u'live_display_blank_check')
|
||||||
|
self.mainWindow.appStartup()
|
||||||
|
DelayStartThread(self.mainWindow).start()
|
||||||
|
# Skip exec_() for gui tests
|
||||||
|
if not testing:
|
||||||
|
return self.exec_()
|
||||||
|
|
||||||
|
def isAlreadyRunning(self):
|
||||||
|
"""
|
||||||
|
Look to see if OpenLP is already running and ask if a 2nd copy
|
||||||
|
is to be started.
|
||||||
|
"""
|
||||||
|
self.sharedMemory = QtCore.QSharedMemory('OpenLP')
|
||||||
|
if self.sharedMemory.attach():
|
||||||
|
status = QtGui.QMessageBox.critical(None,
|
||||||
|
UiStrings().Error, UiStrings().OpenLPStart,
|
||||||
|
QtGui.QMessageBox.StandardButtons(
|
||||||
|
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No))
|
||||||
|
if status == QtGui.QMessageBox.No:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
self.sharedMemory.create(1)
|
||||||
|
return False
|
||||||
|
|
||||||
|
def hookException(self, exctype, value, traceback):
|
||||||
|
if not hasattr(self, u'mainWindow'):
|
||||||
|
log.exception(''.join(format_exception(exctype, value, traceback)))
|
||||||
|
return
|
||||||
|
if not hasattr(self, u'exceptionForm'):
|
||||||
|
self.exceptionForm = ExceptionForm(self.mainWindow)
|
||||||
|
self.exceptionForm.exceptionTextEdit.setPlainText(
|
||||||
|
''.join(format_exception(exctype, value, traceback)))
|
||||||
|
self.setNormalCursor()
|
||||||
|
self.exceptionForm.exec_()
|
||||||
|
|
||||||
|
def setBusyCursor(self):
|
||||||
|
"""
|
||||||
|
Sets the Busy Cursor for the Application
|
||||||
|
"""
|
||||||
|
self.setOverrideCursor(QtCore.Qt.BusyCursor)
|
||||||
|
self.processEvents()
|
||||||
|
|
||||||
|
def setNormalCursor(self):
|
||||||
|
"""
|
||||||
|
Sets the Normal Cursor for the Application
|
||||||
|
"""
|
||||||
|
self.restoreOverrideCursor()
|
||||||
|
|
||||||
|
def event(self, event):
|
||||||
|
"""
|
||||||
|
Enables direct file opening on OS X
|
||||||
|
"""
|
||||||
|
if event.type() == QtCore.QEvent.FileOpen:
|
||||||
|
file_name = event.file()
|
||||||
|
log.debug(u'Got open file event for %s!', file_name)
|
||||||
|
self.args.insert(0, unicode(file_name))
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return QtGui.QApplication.event(self, event)
|
||||||
|
|
||||||
|
|
||||||
|
def main(args=None):
|
||||||
|
"""
|
||||||
|
The main function which parses command line options and then runs
|
||||||
|
the PyQt4 Application.
|
||||||
|
"""
|
||||||
|
# Set up command line options.
|
||||||
|
usage = 'Usage: %prog [options] [qt-options]'
|
||||||
|
parser = OptionParser(usage=usage)
|
||||||
|
parser.add_option('-e', '--no-error-form', dest='no_error_form',
|
||||||
|
action='store_true', help='Disable the error notification form.')
|
||||||
|
parser.add_option('-l', '--log-level', dest='loglevel',
|
||||||
|
default='warning', metavar='LEVEL', help='Set logging to LEVEL '
|
||||||
|
'level. Valid values are "debug", "info", "warning".')
|
||||||
|
parser.add_option('-p', '--portable', dest='portable',
|
||||||
|
action='store_true', help='Specify if this should be run as a '
|
||||||
|
'portable app, off a USB flash drive (not implemented).')
|
||||||
|
parser.add_option('-d', '--dev-version', dest='dev_version',
|
||||||
|
action='store_true', help='Ignore the version file and pull the '
|
||||||
|
'version directly from Bazaar')
|
||||||
|
parser.add_option('-s', '--style', dest='style',
|
||||||
|
help='Set the Qt4 style (passed directly to Qt4).')
|
||||||
|
parser.add_option('--testing', dest='testing',
|
||||||
|
action='store_true', help='Run by testing framework')
|
||||||
|
# Set up logging
|
||||||
|
log_path = AppLocation.get_directory(AppLocation.CacheDir)
|
||||||
|
check_directory_exists(log_path)
|
||||||
|
filename = os.path.join(log_path, u'openlp.log')
|
||||||
|
logfile = logging.FileHandler(filename, u'w')
|
||||||
|
logfile.setFormatter(logging.Formatter(
|
||||||
|
u'%(asctime)s %(name)-55s %(levelname)-8s %(message)s'))
|
||||||
|
log.addHandler(logfile)
|
||||||
|
# Parse command line options and deal with them.
|
||||||
|
# Use args supplied programatically if possible.
|
||||||
|
(options, args) = parser.parse_args(args) if args else parser.parse_args()
|
||||||
|
qt_args = []
|
||||||
|
if options.loglevel.lower() in ['d', 'debug']:
|
||||||
|
log.setLevel(logging.DEBUG)
|
||||||
|
print 'Logging to:', filename
|
||||||
|
elif options.loglevel.lower() in ['w', 'warning']:
|
||||||
|
log.setLevel(logging.WARNING)
|
||||||
|
else:
|
||||||
|
log.setLevel(logging.INFO)
|
||||||
|
if options.style:
|
||||||
|
qt_args.extend(['-style', options.style])
|
||||||
|
# Throw the rest of the arguments at Qt, just in case.
|
||||||
|
qt_args.extend(args)
|
||||||
|
# Initialise the resources
|
||||||
|
qInitResources()
|
||||||
|
# Now create and actually run the application.
|
||||||
|
app = OpenLP(qt_args)
|
||||||
|
app.setOrganizationName(u'OpenLP')
|
||||||
|
app.setOrganizationDomain(u'openlp.org')
|
||||||
|
app.setApplicationName(u'OpenLP')
|
||||||
|
app.setApplicationVersion(get_application_version()[u'version'])
|
||||||
|
# Instance check
|
||||||
|
if not options.testing:
|
||||||
|
# Instance check
|
||||||
|
if app.isAlreadyRunning():
|
||||||
|
sys.exit()
|
||||||
|
# First time checks in settings
|
||||||
|
if not QtCore.QSettings().value(u'general/has run wizard',
|
||||||
|
QtCore.QVariant(False)).toBool():
|
||||||
|
if not FirstTimeLanguageForm().exec_():
|
||||||
|
# if cancel then stop processing
|
||||||
|
sys.exit()
|
||||||
|
# i18n Set Language
|
||||||
|
language = LanguageManager.get_language()
|
||||||
|
app_translator, default_translator = \
|
||||||
|
LanguageManager.get_translator(language)
|
||||||
|
if not app_translator.isEmpty():
|
||||||
|
app.installTranslator(app_translator)
|
||||||
|
if not default_translator.isEmpty():
|
||||||
|
app.installTranslator(default_translator)
|
||||||
|
else:
|
||||||
|
log.debug(u'Could not find default_translator.')
|
||||||
|
if not options.no_error_form:
|
||||||
|
sys.excepthook = app.hookException
|
||||||
|
# Do not run method app.exec_() when running gui tests
|
||||||
|
if options.testing:
|
||||||
|
app.run(qt_args, testing=True)
|
||||||
|
# For gui tests we need access to window intances and their components
|
||||||
|
return app
|
||||||
|
else:
|
||||||
|
sys.exit(app.run(qt_args))
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -35,56 +36,16 @@ from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
base_html_expands = []
|
class MediaType(object):
|
||||||
|
"""
|
||||||
base_html_expands.append({u'desc': u'Red', u'start tag': u'{r}',
|
An enumeration class for types of media.
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:red">',
|
"""
|
||||||
u'end tag': u'{/r}', u'end html': u'</span>', u'protected': True})
|
Audio = 1
|
||||||
base_html_expands.append({u'desc': u'Black', u'start tag': u'{b}',
|
Video = 2
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:black">',
|
|
||||||
u'end tag': u'{/b}', u'end html': u'</span>', u'protected': True})
|
|
||||||
base_html_expands.append({u'desc': u'Blue', u'start tag': u'{bl}',
|
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:blue">',
|
|
||||||
u'end tag': u'{/bl}', u'end html': u'</span>', u'protected': True})
|
|
||||||
base_html_expands.append({u'desc': u'Yellow', u'start tag': u'{y}',
|
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:yellow">',
|
|
||||||
u'end tag': u'{/y}', u'end html': u'</span>', u'protected': True})
|
|
||||||
base_html_expands.append({u'desc': u'Green', u'start tag': u'{g}',
|
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:green">',
|
|
||||||
u'end tag': u'{/g}', u'end html': u'</span>', u'protected': True})
|
|
||||||
base_html_expands.append({u'desc': u'Pink', u'start tag': u'{pk}',
|
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:#CC33CC">',
|
|
||||||
u'end tag': u'{/pk}', u'end html': u'</span>', u'protected': True})
|
|
||||||
base_html_expands.append({u'desc': u'Orange', u'start tag': u'{o}',
|
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:#CC0033">',
|
|
||||||
u'end tag': u'{/o}', u'end html': u'</span>', u'protected': True})
|
|
||||||
base_html_expands.append({u'desc': u'Purple', u'start tag': u'{pp}',
|
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:#9900FF">',
|
|
||||||
u'end tag': u'{/pp}', u'end html': u'</span>', u'protected': True})
|
|
||||||
base_html_expands.append({u'desc': u'White', u'start tag': u'{w}',
|
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:white">',
|
|
||||||
u'end tag': u'{/w}', u'end html': u'</span>', u'protected': True})
|
|
||||||
base_html_expands.append({u'desc': u'Superscript', u'start tag': u'{su}',
|
|
||||||
u'start html': u'<sup>', u'end tag': u'{/su}', u'end html': u'</sup>',
|
|
||||||
u'protected': True})
|
|
||||||
base_html_expands.append({u'desc': u'Subscript', u'start tag': u'{sb}',
|
|
||||||
u'start html': u'<sub>', u'end tag': u'{/sb}', u'end html': u'</sub>',
|
|
||||||
u'protected': True})
|
|
||||||
base_html_expands.append({u'desc': u'Paragraph', u'start tag': u'{p}',
|
|
||||||
u'start html': u'<p>', u'end tag': u'{/p}', u'end html': u'</p>',
|
|
||||||
u'protected': True})
|
|
||||||
base_html_expands.append({u'desc': u'Bold', u'start tag': u'{st}',
|
|
||||||
u'start html': u'<strong>', u'end tag': u'{/st}', u'end html': u'</strong>',
|
|
||||||
u'protected': True})
|
|
||||||
base_html_expands.append({u'desc': u'Italics', u'start tag': u'{it}',
|
|
||||||
u'start html': u'<em>', u'end tag': u'{/it}', u'end html': u'</em>',
|
|
||||||
u'protected': True})
|
|
||||||
base_html_expands.append({u'desc': u'Underline', u'start tag': u'{u}',
|
|
||||||
u'start html': u'<span style="text-decoration: underline;">',
|
|
||||||
u'end tag': u'{/u}', u'end html': u'</span>', u'protected': True})
|
|
||||||
|
|
||||||
def translate(context, text, comment=None,
|
def translate(context, text, comment=None,
|
||||||
encoding=QtCore.QCoreApplication.CodecForTr, n=-1):
|
encoding=QtCore.QCoreApplication.CodecForTr, n=-1,
|
||||||
|
translate=QtCore.QCoreApplication.translate):
|
||||||
"""
|
"""
|
||||||
A special shortcut method to wrap around the Qt4 translation functions.
|
A special shortcut method to wrap around the Qt4 translation functions.
|
||||||
This abstracts the translation procedure so that we can change it if at a
|
This abstracts the translation procedure so that we can change it if at a
|
||||||
|
@ -101,8 +62,7 @@ def translate(context, text, comment=None,
|
||||||
An identifying string for when the same text is used in different roles
|
An identifying string for when the same text is used in different roles
|
||||||
within the same context.
|
within the same context.
|
||||||
"""
|
"""
|
||||||
return QtCore.QCoreApplication.translate(
|
return translate(context, text, comment, encoding, n)
|
||||||
context, text, comment, encoding, n)
|
|
||||||
|
|
||||||
def get_text_file_string(text_file):
|
def get_text_file_string(text_file):
|
||||||
"""
|
"""
|
||||||
|
@ -166,56 +126,6 @@ def build_icon(icon):
|
||||||
QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||||
return button_icon
|
return button_icon
|
||||||
|
|
||||||
def context_menu_action(base, icon, text, slot):
|
|
||||||
"""
|
|
||||||
Utility method to help build context menus for plugins
|
|
||||||
|
|
||||||
``base``
|
|
||||||
The parent menu to add this menu item to
|
|
||||||
|
|
||||||
``icon``
|
|
||||||
An icon for this action
|
|
||||||
|
|
||||||
``text``
|
|
||||||
The text to display for this action
|
|
||||||
|
|
||||||
``slot``
|
|
||||||
The code to run when this action is triggered
|
|
||||||
"""
|
|
||||||
action = QtGui.QAction(text, base)
|
|
||||||
if icon:
|
|
||||||
action.setIcon(build_icon(icon))
|
|
||||||
QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), slot)
|
|
||||||
return action
|
|
||||||
|
|
||||||
def context_menu(base, icon, text):
|
|
||||||
"""
|
|
||||||
Utility method to help build context menus for plugins
|
|
||||||
|
|
||||||
``base``
|
|
||||||
The parent object to add this menu to
|
|
||||||
|
|
||||||
``icon``
|
|
||||||
An icon for this menu
|
|
||||||
|
|
||||||
``text``
|
|
||||||
The text to display for this menu
|
|
||||||
"""
|
|
||||||
action = QtGui.QMenu(text, base)
|
|
||||||
action.setIcon(build_icon(icon))
|
|
||||||
return action
|
|
||||||
|
|
||||||
def context_menu_separator(base):
|
|
||||||
"""
|
|
||||||
Add a separator to a context menu
|
|
||||||
|
|
||||||
``base``
|
|
||||||
The menu object to add the separator to
|
|
||||||
"""
|
|
||||||
action = QtGui.QAction(u'', base)
|
|
||||||
action.setSeparator(True)
|
|
||||||
return action
|
|
||||||
|
|
||||||
def image_to_byte(image):
|
def image_to_byte(image):
|
||||||
"""
|
"""
|
||||||
Resize an image to fit on the current screen for the web and returns
|
Resize an image to fit on the current screen for the web and returns
|
||||||
|
@ -234,13 +144,65 @@ 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 resize_image(image, width, height, background=QtCore.Qt.black):
|
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.
|
Resize an image to fit on the current screen.
|
||||||
|
|
||||||
``image``
|
``image_path``
|
||||||
The image to resize. It has to be either a ``QImage`` instance or the
|
The path to the image to resize.
|
||||||
path to the image.
|
|
||||||
|
|
||||||
``width``
|
``width``
|
||||||
The new image width.
|
The new image width.
|
||||||
|
@ -249,27 +211,36 @@ def resize_image(image, width, height, background=QtCore.Qt.black):
|
||||||
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!
|
||||||
"""
|
"""
|
||||||
log.debug(u'resize_image - start')
|
log.debug(u'resize_image - start')
|
||||||
if isinstance(image, QtGui.QImage):
|
reader = QtGui.QImageReader(image_path)
|
||||||
preview = image
|
# The image's ratio.
|
||||||
|
image_ratio = float(reader.size().width()) / float(reader.size().height())
|
||||||
|
resize_ratio = float(width) / float(height)
|
||||||
|
# Figure out the size we want to resize the image to (keep aspect ratio).
|
||||||
|
if image_ratio == resize_ratio:
|
||||||
|
size = QtCore.QSize(width, height)
|
||||||
|
elif image_ratio < resize_ratio:
|
||||||
|
# Use the image's height as reference for the new size.
|
||||||
|
size = QtCore.QSize(image_ratio * height, height)
|
||||||
else:
|
else:
|
||||||
preview = QtGui.QImage(image)
|
# Use the image's width as reference for the new size.
|
||||||
if not preview.isNull():
|
size = QtCore.QSize(width, 1 / (image_ratio / width))
|
||||||
# Only resize if different size
|
reader.setScaledSize(size)
|
||||||
if preview.width() == width and preview.height == height:
|
preview = reader.read()
|
||||||
|
if image_ratio == resize_ratio:
|
||||||
|
# We neither need to centre the image nor add "bars" to the image.
|
||||||
return preview
|
return preview
|
||||||
preview = preview.scaled(width, height, QtCore.Qt.KeepAspectRatio,
|
|
||||||
QtCore.Qt.SmoothTransformation)
|
|
||||||
realw = preview.width()
|
realw = preview.width()
|
||||||
realh = preview.height()
|
realh = preview.height()
|
||||||
# and move it to the centre of the preview space
|
# and move it to the centre of the preview space
|
||||||
new_image = QtGui.QImage(width, height,
|
new_image = QtGui.QImage(width, height,
|
||||||
QtGui.QImage.Format_ARGB32_Premultiplied)
|
QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||||
painter = QtGui.QPainter(new_image)
|
painter = QtGui.QPainter(new_image)
|
||||||
painter.fillRect(new_image.rect(), background)
|
painter.fillRect(new_image.rect(), QtGui.QColor(background))
|
||||||
painter.drawImage((width - realw) / 2, (height - realh) / 2, preview)
|
painter.drawImage((width - realw) / 2, (height - realh) / 2, preview)
|
||||||
return new_image
|
return new_image
|
||||||
|
|
||||||
|
@ -294,8 +265,9 @@ def clean_tags(text):
|
||||||
Remove Tags from text for display
|
Remove Tags from text for display
|
||||||
"""
|
"""
|
||||||
text = text.replace(u'<br>', u'\n')
|
text = text.replace(u'<br>', u'\n')
|
||||||
|
text = text.replace(u'{br}', u'\n')
|
||||||
text = text.replace(u' ', u' ')
|
text = text.replace(u' ', u' ')
|
||||||
for tag in DisplayTags.get_html_tags():
|
for tag in FormattingTags.get_html_tags():
|
||||||
text = text.replace(tag[u'start tag'], u'')
|
text = text.replace(tag[u'start tag'], u'')
|
||||||
text = text.replace(tag[u'end tag'], u'')
|
text = text.replace(tag[u'end tag'], u'')
|
||||||
return text
|
return text
|
||||||
|
@ -304,7 +276,7 @@ def expand_tags(text):
|
||||||
"""
|
"""
|
||||||
Expand tags HTML for display
|
Expand tags HTML for display
|
||||||
"""
|
"""
|
||||||
for tag in DisplayTags.get_html_tags():
|
for tag in FormattingTags.get_html_tags():
|
||||||
text = text.replace(tag[u'start tag'], tag[u'start html'])
|
text = text.replace(tag[u'start tag'], tag[u'start html'])
|
||||||
text = text.replace(tag[u'end tag'], tag[u'end html'])
|
text = text.replace(tag[u'end tag'], tag[u'end html'])
|
||||||
return text
|
return text
|
||||||
|
@ -317,25 +289,26 @@ def check_directory_exists(dir):
|
||||||
Theme directory to make sure exists
|
Theme directory to make sure exists
|
||||||
"""
|
"""
|
||||||
log.debug(u'check_directory_exists %s' % dir)
|
log.debug(u'check_directory_exists %s' % dir)
|
||||||
|
try:
|
||||||
if not os.path.exists(dir):
|
if not os.path.exists(dir):
|
||||||
os.makedirs(dir)
|
os.makedirs(dir)
|
||||||
|
except IOError:
|
||||||
|
pass
|
||||||
|
|
||||||
from listwidgetwithdnd import ListWidgetWithDnD
|
|
||||||
from displaytags import DisplayTags
|
|
||||||
from spelltextedit import SpellTextEdit
|
|
||||||
from eventreceiver import Receiver
|
from eventreceiver import Receiver
|
||||||
from imagemanager import ImageManager
|
from listwidgetwithdnd import ListWidgetWithDnD
|
||||||
|
from formattingtags import FormattingTags
|
||||||
|
from spelltextedit import SpellTextEdit
|
||||||
from settingsmanager import SettingsManager
|
from settingsmanager import SettingsManager
|
||||||
from plugin import PluginStatus, StringContent, Plugin
|
from plugin import PluginStatus, StringContent, Plugin
|
||||||
from pluginmanager import PluginManager
|
from pluginmanager import PluginManager
|
||||||
from settingstab import SettingsTab
|
from settingstab import SettingsTab
|
||||||
from serviceitem import ServiceItem
|
from serviceitem import ServiceItem, ServiceItemType, ItemCapabilities
|
||||||
from serviceitem import ServiceItemType
|
|
||||||
from serviceitem import ItemCapabilities
|
|
||||||
from htmlbuilder import build_html, build_lyrics_format_css, \
|
from htmlbuilder import build_html, build_lyrics_format_css, \
|
||||||
build_lyrics_outline_css
|
build_lyrics_outline_css
|
||||||
from toolbar import OpenLPToolbar
|
from toolbar import OpenLPToolbar
|
||||||
from dockwidget import OpenLPDockWidget
|
from dockwidget import OpenLPDockWidget
|
||||||
|
from imagemanager import ImageManager
|
||||||
from renderer import Renderer
|
from renderer import Renderer
|
||||||
from rendermanager import RenderManager
|
|
||||||
from mediamanageritem import MediaManagerItem
|
from mediamanageritem import MediaManagerItem
|
||||||
|
from openlp.core.utils.actions import ActionList
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -28,12 +29,16 @@ The :mod:`db` module provides the core database functionality for OpenLP
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
from urllib import quote_plus as urlquote
|
||||||
|
|
||||||
from PyQt4 import QtCore
|
from PyQt4 import QtCore
|
||||||
from sqlalchemy import create_engine, MetaData
|
from sqlalchemy import Table, MetaData, Column, types, create_engine
|
||||||
from sqlalchemy.exceptions import InvalidRequestError
|
from sqlalchemy.exc import SQLAlchemyError, InvalidRequestError, DBAPIError
|
||||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
from sqlalchemy.orm import scoped_session, sessionmaker, mapper
|
||||||
|
from sqlalchemy.pool import NullPool
|
||||||
|
|
||||||
|
from openlp.core.lib import translate
|
||||||
|
from openlp.core.lib.ui import critical_error_message_box
|
||||||
from openlp.core.utils import AppLocation, delete_file
|
from openlp.core.utils import AppLocation, delete_file
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -51,12 +56,70 @@ def init_db(url, auto_flush=True, auto_commit=False):
|
||||||
``auto_commit``
|
``auto_commit``
|
||||||
Sets the commit behaviour of the session
|
Sets the commit behaviour of the session
|
||||||
"""
|
"""
|
||||||
engine = create_engine(url)
|
engine = create_engine(url, poolclass=NullPool)
|
||||||
metadata = MetaData(bind=engine)
|
metadata = MetaData(bind=engine)
|
||||||
session = scoped_session(sessionmaker(autoflush=auto_flush,
|
session = scoped_session(sessionmaker(autoflush=auto_flush,
|
||||||
autocommit=auto_commit, bind=engine))
|
autocommit=auto_commit, bind=engine))
|
||||||
return session, metadata
|
return session, metadata
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade_db(url, upgrade):
|
||||||
|
"""
|
||||||
|
Upgrade a database.
|
||||||
|
|
||||||
|
``url``
|
||||||
|
The url of the database to upgrade.
|
||||||
|
|
||||||
|
``upgrade``
|
||||||
|
The python module that contains the upgrade instructions.
|
||||||
|
"""
|
||||||
|
session, metadata = init_db(url)
|
||||||
|
|
||||||
|
class Metadata(BaseModel):
|
||||||
|
"""
|
||||||
|
Provides a class for the metadata table.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
load_changes = True
|
||||||
|
try:
|
||||||
|
tables = upgrade.upgrade_setup(metadata)
|
||||||
|
except (SQLAlchemyError, DBAPIError):
|
||||||
|
load_changes = False
|
||||||
|
metadata_table = Table(u'metadata', metadata,
|
||||||
|
Column(u'key', types.Unicode(64), primary_key=True),
|
||||||
|
Column(u'value', types.UnicodeText(), default=None)
|
||||||
|
)
|
||||||
|
metadata_table.create(checkfirst=True)
|
||||||
|
mapper(Metadata, metadata_table)
|
||||||
|
version_meta = session.query(Metadata).get(u'version')
|
||||||
|
if version_meta is None:
|
||||||
|
version_meta = Metadata.populate(key=u'version', value=u'0')
|
||||||
|
version = 0
|
||||||
|
else:
|
||||||
|
version = int(version_meta.value)
|
||||||
|
if version > upgrade.__version__:
|
||||||
|
return version, upgrade.__version__
|
||||||
|
version += 1
|
||||||
|
if load_changes:
|
||||||
|
while hasattr(upgrade, u'upgrade_%d' % version):
|
||||||
|
log.debug(u'Running upgrade_%d', version)
|
||||||
|
try:
|
||||||
|
getattr(upgrade, u'upgrade_%d' % version) \
|
||||||
|
(session, metadata, tables)
|
||||||
|
version_meta.value = unicode(version)
|
||||||
|
except (SQLAlchemyError, DBAPIError):
|
||||||
|
log.exception(u'Could not run database upgrade script '
|
||||||
|
'"upgrade_%s", upgrade process has been halted.', version)
|
||||||
|
break
|
||||||
|
version += 1
|
||||||
|
else:
|
||||||
|
version_meta = Metadata.populate(key=u'version',
|
||||||
|
value=int(upgrade.__version__))
|
||||||
|
session.add(version_meta)
|
||||||
|
session.commit()
|
||||||
|
return int(version_meta.value), upgrade.__version__
|
||||||
|
|
||||||
|
|
||||||
def delete_database(plugin_name, db_file_name=None):
|
def delete_database(plugin_name, db_file_name=None):
|
||||||
"""
|
"""
|
||||||
Remove a database file from the system.
|
Remove a database file from the system.
|
||||||
|
@ -77,6 +140,7 @@ def delete_database(plugin_name, db_file_name=None):
|
||||||
AppLocation.get_section_data_path(plugin_name), plugin_name)
|
AppLocation.get_section_data_path(plugin_name), plugin_name)
|
||||||
return delete_file(db_file_path)
|
return delete_file(db_file_path)
|
||||||
|
|
||||||
|
|
||||||
class BaseModel(object):
|
class BaseModel(object):
|
||||||
"""
|
"""
|
||||||
BaseModel provides a base object with a set of generic functions
|
BaseModel provides a base object with a set of generic functions
|
||||||
|
@ -87,16 +151,16 @@ class BaseModel(object):
|
||||||
Creates an instance of a class and populates it, returning the instance
|
Creates an instance of a class and populates it, returning the instance
|
||||||
"""
|
"""
|
||||||
instance = cls()
|
instance = cls()
|
||||||
for key in kwargs:
|
for key, value in kwargs.iteritems():
|
||||||
instance.__setattr__(key, kwargs[key])
|
instance.__setattr__(key, value)
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
class Manager(object):
|
class Manager(object):
|
||||||
"""
|
"""
|
||||||
Provide generic object persistence management
|
Provide generic object persistence management
|
||||||
"""
|
"""
|
||||||
def __init__(self, plugin_name, init_schema, db_file_name=None):
|
def __init__(self, plugin_name, init_schema, db_file_name=None,
|
||||||
|
upgrade_mod=None):
|
||||||
"""
|
"""
|
||||||
Runs the initialisation process that includes creating the connection
|
Runs the initialisation process that includes creating the connection
|
||||||
to the database and the tables if they don't exist.
|
to the database and the tables if they don't exist.
|
||||||
|
@ -107,6 +171,9 @@ class Manager(object):
|
||||||
``init_schema``
|
``init_schema``
|
||||||
The init_schema function for this database
|
The init_schema function for this database
|
||||||
|
|
||||||
|
``upgrade_schema``
|
||||||
|
The upgrade_schema function for this database
|
||||||
|
|
||||||
``db_file_name``
|
``db_file_name``
|
||||||
The file name to use for this database. Defaults to None resulting
|
The file name to use for this database. Defaults to None resulting
|
||||||
in the plugin_name being used.
|
in the plugin_name being used.
|
||||||
|
@ -127,12 +194,33 @@ class Manager(object):
|
||||||
AppLocation.get_section_data_path(plugin_name), plugin_name)
|
AppLocation.get_section_data_path(plugin_name), plugin_name)
|
||||||
else:
|
else:
|
||||||
self.db_url = u'%s://%s:%s@%s/%s' % (db_type,
|
self.db_url = u'%s://%s:%s@%s/%s' % (db_type,
|
||||||
unicode(settings.value(u'db username').toString()),
|
urlquote(unicode(settings.value(u'db username').toString())),
|
||||||
unicode(settings.value(u'db password').toString()),
|
urlquote(unicode(settings.value(u'db password').toString())),
|
||||||
unicode(settings.value(u'db hostname').toString()),
|
urlquote(unicode(settings.value(u'db hostname').toString())),
|
||||||
unicode(settings.value(u'db database').toString()))
|
urlquote(unicode(settings.value(u'db database').toString())))
|
||||||
settings.endGroup()
|
settings.endGroup()
|
||||||
|
if upgrade_mod:
|
||||||
|
db_ver, up_ver = upgrade_db(self.db_url, upgrade_mod)
|
||||||
|
if db_ver > up_ver:
|
||||||
|
critical_error_message_box(
|
||||||
|
translate('OpenLP.Manager', 'Database Error'),
|
||||||
|
unicode(translate('OpenLP.Manager', 'The database being '
|
||||||
|
'loaded was created in a more recent version of '
|
||||||
|
'OpenLP. The database is version %d, while OpenLP '
|
||||||
|
'expects version %d. The database will not be loaded.'
|
||||||
|
'\n\nDatabase: %s')) % \
|
||||||
|
(db_ver, up_ver, self.db_url)
|
||||||
|
)
|
||||||
|
return
|
||||||
|
try:
|
||||||
self.session = init_schema(self.db_url)
|
self.session = init_schema(self.db_url)
|
||||||
|
except (SQLAlchemyError, DBAPIError):
|
||||||
|
log.exception(u'Error loading database: %s', self.db_url)
|
||||||
|
critical_error_message_box(
|
||||||
|
translate('OpenLP.Manager', 'Database Error'),
|
||||||
|
unicode(translate('OpenLP.Manager', 'OpenLP cannot load your '
|
||||||
|
'database.\n\nDatabase: %s')) % self.db_url
|
||||||
|
)
|
||||||
|
|
||||||
def save_object(self, object_instance, commit=True):
|
def save_object(self, object_instance, commit=True):
|
||||||
"""
|
"""
|
||||||
|
@ -221,7 +309,9 @@ class Manager(object):
|
||||||
query = self.session.query(object_class)
|
query = self.session.query(object_class)
|
||||||
if filter_clause is not None:
|
if filter_clause is not None:
|
||||||
query = query.filter(filter_clause)
|
query = query.filter(filter_clause)
|
||||||
if order_by_ref is not None:
|
if isinstance(order_by_ref, list):
|
||||||
|
return query.order_by(*order_by_ref).all()
|
||||||
|
elif order_by_ref is not None:
|
||||||
return query.order_by(order_by_ref).all()
|
return query.order_by(order_by_ref).all()
|
||||||
return query.all()
|
return query.all()
|
||||||
|
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -32,6 +33,7 @@ import logging
|
||||||
from PyQt4 import QtGui
|
from PyQt4 import QtGui
|
||||||
|
|
||||||
from openlp.core.lib import build_icon
|
from openlp.core.lib import build_icon
|
||||||
|
from openlp.core.ui import ScreenList
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -45,8 +47,15 @@ class OpenLPDockWidget(QtGui.QDockWidget):
|
||||||
"""
|
"""
|
||||||
log.debug(u'Initialise the %s widget' % name)
|
log.debug(u'Initialise the %s widget' % name)
|
||||||
QtGui.QDockWidget.__init__(self, parent)
|
QtGui.QDockWidget.__init__(self, parent)
|
||||||
self.parent = parent
|
|
||||||
if name:
|
if name:
|
||||||
self.setObjectName(name)
|
self.setObjectName(name)
|
||||||
if icon:
|
if icon:
|
||||||
self.setWindowIcon(build_icon(icon))
|
self.setWindowIcon(build_icon(icon))
|
||||||
|
# Sort out the minimum width.
|
||||||
|
screens = ScreenList.get_instance()
|
||||||
|
screen_width = screens.current[u'size'].width()
|
||||||
|
mainwindow_docbars = screen_width / 5
|
||||||
|
if mainwindow_docbars > 300:
|
||||||
|
self.setMinimumWidth(300)
|
||||||
|
else:
|
||||||
|
self.setMinimumWidth(mainwindow_docbars)
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -34,200 +35,190 @@ log = logging.getLogger(__name__)
|
||||||
|
|
||||||
class EventReceiver(QtCore.QObject):
|
class EventReceiver(QtCore.QObject):
|
||||||
"""
|
"""
|
||||||
Class to allow events to be passed from different parts of the
|
Class to allow events to be passed from different parts of the system. This
|
||||||
system. This is a private class and should not be used directly
|
is a private class and should not be used directly but rather via the
|
||||||
but rather via the Receiver class.
|
Receiver class.
|
||||||
|
|
||||||
|
**Mainwindow related and generic signals**
|
||||||
|
|
||||||
|
``mainwindow_status_text``
|
||||||
|
Changes the bottom status bar text on the mainwindow.
|
||||||
|
|
||||||
|
``openlp_warning_message``
|
||||||
|
Displays a standalone Warning Message.
|
||||||
|
|
||||||
|
``openlp_error_message``
|
||||||
|
Displays a standalone Error Message.
|
||||||
|
|
||||||
|
``openlp_information_message``
|
||||||
|
Displays a standalone Information Message.
|
||||||
|
|
||||||
|
``cursor_busy``
|
||||||
|
Makes the cursor got to a busy form.
|
||||||
|
|
||||||
|
``cursor_normal``
|
||||||
|
Resets the cursor to default.
|
||||||
|
|
||||||
``openlp_process_events``
|
``openlp_process_events``
|
||||||
Requests the Application to flush the events queue
|
Requests the Application to flush the events queue.
|
||||||
|
|
||||||
``openlp_version_check``
|
``openlp_version_check``
|
||||||
Version has changed so pop up window.
|
Version has changed so pop up window.
|
||||||
|
|
||||||
|
``openlp_stop_wizard``
|
||||||
|
Stops a wizard before completion.
|
||||||
|
|
||||||
|
**Setting related signals**
|
||||||
|
|
||||||
``config_updated``
|
``config_updated``
|
||||||
Informs components the config has changed
|
Informs components that the config has changed.
|
||||||
|
|
||||||
``config_screen_changed``
|
``config_screen_changed``
|
||||||
The display monitor has been changed
|
The display monitor has been changed.
|
||||||
|
|
||||||
``slidecontroller_{live|preview}_first``
|
**Slidecontroller signals**
|
||||||
Moves to the first slide
|
|
||||||
|
|
||||||
``slidecontroller_{live|preview}_next``
|
``slidecontroller_{live|preview}_next``
|
||||||
Moves to the next slide
|
Moves to the next slide.
|
||||||
|
|
||||||
``slidecontroller_{live|preview}_next_noloop``
|
``slidecontroller_{live|preview}_next_noloop``
|
||||||
Moves to the next slide without auto advance
|
Moves to the next slide without auto advance.
|
||||||
|
|
||||||
``slidecontroller_{live|preview}_previous``
|
``slidecontroller_{live|preview}_previous``
|
||||||
Moves to the previous slide
|
Moves to the previous slide.
|
||||||
|
|
||||||
``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.
|
||||||
|
|
||||||
``slidecontroller_{live|preview}_started``
|
``slidecontroller_{live|preview}_started``
|
||||||
Broadcasts that an item has been made live/previewed
|
Broadcasts that an item has been made live/previewed.
|
||||||
|
|
||||||
``slidecontroller_{live|preview}_change``
|
``slidecontroller_{live|preview}_change``
|
||||||
Informs the slidecontroller that a slide change has occurred and to
|
Informs the slidecontroller that a slide change has occurred and to
|
||||||
update itself
|
update itself.
|
||||||
|
|
||||||
``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.
|
||||||
|
|
||||||
``slidecontroller_{live|preview}_unblank``
|
``slidecontroller_{live|preview}_unblank``
|
||||||
Request that the output screen is unblanked
|
Request that the output screen is unblanked.
|
||||||
|
|
||||||
``slidecontroller_live_spin_delay``
|
``slidecontroller_live_spin_delay``
|
||||||
Pushes out the loop delay
|
Pushes out the loop delay.
|
||||||
|
|
||||||
``slidecontroller_live_stop_loop``
|
``slidecontroller_live_stop_loop``
|
||||||
Stop the loop on the main display
|
Stop the loop on the main display.
|
||||||
|
|
||||||
|
**Servicemanager related signals**
|
||||||
|
|
||||||
``servicemanager_previous_item``
|
``servicemanager_previous_item``
|
||||||
Display the previous item in the service
|
Display the previous item in the service.
|
||||||
|
|
||||||
|
``servicemanager_preview_live``
|
||||||
|
Requests a Preview item from the Service Manager to update live and add
|
||||||
|
a new item to the preview panel.
|
||||||
|
|
||||||
``servicemanager_next_item``
|
``servicemanager_next_item``
|
||||||
Display the next item in the service
|
Display the next item in the service.
|
||||||
|
|
||||||
``servicemanager_set_item``
|
``servicemanager_set_item``
|
||||||
Go live on a specific item, by index
|
Go live on a specific item, by index.
|
||||||
|
|
||||||
``servicemanager_list_request``
|
|
||||||
Request the service list. Responds with servicemanager_list_response
|
|
||||||
containing a array of dictionaries
|
|
||||||
|
|
||||||
``maindisplay_blank``
|
|
||||||
Blank the maindisplay window
|
|
||||||
|
|
||||||
``maindisplay_hide``
|
|
||||||
Hide the maindisplay window
|
|
||||||
|
|
||||||
``maindisplay_show``
|
|
||||||
Return the maindisplay window
|
|
||||||
|
|
||||||
``maindisplay_active``
|
|
||||||
The maindisplay has been made active
|
|
||||||
|
|
||||||
``maindisplay_status_text``
|
|
||||||
Changes the bottom status bar text on the maindisplay window
|
|
||||||
|
|
||||||
``maindisplay_blank_check``
|
|
||||||
Check to see if the blank display message is required
|
|
||||||
|
|
||||||
``videodisplay_start``
|
|
||||||
Open a media item and prepare for playing
|
|
||||||
|
|
||||||
``videodisplay_play``
|
|
||||||
Start playing a media item
|
|
||||||
|
|
||||||
``videodisplay_pause``
|
|
||||||
Pause a media item
|
|
||||||
|
|
||||||
``videodisplay_stop``
|
|
||||||
Stop playing a media item
|
|
||||||
|
|
||||||
``videodisplay_background``
|
|
||||||
Replace the background video
|
|
||||||
|
|
||||||
``theme_update_list``
|
|
||||||
send out message with new themes
|
|
||||||
|
|
||||||
``theme_update_global``
|
|
||||||
Tell the components we have a new global theme
|
|
||||||
|
|
||||||
``{plugin}_start``
|
|
||||||
Requests a plugin to start a external program
|
|
||||||
Path and file provided in message
|
|
||||||
|
|
||||||
``{plugin}_first``
|
|
||||||
Requests a plugin to handle a first event
|
|
||||||
|
|
||||||
``{plugin}_previous``
|
|
||||||
Requests a plugin to handle a previous event
|
|
||||||
|
|
||||||
``{plugin}_next``
|
|
||||||
Requests a plugin to handle a next event
|
|
||||||
|
|
||||||
``{plugin}_last``
|
|
||||||
Requests a plugin to handle a last event
|
|
||||||
|
|
||||||
``{plugin}_slide``
|
|
||||||
Requests a plugin to handle a go to specific slide event
|
|
||||||
|
|
||||||
``{plugin}_stop``
|
|
||||||
Requests a plugin to handle a stop event
|
|
||||||
|
|
||||||
``{plugin}_blank``
|
|
||||||
Requests a plugin to handle a blank screen event
|
|
||||||
|
|
||||||
``{plugin}_unblank``
|
|
||||||
Requests a plugin to handle an unblank screen event
|
|
||||||
|
|
||||||
``{plugin}_edit``
|
|
||||||
Requests a plugin edit a database item with the key as the payload
|
|
||||||
|
|
||||||
``{plugin}_edit_clear``
|
|
||||||
Editing has been completed
|
|
||||||
|
|
||||||
``{plugin}_load_list``
|
|
||||||
Tells the the plugin to reload the media manager list
|
|
||||||
|
|
||||||
``{plugin}_preview``
|
|
||||||
Tells the plugin it's item can be previewed
|
|
||||||
|
|
||||||
``{plugin}_add_service_item``
|
|
||||||
Ask the plugin to push the selected items to the service item
|
|
||||||
|
|
||||||
``{plugin}_service_load``
|
|
||||||
Ask the plugin to process an individual service item after it has been
|
|
||||||
loaded
|
|
||||||
|
|
||||||
``service_item_update``
|
``service_item_update``
|
||||||
Passes back to the service manager the service item after it has been
|
Passes back to the service manager the service item after it has been
|
||||||
processed by the plugin
|
processed by the plugin.
|
||||||
|
|
||||||
|
**Display signals**
|
||||||
|
|
||||||
|
``update_display_css``
|
||||||
|
CSS has been updated which needs to be changed on the main display.
|
||||||
|
|
||||||
|
**Live Display signals**
|
||||||
|
|
||||||
|
``live_display_hide``
|
||||||
|
Hide the live display.
|
||||||
|
|
||||||
|
``live_display_show``
|
||||||
|
Return the live display.
|
||||||
|
|
||||||
|
``live_display_active``
|
||||||
|
The live display has been made active.
|
||||||
|
|
||||||
|
``live_display_blank_check``
|
||||||
|
Check to see if the blank display message is required.
|
||||||
|
|
||||||
|
**Theme related singlas**
|
||||||
|
|
||||||
|
``theme_update_list``
|
||||||
|
send out message with new themes.
|
||||||
|
|
||||||
|
``theme_update_global``
|
||||||
|
Tell the components we have a new global theme.
|
||||||
|
|
||||||
|
**Plugin specific signals**
|
||||||
|
|
||||||
|
``{plugin}_start``
|
||||||
|
Requests a plugin to start a external program. Path and file have to
|
||||||
|
be provided in the message.
|
||||||
|
|
||||||
|
``{plugin}_first``
|
||||||
|
Requests a plugin to handle a first event.
|
||||||
|
|
||||||
|
``{plugin}_previous``
|
||||||
|
Requests a plugin to handle a previous event.
|
||||||
|
|
||||||
|
``{plugin}_next``
|
||||||
|
Requests a plugin to handle a next event.
|
||||||
|
|
||||||
|
``{plugin}_last``
|
||||||
|
Requests a plugin to handle a last event.
|
||||||
|
|
||||||
|
``{plugin}_slide``
|
||||||
|
Requests a plugin to handle a go to specific slide event.
|
||||||
|
|
||||||
|
``{plugin}_stop``
|
||||||
|
Requests a plugin to handle a stop event.
|
||||||
|
|
||||||
|
``{plugin}_blank``
|
||||||
|
Requests a plugin to handle a blank screen event.
|
||||||
|
|
||||||
|
``{plugin}_unblank``
|
||||||
|
Requests a plugin to handle an unblank screen event.
|
||||||
|
|
||||||
|
``{plugin}_edit``
|
||||||
|
Requests a plugin edit a database item with the key as the payload.
|
||||||
|
|
||||||
|
``{plugin}_edit_clear``
|
||||||
|
Editing has been completed.
|
||||||
|
|
||||||
|
``{plugin}_load_list``
|
||||||
|
Tells the the plugin to reload the media manager list.
|
||||||
|
|
||||||
|
``{plugin}_preview``
|
||||||
|
Tells the plugin it's item can be previewed.
|
||||||
|
|
||||||
|
``{plugin}_add_service_item``
|
||||||
|
Ask the plugin to push the selected items to the service item.
|
||||||
|
|
||||||
|
``{plugin}_service_load``
|
||||||
|
Ask the plugin to process an individual service item after it has been
|
||||||
|
loaded.
|
||||||
|
|
||||||
``alerts_text``
|
``alerts_text``
|
||||||
Displays an alert message
|
Displays an alert message.
|
||||||
|
|
||||||
``bibles_nobook``
|
``bibles_nobook``
|
||||||
Attempt to find book resulted in no match
|
Attempt to find book resulted in no match.
|
||||||
|
|
||||||
``openlp_stop_wizard``
|
|
||||||
Stops a wizard before completion
|
|
||||||
|
|
||||||
``remotes_poll_request``
|
``remotes_poll_request``
|
||||||
Waits for openlp to do something "interesting" and sends a
|
Waits for openlp to do something "interesting" and sends a
|
||||||
remotes_poll_response signal when it does
|
``remotes_poll_response`` signal when it does.
|
||||||
|
|
||||||
``openlp_warning_message``
|
|
||||||
Displays a standalone Warning Message
|
|
||||||
|
|
||||||
``openlp_error_message``
|
|
||||||
Displays a standalone Error Message
|
|
||||||
|
|
||||||
``openlp_information_message``
|
|
||||||
Displays a standalone Information Message
|
|
||||||
|
|
||||||
``cursor_busy``
|
|
||||||
Makes the cursor got to a busy form
|
|
||||||
|
|
||||||
``cursor_normal``
|
|
||||||
Resets the cursor to default
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
|
@ -0,0 +1,227 @@
|
||||||
|
# -*- 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 #
|
||||||
|
###############################################################################
|
||||||
|
"""
|
||||||
|
Provide HTML Tag management and Formatting Tag access class
|
||||||
|
"""
|
||||||
|
import cPickle
|
||||||
|
|
||||||
|
from PyQt4 import QtCore
|
||||||
|
|
||||||
|
from openlp.core.lib import translate
|
||||||
|
|
||||||
|
|
||||||
|
class FormattingTags(object):
|
||||||
|
"""
|
||||||
|
Static Class to HTML Tags to be access around the code the list is managed
|
||||||
|
by the Options Tab.
|
||||||
|
"""
|
||||||
|
html_expands = []
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_html_tags():
|
||||||
|
"""
|
||||||
|
Provide access to the html_expands list.
|
||||||
|
"""
|
||||||
|
# Load user defined tags otherwise user defined tags are not present.
|
||||||
|
FormattingTags.load_tags()
|
||||||
|
return FormattingTags.html_expands
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def reset_html_tags():
|
||||||
|
"""
|
||||||
|
Resets the html_expands list.
|
||||||
|
"""
|
||||||
|
temporary_tags = [tag for tag in FormattingTags.html_expands
|
||||||
|
if tag.get(u'temporary')]
|
||||||
|
FormattingTags.html_expands = []
|
||||||
|
base_tags = []
|
||||||
|
# Append the base tags.
|
||||||
|
# Hex Color tags from http://www.w3schools.com/html/html_colornames.asp
|
||||||
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Red'),
|
||||||
|
u'start tag': u'{r}',
|
||||||
|
u'start html': u'<span style="-webkit-text-fill-color:red">',
|
||||||
|
u'end tag': u'{/r}', u'end html': u'</span>', u'protected': True,
|
||||||
|
u'temporary': False})
|
||||||
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Black'),
|
||||||
|
u'start tag': u'{b}',
|
||||||
|
u'start html': u'<span style="-webkit-text-fill-color:black">',
|
||||||
|
u'end tag': u'{/b}', u'end html': u'</span>', u'protected': True,
|
||||||
|
u'temporary': False})
|
||||||
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Blue'),
|
||||||
|
u'start tag': u'{bl}',
|
||||||
|
u'start html': u'<span style="-webkit-text-fill-color:blue">',
|
||||||
|
u'end tag': u'{/bl}', u'end html': u'</span>', u'protected': True,
|
||||||
|
u'temporary': False})
|
||||||
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Yellow'),
|
||||||
|
u'start tag': u'{y}',
|
||||||
|
u'start html': u'<span style="-webkit-text-fill-color:yellow">',
|
||||||
|
u'end tag': u'{/y}', u'end html': u'</span>', u'protected': True,
|
||||||
|
u'temporary': False})
|
||||||
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Green'),
|
||||||
|
u'start tag': u'{g}',
|
||||||
|
u'start html': u'<span style="-webkit-text-fill-color:green">',
|
||||||
|
u'end tag': u'{/g}', u'end html': u'</span>', u'protected': True,
|
||||||
|
u'temporary': False})
|
||||||
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Pink'),
|
||||||
|
u'start tag': u'{pk}',
|
||||||
|
u'start html': u'<span style="-webkit-text-fill-color:#FFC0CB">',
|
||||||
|
u'end tag': u'{/pk}', u'end html': u'</span>', u'protected': True,
|
||||||
|
u'temporary': False})
|
||||||
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Orange'),
|
||||||
|
u'start tag': u'{o}',
|
||||||
|
u'start html': u'<span style="-webkit-text-fill-color:#FFA500">',
|
||||||
|
u'end tag': u'{/o}', u'end html': u'</span>', u'protected': True,
|
||||||
|
u'temporary': False})
|
||||||
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Purple'),
|
||||||
|
u'start tag': u'{pp}',
|
||||||
|
u'start html': u'<span style="-webkit-text-fill-color:#800080">',
|
||||||
|
u'end tag': u'{/pp}', u'end html': u'</span>', u'protected': True,
|
||||||
|
u'temporary': False})
|
||||||
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'White'),
|
||||||
|
u'start tag': u'{w}',
|
||||||
|
u'start html': u'<span style="-webkit-text-fill-color:white">',
|
||||||
|
u'end tag': u'{/w}', u'end html': u'</span>', u'protected': True,
|
||||||
|
u'temporary': False})
|
||||||
|
base_tags.append({
|
||||||
|
u'desc': translate('OpenLP.FormattingTags', 'Superscript'),
|
||||||
|
u'start tag': u'{su}', u'start html': u'<sup>',
|
||||||
|
u'end tag': u'{/su}', u'end html': u'</sup>', u'protected': True,
|
||||||
|
u'temporary': False})
|
||||||
|
base_tags.append({
|
||||||
|
u'desc': translate('OpenLP.FormattingTags', 'Subscript'),
|
||||||
|
u'start tag': u'{sb}', u'start html': u'<sub>',
|
||||||
|
u'end tag': u'{/sb}', u'end html': u'</sub>', u'protected': True,
|
||||||
|
u'temporary': False})
|
||||||
|
base_tags.append({
|
||||||
|
u'desc': translate('OpenLP.FormattingTags', 'Paragraph'),
|
||||||
|
u'start tag': u'{p}', u'start html': u'<p>', u'end tag': u'{/p}',
|
||||||
|
u'end html': u'</p>', u'protected': True,
|
||||||
|
u'temporary': False})
|
||||||
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Bold'),
|
||||||
|
u'start tag': u'{st}', u'start html': u'<strong>',
|
||||||
|
u'end tag': u'{/st}', u'end html': u'</strong>',
|
||||||
|
u'protected': True, u'temporary': False})
|
||||||
|
base_tags.append({
|
||||||
|
u'desc': translate('OpenLP.FormattingTags', 'Italics'),
|
||||||
|
u'start tag': u'{it}', u'start html': u'<em>', u'end tag': u'{/it}',
|
||||||
|
u'end html': u'</em>', u'protected': True, u'temporary': False})
|
||||||
|
base_tags.append({
|
||||||
|
u'desc': translate('OpenLP.FormattingTags', 'Underline'),
|
||||||
|
u'start tag': u'{u}',
|
||||||
|
u'start html': u'<span style="text-decoration: underline;">',
|
||||||
|
u'end tag': u'{/u}', u'end html': u'</span>', u'protected': True,
|
||||||
|
u'temporary': False})
|
||||||
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Break'),
|
||||||
|
u'start tag': u'{br}', u'start html': u'<br>', u'end tag': u'',
|
||||||
|
u'end html': u'', u'protected': True, u'temporary': False})
|
||||||
|
FormattingTags.add_html_tags(base_tags)
|
||||||
|
FormattingTags.add_html_tags(temporary_tags)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def save_html_tags():
|
||||||
|
"""
|
||||||
|
Saves all formatting tags except protected ones.
|
||||||
|
"""
|
||||||
|
tags = []
|
||||||
|
for tag in FormattingTags.html_expands:
|
||||||
|
if not tag[u'protected'] and not tag.get(u'temporary'):
|
||||||
|
tags.append(tag)
|
||||||
|
# Remove key 'temporary' from tags. It is not needed to be saved.
|
||||||
|
for tag in tags:
|
||||||
|
if u'temporary' in tag:
|
||||||
|
del tag[u'temporary']
|
||||||
|
# Formatting Tags were also known as display tags.
|
||||||
|
QtCore.QSettings().setValue(u'displayTags/html_tags',
|
||||||
|
QtCore.QVariant(cPickle.dumps(tags) if tags else u''))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_tags():
|
||||||
|
"""
|
||||||
|
Load the Tags from store so can be used in the system or used to
|
||||||
|
update the display. If Cancel was selected this is needed to reset the
|
||||||
|
dsiplay to the correct version.
|
||||||
|
"""
|
||||||
|
# Initial Load of the Tags
|
||||||
|
FormattingTags.reset_html_tags()
|
||||||
|
# Formatting Tags were also known as display tags.
|
||||||
|
user_expands = QtCore.QSettings().value(u'displayTags/html_tags',
|
||||||
|
QtCore.QVariant(u'')).toString()
|
||||||
|
# cPickle only accepts str not unicode strings
|
||||||
|
user_expands_string = str(unicode(user_expands).encode(u'utf8'))
|
||||||
|
if user_expands_string:
|
||||||
|
user_tags = cPickle.loads(user_expands_string)
|
||||||
|
# If we have some user ones added them as well
|
||||||
|
FormattingTags.add_html_tags(user_tags)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_html_tags(tags, save=False):
|
||||||
|
"""
|
||||||
|
Add a list of tags to the list.
|
||||||
|
|
||||||
|
``tags``
|
||||||
|
The list with tags to add.
|
||||||
|
|
||||||
|
``save``
|
||||||
|
Defaults to ``False``. If set to ``True`` the given ``tags`` are
|
||||||
|
saved to the config.
|
||||||
|
|
||||||
|
Each **tag** has to be a ``dict`` and should have the following keys:
|
||||||
|
|
||||||
|
* desc
|
||||||
|
The formatting tag's description, e. g. **Red**
|
||||||
|
|
||||||
|
* start tag
|
||||||
|
The start tag, e. g. ``{r}``
|
||||||
|
|
||||||
|
* end tag
|
||||||
|
The end tag, e. g. ``{/r}``
|
||||||
|
|
||||||
|
* start html
|
||||||
|
The start html tag. For instance ``<span style="
|
||||||
|
-webkit-text-fill-color:red">``
|
||||||
|
|
||||||
|
* end html
|
||||||
|
The end html tag. For example ``</span>``
|
||||||
|
|
||||||
|
* protected
|
||||||
|
A boolean stating whether this is a build-in tag or not. Should be
|
||||||
|
``True`` in most cases.
|
||||||
|
|
||||||
|
* temporary
|
||||||
|
A temporary tag will not be saved, but is also considered when
|
||||||
|
displaying text containing the tag. It has to be a ``boolean``.
|
||||||
|
"""
|
||||||
|
FormattingTags.html_expands.extend(tags)
|
||||||
|
if save:
|
||||||
|
FormattingTags.save_html_tags()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def remove_html_tag(tag_id):
|
||||||
|
"""
|
||||||
|
Removes an individual html_expands tag.
|
||||||
|
"""
|
||||||
|
FormattingTags.html_expands.pop(tag_id)
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -34,6 +35,7 @@ from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, \
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
HTMLSRC = u"""
|
HTMLSRC = u"""
|
||||||
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>OpenLP Display</title>
|
<title>OpenLP Display</title>
|
||||||
|
@ -71,13 +73,7 @@ body {
|
||||||
#video2 {
|
#video2 {
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
#alert {
|
|
||||||
position: absolute;
|
|
||||||
left: 0px;
|
|
||||||
top: 0px;
|
|
||||||
z-index:10;
|
|
||||||
%s
|
%s
|
||||||
}
|
|
||||||
#footer {
|
#footer {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 6;
|
z-index: 6;
|
||||||
|
@ -85,9 +81,14 @@ body {
|
||||||
}
|
}
|
||||||
/* lyric css */
|
/* lyric css */
|
||||||
%s
|
%s
|
||||||
|
sup {
|
||||||
|
font-size: 0.6em;
|
||||||
|
vertical-align: top;
|
||||||
|
position: relative;
|
||||||
|
top: -0.3em;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<script language="javascript">
|
<script>
|
||||||
var timer = null;
|
var timer = null;
|
||||||
var video_timer = null;
|
var video_timer = null;
|
||||||
var current_video = '1';
|
var current_video = '1';
|
||||||
|
@ -172,7 +173,7 @@ body {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
%s
|
||||||
function show_image(src){
|
function show_image(src){
|
||||||
var img = document.getElementById('image');
|
var img = document.getElementById('image');
|
||||||
img.src = src;
|
img.src = src;
|
||||||
|
@ -218,44 +219,17 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function show_alert(alerttext, position){
|
|
||||||
var text = document.getElementById('alert');
|
|
||||||
text.innerHTML = alerttext;
|
|
||||||
if(alerttext == '') {
|
|
||||||
text.style.visibility = 'hidden';
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if(position == ''){
|
|
||||||
position = getComputedStyle(text, '').verticalAlign;
|
|
||||||
}
|
|
||||||
switch(position)
|
|
||||||
{
|
|
||||||
case 'top':
|
|
||||||
text.style.top = '0px';
|
|
||||||
break;
|
|
||||||
case 'middle':
|
|
||||||
text.style.top = ((window.innerHeight - text.clientHeight) / 2)
|
|
||||||
+ 'px';
|
|
||||||
break;
|
|
||||||
case 'bottom':
|
|
||||||
text.style.top = (window.innerHeight - text.clientHeight)
|
|
||||||
+ 'px';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
text.style.visibility = 'visible';
|
|
||||||
return text.clientHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_footer(footertext){
|
function show_footer(footertext){
|
||||||
document.getElementById('footer').innerHTML = footertext;
|
document.getElementById('footer').innerHTML = footertext;
|
||||||
}
|
}
|
||||||
|
|
||||||
function show_text(newtext){
|
function show_text(newtext){
|
||||||
|
var match = /-webkit-text-fill-color:[^;\"]+/gi;
|
||||||
if(timer != null)
|
if(timer != null)
|
||||||
clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
text_fade('lyricsmain', newtext);
|
text_fade('lyricsmain', newtext);
|
||||||
text_fade('lyricsoutline', newtext);
|
text_fade('lyricsoutline', newtext);
|
||||||
text_fade('lyricsshadow', newtext);
|
text_fade('lyricsshadow', newtext.replace(match, ""));
|
||||||
if(text_opacity()==1) return;
|
if(text_opacity()==1) return;
|
||||||
timer = setTimeout(function(){
|
timer = setTimeout(function(){
|
||||||
show_text(newtext);
|
show_text(newtext);
|
||||||
|
@ -302,31 +276,41 @@ body {
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<img id="bgimage" class="size" %s />
|
<img id="bgimage" class="size" %s />
|
||||||
<img id="image" class="size" style="display:none" />
|
<img id="image" class="size" %s />
|
||||||
<video id="video1" class="size" style="visibility:hidden" autobuffer preload>
|
<video id="video1" class="size" style="visibility:hidden" autobuffer preload>
|
||||||
</video>
|
</video>
|
||||||
<video id="video2" class="size" style="visibility:hidden" autobuffer preload>
|
<video id="video2" class="size" style="visibility:hidden" autobuffer preload>
|
||||||
</video>
|
</video>
|
||||||
%s
|
%s
|
||||||
|
%s
|
||||||
<div id="footer" class="footer"></div>
|
<div id="footer" class="footer"></div>
|
||||||
<div id="black" class="size"></div>
|
<div id="black" class="size"></div>
|
||||||
<div id="alert" style="visibility:hidden;"></div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def build_html(item, screen, alert, islive, background):
|
def build_html(item, screen, islive, background, image=None,
|
||||||
|
plugins=None):
|
||||||
"""
|
"""
|
||||||
Build the full web paged structure for display
|
Build the full web paged structure for display
|
||||||
|
|
||||||
`item`
|
``item``
|
||||||
Service Item to be displayed
|
Service Item to be displayed
|
||||||
`screen`
|
|
||||||
|
``screen``
|
||||||
Current display information
|
Current display information
|
||||||
`alert`
|
|
||||||
Alert display display information
|
``islive``
|
||||||
`islive`
|
|
||||||
Item is going live, rather than preview/theme building
|
Item is going live, rather than preview/theme building
|
||||||
|
|
||||||
|
``background``
|
||||||
|
Theme background image - bytes
|
||||||
|
|
||||||
|
``image``
|
||||||
|
Image media item - bytes
|
||||||
|
|
||||||
|
``plugins``
|
||||||
|
The List of available plugins
|
||||||
"""
|
"""
|
||||||
width = screen[u'size'].width()
|
width = screen[u'size'].width()
|
||||||
height = screen[u'size'].height()
|
height = screen[u'size'].height()
|
||||||
|
@ -334,19 +318,33 @@ def build_html(item, screen, alert, islive, background):
|
||||||
webkitvers = webkit_version()
|
webkitvers = webkit_version()
|
||||||
# Image generated and poked in
|
# Image generated and poked in
|
||||||
if background:
|
if background:
|
||||||
image = u'src="data:image/png;base64,%s"' % background
|
bgimage_src = u'src="data:image/png;base64,%s"' % background
|
||||||
elif item.bg_image_bytes:
|
elif item.bg_image_bytes:
|
||||||
image = u'src="data:image/png;base64,%s"' % item.bg_image_bytes
|
bgimage_src = u'src="data:image/png;base64,%s"' % item.bg_image_bytes
|
||||||
else:
|
else:
|
||||||
image = u'style="display:none;"'
|
bgimage_src = u'style="display:none;"'
|
||||||
|
if image:
|
||||||
|
image_src = u'src="data:image/png;base64,%s"' % image
|
||||||
|
else:
|
||||||
|
image_src = u'style="display:none;"'
|
||||||
|
css_additions = u''
|
||||||
|
js_additions = u''
|
||||||
|
html_additions = u''
|
||||||
|
if plugins:
|
||||||
|
for plugin in plugins:
|
||||||
|
css_additions += plugin.getDisplayCss()
|
||||||
|
js_additions += plugin.getDisplayJavaScript()
|
||||||
|
html_additions += plugin.getDisplayHtml()
|
||||||
html = HTMLSRC % (build_background_css(item, width, height),
|
html = HTMLSRC % (build_background_css(item, width, height),
|
||||||
width, height,
|
width, height,
|
||||||
build_alert_css(alert, width),
|
css_additions,
|
||||||
build_footer_css(item, height),
|
build_footer_css(item, height),
|
||||||
build_lyrics_css(item, webkitvers),
|
build_lyrics_css(item, webkitvers),
|
||||||
u'true' if theme and theme.display_slide_transition and islive \
|
u'true' if theme and theme.display_slide_transition and islive \
|
||||||
else u'false',
|
else u'false',
|
||||||
image,
|
js_additions,
|
||||||
|
bgimage_src, image_src,
|
||||||
|
html_additions,
|
||||||
build_lyrics_html(item, webkitvers))
|
build_lyrics_html(item, webkitvers))
|
||||||
return html
|
return html
|
||||||
|
|
||||||
|
@ -366,7 +364,7 @@ def build_background_css(item, width, height):
|
||||||
"""
|
"""
|
||||||
Build the background css
|
Build the background css
|
||||||
|
|
||||||
`item`
|
``item``
|
||||||
Service Item containing theme and location information
|
Service Item containing theme and location information
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -383,7 +381,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( \
|
||||||
|
@ -391,7 +389,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 \
|
||||||
|
@ -399,34 +397,35 @@ 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):
|
||||||
"""
|
"""
|
||||||
Build the lyrics display css
|
Build the lyrics display css
|
||||||
|
|
||||||
`item`
|
``item``
|
||||||
Service Item containing theme and location information
|
Service Item containing theme and location information
|
||||||
|
|
||||||
`webkitvers`
|
``webkitvers``
|
||||||
The version of qtwebkit we're using
|
The version of qtwebkit we're using
|
||||||
|
|
||||||
"""
|
"""
|
||||||
style = """
|
style = u"""
|
||||||
.lyricstable {
|
.lyricstable {
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -455,8 +454,7 @@ def build_lyrics_css(item, webkitvers):
|
||||||
outline = u''
|
outline = u''
|
||||||
shadow = u''
|
shadow = u''
|
||||||
if theme and item.main:
|
if theme and item.main:
|
||||||
lyricstable = u'left: %spx; top: %spx;' % \
|
lyricstable = u'left: %spx; top: %spx;' % (item.main.x(), item.main.y())
|
||||||
(item.main.x(), item.main.y())
|
|
||||||
lyrics = build_lyrics_format_css(theme, item.main.width(),
|
lyrics = build_lyrics_format_css(theme, item.main.width(),
|
||||||
item.main.height())
|
item.main.height())
|
||||||
# For performance reasons we want to show as few DIV's as possible,
|
# For performance reasons we want to show as few DIV's as possible,
|
||||||
|
@ -467,11 +465,11 @@ def build_lyrics_css(item, webkitvers):
|
||||||
# Before 533.3 the webkit-text-fill colour wasn't displayed, only the
|
# Before 533.3 the webkit-text-fill colour wasn't displayed, only the
|
||||||
# stroke (outline) color. So put stroke layer underneath the main text.
|
# stroke (outline) color. So put stroke layer underneath the main text.
|
||||||
#
|
#
|
||||||
# Before 534.4 the webkit-text-stroke was sometimes out of alignment
|
# Up to 534.3 the webkit-text-stroke was sometimes out of alignment
|
||||||
# with the fill, or normal text. letter-spacing=1 is workaround
|
# with the fill, or normal text. letter-spacing=1 is workaround
|
||||||
# https://bugs.webkit.org/show_bug.cgi?id=44403
|
# https://bugs.webkit.org/show_bug.cgi?id=44403
|
||||||
#
|
#
|
||||||
# Before 534.4 the text-shadow didn't get displayed when
|
# Up to 534.3 the text-shadow didn't get displayed when
|
||||||
# webkit-text-stroke was used. So use an offset text layer underneath.
|
# webkit-text-stroke was used. So use an offset text layer underneath.
|
||||||
# https://bugs.webkit.org/show_bug.cgi?id=19728
|
# https://bugs.webkit.org/show_bug.cgi?id=19728
|
||||||
if webkitvers >= 533.3:
|
if webkitvers >= 533.3:
|
||||||
|
@ -479,7 +477,7 @@ def build_lyrics_css(item, webkitvers):
|
||||||
else:
|
else:
|
||||||
outline = build_lyrics_outline_css(theme)
|
outline = build_lyrics_outline_css(theme)
|
||||||
if theme.font_main_shadow:
|
if theme.font_main_shadow:
|
||||||
if theme.font_main_outline and webkitvers < 534.3:
|
if theme.font_main_outline and webkitvers <= 534.3:
|
||||||
shadow = u'padding-left: %spx; padding-top: %spx;' % \
|
shadow = u'padding-left: %spx; padding-top: %spx;' % \
|
||||||
(int(theme.font_main_shadow_size) +
|
(int(theme.font_main_shadow_size) +
|
||||||
(int(theme.font_main_outline_size) * 2),
|
(int(theme.font_main_outline_size) * 2),
|
||||||
|
@ -497,10 +495,10 @@ def build_lyrics_outline_css(theme, is_shadow=False):
|
||||||
Build the css which controls the theme outline
|
Build the css which controls the theme outline
|
||||||
Also used by renderer for splitting verses
|
Also used by renderer for splitting verses
|
||||||
|
|
||||||
`theme`
|
``theme``
|
||||||
Object containing theme information
|
Object containing theme information
|
||||||
|
|
||||||
`is_shadow`
|
``is_shadow``
|
||||||
If true, use the shadow colors instead
|
If true, use the shadow colors instead
|
||||||
"""
|
"""
|
||||||
if theme.font_main_outline:
|
if theme.font_main_outline:
|
||||||
|
@ -521,13 +519,13 @@ def build_lyrics_format_css(theme, width, height):
|
||||||
Build the css which controls the theme format
|
Build the css which controls the theme format
|
||||||
Also used by renderer for splitting verses
|
Also used by renderer for splitting verses
|
||||||
|
|
||||||
`theme`
|
``theme``
|
||||||
Object containing theme information
|
Object containing theme information
|
||||||
|
|
||||||
`width`
|
``width``
|
||||||
Width of the lyrics block
|
Width of the lyrics block
|
||||||
|
|
||||||
`height`
|
``height``
|
||||||
Height of the lyrics block
|
Height of the lyrics block
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -537,15 +535,19 @@ 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:
|
||||||
if webkit_version() < 534.3:
|
if webkit_version() <= 534.3:
|
||||||
lyrics += u' letter-spacing: 1px;'
|
lyrics += u' letter-spacing: 1px;'
|
||||||
if theme.font_main_italics:
|
if theme.font_main_italics:
|
||||||
lyrics += u' font-style:italic; '
|
lyrics += u' font-style:italic; '
|
||||||
|
@ -557,10 +559,10 @@ def build_lyrics_html(item, webkitvers):
|
||||||
"""
|
"""
|
||||||
Build the HTML required to show the lyrics
|
Build the HTML required to show the lyrics
|
||||||
|
|
||||||
`item`
|
``item``
|
||||||
Service Item containing theme and location information
|
Service Item containing theme and location information
|
||||||
|
|
||||||
`webkitvers`
|
``webkitvers``
|
||||||
The version of qtwebkit we're using
|
The version of qtwebkit we're using
|
||||||
"""
|
"""
|
||||||
# Bugs in some versions of QtWebKit mean we sometimes need additional
|
# Bugs in some versions of QtWebKit mean we sometimes need additional
|
||||||
|
@ -569,7 +571,7 @@ def build_lyrics_html(item, webkitvers):
|
||||||
# display:table/display:table-cell are required for each lyric block.
|
# display:table/display:table-cell are required for each lyric block.
|
||||||
lyrics = u''
|
lyrics = u''
|
||||||
theme = item.themedata
|
theme = item.themedata
|
||||||
if webkitvers < 534.4 and theme and theme.font_main_outline:
|
if webkitvers <= 534.3 and theme and theme.font_main_outline:
|
||||||
lyrics += u'<div class="lyricstable">' \
|
lyrics += u'<div class="lyricstable">' \
|
||||||
u'<div id="lyricsshadow" style="opacity:1" ' \
|
u'<div id="lyricsshadow" style="opacity:1" ' \
|
||||||
u'class="lyricscell lyricsshadow"></div></div>'
|
u'class="lyricscell lyricsshadow"></div></div>'
|
||||||
|
@ -586,10 +588,10 @@ def build_footer_css(item, height):
|
||||||
"""
|
"""
|
||||||
Build the display of the item footer
|
Build the display of the item footer
|
||||||
|
|
||||||
`item`
|
``item``
|
||||||
Service Item to be processed.
|
Service Item to be processed.
|
||||||
"""
|
"""
|
||||||
style = """
|
style = u"""
|
||||||
left: %spx;
|
left: %spx;
|
||||||
bottom: %spx;
|
bottom: %spx;
|
||||||
width: %spx;
|
width: %spx;
|
||||||
|
@ -608,24 +610,3 @@ def build_footer_css(item, height):
|
||||||
theme.font_footer_size, theme.font_footer_color)
|
theme.font_footer_size, theme.font_footer_color)
|
||||||
return lyrics_html
|
return lyrics_html
|
||||||
|
|
||||||
def build_alert_css(alertTab, width):
|
|
||||||
"""
|
|
||||||
Build the display of the footer
|
|
||||||
|
|
||||||
`alertTab`
|
|
||||||
Details from the Alert tab for fonts etc
|
|
||||||
"""
|
|
||||||
style = """
|
|
||||||
width: %spx;
|
|
||||||
vertical-align: %s;
|
|
||||||
font-family: %s;
|
|
||||||
font-size: %spt;
|
|
||||||
color: %s;
|
|
||||||
background-color: %s;
|
|
||||||
"""
|
|
||||||
if not alertTab:
|
|
||||||
return u''
|
|
||||||
align = VerticalType.Names[alertTab.location]
|
|
||||||
alert = style % (width, align, alertTab.font_face, alertTab.font_size,
|
|
||||||
alertTab.font_color, alertTab.bg_color)
|
|
||||||
return alert
|
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -31,17 +32,19 @@ to wait for the conversion to happen.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
import Queue
|
||||||
|
|
||||||
from PyQt4 import QtCore
|
from PyQt4 import QtCore
|
||||||
|
|
||||||
from openlp.core.lib import resize_image, image_to_byte
|
from openlp.core.lib import resize_image, image_to_byte, Receiver
|
||||||
|
from openlp.core.ui import ScreenList
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
class ImageThread(QtCore.QThread):
|
class ImageThread(QtCore.QThread):
|
||||||
"""
|
"""
|
||||||
A special Qt thread class to speed up the display of text based frames.
|
A special Qt thread class to speed up the display of images. This is
|
||||||
This is threaded so it loads the frames in background
|
threaded so it loads the frames and generates byte stream in background.
|
||||||
"""
|
"""
|
||||||
def __init__(self, manager):
|
def __init__(self, manager):
|
||||||
QtCore.QThread.__init__(self, None)
|
QtCore.QThread.__init__(self, None)
|
||||||
|
@ -51,15 +54,89 @@ class ImageThread(QtCore.QThread):
|
||||||
"""
|
"""
|
||||||
Run the thread.
|
Run the thread.
|
||||||
"""
|
"""
|
||||||
self.imageManager.process()
|
self.imageManager._process()
|
||||||
|
|
||||||
|
|
||||||
|
class Priority(object):
|
||||||
|
"""
|
||||||
|
Enumeration class for different priorities.
|
||||||
|
|
||||||
|
``Lowest``
|
||||||
|
Only the image's byte stream has to be generated. But neither the
|
||||||
|
``QImage`` nor the byte stream has been requested yet.
|
||||||
|
|
||||||
|
``Low``
|
||||||
|
Only the image's byte stream has to be generated. Because the image's
|
||||||
|
``QImage`` has been requested previously it is reasonable to assume that
|
||||||
|
the byte stream will be needed before the byte stream of other images
|
||||||
|
whose ``QImage`` were not generated due to a request.
|
||||||
|
|
||||||
|
``Normal``
|
||||||
|
The image's byte stream as well as the image has to be generated.
|
||||||
|
Neither the ``QImage`` nor the byte stream has been requested yet.
|
||||||
|
|
||||||
|
``High``
|
||||||
|
The image's byte stream as well as the image has to be generated. The
|
||||||
|
``QImage`` for this image has been requested.
|
||||||
|
**Note**, this priority is only set when the ``QImage`` has not been
|
||||||
|
generated yet.
|
||||||
|
|
||||||
|
``Urgent``
|
||||||
|
The image's byte stream as well as the image has to be generated. The
|
||||||
|
byte stream for this image has been requested.
|
||||||
|
**Note**, this priority is only set when the byte stream has not been
|
||||||
|
generated yet.
|
||||||
|
"""
|
||||||
|
Lowest = 4
|
||||||
|
Low = 3
|
||||||
|
Normal = 2
|
||||||
|
High = 1
|
||||||
|
Urgent = 0
|
||||||
|
|
||||||
|
|
||||||
class Image(object):
|
class Image(object):
|
||||||
name = ''
|
"""
|
||||||
path = ''
|
This class represents an image. To mark an image as *dirty* set the instance
|
||||||
dirty = True
|
variables ``image`` and ``image_bytes`` to ``None`` and add the image object
|
||||||
image = None
|
to the queue of images to process.
|
||||||
image_bytes = None
|
"""
|
||||||
|
def __init__(self, name, path, source, background):
|
||||||
|
self.name = name
|
||||||
|
self.path = path
|
||||||
|
self.image = None
|
||||||
|
self.image_bytes = None
|
||||||
|
self.priority = Priority.Normal
|
||||||
|
self.source = source
|
||||||
|
self.background = background
|
||||||
|
|
||||||
|
|
||||||
|
class PriorityQueue(Queue.PriorityQueue):
|
||||||
|
"""
|
||||||
|
Customised ``Queue.PriorityQueue``.
|
||||||
|
"""
|
||||||
|
def modify_priority(self, image, new_priority):
|
||||||
|
"""
|
||||||
|
Modifies the priority of the given ``image``.
|
||||||
|
|
||||||
|
``image``
|
||||||
|
The image to remove. This should be an ``Image`` instance.
|
||||||
|
|
||||||
|
``new_priority``
|
||||||
|
The image's new priority.
|
||||||
|
"""
|
||||||
|
self.remove(image)
|
||||||
|
image.priority = new_priority
|
||||||
|
self.put((image.priority, image))
|
||||||
|
|
||||||
|
def remove(self, image):
|
||||||
|
"""
|
||||||
|
Removes the given ``image`` from the queue.
|
||||||
|
|
||||||
|
``image``
|
||||||
|
The image to remove. This should be an ``Image`` instance.
|
||||||
|
"""
|
||||||
|
if (image.priority, image) in self.queue:
|
||||||
|
self.queue.remove((image.priority, image))
|
||||||
|
|
||||||
|
|
||||||
class ImageManager(QtCore.QObject):
|
class ImageManager(QtCore.QObject):
|
||||||
|
@ -70,96 +147,158 @@ class ImageManager(QtCore.QObject):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
QtCore.QObject.__init__(self)
|
QtCore.QObject.__init__(self)
|
||||||
|
current_screen = ScreenList.get_instance().current
|
||||||
|
self.width = current_screen[u'size'].width()
|
||||||
|
self.height = current_screen[u'size'].height()
|
||||||
self._cache = {}
|
self._cache = {}
|
||||||
self._thread_running = False
|
self._imageThread = ImageThread(self)
|
||||||
self._cache_dirty = False
|
self._conversion_queue = PriorityQueue()
|
||||||
self.image_thread = ImageThread(self)
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
|
QtCore.SIGNAL(u'config_updated'), self.process_updates)
|
||||||
|
|
||||||
def update_display(self, width, height):
|
def update_display(self):
|
||||||
"""
|
"""
|
||||||
Screen has changed size so rebuild the cache to new size
|
Screen has changed size so rebuild the cache to new size.
|
||||||
"""
|
"""
|
||||||
log.debug(u'update_display')
|
log.debug(u'update_display')
|
||||||
self.width = width
|
current_screen = ScreenList.get_instance().current
|
||||||
self.height = height
|
self.width = current_screen[u'size'].width()
|
||||||
# mark the images as dirty for a rebuild
|
self.height = current_screen[u'size'].height()
|
||||||
for key in self._cache.keys():
|
# Mark the images as dirty for a rebuild by setting the image and byte
|
||||||
image = self._cache[key]
|
# stream to None.
|
||||||
image.dirty = True
|
for key, image in self._cache.iteritems():
|
||||||
image.image = resize_image(image.path, self.width, self.height)
|
self._reset_image(image)
|
||||||
self._cache_dirty = True
|
|
||||||
# only one thread please
|
def update_images(self, image_type, background):
|
||||||
if not self._thread_running:
|
"""
|
||||||
self.image_thread.start()
|
Border has changed so update all the images affected.
|
||||||
|
"""
|
||||||
|
log.debug(u'update_images')
|
||||||
|
# Mark the images as dirty for a rebuild by setting the image and byte
|
||||||
|
# stream to None.
|
||||||
|
for key, image in self._cache.iteritems():
|
||||||
|
if image.source == image_type:
|
||||||
|
image.background = background
|
||||||
|
self._reset_image(image)
|
||||||
|
|
||||||
|
def update_image(self, name, image_type, background):
|
||||||
|
"""
|
||||||
|
Border has changed so update the image affected.
|
||||||
|
"""
|
||||||
|
log.debug(u'update_images')
|
||||||
|
# Mark the images as dirty for a rebuild by setting the image and byte
|
||||||
|
# stream to None.
|
||||||
|
for key, image in self._cache.iteritems():
|
||||||
|
if image.source == image_type and image.name == name:
|
||||||
|
image.background = background
|
||||||
|
self._reset_image(image)
|
||||||
|
|
||||||
|
def _reset_image(self, image):
|
||||||
|
image.image = None
|
||||||
|
image.image_bytes = None
|
||||||
|
self._conversion_queue.modify_priority(image, Priority.Normal)
|
||||||
|
|
||||||
|
def process_updates(self):
|
||||||
|
"""
|
||||||
|
Flush the queue to updated any data to update
|
||||||
|
"""
|
||||||
|
# We want only one thread.
|
||||||
|
if not self._imageThread.isRunning():
|
||||||
|
self._imageThread.start()
|
||||||
|
|
||||||
def get_image(self, name):
|
def get_image(self, name):
|
||||||
"""
|
"""
|
||||||
Return the Qimage from the cache
|
Return the ``QImage`` from the cache. If not present wait for the
|
||||||
|
background thread to process it.
|
||||||
"""
|
"""
|
||||||
log.debug(u'get_image %s' % name)
|
log.debug(u'get_image %s' % name)
|
||||||
return self._cache[name].image
|
image = self._cache[name]
|
||||||
|
if image.image is None:
|
||||||
|
self._conversion_queue.modify_priority(image, Priority.High)
|
||||||
|
# make sure we are running and if not give it a kick
|
||||||
|
self.process_updates()
|
||||||
|
while image.image is None:
|
||||||
|
log.debug(u'get_image - waiting')
|
||||||
|
time.sleep(0.1)
|
||||||
|
elif image.image_bytes is None:
|
||||||
|
# Set the priority to Low, because the image was requested but the
|
||||||
|
# byte stream was not generated yet. However, we only need to do
|
||||||
|
# this, when the image was generated before it was requested
|
||||||
|
# (otherwise this is already taken care of).
|
||||||
|
self._conversion_queue.modify_priority(image, Priority.Low)
|
||||||
|
return image.image
|
||||||
|
|
||||||
def get_image_bytes(self, name):
|
def get_image_bytes(self, name):
|
||||||
"""
|
"""
|
||||||
Returns the byte string for an image
|
Returns the byte string for an image. If not present wait for the
|
||||||
If not present wait for the background thread to process it.
|
background thread to process it.
|
||||||
"""
|
"""
|
||||||
log.debug(u'get_image_bytes %s' % name)
|
log.debug(u'get_image_bytes %s' % name)
|
||||||
if not self._cache[name].image_bytes:
|
image = self._cache[name]
|
||||||
while self._cache[name].dirty:
|
if image.image_bytes is None:
|
||||||
|
self._conversion_queue.modify_priority(image, Priority.Urgent)
|
||||||
|
# make sure we are running and if not give it a kick
|
||||||
|
self.process_updates()
|
||||||
|
while image.image_bytes is None:
|
||||||
log.debug(u'get_image_bytes - waiting')
|
log.debug(u'get_image_bytes - waiting')
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
return self._cache[name].image_bytes
|
return image.image_bytes
|
||||||
|
|
||||||
def del_image(self, name):
|
def del_image(self, name):
|
||||||
"""
|
"""
|
||||||
Delete the Image from the Cache
|
Delete the Image from the cache.
|
||||||
"""
|
"""
|
||||||
log.debug(u'del_image %s' % name)
|
log.debug(u'del_image %s' % name)
|
||||||
if name in self._cache:
|
if name in self._cache:
|
||||||
|
self._conversion_queue.remove(self._cache[name])
|
||||||
del self._cache[name]
|
del self._cache[name]
|
||||||
|
|
||||||
def add_image(self, name, path):
|
def add_image(self, name, path, source, background):
|
||||||
"""
|
"""
|
||||||
Add image to cache if it is not already there
|
Add image to cache if it is not already there.
|
||||||
"""
|
"""
|
||||||
log.debug(u'add_image %s:%s' % (name, path))
|
log.debug(u'add_image %s:%s' % (name, path))
|
||||||
if not name in self._cache:
|
if not name in self._cache:
|
||||||
image = Image()
|
image = Image(name, path, source, background)
|
||||||
image.name = name
|
|
||||||
image.path = path
|
|
||||||
image.image = resize_image(path, self.width, self.height)
|
|
||||||
self._cache[name] = image
|
self._cache[name] = image
|
||||||
|
self._conversion_queue.put((image.priority, image))
|
||||||
else:
|
else:
|
||||||
log.debug(u'Image in cache %s:%s' % (name, path))
|
log.debug(u'Image in cache %s:%s' % (name, path))
|
||||||
self._cache_dirty = True
|
# We want only one thread.
|
||||||
# only one thread please
|
if not self._imageThread.isRunning():
|
||||||
if not self._thread_running:
|
self._imageThread.start()
|
||||||
self.image_thread.start()
|
|
||||||
|
|
||||||
def process(self):
|
def _process(self):
|
||||||
"""
|
"""
|
||||||
Controls the processing called from a QThread
|
Controls the processing called from a ``QtCore.QThread``.
|
||||||
"""
|
"""
|
||||||
log.debug(u'process - started')
|
log.debug(u'_process - started')
|
||||||
self._thread_running = True
|
while not self._conversion_queue.empty():
|
||||||
self.clean_cache()
|
self._process_cache()
|
||||||
# data loaded since we started ?
|
log.debug(u'_process - ended')
|
||||||
while self._cache_dirty:
|
|
||||||
log.debug(u'process - recycle')
|
|
||||||
self.clean_cache()
|
|
||||||
self._thread_running = False
|
|
||||||
log.debug(u'process - ended')
|
|
||||||
|
|
||||||
def clean_cache(self):
|
def _process_cache(self):
|
||||||
"""
|
"""
|
||||||
Actually does the work.
|
Actually does the work.
|
||||||
"""
|
"""
|
||||||
log.debug(u'clean_cache')
|
log.debug(u'_process_cache')
|
||||||
# we will clean the cache now
|
image = self._conversion_queue.get()[1]
|
||||||
self._cache_dirty = False
|
# Generate the QImage for the image.
|
||||||
for key in self._cache.keys():
|
if image.image is None:
|
||||||
image = self._cache[key]
|
image.image = resize_image(image.path, self.width, self.height,
|
||||||
if image.dirty:
|
image.background)
|
||||||
|
# Set the priority to Lowest and stop here as we need to process
|
||||||
|
# more important images first.
|
||||||
|
if image.priority == Priority.Normal:
|
||||||
|
self._conversion_queue.modify_priority(image, Priority.Lowest)
|
||||||
|
return
|
||||||
|
# For image with high priority we set the priority to Low, as the
|
||||||
|
# byte stream might be needed earlier the byte stream of image with
|
||||||
|
# Normal priority. We stop here as we need to process more important
|
||||||
|
# images first.
|
||||||
|
elif image.priority == Priority.High:
|
||||||
|
self._conversion_queue.modify_priority(image, Priority.Low)
|
||||||
|
return
|
||||||
|
# Generate the byte stream for the image.
|
||||||
|
if image.image_bytes is None:
|
||||||
image.image_bytes = image_to_byte(image.image)
|
image.image_bytes = image_to_byte(image.image)
|
||||||
image.dirty = False
|
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -26,8 +27,12 @@
|
||||||
"""
|
"""
|
||||||
Extend QListWidget to handle drag and drop functionality
|
Extend QListWidget to handle drag and drop functionality
|
||||||
"""
|
"""
|
||||||
|
import os.path
|
||||||
|
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
|
from openlp.core.lib import Receiver
|
||||||
|
|
||||||
class ListWidgetWithDnD(QtGui.QListWidget):
|
class ListWidgetWithDnD(QtGui.QListWidget):
|
||||||
"""
|
"""
|
||||||
Provide a list widget to store objects and handle drag and drop events
|
Provide a list widget to store objects and handle drag and drop events
|
||||||
|
@ -40,6 +45,16 @@ class ListWidgetWithDnD(QtGui.QListWidget):
|
||||||
self.mimeDataText = name
|
self.mimeDataText = name
|
||||||
assert(self.mimeDataText)
|
assert(self.mimeDataText)
|
||||||
|
|
||||||
|
def activateDnD(self):
|
||||||
|
"""
|
||||||
|
Activate DnD of widget
|
||||||
|
"""
|
||||||
|
self.setAcceptDrops(True)
|
||||||
|
self.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
|
||||||
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
|
QtCore.SIGNAL(u'%s_dnd' % self.mimeDataText),
|
||||||
|
self.parent().loadFile)
|
||||||
|
|
||||||
def mouseMoveEvent(self, event):
|
def mouseMoveEvent(self, event):
|
||||||
"""
|
"""
|
||||||
Drag and drop event does not care what data is selected
|
Drag and drop event does not care what data is selected
|
||||||
|
@ -49,8 +64,47 @@ class ListWidgetWithDnD(QtGui.QListWidget):
|
||||||
if event.buttons() != QtCore.Qt.LeftButton:
|
if event.buttons() != QtCore.Qt.LeftButton:
|
||||||
event.ignore()
|
event.ignore()
|
||||||
return
|
return
|
||||||
|
if not self.selectedItems():
|
||||||
|
event.ignore()
|
||||||
|
return
|
||||||
drag = QtGui.QDrag(self)
|
drag = QtGui.QDrag(self)
|
||||||
mimeData = QtCore.QMimeData()
|
mimeData = QtCore.QMimeData()
|
||||||
drag.setMimeData(mimeData)
|
drag.setMimeData(mimeData)
|
||||||
mimeData.setText(self.mimeDataText)
|
mimeData.setText(self.mimeDataText)
|
||||||
drag.start(QtCore.Qt.CopyAction)
|
drag.start(QtCore.Qt.CopyAction)
|
||||||
|
|
||||||
|
def dragEnterEvent(self, event):
|
||||||
|
if event.mimeData().hasUrls():
|
||||||
|
event.accept()
|
||||||
|
else:
|
||||||
|
event.ignore()
|
||||||
|
|
||||||
|
def dragMoveEvent(self, event):
|
||||||
|
if event.mimeData().hasUrls():
|
||||||
|
event.setDropAction(QtCore.Qt.CopyAction)
|
||||||
|
event.accept()
|
||||||
|
else:
|
||||||
|
event.ignore()
|
||||||
|
|
||||||
|
def dropEvent(self, event):
|
||||||
|
"""
|
||||||
|
Receive drop event check if it is a file and process it if it is.
|
||||||
|
|
||||||
|
``event``
|
||||||
|
Handle of the event pint passed
|
||||||
|
"""
|
||||||
|
if event.mimeData().hasUrls():
|
||||||
|
event.setDropAction(QtCore.Qt.CopyAction)
|
||||||
|
event.accept()
|
||||||
|
files = []
|
||||||
|
for url in event.mimeData().urls():
|
||||||
|
localFile = unicode(url.toLocalFile())
|
||||||
|
if os.path.isfile(localFile):
|
||||||
|
files.append(localFile)
|
||||||
|
elif os.path.isdir(localFile):
|
||||||
|
listing = os.listdir(localFile)
|
||||||
|
for file in listing:
|
||||||
|
files.append(os.path.join(localFile,file))
|
||||||
|
Receiver.send_message(u'%s_dnd' % self.mimeDataText,files)
|
||||||
|
else:
|
||||||
|
event.ignore()
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -28,13 +29,14 @@ Provides the generic functions for interfacing plugins with the Media Manager.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.lib import context_menu_action, context_menu_separator, \
|
from openlp.core.lib import SettingsManager, OpenLPToolbar, ServiceItem, \
|
||||||
SettingsManager, OpenLPToolbar, ServiceItem, StringContent, build_icon, \
|
StringContent, build_icon, translate, Receiver, ListWidgetWithDnD
|
||||||
translate, Receiver, ListWidgetWithDnD
|
from openlp.core.lib.ui import UiStrings, context_menu_action, \
|
||||||
from openlp.core.lib.ui import UiStrings
|
context_menu_separator, critical_error_message_box
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -89,26 +91,29 @@ class MediaManagerItem(QtGui.QWidget):
|
||||||
Constructor to create the media manager item.
|
Constructor to create the media manager item.
|
||||||
"""
|
"""
|
||||||
QtGui.QWidget.__init__(self)
|
QtGui.QWidget.__init__(self)
|
||||||
self.parent = parent
|
self.hide()
|
||||||
#TODO: plugin should not be the parent in future
|
self.whitespace = re.compile(r'[\W_]+', re.UNICODE)
|
||||||
self.plugin = parent # plugin
|
self.plugin = plugin
|
||||||
visible_title = self.plugin.getString(StringContent.VisibleName)
|
visible_title = self.plugin.getString(StringContent.VisibleName)
|
||||||
self.title = unicode(visible_title[u'title'])
|
self.title = unicode(visible_title[u'title'])
|
||||||
self.settingsSection = self.plugin.name.lower()
|
self.settingsSection = self.plugin.name
|
||||||
self.icon = None
|
self.icon = None
|
||||||
if icon:
|
if icon:
|
||||||
self.icon = build_icon(icon)
|
self.icon = build_icon(icon)
|
||||||
self.toolbar = None
|
self.toolbar = None
|
||||||
self.remoteTriggered = None
|
self.remoteTriggered = None
|
||||||
self.singleServiceItem = True
|
self.singleServiceItem = True
|
||||||
|
self.quickPreviewAllowed = False
|
||||||
|
self.hasSearch = False
|
||||||
self.pageLayout = QtGui.QVBoxLayout(self)
|
self.pageLayout = QtGui.QVBoxLayout(self)
|
||||||
self.pageLayout.setSpacing(0)
|
self.pageLayout.setSpacing(0)
|
||||||
self.pageLayout.setMargin(0)
|
self.pageLayout.setMargin(0)
|
||||||
self.requiredIcons()
|
self.requiredIcons()
|
||||||
self.setupUi()
|
self.setupUi()
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
|
self.autoSelectId = -1
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'%s_service_load' % self.parent.name.lower()),
|
QtCore.SIGNAL(u'%s_service_load' % self.plugin.name),
|
||||||
self.serviceLoad)
|
self.serviceLoad)
|
||||||
|
|
||||||
def requiredIcons(self):
|
def requiredIcons(self):
|
||||||
|
@ -243,56 +248,65 @@ class MediaManagerItem(QtGui.QWidget):
|
||||||
"""
|
"""
|
||||||
# Add the List widget
|
# Add the List widget
|
||||||
self.listView = ListWidgetWithDnD(self, self.plugin.name)
|
self.listView = ListWidgetWithDnD(self, self.plugin.name)
|
||||||
self.listView.uniformItemSizes = True
|
|
||||||
self.listView.setSpacing(1)
|
self.listView.setSpacing(1)
|
||||||
self.listView.setSelectionMode(
|
self.listView.setSelectionMode(
|
||||||
QtGui.QAbstractItemView.ExtendedSelection)
|
QtGui.QAbstractItemView.ExtendedSelection)
|
||||||
self.listView.setAlternatingRowColors(True)
|
self.listView.setAlternatingRowColors(True)
|
||||||
self.listView.setDragEnabled(True)
|
|
||||||
self.listView.setObjectName(u'%sListView' % self.plugin.name)
|
self.listView.setObjectName(u'%sListView' % self.plugin.name)
|
||||||
# Add to pageLayout
|
# Add to pageLayout
|
||||||
self.pageLayout.addWidget(self.listView)
|
self.pageLayout.addWidget(self.listView)
|
||||||
# define and add the context menu
|
# define and add the context menu
|
||||||
self.listView.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
|
self.listView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||||
if self.hasEditIcon:
|
if self.hasEditIcon:
|
||||||
self.listView.addAction(
|
|
||||||
context_menu_action(
|
context_menu_action(
|
||||||
self.listView, u':/general/general_edit.png',
|
self.listView, u':/general/general_edit.png',
|
||||||
self.plugin.getString(StringContent.Edit)[u'title'],
|
self.plugin.getString(StringContent.Edit)[u'title'],
|
||||||
self.onEditClick))
|
self.onEditClick)
|
||||||
self.listView.addAction(context_menu_separator(self.listView))
|
context_menu_separator(self.listView)
|
||||||
if self.hasDeleteIcon:
|
if self.hasDeleteIcon:
|
||||||
self.listView.addAction(
|
|
||||||
context_menu_action(
|
context_menu_action(
|
||||||
self.listView, u':/general/general_delete.png',
|
self.listView, u':/general/general_delete.png',
|
||||||
self.plugin.getString(StringContent.Delete)[u'title'],
|
self.plugin.getString(StringContent.Delete)[u'title'],
|
||||||
self.onDeleteClick))
|
self.onDeleteClick, [QtCore.Qt.Key_Delete])
|
||||||
self.listView.addAction(context_menu_separator(self.listView))
|
context_menu_separator(self.listView)
|
||||||
self.listView.addAction(
|
|
||||||
context_menu_action(
|
context_menu_action(
|
||||||
self.listView, u':/general/general_preview.png',
|
self.listView, u':/general/general_preview.png',
|
||||||
self.plugin.getString(StringContent.Preview)[u'title'],
|
self.plugin.getString(StringContent.Preview)[u'title'],
|
||||||
self.onPreviewClick))
|
self.onPreviewClick, [QtCore.Qt.Key_Enter, QtCore.Qt.Key_Return])
|
||||||
self.listView.addAction(
|
|
||||||
context_menu_action(
|
context_menu_action(
|
||||||
self.listView, u':/general/general_live.png',
|
self.listView, u':/general/general_live.png',
|
||||||
self.plugin.getString(StringContent.Live)[u'title'],
|
self.plugin.getString(StringContent.Live)[u'title'],
|
||||||
self.onLiveClick))
|
self.onLiveClick, [QtCore.Qt.ShiftModifier + QtCore.Qt.Key_Enter,
|
||||||
self.listView.addAction(
|
QtCore.Qt.ShiftModifier + QtCore.Qt.Key_Return])
|
||||||
context_menu_action(
|
context_menu_action(
|
||||||
self.listView, u':/general/general_add.png',
|
self.listView, u':/general/general_add.png',
|
||||||
self.plugin.getString(StringContent.Service)[u'title'],
|
self.plugin.getString(StringContent.Service)[u'title'],
|
||||||
self.onAddClick))
|
self.onAddClick, [QtCore.Qt.Key_Plus, QtCore.Qt.Key_Equal])
|
||||||
if self.addToServiceItem:
|
if self.addToServiceItem:
|
||||||
self.listView.addAction(
|
|
||||||
context_menu_action(
|
context_menu_action(
|
||||||
self.listView, u':/general/general_add.png',
|
self.listView, u':/general/general_add.png',
|
||||||
translate('OpenLP.MediaManagerItem',
|
translate('OpenLP.MediaManagerItem',
|
||||||
'&Add to selected Service Item'),
|
'&Add to selected Service Item'), self.onAddEditClick)
|
||||||
self.onAddEditClick))
|
self.addCustomContextActions()
|
||||||
|
# Create the context menu and add all actions from the listView.
|
||||||
|
self.menu = QtGui.QMenu()
|
||||||
|
self.menu.addActions(self.listView.actions())
|
||||||
QtCore.QObject.connect(self.listView,
|
QtCore.QObject.connect(self.listView,
|
||||||
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
|
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
|
||||||
self.onClickPressed)
|
self.onClickPressed)
|
||||||
|
QtCore.QObject.connect(self.listView,
|
||||||
|
QtCore.SIGNAL(u'itemSelectionChanged()'),
|
||||||
|
self.onSelectionChange)
|
||||||
|
QtCore.QObject.connect(self.listView,
|
||||||
|
QtCore.SIGNAL('customContextMenuRequested(QPoint)'),
|
||||||
|
self.contextMenu)
|
||||||
|
|
||||||
|
def addCustomContextActions(self):
|
||||||
|
"""
|
||||||
|
Implement this method in your descendent media manager item to
|
||||||
|
add any context menu items. This method is called automatically.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
def initialise(self):
|
def initialise(self):
|
||||||
"""
|
"""
|
||||||
|
@ -324,12 +338,79 @@ class MediaManagerItem(QtGui.QWidget):
|
||||||
log.info(u'New files(s) %s', unicode(files))
|
log.info(u'New files(s) %s', unicode(files))
|
||||||
if files:
|
if files:
|
||||||
Receiver.send_message(u'cursor_busy')
|
Receiver.send_message(u'cursor_busy')
|
||||||
self.loadList(files)
|
self.validateAndLoad(files)
|
||||||
|
Receiver.send_message(u'cursor_normal')
|
||||||
|
|
||||||
|
def loadFile(self, files):
|
||||||
|
"""
|
||||||
|
Turn file from Drag and Drop into an array so the Validate code
|
||||||
|
can run it.
|
||||||
|
|
||||||
|
``files``
|
||||||
|
The list of files to be loaded
|
||||||
|
"""
|
||||||
|
newFiles = []
|
||||||
|
errorShown = False
|
||||||
|
for file in files:
|
||||||
|
type = file.split(u'.')[-1]
|
||||||
|
if type.lower() not in self.onNewFileMasks:
|
||||||
|
if not errorShown:
|
||||||
|
critical_error_message_box(
|
||||||
|
translate('OpenLP.MediaManagerItem',
|
||||||
|
'Invalid File Type'),
|
||||||
|
unicode(translate('OpenLP.MediaManagerItem',
|
||||||
|
'Invalid File %s.\nSuffix not supported'))
|
||||||
|
% file)
|
||||||
|
errorShown = True
|
||||||
|
else:
|
||||||
|
newFiles.append(file)
|
||||||
|
if file:
|
||||||
|
self.validateAndLoad(newFiles)
|
||||||
|
|
||||||
|
def validateAndLoad(self, files):
|
||||||
|
"""
|
||||||
|
Process a list for files either from the File Dialog or from Drag and
|
||||||
|
Drop
|
||||||
|
|
||||||
|
``files``
|
||||||
|
The files to be loaded
|
||||||
|
"""
|
||||||
|
names = []
|
||||||
|
fullList = []
|
||||||
|
for count in range(0, self.listView.count()):
|
||||||
|
names.append(unicode(self.listView.item(count).text()))
|
||||||
|
fullList.append(unicode(self.listView.item(count).
|
||||||
|
data(QtCore.Qt.UserRole).toString()))
|
||||||
|
duplicatesFound = False
|
||||||
|
filesAdded = False
|
||||||
|
for file in files:
|
||||||
|
filename = os.path.split(unicode(file))[1]
|
||||||
|
if filename in names:
|
||||||
|
duplicatesFound = True
|
||||||
|
else:
|
||||||
|
filesAdded = True
|
||||||
|
fullList.append(file)
|
||||||
|
if fullList and filesAdded:
|
||||||
|
self.listView.clear()
|
||||||
|
self.loadList(fullList)
|
||||||
lastDir = os.path.split(unicode(files[0]))[0]
|
lastDir = os.path.split(unicode(files[0]))[0]
|
||||||
SettingsManager.set_last_dir(self.settingsSection, lastDir)
|
SettingsManager.set_last_dir(self.settingsSection, lastDir)
|
||||||
SettingsManager.set_list(self.settingsSection,
|
SettingsManager.set_list(self.settingsSection,
|
||||||
self.settingsSection, self.getFileList())
|
self.settingsSection, self.getFileList())
|
||||||
Receiver.send_message(u'cursor_normal')
|
if duplicatesFound:
|
||||||
|
critical_error_message_box(
|
||||||
|
UiStrings().Duplicate,
|
||||||
|
unicode(translate('OpenLP.MediaManagerItem',
|
||||||
|
'Duplicate files were found on import and were ignored.')))
|
||||||
|
|
||||||
|
def contextMenu(self, point):
|
||||||
|
item = self.listView.itemAt(point)
|
||||||
|
# Decide if we have to show the context menu or not.
|
||||||
|
if item is None:
|
||||||
|
return
|
||||||
|
if not item.flags() & QtCore.Qt.ItemIsSelectable:
|
||||||
|
return
|
||||||
|
self.menu.exec_(self.listView.mapToGlobal(point))
|
||||||
|
|
||||||
def getFileList(self):
|
def getFileList(self):
|
||||||
"""
|
"""
|
||||||
|
@ -344,39 +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(image):
|
|
||||||
return False
|
|
||||||
if os.path.exists(thumb):
|
|
||||||
imageDate = os.stat(image).st_mtime
|
|
||||||
thumbDate = os.stat(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, thumb):
|
|
||||||
"""
|
|
||||||
Create a thumbnail icon from a given image.
|
|
||||||
|
|
||||||
``image``
|
|
||||||
The image file to create the icon from.
|
|
||||||
|
|
||||||
``thumb``
|
|
||||||
The filename to save the thumbnail to
|
|
||||||
"""
|
|
||||||
icon = build_icon(unicode(image))
|
|
||||||
pixmap = icon.pixmap(QtCore.QSize(88, 50))
|
|
||||||
ext = os.path.splitext(thumb)[1].lower()
|
|
||||||
pixmap.save(thumb, ext[1:])
|
|
||||||
return icon
|
|
||||||
|
|
||||||
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')
|
||||||
|
@ -397,7 +445,15 @@ class MediaManagerItem(QtGui.QWidget):
|
||||||
raise NotImplementedError(u'MediaManagerItem.onDeleteClick needs to '
|
raise NotImplementedError(u'MediaManagerItem.onDeleteClick needs to '
|
||||||
u'be defined by the plugin')
|
u'be defined by the plugin')
|
||||||
|
|
||||||
def generateSlideData(self, serviceItem, item=None, xmlVersion=False):
|
def onFocus(self):
|
||||||
|
"""
|
||||||
|
Run when a tab in the media manager gains focus. This gives the media
|
||||||
|
item a chance to focus any elements it wants to.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def generateSlideData(self, serviceItem, item=None, xmlVersion=False,
|
||||||
|
remote=False):
|
||||||
raise NotImplementedError(u'MediaManagerItem.generateSlideData needs '
|
raise NotImplementedError(u'MediaManagerItem.generateSlideData needs '
|
||||||
u'to be defined by the plugin')
|
u'to be defined by the plugin')
|
||||||
|
|
||||||
|
@ -411,13 +467,23 @@ class MediaManagerItem(QtGui.QWidget):
|
||||||
else:
|
else:
|
||||||
self.onPreviewClick()
|
self.onPreviewClick()
|
||||||
|
|
||||||
def onPreviewClick(self):
|
def onSelectionChange(self):
|
||||||
|
"""
|
||||||
|
Allows the change of current item in the list to be actioned
|
||||||
|
"""
|
||||||
|
if QtCore.QSettings().value(u'advanced/single click preview',
|
||||||
|
QtCore.QVariant(False)).toBool() and self.quickPreviewAllowed \
|
||||||
|
and self.listView.selectedIndexes() \
|
||||||
|
and self.autoSelectId == -1:
|
||||||
|
self.onPreviewClick(True)
|
||||||
|
|
||||||
|
def onPreviewClick(self, keepFocus=False):
|
||||||
"""
|
"""
|
||||||
Preview an item by building a service item then adding that service
|
Preview an item by building a service item then adding that service
|
||||||
item to the preview slide controller.
|
item to the preview slide controller.
|
||||||
"""
|
"""
|
||||||
if not self.listView.selectedIndexes() and not self.remoteTriggered:
|
if not self.listView.selectedIndexes() and not self.remoteTriggered:
|
||||||
QtGui.QMessageBox.information(self, UiStrings.NISp,
|
QtGui.QMessageBox.information(self, UiStrings().NISp,
|
||||||
translate('OpenLP.MediaManagerItem',
|
translate('OpenLP.MediaManagerItem',
|
||||||
'You must select one or more items to preview.'))
|
'You must select one or more items to preview.'))
|
||||||
else:
|
else:
|
||||||
|
@ -425,7 +491,9 @@ class MediaManagerItem(QtGui.QWidget):
|
||||||
serviceItem = self.buildServiceItem()
|
serviceItem = self.buildServiceItem()
|
||||||
if serviceItem:
|
if serviceItem:
|
||||||
serviceItem.from_plugin = True
|
serviceItem.from_plugin = True
|
||||||
self.parent.previewController.addServiceItem(serviceItem)
|
self.plugin.previewController.addServiceItem(serviceItem)
|
||||||
|
if keepFocus:
|
||||||
|
self.listView.setFocus()
|
||||||
|
|
||||||
def onLiveClick(self):
|
def onLiveClick(self):
|
||||||
"""
|
"""
|
||||||
|
@ -433,60 +501,72 @@ class MediaManagerItem(QtGui.QWidget):
|
||||||
item to the live slide controller.
|
item to the live slide controller.
|
||||||
"""
|
"""
|
||||||
if not self.listView.selectedIndexes():
|
if not self.listView.selectedIndexes():
|
||||||
QtGui.QMessageBox.information(self, UiStrings.NISp,
|
QtGui.QMessageBox.information(self, UiStrings().NISp,
|
||||||
translate('OpenLP.MediaManagerItem',
|
translate('OpenLP.MediaManagerItem',
|
||||||
'You must select one or more items to send live.'))
|
'You must select one or more items to send live.'))
|
||||||
else:
|
else:
|
||||||
|
self.goLive()
|
||||||
|
|
||||||
|
def goLive(self, item_id=None, remote=False):
|
||||||
log.debug(u'%s Live requested', self.plugin.name)
|
log.debug(u'%s Live requested', self.plugin.name)
|
||||||
serviceItem = self.buildServiceItem()
|
item = None
|
||||||
|
if item_id:
|
||||||
|
item = self.createItemFromId(item_id)
|
||||||
|
serviceItem = self.buildServiceItem(item, remote=remote)
|
||||||
if serviceItem:
|
if serviceItem:
|
||||||
|
if not item_id:
|
||||||
serviceItem.from_plugin = True
|
serviceItem.from_plugin = True
|
||||||
self.parent.liveController.addServiceItem(serviceItem)
|
self.plugin.liveController.addServiceItem(serviceItem)
|
||||||
|
|
||||||
|
def createItemFromId(self, item_id):
|
||||||
|
item = QtGui.QListWidgetItem()
|
||||||
|
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(item_id))
|
||||||
|
return item
|
||||||
|
|
||||||
def onAddClick(self):
|
def onAddClick(self):
|
||||||
"""
|
"""
|
||||||
Add a selected item to the current service
|
Add a selected item to the current service
|
||||||
"""
|
"""
|
||||||
if not self.listView.selectedIndexes() and not self.remoteTriggered:
|
if not self.listView.selectedIndexes() and not self.remoteTriggered:
|
||||||
QtGui.QMessageBox.information(self, UiStrings.NISp,
|
QtGui.QMessageBox.information(self, UiStrings().NISp,
|
||||||
translate('OpenLP.MediaManagerItem',
|
translate('OpenLP.MediaManagerItem',
|
||||||
'You must select one or more items.'))
|
'You must select one or more items to add.'))
|
||||||
else:
|
else:
|
||||||
# Is it posssible to process multiple list items to generate
|
# Is it posssible to process multiple list items to generate
|
||||||
# multiple service items?
|
# multiple service items?
|
||||||
if self.singleServiceItem or self.remoteTriggered:
|
if self.singleServiceItem or self.remoteTriggered:
|
||||||
log.debug(u'%s Add requested', self.plugin.name)
|
log.debug(u'%s Add requested', self.plugin.name)
|
||||||
serviceItem = self.buildServiceItem(None, True)
|
self.addToService(replace=self.remoteTriggered)
|
||||||
if serviceItem:
|
|
||||||
serviceItem.from_plugin = False
|
|
||||||
self.parent.serviceManager.addServiceItem(serviceItem,
|
|
||||||
replace=self.remoteTriggered)
|
|
||||||
else:
|
else:
|
||||||
items = self.listView.selectedIndexes()
|
items = self.listView.selectedIndexes()
|
||||||
for item in items:
|
for item in items:
|
||||||
serviceItem = self.buildServiceItem(item, True)
|
self.addToService(item)
|
||||||
|
|
||||||
|
def addToService(self, item=None, replace=None, remote=False):
|
||||||
|
serviceItem = self.buildServiceItem(item, True, remote=remote)
|
||||||
if serviceItem:
|
if serviceItem:
|
||||||
serviceItem.from_plugin = False
|
serviceItem.from_plugin = False
|
||||||
self.parent.serviceManager.addServiceItem(serviceItem)
|
self.plugin.serviceManager.addServiceItem(serviceItem,
|
||||||
|
replace=replace)
|
||||||
|
|
||||||
def onAddEditClick(self):
|
def onAddEditClick(self):
|
||||||
"""
|
"""
|
||||||
Add a selected item to an existing item in the current service.
|
Add a selected item to an existing item in the current service.
|
||||||
"""
|
"""
|
||||||
if not self.listView.selectedIndexes() and not self.remoteTriggered:
|
if not self.listView.selectedIndexes() and not self.remoteTriggered:
|
||||||
QtGui.QMessageBox.information(self, UiStrings.NISp,
|
QtGui.QMessageBox.information(self, UiStrings().NISp,
|
||||||
translate('OpenLP.MediaManagerItem',
|
translate('OpenLP.MediaManagerItem',
|
||||||
'You must select one or more items.'))
|
'You must select one or more items.'))
|
||||||
else:
|
else:
|
||||||
log.debug(u'%s Add requested', self.plugin.name)
|
log.debug(u'%s Add requested', self.plugin.name)
|
||||||
serviceItem = self.parent.serviceManager.getServiceItem()
|
serviceItem = self.plugin.serviceManager.getServiceItem()
|
||||||
if not serviceItem:
|
if not serviceItem:
|
||||||
QtGui.QMessageBox.information(self, UiStrings.NISs,
|
QtGui.QMessageBox.information(self, UiStrings().NISs,
|
||||||
translate('OpenLP.MediaManagerItem',
|
translate('OpenLP.MediaManagerItem',
|
||||||
'You must select an existing service item to add to.'))
|
'You must select an existing service item to add to.'))
|
||||||
elif self.plugin.name.lower() == serviceItem.name.lower():
|
elif self.plugin.name == serviceItem.name:
|
||||||
self.generateSlideData(serviceItem)
|
self.generateSlideData(serviceItem)
|
||||||
self.parent.serviceManager.addServiceItem(serviceItem,
|
self.plugin.serviceManager.addServiceItem(serviceItem,
|
||||||
replace=True)
|
replace=True)
|
||||||
else:
|
else:
|
||||||
# Turn off the remote edit update message indicator
|
# Turn off the remote edit update message indicator
|
||||||
|
@ -496,13 +576,13 @@ class MediaManagerItem(QtGui.QWidget):
|
||||||
unicode(translate('OpenLP.MediaManagerItem',
|
unicode(translate('OpenLP.MediaManagerItem',
|
||||||
'You must select a %s service item.')) % self.title)
|
'You must select a %s service item.')) % self.title)
|
||||||
|
|
||||||
def buildServiceItem(self, item=None, xmlVersion=False):
|
def buildServiceItem(self, item=None, xmlVersion=False, remote=False):
|
||||||
"""
|
"""
|
||||||
Common method for generating a service item
|
Common method for generating a service item
|
||||||
"""
|
"""
|
||||||
serviceItem = ServiceItem(self.parent)
|
serviceItem = ServiceItem(self.plugin)
|
||||||
serviceItem.add_icon(self.parent.icon_path)
|
serviceItem.add_icon(self.plugin.icon_path)
|
||||||
if self.generateSlideData(serviceItem, item, xmlVersion):
|
if self.generateSlideData(serviceItem, item, xmlVersion, remote):
|
||||||
return serviceItem
|
return serviceItem
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
@ -514,6 +594,20 @@ class MediaManagerItem(QtGui.QWidget):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def checkSearchResult(self):
|
||||||
|
"""
|
||||||
|
Checks if the listView is empty and adds a "No Search Results" item.
|
||||||
|
"""
|
||||||
|
if self.listView.count():
|
||||||
|
return
|
||||||
|
message = translate('OpenLP.MediaManagerItem', 'No Search Results')
|
||||||
|
item = QtGui.QListWidgetItem(message)
|
||||||
|
item.setFlags(QtCore.Qt.NoItemFlags)
|
||||||
|
font = QtGui.QFont()
|
||||||
|
font.setItalic(True)
|
||||||
|
item.setFont(font)
|
||||||
|
self.listView.addItem(item)
|
||||||
|
|
||||||
def _getIdOfItemToGenerate(self, item, remoteItem):
|
def _getIdOfItemToGenerate(self, item, remoteItem):
|
||||||
"""
|
"""
|
||||||
Utility method to check items being submitted for slide generation.
|
Utility method to check items being submitted for slide generation.
|
||||||
|
@ -535,3 +629,20 @@ class MediaManagerItem(QtGui.QWidget):
|
||||||
else:
|
else:
|
||||||
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||||
return item_id
|
return item_id
|
||||||
|
|
||||||
|
def saveAutoSelectId(self):
|
||||||
|
"""
|
||||||
|
Sorts out, what item to select after loading a list.
|
||||||
|
"""
|
||||||
|
# The item to select has not been set.
|
||||||
|
if self.autoSelectId == -1:
|
||||||
|
item = self.listView.currentItem()
|
||||||
|
if item:
|
||||||
|
self.autoSelectId = item.data(QtCore.Qt.UserRole).toInt()[0]
|
||||||
|
|
||||||
|
def search(self, string):
|
||||||
|
"""
|
||||||
|
Performs a plugin specific search for items containing ``string``
|
||||||
|
"""
|
||||||
|
raise NotImplementedError(
|
||||||
|
u'Plugin.search needs to be defined by the plugin')
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -32,6 +33,7 @@ from PyQt4 import QtCore
|
||||||
|
|
||||||
from openlp.core.lib import Receiver
|
from openlp.core.lib import Receiver
|
||||||
from openlp.core.lib.ui import UiStrings
|
from openlp.core.lib.ui import UiStrings
|
||||||
|
from openlp.core.utils import get_application_version
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -114,8 +116,8 @@ class Plugin(QtCore.QObject):
|
||||||
"""
|
"""
|
||||||
log.info(u'loaded')
|
log.info(u'loaded')
|
||||||
|
|
||||||
def __init__(self, name, version=None, pluginHelpers=None,
|
def __init__(self, name, plugin_helpers=None, media_item_class=None,
|
||||||
mediaItemClass=None, settingsTabClass=None):
|
settings_tab_class=None, version=None):
|
||||||
"""
|
"""
|
||||||
This is the constructor for the plugin object. This provides an easy
|
This is the constructor for the plugin object. This provides an easy
|
||||||
way for descendent plugins to populate common data. This method *must*
|
way for descendent plugins to populate common data. This method *must*
|
||||||
|
@ -123,7 +125,7 @@ class Plugin(QtCore.QObject):
|
||||||
|
|
||||||
class MyPlugin(Plugin):
|
class MyPlugin(Plugin):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
Plugin.__init__(self, u'MyPlugin', u'0.1')
|
Plugin.__init__(self, u'MyPlugin', version=u'0.1')
|
||||||
|
|
||||||
``name``
|
``name``
|
||||||
Defaults to *None*. The name of the plugin.
|
Defaults to *None*. The name of the plugin.
|
||||||
|
@ -131,15 +133,16 @@ class Plugin(QtCore.QObject):
|
||||||
``version``
|
``version``
|
||||||
Defaults to *None*. The version of the plugin.
|
Defaults to *None*. The version of the plugin.
|
||||||
|
|
||||||
``pluginHelpers``
|
``plugin_helpers``
|
||||||
Defaults to *None*. A list of helper objects.
|
Defaults to *None*. A list of helper objects.
|
||||||
|
|
||||||
``mediaItemClass``
|
``media_item_class``
|
||||||
The class name of the plugin's media item.
|
The class name of the plugin's media item.
|
||||||
|
|
||||||
``settingsTabClass``
|
``settings_tab_class``
|
||||||
The class name of the plugin's settings tab.
|
The class name of the plugin's settings tab.
|
||||||
"""
|
"""
|
||||||
|
log.debug(u'Plugin %s initialised' % name)
|
||||||
QtCore.QObject.__init__(self)
|
QtCore.QObject.__init__(self)
|
||||||
self.name = name
|
self.name = name
|
||||||
self.textStrings = {}
|
self.textStrings = {}
|
||||||
|
@ -147,22 +150,24 @@ class Plugin(QtCore.QObject):
|
||||||
self.nameStrings = self.textStrings[StringContent.Name]
|
self.nameStrings = self.textStrings[StringContent.Name]
|
||||||
if version:
|
if version:
|
||||||
self.version = version
|
self.version = version
|
||||||
self.settingsSection = self.name.lower()
|
else:
|
||||||
|
self.version = get_application_version()[u'version']
|
||||||
|
self.settingsSection = self.name
|
||||||
self.icon = None
|
self.icon = None
|
||||||
self.mediaItemClass = mediaItemClass
|
self.media_item_class = media_item_class
|
||||||
self.settingsTabClass = settingsTabClass
|
self.settings_tab_class = settings_tab_class
|
||||||
self.weight = 0
|
self.weight = 0
|
||||||
self.status = PluginStatus.Inactive
|
self.status = PluginStatus.Inactive
|
||||||
# Set up logging
|
# Set up logging
|
||||||
self.log = logging.getLogger(self.name)
|
self.log = logging.getLogger(self.name)
|
||||||
self.previewController = pluginHelpers[u'preview']
|
self.previewController = plugin_helpers[u'preview']
|
||||||
self.liveController = pluginHelpers[u'live']
|
self.liveController = plugin_helpers[u'live']
|
||||||
self.renderManager = pluginHelpers[u'render']
|
self.renderer = plugin_helpers[u'renderer']
|
||||||
self.serviceManager = pluginHelpers[u'service']
|
self.serviceManager = plugin_helpers[u'service']
|
||||||
self.settingsForm = pluginHelpers[u'settings form']
|
self.settingsForm = plugin_helpers[u'settings form']
|
||||||
self.mediadock = pluginHelpers[u'toolbox']
|
self.mediadock = plugin_helpers[u'toolbox']
|
||||||
self.pluginManager = pluginHelpers[u'pluginmanager']
|
self.pluginManager = plugin_helpers[u'pluginmanager']
|
||||||
self.formparent = pluginHelpers[u'formparent']
|
self.formparent = plugin_helpers[u'formparent']
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'%s_add_service_item' % self.name),
|
QtCore.SIGNAL(u'%s_add_service_item' % self.name),
|
||||||
self.processAddServiceEvent)
|
self.processAddServiceEvent)
|
||||||
|
@ -209,8 +214,9 @@ class Plugin(QtCore.QObject):
|
||||||
Construct a MediaManagerItem object with all the buttons and things
|
Construct a MediaManagerItem object with all the buttons and things
|
||||||
you need, and return it for integration into openlp.org.
|
you need, and return it for integration into openlp.org.
|
||||||
"""
|
"""
|
||||||
if self.mediaItemClass:
|
if self.media_item_class:
|
||||||
return self.mediaItemClass(self, self, self.icon)
|
return self.media_item_class(self.mediadock.media_dock, self,
|
||||||
|
self.icon)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def addImportMenuItem(self, importMenu):
|
def addImportMenuItem(self, importMenu):
|
||||||
|
@ -240,14 +246,15 @@ class Plugin(QtCore.QObject):
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def getSettingsTab(self):
|
def getSettingsTab(self, parent):
|
||||||
"""
|
"""
|
||||||
Create a tab for the settings window to display the configurable
|
Create a tab for the settings window to display the configurable
|
||||||
options for this plugin to the user.
|
options for this plugin to the user.
|
||||||
"""
|
"""
|
||||||
if self.settingsTabClass:
|
if self.settings_tab_class:
|
||||||
return self.settingsTabClass(self.name,
|
return self.settings_tab_class(parent, self.name,
|
||||||
self.getString(StringContent.VisibleName)[u'title'])
|
self.getString(StringContent.VisibleName)[u'title'],
|
||||||
|
self.icon_path)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def addToMenu(self, menubar):
|
def addToMenu(self, menubar):
|
||||||
|
@ -284,31 +291,20 @@ class Plugin(QtCore.QObject):
|
||||||
"""
|
"""
|
||||||
if self.mediaItem:
|
if self.mediaItem:
|
||||||
self.mediaItem.initialise()
|
self.mediaItem.initialise()
|
||||||
self.insertToolboxItem()
|
self.mediadock.insert_dock(self.mediaItem, self.icon, self.weight)
|
||||||
|
|
||||||
def finalise(self):
|
def finalise(self):
|
||||||
"""
|
"""
|
||||||
Called by the plugin Manager to cleanup things.
|
Called by the plugin Manager to cleanup things.
|
||||||
"""
|
"""
|
||||||
self.removeToolboxItem()
|
|
||||||
|
|
||||||
def removeToolboxItem(self):
|
|
||||||
"""
|
|
||||||
Called by the plugin to remove toolbar
|
|
||||||
"""
|
|
||||||
if self.mediaItem:
|
if self.mediaItem:
|
||||||
self.mediadock.remove_dock(self.mediaItem)
|
self.mediadock.remove_dock(self.mediaItem)
|
||||||
if self.settings_tab:
|
|
||||||
self.settingsForm.removeTab(self.settings_tab)
|
|
||||||
|
|
||||||
def insertToolboxItem(self):
|
def appStartup(self):
|
||||||
"""
|
"""
|
||||||
Called by plugin to replace toolbar
|
Perform tasks on application starup
|
||||||
"""
|
"""
|
||||||
if self.mediaItem:
|
pass
|
||||||
self.mediadock.insert_dock(self.mediaItem, self.icon, self.weight)
|
|
||||||
if self.settings_tab:
|
|
||||||
self.settingsForm.insertTab(self.settings_tab, self.weight)
|
|
||||||
|
|
||||||
def usesTheme(self, theme):
|
def usesTheme(self, theme):
|
||||||
"""
|
"""
|
||||||
|
@ -342,28 +338,28 @@ class Plugin(QtCore.QObject):
|
||||||
"""
|
"""
|
||||||
## Load Action ##
|
## Load Action ##
|
||||||
self.__setNameTextString(StringContent.Load,
|
self.__setNameTextString(StringContent.Load,
|
||||||
UiStrings.Load, tooltips[u'load'])
|
UiStrings().Load, tooltips[u'load'])
|
||||||
## Import Action ##
|
## Import Action ##
|
||||||
self.__setNameTextString(StringContent.Import,
|
self.__setNameTextString(StringContent.Import,
|
||||||
UiStrings.Import, tooltips[u'import'])
|
UiStrings().Import, tooltips[u'import'])
|
||||||
## New Action ##
|
## New Action ##
|
||||||
self.__setNameTextString(StringContent.New,
|
self.__setNameTextString(StringContent.New,
|
||||||
UiStrings.Add, tooltips[u'new'])
|
UiStrings().Add, tooltips[u'new'])
|
||||||
## Edit Action ##
|
## Edit Action ##
|
||||||
self.__setNameTextString(StringContent.Edit,
|
self.__setNameTextString(StringContent.Edit,
|
||||||
UiStrings.Edit, tooltips[u'edit'])
|
UiStrings().Edit, tooltips[u'edit'])
|
||||||
## Delete Action ##
|
## Delete Action ##
|
||||||
self.__setNameTextString(StringContent.Delete,
|
self.__setNameTextString(StringContent.Delete,
|
||||||
UiStrings.Delete, tooltips[u'delete'])
|
UiStrings().Delete, tooltips[u'delete'])
|
||||||
## Preview Action ##
|
## Preview Action ##
|
||||||
self.__setNameTextString(StringContent.Preview,
|
self.__setNameTextString(StringContent.Preview,
|
||||||
UiStrings.Preview, tooltips[u'preview'])
|
UiStrings().Preview, tooltips[u'preview'])
|
||||||
## Send Live Action ##
|
## Send Live Action ##
|
||||||
self.__setNameTextString(StringContent.Live,
|
self.__setNameTextString(StringContent.Live,
|
||||||
UiStrings.Live, tooltips[u'live'])
|
UiStrings().Live, tooltips[u'live'])
|
||||||
## Add to Service Action ##
|
## Add to Service Action ##
|
||||||
self.__setNameTextString(StringContent.Service,
|
self.__setNameTextString(StringContent.Service,
|
||||||
UiStrings.Service, tooltips[u'service'])
|
UiStrings().Service, tooltips[u'service'])
|
||||||
|
|
||||||
def __setNameTextString(self, name, title, tooltip):
|
def __setNameTextString(self, name, title, tooltip):
|
||||||
"""
|
"""
|
||||||
|
@ -372,3 +368,31 @@ class Plugin(QtCore.QObject):
|
||||||
after this has been set.
|
after this has been set.
|
||||||
"""
|
"""
|
||||||
self.textStrings[name] = {u'title': title, u'tooltip': tooltip}
|
self.textStrings[name] = {u'title': title, u'tooltip': tooltip}
|
||||||
|
|
||||||
|
def getDisplayCss(self):
|
||||||
|
"""
|
||||||
|
Add css style sheets to htmlbuilder.
|
||||||
|
"""
|
||||||
|
return u''
|
||||||
|
|
||||||
|
def getDisplayJavaScript(self):
|
||||||
|
"""
|
||||||
|
Add javascript functions to htmlbuilder.
|
||||||
|
"""
|
||||||
|
return u''
|
||||||
|
|
||||||
|
def refreshCss(self, frame):
|
||||||
|
"""
|
||||||
|
Allow plugins to refresh javascript on displayed screen.
|
||||||
|
|
||||||
|
``frame``
|
||||||
|
The Web frame holding the page.
|
||||||
|
"""
|
||||||
|
return u''
|
||||||
|
|
||||||
|
def getDisplayHtml(self):
|
||||||
|
"""
|
||||||
|
Add html code to htmlbuilder.
|
||||||
|
"""
|
||||||
|
return u''
|
||||||
|
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -30,7 +31,7 @@ import os
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from openlp.core.lib import Plugin, StringContent, PluginStatus
|
from openlp.core.lib import Plugin, PluginStatus
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -41,6 +42,13 @@ class PluginManager(object):
|
||||||
"""
|
"""
|
||||||
log.info(u'Plugin manager loaded')
|
log.info(u'Plugin manager loaded')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_instance():
|
||||||
|
"""
|
||||||
|
Obtain a single instance of class.
|
||||||
|
"""
|
||||||
|
return PluginManager.instance
|
||||||
|
|
||||||
def __init__(self, plugin_dir):
|
def __init__(self, plugin_dir):
|
||||||
"""
|
"""
|
||||||
The constructor for the plugin manager. Passes the controllers on to
|
The constructor for the plugin manager. Passes the controllers on to
|
||||||
|
@ -49,16 +57,14 @@ class PluginManager(object):
|
||||||
``plugin_dir``
|
``plugin_dir``
|
||||||
The directory to search for plugins.
|
The directory to search for plugins.
|
||||||
"""
|
"""
|
||||||
log.info(u'Plugin manager initing')
|
log.info(u'Plugin manager Initialising')
|
||||||
|
PluginManager.instance = self
|
||||||
if not plugin_dir in sys.path:
|
if not plugin_dir in sys.path:
|
||||||
log.debug(u'Inserting %s into sys.path', plugin_dir)
|
log.debug(u'Inserting %s into sys.path', plugin_dir)
|
||||||
sys.path.insert(0, plugin_dir)
|
sys.path.insert(0, plugin_dir)
|
||||||
self.basepath = os.path.abspath(plugin_dir)
|
self.basepath = os.path.abspath(plugin_dir)
|
||||||
log.debug(u'Base path %s ', self.basepath)
|
log.debug(u'Base path %s ', self.basepath)
|
||||||
self.plugin_helpers = []
|
|
||||||
self.plugins = []
|
self.plugins = []
|
||||||
# this has to happen after the UI is sorted
|
|
||||||
# self.find_plugins(plugin_dir)
|
|
||||||
log.info(u'Plugin manager Initialised')
|
log.info(u'Plugin manager Initialised')
|
||||||
|
|
||||||
def find_plugins(self, plugin_dir, plugin_helpers):
|
def find_plugins(self, plugin_dir, plugin_helpers):
|
||||||
|
@ -73,7 +79,7 @@ class PluginManager(object):
|
||||||
A list of helper objects to pass to the plugins.
|
A list of helper objects to pass to the plugins.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.plugin_helpers = plugin_helpers
|
log.info(u'Finding plugins')
|
||||||
startdepth = len(os.path.abspath(plugin_dir).split(os.sep))
|
startdepth = len(os.path.abspath(plugin_dir).split(os.sep))
|
||||||
log.debug(u'finding plugins in %s at depth %d',
|
log.debug(u'finding plugins in %s at depth %d',
|
||||||
unicode(plugin_dir), startdepth)
|
unicode(plugin_dir), startdepth)
|
||||||
|
@ -102,11 +108,11 @@ class PluginManager(object):
|
||||||
plugin_objects = []
|
plugin_objects = []
|
||||||
for p in plugin_classes:
|
for p in plugin_classes:
|
||||||
try:
|
try:
|
||||||
plugin = p(self.plugin_helpers)
|
plugin = p(plugin_helpers)
|
||||||
log.debug(u'Loaded plugin %s with helpers', unicode(p))
|
log.debug(u'Loaded plugin %s', unicode(p))
|
||||||
plugin_objects.append(plugin)
|
plugin_objects.append(plugin)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
log.exception(u'loaded plugin %s has no helpers', unicode(p))
|
log.exception(u'Failed to load plugin %s', unicode(p))
|
||||||
plugins_list = sorted(plugin_objects, self.order_by_weight)
|
plugins_list = sorted(plugin_objects, self.order_by_weight)
|
||||||
for plugin in plugins_list:
|
for plugin in plugins_list:
|
||||||
if plugin.checkPreConditions():
|
if plugin.checkPreConditions():
|
||||||
|
@ -140,7 +146,7 @@ class PluginManager(object):
|
||||||
if plugin.status is not PluginStatus.Disabled:
|
if plugin.status is not PluginStatus.Disabled:
|
||||||
plugin.mediaItem = plugin.getMediaManagerItem()
|
plugin.mediaItem = plugin.getMediaManagerItem()
|
||||||
|
|
||||||
def hook_settings_tabs(self, settingsform=None):
|
def hook_settings_tabs(self, settings_form=None):
|
||||||
"""
|
"""
|
||||||
Loop through all the plugins. If a plugin has a valid settings tab
|
Loop through all the plugins. If a plugin has a valid settings tab
|
||||||
item, add it to the settings tab.
|
item, add it to the settings tab.
|
||||||
|
@ -151,16 +157,8 @@ class PluginManager(object):
|
||||||
"""
|
"""
|
||||||
for plugin in self.plugins:
|
for plugin in self.plugins:
|
||||||
if plugin.status is not PluginStatus.Disabled:
|
if plugin.status is not PluginStatus.Disabled:
|
||||||
plugin.settings_tab = plugin.getSettingsTab()
|
plugin.settings_tab = plugin.getSettingsTab(settings_form)
|
||||||
visible_title = plugin.getString(StringContent.VisibleName)
|
settings_form.plugins = self.plugins
|
||||||
if plugin.settings_tab:
|
|
||||||
log.debug(u'Inserting settings tab item from %s' %
|
|
||||||
visible_title[u'title'])
|
|
||||||
settingsform.addTab(visible_title[u'title'],
|
|
||||||
plugin.settings_tab)
|
|
||||||
else:
|
|
||||||
log.debug(
|
|
||||||
u'No tab settings in %s' % visible_title[u'title'])
|
|
||||||
|
|
||||||
def hook_import_menu(self, import_menu):
|
def hook_import_menu(self, import_menu):
|
||||||
"""
|
"""
|
||||||
|
@ -203,14 +201,14 @@ class PluginManager(object):
|
||||||
Loop through all the plugins and give them an opportunity to
|
Loop through all the plugins and give them an opportunity to
|
||||||
initialise themselves.
|
initialise themselves.
|
||||||
"""
|
"""
|
||||||
|
log.info(u'Initialise Plugins - Started')
|
||||||
for plugin in self.plugins:
|
for plugin in self.plugins:
|
||||||
log.info(u'initialising plugins %s in a %s state'
|
log.info(u'initialising plugins %s in a %s state'
|
||||||
% (plugin.name, plugin.isActive()))
|
% (plugin.name, plugin.isActive()))
|
||||||
if plugin.isActive():
|
if plugin.isActive():
|
||||||
plugin.initialise()
|
plugin.initialise()
|
||||||
log.info(u'Initialisation Complete for %s ' % plugin.name)
|
log.info(u'Initialisation Complete for %s ' % plugin.name)
|
||||||
if not plugin.isActive():
|
log.info(u'Initialise Plugins - Finished')
|
||||||
plugin.removeToolboxItem()
|
|
||||||
|
|
||||||
def finalise_plugins(self):
|
def finalise_plugins(self):
|
||||||
"""
|
"""
|
||||||
|
@ -222,3 +220,12 @@ class PluginManager(object):
|
||||||
if plugin.isActive():
|
if plugin.isActive():
|
||||||
plugin.finalise()
|
plugin.finalise()
|
||||||
log.info(u'Finalisation Complete for %s ' % plugin.name)
|
log.info(u'Finalisation Complete for %s ' % plugin.name)
|
||||||
|
|
||||||
|
def get_plugin_by_name(self, name):
|
||||||
|
"""
|
||||||
|
Return the plugin which has a name with value ``name``
|
||||||
|
"""
|
||||||
|
for plugin in self.plugins:
|
||||||
|
if plugin.name == name:
|
||||||
|
return plugin
|
||||||
|
return None
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -23,46 +24,325 @@
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
"""
|
|
||||||
The :mod:`renderer` module enables OpenLP to take the input from plugins and
|
|
||||||
format it for the output display.
|
|
||||||
"""
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from PyQt4 import QtWebKit
|
from PyQt4 import QtGui, QtCore, QtWebKit
|
||||||
|
|
||||||
from openlp.core.lib import expand_tags, build_lyrics_format_css, \
|
from openlp.core.lib import ServiceItem, expand_tags, \
|
||||||
build_lyrics_outline_css, Receiver
|
build_lyrics_format_css, build_lyrics_outline_css, Receiver, \
|
||||||
|
ItemCapabilities, FormattingTags
|
||||||
|
from openlp.core.lib.theme import ThemeLevel
|
||||||
|
from openlp.core.ui import MainDisplay, ScreenList
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
VERSE = u'The Lord said to {r}Noah{/r}: \n' \
|
||||||
|
'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n' \
|
||||||
|
'The Lord said to {g}Noah{/g}:\n' \
|
||||||
|
'There\'s gonna be a {st}floody{/st}, {it}floody{/it}\n' \
|
||||||
|
'Get those children out of the muddy, muddy \n' \
|
||||||
|
'{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}' \
|
||||||
|
'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
|
||||||
|
VERSE_FOR_LINE_COUNT = u'\n'.join(map(unicode, xrange(50)))
|
||||||
|
FOOTER = [u'Arky Arky (Unknown)', u'Public Domain', u'CCLI 123456']
|
||||||
|
|
||||||
class Renderer(object):
|
class Renderer(object):
|
||||||
"""
|
"""
|
||||||
Genarates a pixmap image of a array of text. The Text is formatted to
|
Class to pull all Renderer interactions into one place. The plugins will
|
||||||
make sure it fits on the screen and if not extra frames are generated.
|
call helper methods to do the rendering but this class will provide
|
||||||
|
display defense code.
|
||||||
"""
|
"""
|
||||||
log.info(u'Renderer Loaded')
|
log.info(u'Renderer Loaded')
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, imageManager, themeManager):
|
||||||
"""
|
"""
|
||||||
Initialise the renderer.
|
Initialise the renderer.
|
||||||
"""
|
|
||||||
self._rect = None
|
|
||||||
self.theme_name = None
|
|
||||||
self._theme = None
|
|
||||||
|
|
||||||
def set_theme(self, theme):
|
``imageManager``
|
||||||
|
A imageManager instance which takes care of e. g. caching and resizing
|
||||||
|
images.
|
||||||
|
|
||||||
|
``themeManager``
|
||||||
|
The themeManager instance, used to get the current theme details.
|
||||||
"""
|
"""
|
||||||
Set the theme to be used.
|
log.debug(u'Initialisation started')
|
||||||
|
self.themeManager = themeManager
|
||||||
|
self.imageManager = imageManager
|
||||||
|
self.screens = ScreenList.get_instance()
|
||||||
|
self.service_theme = u''
|
||||||
|
self.theme_level = u''
|
||||||
|
self.override_background = None
|
||||||
|
self.theme_data = None
|
||||||
|
self.bg_frame = None
|
||||||
|
self.force_page = False
|
||||||
|
self.display = MainDisplay(None, self.imageManager, False)
|
||||||
|
self.display.setup()
|
||||||
|
|
||||||
|
def update_display(self):
|
||||||
|
"""
|
||||||
|
Updates the renderer's information about the current screen.
|
||||||
|
"""
|
||||||
|
log.debug(u'Update Display')
|
||||||
|
self._calculate_default()
|
||||||
|
if self.display:
|
||||||
|
self.display.close()
|
||||||
|
self.display = MainDisplay(None, self.imageManager, False)
|
||||||
|
self.display.setup()
|
||||||
|
self.bg_frame = None
|
||||||
|
self.theme_data = None
|
||||||
|
|
||||||
|
def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global):
|
||||||
|
"""
|
||||||
|
Set the global-level theme and the theme level.
|
||||||
|
|
||||||
|
``global_theme``
|
||||||
|
The global-level theme to be set.
|
||||||
|
|
||||||
|
``theme_level``
|
||||||
|
Defaults to ``ThemeLevel.Global``. The theme level, can be
|
||||||
|
``ThemeLevel.Global``, ``ThemeLevel.Service`` or
|
||||||
|
``ThemeLevel.Song``.
|
||||||
|
"""
|
||||||
|
self.global_theme = global_theme
|
||||||
|
self.theme_level = theme_level
|
||||||
|
self.global_theme_data = \
|
||||||
|
self.themeManager.getThemeData(self.global_theme)
|
||||||
|
self.theme_data = None
|
||||||
|
|
||||||
|
def set_service_theme(self, service_theme):
|
||||||
|
"""
|
||||||
|
Set the service-level theme.
|
||||||
|
|
||||||
|
``service_theme``
|
||||||
|
The service-level theme to be set.
|
||||||
|
"""
|
||||||
|
self.service_theme = service_theme
|
||||||
|
self.theme_data = None
|
||||||
|
|
||||||
|
def set_override_theme(self, override_theme, override_levels=False):
|
||||||
|
"""
|
||||||
|
Set the appropriate theme depending on the theme level.
|
||||||
|
Called by the service item when building a display frame
|
||||||
|
|
||||||
``theme``
|
``theme``
|
||||||
The theme to be used.
|
The name of the song-level theme. None means the service
|
||||||
"""
|
item wants to use the given value.
|
||||||
log.debug(u'set theme')
|
|
||||||
self._theme = theme
|
|
||||||
self.theme_name = theme.theme_name
|
|
||||||
|
|
||||||
def set_text_rectangle(self, rect_main, rect_footer):
|
``override_levels``
|
||||||
|
Used to force the theme data passed in to be used.
|
||||||
|
|
||||||
|
"""
|
||||||
|
log.debug(u'set override theme to %s', override_theme)
|
||||||
|
theme_level = self.theme_level
|
||||||
|
if override_levels:
|
||||||
|
theme_level = ThemeLevel.Song
|
||||||
|
if theme_level == ThemeLevel.Global:
|
||||||
|
theme = self.global_theme
|
||||||
|
elif theme_level == ThemeLevel.Service:
|
||||||
|
if self.service_theme == u'':
|
||||||
|
theme = self.global_theme
|
||||||
|
else:
|
||||||
|
theme = self.service_theme
|
||||||
|
else:
|
||||||
|
# Images have a theme of -1
|
||||||
|
if override_theme and override_theme != -1:
|
||||||
|
theme = override_theme
|
||||||
|
elif theme_level == ThemeLevel.Song or \
|
||||||
|
theme_level == ThemeLevel.Service:
|
||||||
|
if self.service_theme == u'':
|
||||||
|
theme = self.global_theme
|
||||||
|
else:
|
||||||
|
theme = self.service_theme
|
||||||
|
else:
|
||||||
|
theme = self.global_theme
|
||||||
|
log.debug(u'theme is now %s', theme)
|
||||||
|
# Force the theme to be the one passed in.
|
||||||
|
if override_levels:
|
||||||
|
self.theme_data = override_theme
|
||||||
|
else:
|
||||||
|
self.theme_data = self.themeManager.getThemeData(theme)
|
||||||
|
self._calculate_default()
|
||||||
|
self._build_text_rectangle(self.theme_data)
|
||||||
|
# if No file do not update cache
|
||||||
|
if self.theme_data.background_filename:
|
||||||
|
self.imageManager.add_image(self.theme_data.theme_name,
|
||||||
|
self.theme_data.background_filename, u'theme',
|
||||||
|
QtGui.QColor(self.theme_data.background_border_color))
|
||||||
|
return self._rect, self._rect_footer
|
||||||
|
|
||||||
|
def generate_preview(self, theme_data, force_page=False):
|
||||||
|
"""
|
||||||
|
Generate a preview of a theme.
|
||||||
|
|
||||||
|
``theme_data``
|
||||||
|
The theme to generated a preview for.
|
||||||
|
|
||||||
|
``force_page``
|
||||||
|
Flag to tell message lines per page need to be generated.
|
||||||
|
"""
|
||||||
|
log.debug(u'generate preview')
|
||||||
|
# save value for use in format_slide
|
||||||
|
self.force_page = force_page
|
||||||
|
# set the default image size for previews
|
||||||
|
self._calculate_default()
|
||||||
|
# build a service item to generate preview
|
||||||
|
serviceItem = ServiceItem()
|
||||||
|
serviceItem.theme = theme_data
|
||||||
|
if self.force_page:
|
||||||
|
# make big page for theme edit dialog to get line count
|
||||||
|
serviceItem.add_from_text(u'', VERSE_FOR_LINE_COUNT)
|
||||||
|
else:
|
||||||
|
self.imageManager.del_image(theme_data.theme_name)
|
||||||
|
serviceItem.add_from_text(u'', VERSE)
|
||||||
|
serviceItem.renderer = self
|
||||||
|
serviceItem.raw_footer = FOOTER
|
||||||
|
serviceItem.render(True)
|
||||||
|
if not self.force_page:
|
||||||
|
self.display.buildHtml(serviceItem)
|
||||||
|
raw_html = serviceItem.get_rendered_frame(0)
|
||||||
|
self.display.text(raw_html)
|
||||||
|
preview = self.display.preview()
|
||||||
|
# Reset the real screen size for subsequent render requests
|
||||||
|
self._calculate_default()
|
||||||
|
return preview
|
||||||
|
self.force_page = False
|
||||||
|
|
||||||
|
def format_slide(self, text, item):
|
||||||
|
"""
|
||||||
|
Calculate how much text can fit on a slide.
|
||||||
|
|
||||||
|
``text``
|
||||||
|
The words to go on the slides.
|
||||||
|
|
||||||
|
``item``
|
||||||
|
The :class:`~openlp.core.lib.serviceitem.ServiceItem` item object.
|
||||||
|
"""
|
||||||
|
log.debug(u'format slide')
|
||||||
|
# Add line endings after each line of text used for bibles.
|
||||||
|
line_end = u'<br>'
|
||||||
|
if item.is_capable(ItemCapabilities.NoLineBreaks):
|
||||||
|
line_end = u' '
|
||||||
|
# Bibles
|
||||||
|
if item.is_capable(ItemCapabilities.CanWordSplit):
|
||||||
|
pages = self._paginate_slide_words(text.split(u'\n'), line_end)
|
||||||
|
# Songs and Custom
|
||||||
|
elif item.is_capable(ItemCapabilities.CanSoftBreak):
|
||||||
|
pages = []
|
||||||
|
if u'[---]' in text:
|
||||||
|
while True:
|
||||||
|
slides = text.split(u'\n[---]\n', 2)
|
||||||
|
# If there are (at least) two occurrences of [---] we use
|
||||||
|
# the first two slides (and neglect the last for now).
|
||||||
|
if len(slides) == 3:
|
||||||
|
html_text = expand_tags(u'\n'.join(slides[:2]))
|
||||||
|
# We check both slides to determine if the virtual break is
|
||||||
|
# needed (there is only one virtual break).
|
||||||
|
else:
|
||||||
|
html_text = expand_tags(u'\n'.join(slides))
|
||||||
|
html_text = html_text.replace(u'\n', u'<br>')
|
||||||
|
if self._text_fits_on_slide(html_text):
|
||||||
|
# The first two virtual slides fit (as a whole) on one
|
||||||
|
# slide. Replace the first occurrence of [---].
|
||||||
|
text = text.replace(u'\n[---]', u'', 1)
|
||||||
|
else:
|
||||||
|
# The first virtual slide fits, which means we have to
|
||||||
|
# render the first virtual slide.
|
||||||
|
text_contains_break = u'[---]' in text
|
||||||
|
if text_contains_break:
|
||||||
|
try:
|
||||||
|
text_to_render, text = \
|
||||||
|
text.split(u'\n[---]\n', 1)
|
||||||
|
except:
|
||||||
|
text_to_render = text.split(u'\n[---]\n')[0]
|
||||||
|
text = u''
|
||||||
|
else:
|
||||||
|
text_to_render = text
|
||||||
|
text = u''
|
||||||
|
lines = text_to_render.strip(u'\n').split(u'\n')
|
||||||
|
slides = self._paginate_slide(lines, line_end)
|
||||||
|
if len(slides) > 1 and text:
|
||||||
|
# Add all slides apart from the last one the list.
|
||||||
|
pages.extend(slides[:-1])
|
||||||
|
if text_contains_break:
|
||||||
|
text = slides[-1] + u'\n[---]\n' + text
|
||||||
|
else:
|
||||||
|
text = slides[-1] + u'\n'+ text
|
||||||
|
text = text.replace(u'<br>', u'\n')
|
||||||
|
else:
|
||||||
|
pages.extend(slides)
|
||||||
|
if u'[---]' not in text:
|
||||||
|
lines = text.strip(u'\n').split(u'\n')
|
||||||
|
pages.extend(self._paginate_slide(lines, line_end))
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# Clean up line endings.
|
||||||
|
pages = self._paginate_slide(text.split(u'\n'), line_end)
|
||||||
|
else:
|
||||||
|
pages = self._paginate_slide(text.split(u'\n'), line_end)
|
||||||
|
new_pages = []
|
||||||
|
for page in pages:
|
||||||
|
while page.endswith(u'<br>'):
|
||||||
|
page = page[:-4]
|
||||||
|
new_pages.append(page)
|
||||||
|
return new_pages
|
||||||
|
|
||||||
|
def _calculate_default(self):
|
||||||
|
"""
|
||||||
|
Calculate the default dimentions of the screen.
|
||||||
|
"""
|
||||||
|
screen_size = self.screens.current[u'size']
|
||||||
|
self.width = screen_size.width()
|
||||||
|
self.height = screen_size.height()
|
||||||
|
self.screen_ratio = float(self.height) / float(self.width)
|
||||||
|
log.debug(u'_calculate default %s, %f' % (screen_size,
|
||||||
|
self.screen_ratio))
|
||||||
|
# 90% is start of footer
|
||||||
|
self.footer_start = int(self.height * 0.90)
|
||||||
|
|
||||||
|
def _build_text_rectangle(self, theme):
|
||||||
|
"""
|
||||||
|
Builds a text block using the settings in ``theme``
|
||||||
|
and the size of the display screen.height.
|
||||||
|
Note the system has a 10 pixel border round the screen
|
||||||
|
|
||||||
|
``theme``
|
||||||
|
The theme to build a text block for.
|
||||||
|
"""
|
||||||
|
log.debug(u'_build_text_rectangle')
|
||||||
|
main_rect = 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:
|
||||||
|
return QtCore.QRect(10, 0, self.width - 20, self.footer_start)
|
||||||
|
else:
|
||||||
|
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:
|
||||||
|
return QtCore.QRect(10, self.footer_start, self.width - 20,
|
||||||
|
self.height - self.footer_start)
|
||||||
|
else:
|
||||||
|
return QtCore.QRect(theme.font_footer_x,
|
||||||
|
theme.font_footer_y, theme.font_footer_width - 1,
|
||||||
|
theme.font_footer_height - 1)
|
||||||
|
|
||||||
|
def _set_text_rectangle(self, rect_main, rect_footer):
|
||||||
"""
|
"""
|
||||||
Sets the rectangle within which text should be rendered.
|
Sets the rectangle within which text should be rendered.
|
||||||
|
|
||||||
|
@ -72,76 +352,260 @@ class Renderer(object):
|
||||||
``rect_footer``
|
``rect_footer``
|
||||||
The footer text block.
|
The footer text block.
|
||||||
"""
|
"""
|
||||||
log.debug(u'set_text_rectangle %s , %s' % (rect_main, rect_footer))
|
log.debug(u'_set_text_rectangle %s , %s' % (rect_main, rect_footer))
|
||||||
self._rect = rect_main
|
self._rect = rect_main
|
||||||
self._rect_footer = rect_footer
|
self._rect_footer = rect_footer
|
||||||
self.page_width = self._rect.width()
|
self.page_width = self._rect.width()
|
||||||
self.page_height = self._rect.height()
|
self.page_height = self._rect.height()
|
||||||
if self._theme.font_main_shadow:
|
if self.theme_data.font_main_shadow:
|
||||||
self.page_width -= int(self._theme.font_main_shadow_size)
|
self.page_width -= int(self.theme_data.font_main_shadow_size)
|
||||||
self.page_height -= int(self._theme.font_main_shadow_size)
|
self.page_height -= int(self.theme_data.font_main_shadow_size)
|
||||||
self.web = QtWebKit.QWebView()
|
self.web = QtWebKit.QWebView()
|
||||||
self.web.setVisible(False)
|
self.web.setVisible(False)
|
||||||
self.web.resize(self.page_width, self.page_height)
|
self.web.resize(self.page_width, self.page_height)
|
||||||
self.web_frame = self.web.page().mainFrame()
|
self.web_frame = self.web.page().mainFrame()
|
||||||
# Adjust width and height to account for shadow. outline done in css
|
# Adjust width and height to account for shadow. outline done in css
|
||||||
self.page_shell = u'<html><head><style>' \
|
html = u"""<!DOCTYPE html><html><head><script>
|
||||||
u'*{margin: 0; padding: 0; border: 0;} '\
|
function show_text(newtext) {
|
||||||
u'#main {position:absolute; top:0px; %s %s}</style><body>' \
|
var main = document.getElementById('main');
|
||||||
u'<div id="main">' % \
|
main.innerHTML = newtext;
|
||||||
(build_lyrics_format_css(self._theme, self.page_width,
|
// We need to be sure that the page is loaded, that is why we
|
||||||
self.page_height), build_lyrics_outline_css(self._theme))
|
// return the element's height (even though we do not use the
|
||||||
|
// returned value).
|
||||||
|
return main.offsetHeight;
|
||||||
|
}
|
||||||
|
</script><style>*{margin: 0; padding: 0; border: 0;}
|
||||||
|
#main {position: absolute; top: 0px; %s %s}</style></head><body>
|
||||||
|
<div id="main"></div></body></html>""" % \
|
||||||
|
(build_lyrics_format_css(self.theme_data, self.page_width,
|
||||||
|
self.page_height), build_lyrics_outline_css(self.theme_data))
|
||||||
|
self.web.setHtml(html)
|
||||||
|
|
||||||
def format_slide(self, words, line_break, force_page=False):
|
def _paginate_slide(self, lines, line_end):
|
||||||
"""
|
"""
|
||||||
Figure out how much text can appear on a slide, using the current
|
Figure out how much text can appear on a slide, using the current
|
||||||
theme settings.
|
theme settings.
|
||||||
|
**Note:** The smallest possible "unit" of text for a slide is one line.
|
||||||
|
If the line is too long it will be cut off when displayed.
|
||||||
|
|
||||||
``words``
|
``lines``
|
||||||
The words to be fitted on the slide.
|
The text to be fitted on the slide split into lines.
|
||||||
|
|
||||||
``line_break``
|
|
||||||
Add line endings after each line of text used for bibles.
|
|
||||||
|
|
||||||
``force_page``
|
|
||||||
Flag to tell message lines in page.
|
|
||||||
|
|
||||||
|
``line_end``
|
||||||
|
The text added after each line. Either ``u' '`` or ``u'<br>``.
|
||||||
"""
|
"""
|
||||||
log.debug(u'format_slide - Start')
|
log.debug(u'_paginate_slide - Start')
|
||||||
line_end = u''
|
|
||||||
if line_break:
|
|
||||||
line_end = u'<br>'
|
|
||||||
words = words.replace(u'\r\n', u'\n')
|
|
||||||
verses_text = words.split(u'\n')
|
|
||||||
text = []
|
|
||||||
for verse in verses_text:
|
|
||||||
lines = verse.split(u'\n')
|
|
||||||
for line in lines:
|
|
||||||
text.append(line)
|
|
||||||
formatted = []
|
formatted = []
|
||||||
html_text = u''
|
previous_html = u''
|
||||||
styled_text = u''
|
previous_raw = u''
|
||||||
line_count = 0
|
separator = u'<br>'
|
||||||
for line in text:
|
html_lines = map(expand_tags, lines)
|
||||||
if line_count != -1:
|
# Text too long so go to next page.
|
||||||
line_count += 1
|
if not self._text_fits_on_slide(separator.join(html_lines)):
|
||||||
styled_line = expand_tags(line) + line_end
|
html_text, previous_raw = self._binary_chop(formatted,
|
||||||
styled_text += styled_line
|
previous_html, previous_raw, html_lines, lines, separator, u'')
|
||||||
html = self.page_shell + styled_text + u'</div></body></html>'
|
else:
|
||||||
self.web.setHtml(html)
|
previous_raw = separator.join(lines)
|
||||||
# Text too long so go to next page
|
if previous_raw:
|
||||||
if self.web_frame.contentsSize().height() > self.page_height:
|
formatted.append(previous_raw)
|
||||||
if force_page and line_count > 0:
|
log.debug(u'_paginate_slide - End')
|
||||||
Receiver.send_message(u'theme_line_count', line_count)
|
|
||||||
line_count = -1
|
|
||||||
if html_text.endswith(u'<br>'):
|
|
||||||
html_text = html_text[:len(html_text)-4]
|
|
||||||
formatted.append(html_text)
|
|
||||||
html_text = u''
|
|
||||||
styled_text = styled_line
|
|
||||||
html_text += line + line_end
|
|
||||||
if html_text.endswith(u'<br>'):
|
|
||||||
html_text = html_text[:len(html_text)-4]
|
|
||||||
formatted.append(html_text)
|
|
||||||
log.debug(u'format_slide - End')
|
|
||||||
return formatted
|
return formatted
|
||||||
|
|
||||||
|
def _paginate_slide_words(self, lines, line_end):
|
||||||
|
"""
|
||||||
|
Figure out how much text can appear on a slide, using the current
|
||||||
|
theme settings.
|
||||||
|
**Note:** The smallest possible "unit" of text for a slide is one word.
|
||||||
|
If one line is too long it will be processed word by word. This is
|
||||||
|
sometimes need for **bible** verses.
|
||||||
|
|
||||||
|
``lines``
|
||||||
|
The text to be fitted on the slide split into lines.
|
||||||
|
|
||||||
|
``line_end``
|
||||||
|
The text added after each line. Either ``u' '`` or ``u'<br>``.
|
||||||
|
This is needed for **bibles**.
|
||||||
|
"""
|
||||||
|
log.debug(u'_paginate_slide_words - Start')
|
||||||
|
formatted = []
|
||||||
|
previous_html = u''
|
||||||
|
previous_raw = u''
|
||||||
|
for line in lines:
|
||||||
|
line = line.strip()
|
||||||
|
html_line = expand_tags(line)
|
||||||
|
# Text too long so go to next page.
|
||||||
|
if not self._text_fits_on_slide(previous_html + html_line):
|
||||||
|
# Check if there was a verse before the current one and append
|
||||||
|
# it, when it fits on the page.
|
||||||
|
if previous_html:
|
||||||
|
if self._text_fits_on_slide(previous_html):
|
||||||
|
formatted.append(previous_raw)
|
||||||
|
previous_html = u''
|
||||||
|
previous_raw = u''
|
||||||
|
# Now check if the current verse will fit, if it does
|
||||||
|
# not we have to start to process the verse word by
|
||||||
|
# word.
|
||||||
|
if self._text_fits_on_slide(html_line):
|
||||||
|
previous_html = html_line + line_end
|
||||||
|
previous_raw = line + line_end
|
||||||
|
continue
|
||||||
|
# Figure out how many words of the line will fit on screen as
|
||||||
|
# the line will not fit as a whole.
|
||||||
|
raw_words = self._words_split(line)
|
||||||
|
html_words = map(expand_tags, raw_words)
|
||||||
|
previous_html, previous_raw = self._binary_chop(
|
||||||
|
formatted, previous_html, previous_raw, html_words,
|
||||||
|
raw_words, u' ', line_end)
|
||||||
|
else:
|
||||||
|
previous_html += html_line + line_end
|
||||||
|
previous_raw += line + line_end
|
||||||
|
formatted.append(previous_raw)
|
||||||
|
log.debug(u'_paginate_slide_words - End')
|
||||||
|
return formatted
|
||||||
|
|
||||||
|
def _get_start_tags(self, raw_text):
|
||||||
|
"""
|
||||||
|
Tests the given text for not closed formatting tags and returns a tuple
|
||||||
|
consisting of three unicode strings::
|
||||||
|
|
||||||
|
(u'{st}{r}Text text text{/r}{/st}', u'{st}{r}', u'<strong>
|
||||||
|
<span style="-webkit-text-fill-color:red">')
|
||||||
|
|
||||||
|
The first unicode string is the text, with correct closing tags. The
|
||||||
|
second unicode string are OpenLP's opening formatting tags and the third
|
||||||
|
unicode string the html opening formatting tags.
|
||||||
|
|
||||||
|
``raw_text``
|
||||||
|
The text to test. The text must **not** contain html tags, only
|
||||||
|
OpenLP formatting tags are allowed::
|
||||||
|
|
||||||
|
{st}{r}Text text text
|
||||||
|
"""
|
||||||
|
raw_tags = []
|
||||||
|
html_tags = []
|
||||||
|
for tag in FormattingTags.get_html_tags():
|
||||||
|
if tag[u'start tag'] == u'{br}':
|
||||||
|
continue
|
||||||
|
if raw_text.count(tag[u'start tag']) != \
|
||||||
|
raw_text.count(tag[u'end tag']):
|
||||||
|
raw_tags.append(
|
||||||
|
(raw_text.find(tag[u'start tag']), tag[u'start tag'],
|
||||||
|
tag[u'end tag']))
|
||||||
|
html_tags.append(
|
||||||
|
(raw_text.find(tag[u'start tag']), tag[u'start html']))
|
||||||
|
# Sort the lists, so that the tags which were opened first on the first
|
||||||
|
# slide (the text we are checking) will be opened first on the next
|
||||||
|
# slide as well.
|
||||||
|
raw_tags.sort(key=lambda tag: tag[0])
|
||||||
|
html_tags.sort(key=lambda tag: tag[0])
|
||||||
|
# Create a list with closing tags for the raw_text.
|
||||||
|
end_tags = [tag[2] for tag in raw_tags]
|
||||||
|
end_tags.reverse()
|
||||||
|
# Remove the indexes.
|
||||||
|
raw_tags = [tag[1] for tag in raw_tags]
|
||||||
|
html_tags = [tag[1] for tag in html_tags]
|
||||||
|
return raw_text + u''.join(end_tags), u''.join(raw_tags), \
|
||||||
|
u''.join(html_tags)
|
||||||
|
|
||||||
|
def _binary_chop(self, formatted, previous_html, previous_raw, html_list,
|
||||||
|
raw_list, separator, line_end):
|
||||||
|
"""
|
||||||
|
This implements the binary chop algorithm for faster rendering. This
|
||||||
|
algorithm works line based (line by line) and word based (word by word).
|
||||||
|
It is assumed that this method is **only** called, when the lines/words
|
||||||
|
to be rendered do **not** fit as a whole.
|
||||||
|
|
||||||
|
``formatted``
|
||||||
|
The list to append any slides.
|
||||||
|
|
||||||
|
``previous_html``
|
||||||
|
The html text which is know to fit on a slide, but is not yet added
|
||||||
|
to the list of slides. (unicode string)
|
||||||
|
|
||||||
|
``previous_raw``
|
||||||
|
The raw text (with formatting tags) which is know to fit on a slide,
|
||||||
|
but is not yet added to the list of slides. (unicode string)
|
||||||
|
|
||||||
|
``html_list``
|
||||||
|
The elements which do not fit on a slide and needs to be processed
|
||||||
|
using the binary chop. The text contains html.
|
||||||
|
|
||||||
|
``raw_list``
|
||||||
|
The elements which do not fit on a slide and needs to be processed
|
||||||
|
using the binary chop. The elements can contain formatting tags.
|
||||||
|
|
||||||
|
``separator``
|
||||||
|
The separator for the elements. For lines this is ``u'<br>'`` and
|
||||||
|
for words this is ``u' '``.
|
||||||
|
|
||||||
|
``line_end``
|
||||||
|
The text added after each "element line". Either ``u' '`` or
|
||||||
|
``u'<br>``. This is needed for bibles.
|
||||||
|
"""
|
||||||
|
smallest_index = 0
|
||||||
|
highest_index = len(html_list) - 1
|
||||||
|
index = int(highest_index / 2)
|
||||||
|
while True:
|
||||||
|
if not self._text_fits_on_slide(
|
||||||
|
previous_html + separator.join(html_list[:index + 1]).strip()):
|
||||||
|
# We know that it does not fit, so change/calculate the
|
||||||
|
# new index and highest_index accordingly.
|
||||||
|
highest_index = index
|
||||||
|
index = int(index - (index - smallest_index) / 2)
|
||||||
|
else:
|
||||||
|
smallest_index = index
|
||||||
|
index = int(index + (highest_index - index) / 2)
|
||||||
|
# We found the number of words which will fit.
|
||||||
|
if smallest_index == index or highest_index == index:
|
||||||
|
index = smallest_index
|
||||||
|
text = previous_raw.rstrip(u'<br>') + \
|
||||||
|
separator.join(raw_list[:index + 1])
|
||||||
|
text, raw_tags, html_tags = self._get_start_tags(text)
|
||||||
|
formatted.append(text)
|
||||||
|
previous_html = u''
|
||||||
|
previous_raw = u''
|
||||||
|
# Stop here as the theme line count was requested.
|
||||||
|
if self.force_page:
|
||||||
|
Receiver.send_message(u'theme_line_count', index + 1)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
# Check if the remaining elements fit on the slide.
|
||||||
|
if self._text_fits_on_slide(
|
||||||
|
html_tags + separator.join(html_list[index + 1:]).strip()):
|
||||||
|
previous_html = html_tags + separator.join(
|
||||||
|
html_list[index + 1:]).strip() + line_end
|
||||||
|
previous_raw = raw_tags + separator.join(
|
||||||
|
raw_list[index + 1:]).strip() + line_end
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# The remaining elements do not fit, thus reset the indexes,
|
||||||
|
# create a new list and continue.
|
||||||
|
raw_list = raw_list[index + 1:]
|
||||||
|
raw_list[0] = raw_tags + raw_list[0]
|
||||||
|
html_list = html_list[index + 1:]
|
||||||
|
html_list[0] = html_tags + html_list[0]
|
||||||
|
smallest_index = 0
|
||||||
|
highest_index = len(html_list) - 1
|
||||||
|
index = int(highest_index / 2)
|
||||||
|
return previous_html, previous_raw
|
||||||
|
|
||||||
|
def _text_fits_on_slide(self, text):
|
||||||
|
"""
|
||||||
|
Checks if the given ``text`` fits on a slide. If it does ``True`` is
|
||||||
|
returned, otherwise ``False``.
|
||||||
|
|
||||||
|
``text``
|
||||||
|
The text to check. It may contain HTML tags.
|
||||||
|
"""
|
||||||
|
self.web_frame.evaluateJavaScript(u'show_text("%s")' %
|
||||||
|
text.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
|
||||||
|
return self.web_frame.contentsSize().height() <= self.page_height
|
||||||
|
|
||||||
|
def _words_split(self, line):
|
||||||
|
"""
|
||||||
|
Split the slide up by word so can wrap better
|
||||||
|
"""
|
||||||
|
# this parse we are to be wordy
|
||||||
|
line = line.replace(u'\n', u' ')
|
||||||
|
return line.split(u' ')
|
||||||
|
|
|
@ -1,260 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# OpenLP - Open Source Lyrics Projection #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
|
||||||
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
|
|
||||||
# Gorven, Scott Guerrieri, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# This program is free software; you can redistribute it and/or modify it #
|
|
||||||
# under the terms of the GNU General Public License as published by the Free #
|
|
||||||
# Software Foundation; version 2 of the License. #
|
|
||||||
# #
|
|
||||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
|
||||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
|
||||||
# more details. #
|
|
||||||
# #
|
|
||||||
# You should have received a copy of the GNU General Public License along #
|
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
|
||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from PyQt4 import QtCore
|
|
||||||
|
|
||||||
from openlp.core.lib import Renderer, ServiceItem, ImageManager
|
|
||||||
from openlp.core.lib.theme import ThemeLevel
|
|
||||||
from openlp.core.ui import MainDisplay
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
VERSE = u'The Lord said to {r}Noah{/r}: \n' \
|
|
||||||
'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n' \
|
|
||||||
'The Lord said to {g}Noah{/g}:\n' \
|
|
||||||
'There\'s gonna be a {st}floody{/st}, {it}floody{/it}\n' \
|
|
||||||
'Get those children out of the muddy, muddy \n' \
|
|
||||||
'{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}' \
|
|
||||||
'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
|
|
||||||
FOOTER = [u'Arky Arky (Unknown)', u'Public Domain', u'CCLI 123456']
|
|
||||||
|
|
||||||
class RenderManager(object):
|
|
||||||
"""
|
|
||||||
Class to pull all Renderer interactions into one place. The plugins will
|
|
||||||
call helper methods to do the rendering but this class will provide
|
|
||||||
display defense code.
|
|
||||||
|
|
||||||
``theme_manager``
|
|
||||||
The ThemeManager instance, used to get the current theme details.
|
|
||||||
|
|
||||||
``screens``
|
|
||||||
Contains information about the Screens.
|
|
||||||
|
|
||||||
``screen_number``
|
|
||||||
Defaults to *0*. The index of the output/display screen.
|
|
||||||
"""
|
|
||||||
log.info(u'RenderManager Loaded')
|
|
||||||
|
|
||||||
def __init__(self, theme_manager, screens):
|
|
||||||
"""
|
|
||||||
Initialise the render manager.
|
|
||||||
"""
|
|
||||||
log.debug(u'Initilisation started')
|
|
||||||
self.screens = screens
|
|
||||||
self.image_manager = ImageManager()
|
|
||||||
self.display = MainDisplay(self, screens, False)
|
|
||||||
self.display.imageManager = self.image_manager
|
|
||||||
self.theme_manager = theme_manager
|
|
||||||
self.renderer = Renderer()
|
|
||||||
self.calculate_default(self.screens.current[u'size'])
|
|
||||||
self.theme = u''
|
|
||||||
self.service_theme = u''
|
|
||||||
self.theme_level = u''
|
|
||||||
self.override_background = None
|
|
||||||
self.theme_data = None
|
|
||||||
self.force_page = False
|
|
||||||
|
|
||||||
def update_display(self):
|
|
||||||
"""
|
|
||||||
Updates the render manager's information about the current screen.
|
|
||||||
"""
|
|
||||||
log.debug(u'Update Display')
|
|
||||||
self.calculate_default(self.screens.current[u'size'])
|
|
||||||
self.display = MainDisplay(self, self.screens, False)
|
|
||||||
self.display.imageManager = self.image_manager
|
|
||||||
self.display.setup()
|
|
||||||
self.renderer.bg_frame = None
|
|
||||||
self.theme_data = None
|
|
||||||
self.image_manager.update_display(self.width, self.height)
|
|
||||||
|
|
||||||
def set_global_theme(self, global_theme, theme_level=ThemeLevel.Global):
|
|
||||||
"""
|
|
||||||
Set the global-level theme and the theme level.
|
|
||||||
|
|
||||||
``global_theme``
|
|
||||||
The global-level theme to be set.
|
|
||||||
|
|
||||||
``theme_level``
|
|
||||||
Defaults to *``ThemeLevel.Global``*. The theme level, can be
|
|
||||||
``ThemeLevel.Global``, ``ThemeLevel.Service`` or
|
|
||||||
``ThemeLevel.Song``.
|
|
||||||
"""
|
|
||||||
self.global_theme = global_theme
|
|
||||||
self.theme_level = theme_level
|
|
||||||
self.global_theme_data = \
|
|
||||||
self.theme_manager.getThemeData(self.global_theme)
|
|
||||||
self.theme_data = None
|
|
||||||
|
|
||||||
def set_service_theme(self, service_theme):
|
|
||||||
"""
|
|
||||||
Set the service-level theme.
|
|
||||||
|
|
||||||
``service_theme``
|
|
||||||
The service-level theme to be set.
|
|
||||||
"""
|
|
||||||
self.service_theme = service_theme
|
|
||||||
self.theme_data = None
|
|
||||||
|
|
||||||
def set_override_theme(self, theme, overrideLevels=False):
|
|
||||||
"""
|
|
||||||
Set the appropriate theme depending on the theme level.
|
|
||||||
Called by the service item when building a display frame
|
|
||||||
|
|
||||||
``theme``
|
|
||||||
The name of the song-level theme. None means the service
|
|
||||||
item wants to use the given value.
|
|
||||||
|
|
||||||
``overrideLevels``
|
|
||||||
Used to force the theme data passed in to be used.
|
|
||||||
|
|
||||||
"""
|
|
||||||
log.debug(u'set override theme to %s', theme)
|
|
||||||
theme_level = self.theme_level
|
|
||||||
if overrideLevels:
|
|
||||||
theme_level = ThemeLevel.Song
|
|
||||||
if theme_level == ThemeLevel.Global:
|
|
||||||
self.theme = self.global_theme
|
|
||||||
elif theme_level == ThemeLevel.Service:
|
|
||||||
if self.service_theme == u'':
|
|
||||||
self.theme = self.global_theme
|
|
||||||
else:
|
|
||||||
self.theme = self.service_theme
|
|
||||||
else:
|
|
||||||
if theme:
|
|
||||||
self.theme = theme
|
|
||||||
elif theme_level == ThemeLevel.Song or \
|
|
||||||
theme_level == ThemeLevel.Service:
|
|
||||||
if self.service_theme == u'':
|
|
||||||
self.theme = self.global_theme
|
|
||||||
else:
|
|
||||||
self.theme = self.service_theme
|
|
||||||
else:
|
|
||||||
self.theme = self.global_theme
|
|
||||||
if self.theme != self.renderer.theme_name or self.theme_data is None \
|
|
||||||
or overrideLevels:
|
|
||||||
log.debug(u'theme is now %s', self.theme)
|
|
||||||
# Force the theme to be the one passed in.
|
|
||||||
if overrideLevels:
|
|
||||||
self.theme_data = theme
|
|
||||||
else:
|
|
||||||
self.theme_data = self.theme_manager.getThemeData(self.theme)
|
|
||||||
self.calculate_default(self.screens.current[u'size'])
|
|
||||||
self.renderer.set_theme(self.theme_data)
|
|
||||||
self.build_text_rectangle(self.theme_data)
|
|
||||||
self.image_manager.add_image(self.theme_data.theme_name,
|
|
||||||
self.theme_data.background_filename)
|
|
||||||
return self.renderer._rect, self.renderer._rect_footer
|
|
||||||
|
|
||||||
def build_text_rectangle(self, theme):
|
|
||||||
"""
|
|
||||||
Builds a text block using the settings in ``theme``
|
|
||||||
and the size of the display screen.height.
|
|
||||||
|
|
||||||
``theme``
|
|
||||||
The theme to build a text block for.
|
|
||||||
"""
|
|
||||||
log.debug(u'build_text_rectangle')
|
|
||||||
main_rect = None
|
|
||||||
footer_rect = None
|
|
||||||
if not theme.font_main_override:
|
|
||||||
main_rect = QtCore.QRect(10, 0, self.width - 20, self.footer_start)
|
|
||||||
else:
|
|
||||||
main_rect = QtCore.QRect(theme.font_main_x, theme.font_main_y,
|
|
||||||
theme.font_main_width - 1, theme.font_main_height - 1)
|
|
||||||
if not theme.font_footer_override:
|
|
||||||
footer_rect = QtCore.QRect(10, self.footer_start, self.width - 20,
|
|
||||||
self.height - self.footer_start)
|
|
||||||
else:
|
|
||||||
footer_rect = QtCore.QRect(theme.font_footer_x,
|
|
||||||
theme.font_footer_y, theme.font_footer_width - 1,
|
|
||||||
theme.font_footer_height - 1)
|
|
||||||
self.renderer.set_text_rectangle(main_rect, footer_rect)
|
|
||||||
|
|
||||||
def generate_preview(self, theme_data, force_page=False):
|
|
||||||
"""
|
|
||||||
Generate a preview of a theme.
|
|
||||||
|
|
||||||
``theme_data``
|
|
||||||
The theme to generated a preview for.
|
|
||||||
|
|
||||||
``force_page``
|
|
||||||
Flag to tell message lines per page need to be generated.
|
|
||||||
"""
|
|
||||||
log.debug(u'generate preview')
|
|
||||||
# save value for use in format_slide
|
|
||||||
self.force_page = force_page
|
|
||||||
# set the default image size for previews
|
|
||||||
self.calculate_default(self.screens.preview[u'size'])
|
|
||||||
# build a service item to generate preview
|
|
||||||
serviceItem = ServiceItem()
|
|
||||||
serviceItem.theme = theme_data
|
|
||||||
if self.force_page:
|
|
||||||
# make big page for theme edit dialog to get line count
|
|
||||||
serviceItem.add_from_text(u'', VERSE + VERSE + VERSE, FOOTER)
|
|
||||||
else:
|
|
||||||
self.image_manager.del_image(theme_data.theme_name)
|
|
||||||
serviceItem.add_from_text(u'', VERSE, FOOTER)
|
|
||||||
serviceItem.render_manager = self
|
|
||||||
serviceItem.raw_footer = FOOTER
|
|
||||||
serviceItem.render(True)
|
|
||||||
if not self.force_page:
|
|
||||||
self.display.buildHtml(serviceItem)
|
|
||||||
raw_html = serviceItem.get_rendered_frame(0)
|
|
||||||
preview = self.display.text(raw_html)
|
|
||||||
# Reset the real screen size for subsequent render requests
|
|
||||||
self.calculate_default(self.screens.current[u'size'])
|
|
||||||
return preview
|
|
||||||
|
|
||||||
def format_slide(self, words, line_break):
|
|
||||||
"""
|
|
||||||
Calculate how much text can fit on a slide.
|
|
||||||
|
|
||||||
``words``
|
|
||||||
The words to go on the slides.
|
|
||||||
|
|
||||||
``line_break``
|
|
||||||
Add line endings after each line of text used for bibles.
|
|
||||||
"""
|
|
||||||
log.debug(u'format slide')
|
|
||||||
return self.renderer.format_slide(words, line_break, self.force_page)
|
|
||||||
|
|
||||||
def calculate_default(self, screen):
|
|
||||||
"""
|
|
||||||
Calculate the default dimentions of the screen.
|
|
||||||
|
|
||||||
``screen``
|
|
||||||
The QSize of the screen.
|
|
||||||
"""
|
|
||||||
log.debug(u'calculate default %s', screen)
|
|
||||||
self.width = screen.width()
|
|
||||||
self.height = screen.height()
|
|
||||||
self.screen_ratio = float(self.height) / float(self.width)
|
|
||||||
log.debug(u'calculate default %d, %d, %f',
|
|
||||||
self.width, self.height, self.screen_ratio)
|
|
||||||
# 90% is start of footer
|
|
||||||
self.footer_start = int(self.height * 0.90)
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -29,6 +30,7 @@ import logging
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.lib import build_icon
|
from openlp.core.lib import build_icon
|
||||||
|
from openlp.core.lib.ui import icon_action
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -61,6 +63,7 @@ class SearchEdit(QtGui.QLineEdit):
|
||||||
self._onSearchEditTextChanged
|
self._onSearchEditTextChanged
|
||||||
)
|
)
|
||||||
self._updateStyleSheet()
|
self._updateStyleSheet()
|
||||||
|
self.setAcceptDrops(False)
|
||||||
|
|
||||||
def _updateStyleSheet(self):
|
def _updateStyleSheet(self):
|
||||||
"""
|
"""
|
||||||
|
@ -73,10 +76,10 @@ class SearchEdit(QtGui.QLineEdit):
|
||||||
if hasattr(self, u'menuButton'):
|
if hasattr(self, u'menuButton'):
|
||||||
leftPadding = self.menuButton.width()
|
leftPadding = self.menuButton.width()
|
||||||
self.setStyleSheet(
|
self.setStyleSheet(
|
||||||
u'QLineEdit { padding-left: %spx; padding-right: %spx; } ' % \
|
u'QLineEdit { padding-left: %spx; padding-right: %spx; } ' %
|
||||||
(leftPadding, rightPadding))
|
(leftPadding, rightPadding))
|
||||||
else:
|
else:
|
||||||
self.setStyleSheet(u'QLineEdit { padding-right: %spx; } ' % \
|
self.setStyleSheet(u'QLineEdit { padding-right: %spx; } ' %
|
||||||
rightPadding)
|
rightPadding)
|
||||||
msz = self.minimumSizeHint()
|
msz = self.minimumSizeHint()
|
||||||
self.setMinimumSize(
|
self.setMinimumSize(
|
||||||
|
@ -109,6 +112,21 @@ class SearchEdit(QtGui.QLineEdit):
|
||||||
"""
|
"""
|
||||||
return self._currentSearchType
|
return self._currentSearchType
|
||||||
|
|
||||||
|
def setCurrentSearchType(self, identifier):
|
||||||
|
"""
|
||||||
|
Set a new current search type.
|
||||||
|
|
||||||
|
``identifier``
|
||||||
|
The search type identifier (int).
|
||||||
|
"""
|
||||||
|
menu = self.menuButton.menu()
|
||||||
|
for action in menu.actions():
|
||||||
|
if identifier == action.data().toInt()[0]:
|
||||||
|
self.menuButton.setDefaultAction(action)
|
||||||
|
self._currentSearchType = identifier
|
||||||
|
self.emit(QtCore.SIGNAL(u'searchTypeChanged(int)'), identifier)
|
||||||
|
return True
|
||||||
|
|
||||||
def setSearchTypes(self, items):
|
def setSearchTypes(self, items):
|
||||||
"""
|
"""
|
||||||
A list of tuples to be used in the search type menu. The first item in
|
A list of tuples to be used in the search type menu. The first item in
|
||||||
|
@ -132,7 +150,8 @@ class SearchEdit(QtGui.QLineEdit):
|
||||||
menu = QtGui.QMenu(self)
|
menu = QtGui.QMenu(self)
|
||||||
first = None
|
first = None
|
||||||
for identifier, icon, title in items:
|
for identifier, icon, title in items:
|
||||||
action = QtGui.QAction(build_icon(icon), title, menu)
|
action = icon_action(menu, u'', icon)
|
||||||
|
action.setText(title)
|
||||||
action.setData(QtCore.QVariant(identifier))
|
action.setData(QtCore.QVariant(identifier))
|
||||||
menu.addAction(action)
|
menu.addAction(action)
|
||||||
QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered(bool)'),
|
QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered(bool)'),
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -28,13 +29,13 @@ The :mod:`serviceitem` provides the service item functionality including the
|
||||||
type and capability of an item.
|
type and capability of an item.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import cgi
|
||||||
import datetime
|
import datetime
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from openlp.core.lib import build_icon, clean_tags, expand_tags
|
from openlp.core.lib import build_icon, clean_tags, expand_tags, translate
|
||||||
from openlp.core.lib.ui import UiStrings
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -51,18 +52,21 @@ class ItemCapabilities(object):
|
||||||
"""
|
"""
|
||||||
Provides an enumeration of a serviceitem's capabilities
|
Provides an enumeration of a serviceitem's capabilities
|
||||||
"""
|
"""
|
||||||
AllowsPreview = 1
|
CanPreview = 1
|
||||||
AllowsEdit = 2
|
CanEdit = 2
|
||||||
AllowsMaintain = 3
|
CanMaintain = 3
|
||||||
RequiresMedia = 4
|
RequiresMedia = 4
|
||||||
AllowsLoop = 5
|
CanLoop = 5
|
||||||
AllowsAdditions = 6
|
CanAppend = 6
|
||||||
NoLineBreaks = 7
|
NoLineBreaks = 7
|
||||||
OnLoadUpdate = 8
|
OnLoadUpdate = 8
|
||||||
AddIfNewItem = 9
|
AddIfNewItem = 9
|
||||||
ProvidesOwnDisplay = 10
|
ProvidesOwnDisplay = 10
|
||||||
AllowsDetailedTitleDisplay = 11
|
HasDetailedTitleDisplay = 11
|
||||||
AllowsVarableStartTime = 12
|
HasVariableStartTime = 12
|
||||||
|
CanSoftBreak = 13
|
||||||
|
CanWordSplit = 14
|
||||||
|
HasBackgroundAudio = 15
|
||||||
|
|
||||||
|
|
||||||
class ServiceItem(object):
|
class ServiceItem(object):
|
||||||
|
@ -81,7 +85,7 @@ class ServiceItem(object):
|
||||||
The plugin that this service item belongs to.
|
The plugin that this service item belongs to.
|
||||||
"""
|
"""
|
||||||
if plugin:
|
if plugin:
|
||||||
self.render_manager = plugin.renderManager
|
self.renderer = plugin.renderer
|
||||||
self.name = plugin.name
|
self.name = plugin.name
|
||||||
self.title = u''
|
self.title = u''
|
||||||
self.shortname = u''
|
self.shortname = u''
|
||||||
|
@ -109,14 +113,18 @@ class ServiceItem(object):
|
||||||
self.edit_id = None
|
self.edit_id = None
|
||||||
self.xml_version = None
|
self.xml_version = None
|
||||||
self.start_time = 0
|
self.start_time = 0
|
||||||
|
self.end_time = 0
|
||||||
self.media_length = 0
|
self.media_length = 0
|
||||||
|
self.from_service = False
|
||||||
|
self.image_border = u'#000000'
|
||||||
|
self.background_audio = []
|
||||||
|
self.theme_overwritten = False
|
||||||
self._new_item()
|
self._new_item()
|
||||||
|
|
||||||
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())
|
||||||
|
|
||||||
|
@ -149,44 +157,47 @@ class ServiceItem(object):
|
||||||
self.icon = icon
|
self.icon = icon
|
||||||
self.iconic_representation = build_icon(icon)
|
self.iconic_representation = build_icon(icon)
|
||||||
|
|
||||||
def render(self, useOverride=False):
|
def render(self, use_override=False):
|
||||||
"""
|
"""
|
||||||
The render method is what generates the frames for the screen and
|
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 build 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 = []
|
||||||
self.bg_image_bytes = None
|
self.bg_image_bytes = None
|
||||||
line_break = True
|
|
||||||
if self.is_capable(ItemCapabilities.NoLineBreaks):
|
|
||||||
line_break = False
|
|
||||||
theme = self.theme if self.theme else None
|
theme = self.theme if self.theme else None
|
||||||
self.main, self.footer = \
|
self.main, self.footer = \
|
||||||
self.render_manager.set_override_theme(theme, useOverride)
|
self.renderer.set_override_theme(theme, use_override)
|
||||||
self.themedata = self.render_manager.renderer._theme
|
self.themedata = self.renderer.theme_data
|
||||||
if self.service_item_type == ServiceItemType.Text:
|
if self.service_item_type == ServiceItemType.Text:
|
||||||
log.debug(u'Formatting slides')
|
log.debug(u'Formatting slides')
|
||||||
for slide in self._raw_frames:
|
for slide in self._raw_frames:
|
||||||
formatted = self.render_manager \
|
pages = self.renderer.format_slide(slide[u'raw_slide'], self)
|
||||||
.format_slide(slide[u'raw_slide'], line_break)
|
for page in pages:
|
||||||
for page in formatted:
|
page = page.replace(u'<br>', u'{br}')
|
||||||
self._display_frames.append(
|
html = expand_tags(cgi.escape(page.rstrip()))
|
||||||
{u'title': clean_tags(page),
|
self._display_frames.append({
|
||||||
|
u'title': clean_tags(page),
|
||||||
u'text': clean_tags(page.rstrip()),
|
u'text': clean_tags(page.rstrip()),
|
||||||
u'html': expand_tags(page.rstrip()),
|
u'html': html.replace(u'&nbsp;', u' '),
|
||||||
u'verseTag': slide[u'verseTag'] })
|
u'verseTag': slide[u'verseTag']
|
||||||
|
})
|
||||||
elif self.service_item_type == ServiceItemType.Image or \
|
elif self.service_item_type == ServiceItemType.Image or \
|
||||||
self.service_item_type == ServiceItemType.Command:
|
self.service_item_type == ServiceItemType.Command:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
log.error(u'Invalid value renderer: %s' % self.service_item_type)
|
log.error(u'Invalid value renderer: %s' % self.service_item_type)
|
||||||
self.title = clean_tags(self.title)
|
self.title = clean_tags(self.title)
|
||||||
|
# The footer should never be None, but to be compatible with a few
|
||||||
|
# nightly builds between 1.9.4 and 1.9.5, we have to correct this to
|
||||||
|
# avoid tracebacks.
|
||||||
|
if self.raw_footer is None:
|
||||||
|
self.raw_footer = []
|
||||||
self.foot_text = \
|
self.foot_text = \
|
||||||
u'<br>'.join([footer for footer in self.raw_footer if footer])
|
u'<br>'.join([footer for footer in self.raw_footer if footer])
|
||||||
|
|
||||||
def add_from_image(self, path, title):
|
def add_from_image(self, path, title, background=None):
|
||||||
"""
|
"""
|
||||||
Add an image slide to the service item.
|
Add an image slide to the service item.
|
||||||
|
|
||||||
|
@ -196,9 +207,12 @@ class ServiceItem(object):
|
||||||
``title``
|
``title``
|
||||||
A title for the slide in the service item.
|
A title for the slide in the service item.
|
||||||
"""
|
"""
|
||||||
|
if background:
|
||||||
|
self.image_border = background
|
||||||
self.service_item_type = ServiceItemType.Image
|
self.service_item_type = ServiceItemType.Image
|
||||||
self._raw_frames.append({u'title': title, u'path': path})
|
self._raw_frames.append({u'title': title, u'path': path})
|
||||||
self.render_manager.image_manager.add_image(title, path)
|
self.renderer.imageManager.add_image(title, path, u'image',
|
||||||
|
self.image_border)
|
||||||
self._new_item()
|
self._new_item()
|
||||||
|
|
||||||
def add_from_text(self, title, raw_slide, verse_tag=None):
|
def add_from_text(self, title, raw_slide, verse_tag=None):
|
||||||
|
@ -211,6 +225,8 @@ class ServiceItem(object):
|
||||||
``raw_slide``
|
``raw_slide``
|
||||||
The raw text of the slide.
|
The raw text of the slide.
|
||||||
"""
|
"""
|
||||||
|
if verse_tag:
|
||||||
|
verse_tag = verse_tag.upper()
|
||||||
self.service_item_type = ServiceItemType.Text
|
self.service_item_type = ServiceItemType.Text
|
||||||
title = title.split(u'\n')[0]
|
title = title.split(u'\n')[0]
|
||||||
self._raw_frames.append(
|
self._raw_frames.append(
|
||||||
|
@ -256,15 +272,16 @@ class ServiceItem(object):
|
||||||
u'data': self.data_string,
|
u'data': self.data_string,
|
||||||
u'xml_version': self.xml_version,
|
u'xml_version': self.xml_version,
|
||||||
u'start_time': self.start_time,
|
u'start_time': self.start_time,
|
||||||
u'media_length': self.media_length
|
u'end_time': self.end_time,
|
||||||
|
u'media_length': self.media_length,
|
||||||
|
u'background_audio': self.background_audio,
|
||||||
|
u'theme_overwritten': self.theme_overwritten
|
||||||
}
|
}
|
||||||
service_data = []
|
service_data = []
|
||||||
if self.service_item_type == ServiceItemType.Text:
|
if self.service_item_type == ServiceItemType.Text:
|
||||||
for slide in self._raw_frames:
|
service_data = [slide for slide in self._raw_frames]
|
||||||
service_data.append(slide)
|
|
||||||
elif self.service_item_type == ServiceItemType.Image:
|
elif self.service_item_type == ServiceItemType.Image:
|
||||||
for slide in self._raw_frames:
|
service_data = [slide[u'title'] for slide in self._raw_frames]
|
||||||
service_data.append(slide[u'title'])
|
|
||||||
elif self.service_item_type == ServiceItemType.Command:
|
elif self.service_item_type == ServiceItemType.Command:
|
||||||
for slide in self._raw_frames:
|
for slide in self._raw_frames:
|
||||||
service_data.append(
|
service_data.append(
|
||||||
|
@ -302,8 +319,13 @@ class ServiceItem(object):
|
||||||
self.xml_version = header[u'xml_version']
|
self.xml_version = header[u'xml_version']
|
||||||
if u'start_time' in header:
|
if u'start_time' in header:
|
||||||
self.start_time = header[u'start_time']
|
self.start_time = header[u'start_time']
|
||||||
|
if u'end_time' in header:
|
||||||
|
self.end_time = header[u'end_time']
|
||||||
if u'media_length' in header:
|
if u'media_length' in header:
|
||||||
self.media_length = header[u'media_length']
|
self.media_length = header[u'media_length']
|
||||||
|
if u'background_audio' in header:
|
||||||
|
self.background_audio = header[u'background_audio']
|
||||||
|
self.theme_overwritten = header.get(u'theme_overwritten', False)
|
||||||
if self.service_item_type == ServiceItemType.Text:
|
if self.service_item_type == ServiceItemType.Text:
|
||||||
for slide in serviceitem[u'serviceitem'][u'data']:
|
for slide in serviceitem[u'serviceitem'][u'data']:
|
||||||
self._raw_frames.append(slide)
|
self._raw_frames.append(slide)
|
||||||
|
@ -325,7 +347,7 @@ class ServiceItem(object):
|
||||||
if self.is_text():
|
if self.is_text():
|
||||||
return self.title
|
return self.title
|
||||||
else:
|
else:
|
||||||
if ItemCapabilities.AllowsDetailedTitleDisplay in self.capabilities:
|
if ItemCapabilities.HasDetailedTitleDisplay in self.capabilities:
|
||||||
return self._raw_frames[0][u'title']
|
return self._raw_frames[0][u'title']
|
||||||
elif len(self._raw_frames) > 1:
|
elif len(self._raw_frames) > 1:
|
||||||
return self.title
|
return self.title
|
||||||
|
@ -337,8 +359,19 @@ class ServiceItem(object):
|
||||||
Updates the _uuid with the value from the original one
|
Updates the _uuid with the value from the original one
|
||||||
The _uuid is unique for a given service item but this allows one to
|
The _uuid is unique for a given service item but this allows one to
|
||||||
replace an original version.
|
replace an original version.
|
||||||
|
|
||||||
|
``other``
|
||||||
|
The service item to be merged with
|
||||||
"""
|
"""
|
||||||
self._uuid = other._uuid
|
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)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
"""
|
"""
|
||||||
|
@ -431,16 +464,31 @@ class ServiceItem(object):
|
||||||
start = None
|
start = None
|
||||||
end = None
|
end = None
|
||||||
if self.start_time != 0:
|
if self.start_time != 0:
|
||||||
start = UiStrings.StartTimeCode % \
|
start = unicode(translate('OpenLP.ServiceItem',
|
||||||
|
'<strong>Start</strong>: %s')) % \
|
||||||
unicode(datetime.timedelta(seconds=self.start_time))
|
unicode(datetime.timedelta(seconds=self.start_time))
|
||||||
if self.media_length != 0:
|
if self.media_length != 0:
|
||||||
end = UiStrings.LengthTime % \
|
end = unicode(translate('OpenLP.ServiceItem',
|
||||||
|
'<strong>Length</strong>: %s')) % \
|
||||||
unicode(datetime.timedelta(seconds=self.media_length))
|
unicode(datetime.timedelta(seconds=self.media_length))
|
||||||
if not start and not end:
|
if not start and not end:
|
||||||
return None
|
return u''
|
||||||
elif start and not end:
|
elif start and not end:
|
||||||
return start
|
return start
|
||||||
elif not start and end:
|
elif not start and end:
|
||||||
return end
|
return end
|
||||||
else:
|
else:
|
||||||
return u'%s : %s' % (start, end)
|
return u'%s <br>%s' % (start, end)
|
||||||
|
|
||||||
|
def update_theme(self, theme):
|
||||||
|
"""
|
||||||
|
updates the theme in the service item
|
||||||
|
|
||||||
|
``theme``
|
||||||
|
The new theme to be replaced in the service item
|
||||||
|
"""
|
||||||
|
self.theme_overwritten = (theme == None)
|
||||||
|
self.theme = theme
|
||||||
|
self._new_item()
|
||||||
|
self.render()
|
||||||
|
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -37,26 +38,9 @@ from openlp.core.utils import AppLocation
|
||||||
|
|
||||||
class SettingsManager(object):
|
class SettingsManager(object):
|
||||||
"""
|
"""
|
||||||
Class to control the initial settings for the UI and provide helper
|
Class to provide helper functions for the loading and saving of application
|
||||||
functions for the loading and saving of application settings.
|
settings.
|
||||||
"""
|
"""
|
||||||
def __init__(self, screen):
|
|
||||||
self.screen = screen.current
|
|
||||||
self.width = self.screen[u'size'].width()
|
|
||||||
self.height = self.screen[u'size'].height()
|
|
||||||
self.mainwindow_height = self.height * 0.8
|
|
||||||
mainwindow_docbars = self.width / 5
|
|
||||||
self.mainwindow_left = 0
|
|
||||||
self.mainwindow_right = 0
|
|
||||||
if mainwindow_docbars > 300:
|
|
||||||
self.mainwindow_left = 300
|
|
||||||
self.mainwindow_right = 300
|
|
||||||
else:
|
|
||||||
self.mainwindow_left = mainwindow_docbars
|
|
||||||
self.mainwindow_right = mainwindow_docbars
|
|
||||||
self.slidecontroller = (self.width - (
|
|
||||||
self.mainwindow_left + self.mainwindow_right) - 100) / 2
|
|
||||||
self.slidecontroller_image = self.slidecontroller - 50
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_last_dir(section, num=None):
|
def get_last_dir(section, num=None):
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -31,7 +32,7 @@ class SettingsTab(QtGui.QWidget):
|
||||||
SettingsTab is a helper widget for plugins to define Tabs for the settings
|
SettingsTab is a helper widget for plugins to define Tabs for the settings
|
||||||
dialog.
|
dialog.
|
||||||
"""
|
"""
|
||||||
def __init__(self, title, visible_title=None):
|
def __init__(self, parent, title, visible_title=None, icon_path=None):
|
||||||
"""
|
"""
|
||||||
Constructor to create the Settings tab item.
|
Constructor to create the Settings tab item.
|
||||||
|
|
||||||
|
@ -41,14 +42,15 @@ class SettingsTab(QtGui.QWidget):
|
||||||
``visible_title``
|
``visible_title``
|
||||||
The title of the tab, which is usually displayed on the tab.
|
The title of the tab, which is usually displayed on the tab.
|
||||||
"""
|
"""
|
||||||
QtGui.QWidget.__init__(self)
|
QtGui.QWidget.__init__(self, parent)
|
||||||
self.tabTitle = title
|
self.tabTitle = title
|
||||||
self.tabTitleVisible = visible_title
|
self.tabTitleVisible = visible_title
|
||||||
self.settingsSection = self.tabTitle.lower()
|
self.settingsSection = self.tabTitle.lower()
|
||||||
|
if icon_path:
|
||||||
|
self.icon_path = icon_path
|
||||||
self.setupUi()
|
self.setupUi()
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
self.initialise()
|
self.initialise()
|
||||||
self.preLoad()
|
|
||||||
self.load()
|
self.load()
|
||||||
|
|
||||||
def setupUi(self):
|
def setupUi(self):
|
||||||
|
@ -84,12 +86,6 @@ class SettingsTab(QtGui.QWidget):
|
||||||
left_width = max(left_width, self.leftColumn.minimumSizeHint().width())
|
left_width = max(left_width, self.leftColumn.minimumSizeHint().width())
|
||||||
self.leftColumn.setFixedWidth(left_width)
|
self.leftColumn.setFixedWidth(left_width)
|
||||||
|
|
||||||
def preLoad(self):
|
|
||||||
"""
|
|
||||||
Setup the tab's interface.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def retranslateUi(self):
|
def retranslateUi(self):
|
||||||
"""
|
"""
|
||||||
Setup the interface translation strings.
|
Setup the interface translation strings.
|
||||||
|
@ -116,9 +112,9 @@ class SettingsTab(QtGui.QWidget):
|
||||||
|
|
||||||
def cancel(self):
|
def cancel(self):
|
||||||
"""
|
"""
|
||||||
Reset any settings
|
Reset any settings if cancel pressed
|
||||||
"""
|
"""
|
||||||
pass
|
self.load()
|
||||||
|
|
||||||
def postSetUp(self, postUpdate=False):
|
def postSetUp(self, postUpdate=False):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -23,11 +24,12 @@
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
import logging
|
||||||
import re
|
import re
|
||||||
try:
|
try:
|
||||||
import enchant
|
import enchant
|
||||||
from enchant import DictNotFoundError
|
from enchant import DictNotFoundError
|
||||||
|
from enchant.errors import Error
|
||||||
ENCHANT_AVAILABLE = True
|
ENCHANT_AVAILABLE = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
ENCHANT_AVAILABLE = False
|
ENCHANT_AVAILABLE = False
|
||||||
|
@ -36,22 +38,29 @@ except ImportError:
|
||||||
# http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check
|
# http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check
|
||||||
|
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
from openlp.core.lib import translate, DisplayTags
|
|
||||||
|
from openlp.core.lib import translate, FormattingTags
|
||||||
|
from openlp.core.lib.ui import checkable_action
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
class SpellTextEdit(QtGui.QPlainTextEdit):
|
class SpellTextEdit(QtGui.QPlainTextEdit):
|
||||||
"""
|
"""
|
||||||
Spell checking widget based on QPlanTextEdit.
|
Spell checking widget based on QPlanTextEdit.
|
||||||
"""
|
"""
|
||||||
def __init__(self, *args):
|
def __init__(self, parent=None, formattingTagsAllowed=True):
|
||||||
QtGui.QPlainTextEdit.__init__(self, *args)
|
global ENCHANT_AVAILABLE
|
||||||
|
QtGui.QPlainTextEdit.__init__(self, parent)
|
||||||
|
self.formattingTagsAllowed = formattingTagsAllowed
|
||||||
# Default dictionary based on the current locale.
|
# Default dictionary based on the current locale.
|
||||||
if ENCHANT_AVAILABLE:
|
if ENCHANT_AVAILABLE:
|
||||||
try:
|
try:
|
||||||
self.dictionary = enchant.Dict()
|
self.dictionary = enchant.Dict()
|
||||||
except DictNotFoundError:
|
|
||||||
self.dictionary = enchant.Dict(u'en_US')
|
|
||||||
self.highlighter = Highlighter(self.document())
|
self.highlighter = Highlighter(self.document())
|
||||||
self.highlighter.spellingDictionary = self.dictionary
|
self.highlighter.spellingDictionary = self.dictionary
|
||||||
|
except (Error, DictNotFoundError):
|
||||||
|
ENCHANT_AVAILABLE = False
|
||||||
|
log.debug(u'Could not load default dictionary')
|
||||||
|
|
||||||
def mousePressEvent(self, event):
|
def mousePressEvent(self, event):
|
||||||
"""
|
"""
|
||||||
|
@ -76,6 +85,19 @@ class SpellTextEdit(QtGui.QPlainTextEdit):
|
||||||
if not cursor.hasSelection():
|
if not cursor.hasSelection():
|
||||||
cursor.select(QtGui.QTextCursor.WordUnderCursor)
|
cursor.select(QtGui.QTextCursor.WordUnderCursor)
|
||||||
self.setTextCursor(cursor)
|
self.setTextCursor(cursor)
|
||||||
|
# Add menu with available languages.
|
||||||
|
if ENCHANT_AVAILABLE:
|
||||||
|
lang_menu = QtGui.QMenu(
|
||||||
|
translate('OpenLP.SpellTextEdit', 'Language:'))
|
||||||
|
for lang in enchant.list_languages():
|
||||||
|
action = checkable_action(
|
||||||
|
lang_menu, lang, lang == self.dictionary.tag)
|
||||||
|
action.setText(lang)
|
||||||
|
lang_menu.addAction(action)
|
||||||
|
popupMenu.insertSeparator(popupMenu.actions()[0])
|
||||||
|
popupMenu.insertMenu(popupMenu.actions()[0], lang_menu)
|
||||||
|
QtCore.QObject.connect(lang_menu,
|
||||||
|
QtCore.SIGNAL(u'triggered(QAction*)'), self.setLanguage)
|
||||||
# Check if the selected word is misspelled and offer spelling
|
# Check if the selected word is misspelled and offer spelling
|
||||||
# suggestions if it is.
|
# suggestions if it is.
|
||||||
if ENCHANT_AVAILABLE and self.textCursor().hasSelection():
|
if ENCHANT_AVAILABLE and self.textCursor().hasSelection():
|
||||||
|
@ -89,12 +111,12 @@ class SpellTextEdit(QtGui.QPlainTextEdit):
|
||||||
spell_menu.addAction(action)
|
spell_menu.addAction(action)
|
||||||
# Only add the spelling suggests to the menu if there are
|
# Only add the spelling suggests to the menu if there are
|
||||||
# suggestions.
|
# suggestions.
|
||||||
if len(spell_menu.actions()) != 0:
|
if spell_menu.actions():
|
||||||
popupMenu.insertSeparator(popupMenu.actions()[0])
|
|
||||||
popupMenu.insertMenu(popupMenu.actions()[0], spell_menu)
|
popupMenu.insertMenu(popupMenu.actions()[0], spell_menu)
|
||||||
tagMenu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
|
tagMenu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
|
||||||
'Formatting Tags'))
|
'Formatting Tags'))
|
||||||
for html in DisplayTags.get_html_tags():
|
if self.formattingTagsAllowed:
|
||||||
|
for html in FormattingTags.get_html_tags():
|
||||||
action = SpellAction(html[u'desc'], tagMenu)
|
action = SpellAction(html[u'desc'], tagMenu)
|
||||||
action.correct.connect(self.htmlTag)
|
action.correct.connect(self.htmlTag)
|
||||||
tagMenu.addAction(action)
|
tagMenu.addAction(action)
|
||||||
|
@ -102,6 +124,18 @@ class SpellTextEdit(QtGui.QPlainTextEdit):
|
||||||
popupMenu.insertMenu(popupMenu.actions()[0], tagMenu)
|
popupMenu.insertMenu(popupMenu.actions()[0], tagMenu)
|
||||||
popupMenu.exec_(event.globalPos())
|
popupMenu.exec_(event.globalPos())
|
||||||
|
|
||||||
|
def setLanguage(self, action):
|
||||||
|
"""
|
||||||
|
Changes the language for this spelltextedit.
|
||||||
|
|
||||||
|
``action``
|
||||||
|
The action.
|
||||||
|
"""
|
||||||
|
self.dictionary = enchant.Dict(action.text())
|
||||||
|
self.highlighter.spellingDictionary = self.dictionary
|
||||||
|
self.highlighter.highlightBlock(self.toPlainText())
|
||||||
|
self.highlighter.rehighlight()
|
||||||
|
|
||||||
def correctWord(self, word):
|
def correctWord(self, word):
|
||||||
"""
|
"""
|
||||||
Replaces the selected text with word.
|
Replaces the selected text with word.
|
||||||
|
@ -116,7 +150,7 @@ class SpellTextEdit(QtGui.QPlainTextEdit):
|
||||||
"""
|
"""
|
||||||
Replaces the selected text with word.
|
Replaces the selected text with word.
|
||||||
"""
|
"""
|
||||||
for html in DisplayTags.get_html_tags():
|
for html in FormattingTags.get_html_tags():
|
||||||
if tag == html[u'desc']:
|
if tag == html[u'desc']:
|
||||||
cursor = self.textCursor()
|
cursor = self.textCursor()
|
||||||
if self.textCursor().hasSelection():
|
if self.textCursor().hasSelection():
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -33,8 +34,7 @@ import logging
|
||||||
from xml.dom.minidom import Document
|
from xml.dom.minidom import Document
|
||||||
from lxml import etree, objectify
|
from lxml import etree, objectify
|
||||||
|
|
||||||
from openlp.core.lib import str_to_bool, translate
|
from openlp.core.lib import str_to_bool
|
||||||
from openlp.core.lib.ui import UiStrings
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -44,6 +44,7 @@ BLANK_THEME_XML = \
|
||||||
<name> </name>
|
<name> </name>
|
||||||
<background type="image">
|
<background type="image">
|
||||||
<filename></filename>
|
<filename></filename>
|
||||||
|
<borderColor>#000000</borderColor>
|
||||||
</background>
|
</background>
|
||||||
<background type="gradient">
|
<background type="gradient">
|
||||||
<startColor>#000000</startColor>
|
<startColor>#000000</startColor>
|
||||||
|
@ -175,12 +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']
|
||||||
TranslatedNames = [
|
|
||||||
translate('OpenLP.ThemeWizard', 'Left'),
|
|
||||||
translate('OpenLP.ThemeWizard', 'Right'),
|
|
||||||
translate('OpenLP.ThemeWizard', 'Center')]
|
|
||||||
|
|
||||||
|
|
||||||
class VerticalType(object):
|
class VerticalType(object):
|
||||||
|
@ -192,7 +190,6 @@ class VerticalType(object):
|
||||||
Bottom = 2
|
Bottom = 2
|
||||||
|
|
||||||
Names = [u'top', u'middle', u'bottom']
|
Names = [u'top', u'middle', u'bottom']
|
||||||
TranslatedNames = [UiStrings.Top, UiStrings.Middle, UiStrings.Bottom]
|
|
||||||
|
|
||||||
|
|
||||||
BOOLEAN_LIST = [u'bold', u'italics', u'override', u'outline', u'shadow',
|
BOOLEAN_LIST = [u'bold', u'italics', u'override', u'outline', u'shadow',
|
||||||
|
@ -207,6 +204,8 @@ class ThemeXML(object):
|
||||||
"""
|
"""
|
||||||
A class to encapsulate the Theme XML.
|
A class to encapsulate the Theme XML.
|
||||||
"""
|
"""
|
||||||
|
FIRST_CAMEL_REGEX = re.compile(u'(.)([A-Z][a-z]+)')
|
||||||
|
SECOND_CAMEL_REGEX = re.compile(u'([a-z0-9])([A-Z])')
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
Initialise the theme object.
|
Initialise the theme object.
|
||||||
|
@ -285,7 +284,7 @@ class ThemeXML(object):
|
||||||
# Create direction element
|
# Create direction element
|
||||||
self.child_element(background, u'direction', unicode(direction))
|
self.child_element(background, u'direction', unicode(direction))
|
||||||
|
|
||||||
def add_background_image(self, filename):
|
def add_background_image(self, filename, borderColor):
|
||||||
"""
|
"""
|
||||||
Add a image background.
|
Add a image background.
|
||||||
|
|
||||||
|
@ -297,6 +296,8 @@ class ThemeXML(object):
|
||||||
self.theme.appendChild(background)
|
self.theme.appendChild(background)
|
||||||
# Create Filename element
|
# Create Filename element
|
||||||
self.child_element(background, u'filename', filename)
|
self.child_element(background, u'filename', filename)
|
||||||
|
# Create endColor element
|
||||||
|
self.child_element(background, u'borderColor', unicode(borderColor))
|
||||||
|
|
||||||
def add_font(self, name, color, size, override, fonttype=u'main',
|
def add_font(self, name, color, size, override, fonttype=u'main',
|
||||||
bold=u'False', italics=u'False', line_adjustment=0,
|
bold=u'False', italics=u'False', line_adjustment=0,
|
||||||
|
@ -581,8 +582,8 @@ class ThemeXML(object):
|
||||||
"""
|
"""
|
||||||
Change Camel Case string to python string
|
Change Camel Case string to python string
|
||||||
"""
|
"""
|
||||||
sub_name = re.sub(u'(.)([A-Z][a-z]+)', r'\1_\2', name)
|
sub_name = ThemeXML.FIRST_CAMEL_REGEX.sub(r'\1_\2', name)
|
||||||
return re.sub(u'([a-z0-9])([A-Z])', r'\1_\2', sub_name).lower()
|
return ThemeXML.SECOND_CAMEL_REGEX.sub(r'\1_\2', sub_name).lower()
|
||||||
|
|
||||||
def _build_xml_from_attrs(self):
|
def _build_xml_from_attrs(self):
|
||||||
"""
|
"""
|
||||||
|
@ -600,7 +601,7 @@ class ThemeXML(object):
|
||||||
self.background_direction)
|
self.background_direction)
|
||||||
else:
|
else:
|
||||||
filename = os.path.split(self.background_filename)[1]
|
filename = os.path.split(self.background_filename)[1]
|
||||||
self.add_background_image(filename)
|
self.add_background_image(filename, self.background_border_color)
|
||||||
self.add_font(self.font_main_name,
|
self.add_font(self.font_main_name,
|
||||||
self.font_main_color,
|
self.font_main_color,
|
||||||
self.font_main_size,
|
self.font_main_size,
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -48,11 +49,10 @@ class OpenLPToolbar(QtGui.QToolBar):
|
||||||
self.icons = {}
|
self.icons = {}
|
||||||
self.setIconSize(QtCore.QSize(20, 20))
|
self.setIconSize(QtCore.QSize(20, 20))
|
||||||
self.actions = {}
|
self.actions = {}
|
||||||
log.debug(u'Init done')
|
log.debug(u'Init done for %s' % parent.__class__.__name__)
|
||||||
|
|
||||||
def addToolbarButton(self, title, icon, tooltip=None, slot=None,
|
def addToolbarButton(self, title, icon, tooltip=None, slot=None,
|
||||||
checkable=False, shortcut=0, alternate=0,
|
checkable=False, shortcuts=None, context=QtCore.Qt.WidgetShortcut):
|
||||||
context=QtCore.Qt.WidgetShortcut):
|
|
||||||
"""
|
"""
|
||||||
A method to help developers easily add a button to the toolbar.
|
A method to help developers easily add a button to the toolbar.
|
||||||
|
|
||||||
|
@ -74,16 +74,12 @@ class OpenLPToolbar(QtGui.QToolBar):
|
||||||
If *True* the button has two, *off* and *on*, states. Default is
|
If *True* the button has two, *off* and *on*, states. Default is
|
||||||
*False*, which means the buttons has only one state.
|
*False*, which means the buttons has only one state.
|
||||||
|
|
||||||
``shortcut``
|
``shortcuts``
|
||||||
The primary shortcut for this action
|
The list of shortcuts for this action
|
||||||
|
|
||||||
``alternate``
|
|
||||||
The alternate shortcut for this action
|
|
||||||
|
|
||||||
``context``
|
``context``
|
||||||
Specify the context in which this shortcut is valid
|
Specify the context in which this shortcut is valid
|
||||||
"""
|
"""
|
||||||
newAction = None
|
|
||||||
if icon:
|
if icon:
|
||||||
actionIcon = build_icon(icon)
|
actionIcon = build_icon(icon)
|
||||||
if slot and not checkable:
|
if slot and not checkable:
|
||||||
|
@ -92,7 +88,7 @@ class OpenLPToolbar(QtGui.QToolBar):
|
||||||
newAction = self.addAction(actionIcon, title)
|
newAction = self.addAction(actionIcon, title)
|
||||||
self.icons[title] = actionIcon
|
self.icons[title] = actionIcon
|
||||||
else:
|
else:
|
||||||
newAction = QtGui.QAction(title, newAction)
|
newAction = QtGui.QAction(title, self)
|
||||||
self.addAction(newAction)
|
self.addAction(newAction)
|
||||||
QtCore.QObject.connect(newAction,
|
QtCore.QObject.connect(newAction,
|
||||||
QtCore.SIGNAL(u'triggered()'), slot)
|
QtCore.SIGNAL(u'triggered()'), slot)
|
||||||
|
@ -103,7 +99,8 @@ class OpenLPToolbar(QtGui.QToolBar):
|
||||||
QtCore.QObject.connect(newAction,
|
QtCore.QObject.connect(newAction,
|
||||||
QtCore.SIGNAL(u'toggled(bool)'), slot)
|
QtCore.SIGNAL(u'toggled(bool)'), slot)
|
||||||
self.actions[title] = newAction
|
self.actions[title] = newAction
|
||||||
newAction.setShortcuts([shortcut, alternate])
|
if shortcuts is not None:
|
||||||
|
newAction.setShortcuts(shortcuts)
|
||||||
newAction.setShortcutContext(context)
|
newAction.setShortcutContext(context)
|
||||||
return newAction
|
return newAction
|
||||||
|
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
@ -31,6 +32,7 @@ import logging
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.lib import build_icon, Receiver, translate
|
from openlp.core.lib import build_icon, Receiver, translate
|
||||||
|
from openlp.core.utils.actions import ActionList
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -38,61 +40,105 @@ class UiStrings(object):
|
||||||
"""
|
"""
|
||||||
Provide standard strings for objects to use.
|
Provide standard strings for objects to use.
|
||||||
"""
|
"""
|
||||||
# These strings should need a good reason to be retranslated elsewhere.
|
__instance__ = None
|
||||||
# Should some/more/less of these have an & attached?
|
|
||||||
About = translate('OpenLP.Ui', 'About')
|
def __new__(cls):
|
||||||
Add = translate('OpenLP.Ui', '&Add')
|
"""
|
||||||
Advanced = translate('OpenLP.Ui', 'Advanced')
|
Override the default object creation method to return a single instance.
|
||||||
AllFiles = translate('OpenLP.Ui', 'All Files')
|
"""
|
||||||
Bottom = translate('OpenLP.Ui', 'Bottom')
|
if not cls.__instance__:
|
||||||
Browse = translate('OpenLP.Ui', 'Browse...')
|
cls.__instance__ = object.__new__(cls)
|
||||||
Cancel = translate('OpenLP.Ui', 'Cancel')
|
return cls.__instance__
|
||||||
CCLINumberLabel = translate('OpenLP.Ui', 'CCLI number:')
|
|
||||||
CreateService = translate('OpenLP.Ui', 'Create a new service.')
|
def __init__(self):
|
||||||
Delete = translate('OpenLP.Ui', '&Delete')
|
"""
|
||||||
Edit = translate('OpenLP.Ui', '&Edit')
|
These strings should need a good reason to be retranslated elsewhere.
|
||||||
EmptyField = translate('OpenLP.Ui', 'Empty Field')
|
Should some/more/less of these have an & attached?
|
||||||
Error = translate('OpenLP.Ui', 'Error')
|
"""
|
||||||
Export = translate('OpenLP.Ui', 'Export')
|
self.About = translate('OpenLP.Ui', 'About')
|
||||||
FontSizePtUnit = translate('OpenLP.Ui', 'pt',
|
self.Add = translate('OpenLP.Ui', '&Add')
|
||||||
|
self.Advanced = translate('OpenLP.Ui', 'Advanced')
|
||||||
|
self.AllFiles = translate('OpenLP.Ui', 'All Files')
|
||||||
|
self.Bottom = translate('OpenLP.Ui', 'Bottom')
|
||||||
|
self.Browse = translate('OpenLP.Ui', 'Browse...')
|
||||||
|
self.Cancel = translate('OpenLP.Ui', 'Cancel')
|
||||||
|
self.CCLINumberLabel = translate('OpenLP.Ui', 'CCLI number:')
|
||||||
|
self.CreateService = translate('OpenLP.Ui', 'Create a new service.')
|
||||||
|
self.ConfirmDelete = translate('OpenLP.Ui', 'Confirm Delete')
|
||||||
|
self.Continuous = translate('OpenLP.Ui', 'Continuous')
|
||||||
|
self.Default = unicode(translate('OpenLP.Ui', 'Default'))
|
||||||
|
self.Delete = translate('OpenLP.Ui', '&Delete')
|
||||||
|
self.DisplayStyle = translate('OpenLP.Ui', 'Display style:')
|
||||||
|
self.Duplicate = translate('OpenLP.Ui', 'Duplicate Error')
|
||||||
|
self.Edit = translate('OpenLP.Ui', '&Edit')
|
||||||
|
self.EmptyField = translate('OpenLP.Ui', 'Empty Field')
|
||||||
|
self.Error = translate('OpenLP.Ui', 'Error')
|
||||||
|
self.Export = translate('OpenLP.Ui', 'Export')
|
||||||
|
self.File = translate('OpenLP.Ui', 'File')
|
||||||
|
self.FontSizePtUnit = translate('OpenLP.Ui', 'pt',
|
||||||
'Abbreviated font pointsize unit')
|
'Abbreviated font pointsize unit')
|
||||||
Image = translate('OpenLP.Ui', 'Image')
|
self.Help = translate('OpenLP.Ui', 'Help')
|
||||||
Import = translate('OpenLP.Ui', 'Import')
|
self.Hours = translate('OpenLP.Ui', 'h',
|
||||||
LengthTime = unicode(translate('OpenLP.Ui', 'Length %s'))
|
'The abbreviated unit for hours')
|
||||||
Live = translate('OpenLP.Ui', 'Live')
|
self.Image = translate('OpenLP.Ui', 'Image')
|
||||||
LiveBGError = translate('OpenLP.Ui', 'Live Background Error')
|
self.Import = translate('OpenLP.Ui', 'Import')
|
||||||
LivePanel = translate('OpenLP.Ui', 'Live Panel')
|
self.LayoutStyle = translate('OpenLP.Ui', 'Layout style:')
|
||||||
Load = translate('OpenLP.Ui', 'Load')
|
self.Live = translate('OpenLP.Ui', 'Live')
|
||||||
Middle = translate('OpenLP.Ui', 'Middle')
|
self.LiveBGError = translate('OpenLP.Ui', 'Live Background Error')
|
||||||
New = translate('OpenLP.Ui', 'New')
|
self.LiveToolbar = translate('OpenLP.Ui', 'Live Toolbar')
|
||||||
NewService = translate('OpenLP.Ui', 'New Service')
|
self.Load = translate('OpenLP.Ui', 'Load')
|
||||||
NewTheme = translate('OpenLP.Ui', 'New Theme')
|
self.Minutes = translate('OpenLP.Ui', 'm',
|
||||||
NFSs = translate('OpenLP.Ui', 'No File Selected', 'Singular')
|
'The abbreviated unit for minutes')
|
||||||
NFSp = translate('OpenLP.Ui', 'No Files Selected', 'Plural')
|
self.Middle = translate('OpenLP.Ui', 'Middle')
|
||||||
NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular')
|
self.New = translate('OpenLP.Ui', 'New')
|
||||||
NISp = translate('OpenLP.Ui', 'No Items Selected', 'Plural')
|
self.NewService = translate('OpenLP.Ui', 'New Service')
|
||||||
OLPV1 = translate('OpenLP.Ui', 'openlp.org 1.x')
|
self.NewTheme = translate('OpenLP.Ui', 'New Theme')
|
||||||
OLPV2 = translate('OpenLP.Ui', 'OpenLP 2.0')
|
self.NFSs = translate('OpenLP.Ui', 'No File Selected', 'Singular')
|
||||||
OpenService = translate('OpenLP.Ui', 'Open Service')
|
self.NFSp = translate('OpenLP.Ui', 'No Files Selected', 'Plural')
|
||||||
Preview = translate('OpenLP.Ui', 'Preview')
|
self.NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular')
|
||||||
PreviewPanel = translate('OpenLP.Ui', 'Preview Panel')
|
self.NISp = translate('OpenLP.Ui', 'No Items Selected', 'Plural')
|
||||||
PrintServiceOrder = translate('OpenLP.Ui', 'Print Service Order')
|
self.OLPV1 = translate('OpenLP.Ui', 'openlp.org 1.x')
|
||||||
ReplaceBG = translate('OpenLP.Ui', 'Replace Background')
|
self.OLPV2 = translate('OpenLP.Ui', 'OpenLP 2.0')
|
||||||
ReplaceLiveBG = translate('OpenLP.Ui', 'Replace Live Background')
|
self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. '
|
||||||
ResetBG = translate('OpenLP.Ui', 'Reset Background')
|
'Do you wish to continue?')
|
||||||
ResetLiveBG = translate('OpenLP.Ui', 'Reset Live Background')
|
self.OpenService = translate('OpenLP.Ui', 'Open service.')
|
||||||
S = translate('OpenLP.Ui', 's', 'The abbreviated unit for seconds')
|
self.PlaySlidesInLoop = translate('OpenLP.Ui','Play Slides in Loop')
|
||||||
SaveAndPreview = translate('OpenLP.Ui', 'Save && Preview')
|
self.PlaySlidesToEnd = translate('OpenLP.Ui','Play Slides to End')
|
||||||
Search = translate('OpenLP.Ui', 'Search')
|
self.Preview = translate('OpenLP.Ui', 'Preview')
|
||||||
SelectDelete = translate('OpenLP.Ui', 'You must select an item to delete.')
|
self.PrintService = translate('OpenLP.Ui', 'Print Service')
|
||||||
SelectEdit = translate('OpenLP.Ui', 'You must select an item to edit.')
|
self.ReplaceBG = translate('OpenLP.Ui', 'Replace Background')
|
||||||
SaveService = translate('OpenLP.Ui', 'Save Service')
|
self.ReplaceLiveBG = translate('OpenLP.Ui', 'Replace live background.')
|
||||||
Service = translate('OpenLP.Ui', 'Service')
|
self.ResetBG = translate('OpenLP.Ui', 'Reset Background')
|
||||||
StartTimeCode = unicode(translate('OpenLP.Ui', 'Start %s'))
|
self.ResetLiveBG = translate('OpenLP.Ui', 'Reset live background.')
|
||||||
Theme = translate('OpenLP.Ui', 'Theme', 'Singular')
|
self.Seconds = translate('OpenLP.Ui', 's',
|
||||||
Themes = translate('OpenLP.Ui', 'Themes', 'Plural')
|
'The abbreviated unit for seconds')
|
||||||
Top = translate('OpenLP.Ui', 'Top')
|
self.SaveAndPreview = translate('OpenLP.Ui', 'Save && Preview')
|
||||||
Version = translate('OpenLP.Ui', 'Version')
|
self.Search = translate('OpenLP.Ui', 'Search')
|
||||||
|
self.SelectDelete = translate('OpenLP.Ui', 'You must select an item '
|
||||||
|
'to delete.')
|
||||||
|
self.SelectEdit = translate('OpenLP.Ui', 'You must select an item to '
|
||||||
|
'edit.')
|
||||||
|
self.Settings = translate('OpenLP.Ui', 'Settings')
|
||||||
|
self.SaveService = translate('OpenLP.Ui', 'Save Service')
|
||||||
|
self.Service = translate('OpenLP.Ui', 'Service')
|
||||||
|
self.Split = translate('OpenLP.Ui', '&Split')
|
||||||
|
self.SplitToolTip = translate('OpenLP.Ui', 'Split a slide into two '
|
||||||
|
'only if it does not fit on the screen as one slide.')
|
||||||
|
self.StartTimeCode = unicode(translate('OpenLP.Ui', 'Start %s'))
|
||||||
|
self.StopPlaySlidesInLoop = translate('OpenLP.Ui',
|
||||||
|
'Stop Play Slides in Loop')
|
||||||
|
self.StopPlaySlidesToEnd = translate('OpenLP.Ui',
|
||||||
|
'Stop Play Slides to End')
|
||||||
|
self.Theme = translate('OpenLP.Ui', 'Theme', 'Singular')
|
||||||
|
self.Themes = translate('OpenLP.Ui', 'Themes', 'Plural')
|
||||||
|
self.Tools = translate('OpenLP.Ui', 'Tools')
|
||||||
|
self.Top = translate('OpenLP.Ui', 'Top')
|
||||||
|
self.UnsupportedFile = translate('OpenLP.Ui', 'Unsupported File')
|
||||||
|
self.VersePerSlide = translate('OpenLP.Ui', 'Verse Per Slide')
|
||||||
|
self.VersePerLine = translate('OpenLP.Ui', 'Verse Per Line')
|
||||||
|
self.Version = translate('OpenLP.Ui', 'Version')
|
||||||
|
self.View = translate('OpenLP.Ui', 'View')
|
||||||
|
self.ViewMode = translate('OpenLP.Ui', 'View Mode')
|
||||||
|
|
||||||
def add_welcome_page(parent, image):
|
def add_welcome_page(parent, image):
|
||||||
"""
|
"""
|
||||||
|
@ -139,7 +185,8 @@ def create_accept_reject_button_box(parent, okay=False):
|
||||||
accept_button = QtGui.QDialogButtonBox.Save
|
accept_button = QtGui.QDialogButtonBox.Save
|
||||||
if okay:
|
if okay:
|
||||||
accept_button = QtGui.QDialogButtonBox.Ok
|
accept_button = QtGui.QDialogButtonBox.Ok
|
||||||
button_box.setStandardButtons(accept_button | QtGui.QDialogButtonBox.Cancel)
|
button_box.setStandardButtons(
|
||||||
|
accept_button | QtGui.QDialogButtonBox.Cancel)
|
||||||
button_box.setObjectName(u'%sButtonBox' % parent)
|
button_box.setObjectName(u'%sButtonBox' % parent)
|
||||||
QtCore.QObject.connect(button_box, QtCore.SIGNAL(u'accepted()'),
|
QtCore.QObject.connect(button_box, QtCore.SIGNAL(u'accepted()'),
|
||||||
parent.accept)
|
parent.accept)
|
||||||
|
@ -166,11 +213,11 @@ def critical_error_message_box(title=None, message=None, parent=None,
|
||||||
Should this message box question the user.
|
Should this message box question the user.
|
||||||
"""
|
"""
|
||||||
if question:
|
if question:
|
||||||
return QtGui.QMessageBox.critical(parent, UiStrings.Error, message,
|
return QtGui.QMessageBox.critical(parent, UiStrings().Error, message,
|
||||||
QtGui.QMessageBox.StandardButtons(
|
QtGui.QMessageBox.StandardButtons(
|
||||||
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No))
|
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No))
|
||||||
data = {u'message': message}
|
data = {u'message': message}
|
||||||
data[u'title'] = title if title else UiStrings.Error
|
data[u'title'] = title if title else UiStrings().Error
|
||||||
return Receiver.send_message(u'openlp_error_message', data)
|
return Receiver.send_message(u'openlp_error_message', data)
|
||||||
|
|
||||||
def media_item_combo_box(parent, name):
|
def media_item_combo_box(parent, name):
|
||||||
|
@ -200,7 +247,7 @@ def create_delete_push_button(parent, icon=None):
|
||||||
delete_button.setObjectName(u'deleteButton')
|
delete_button.setObjectName(u'deleteButton')
|
||||||
delete_icon = icon if icon else u':/general/general_delete.png'
|
delete_icon = icon if icon else u':/general/general_delete.png'
|
||||||
delete_button.setIcon(build_icon(delete_icon))
|
delete_button.setIcon(build_icon(delete_icon))
|
||||||
delete_button.setText(UiStrings.Delete)
|
delete_button.setText(UiStrings().Delete)
|
||||||
delete_button.setToolTip(
|
delete_button.setToolTip(
|
||||||
translate('OpenLP.Ui', 'Delete the selected item.'))
|
translate('OpenLP.Ui', 'Delete the selected item.'))
|
||||||
QtCore.QObject.connect(delete_button,
|
QtCore.QObject.connect(delete_button,
|
||||||
|
@ -233,43 +280,129 @@ def create_up_down_push_button_set(parent):
|
||||||
QtCore.SIGNAL(u'clicked()'), parent.onDownButtonClicked)
|
QtCore.SIGNAL(u'clicked()'), parent.onDownButtonClicked)
|
||||||
return up_button, down_button
|
return up_button, down_button
|
||||||
|
|
||||||
def base_action(parent, name):
|
def base_action(parent, name, category=None):
|
||||||
"""
|
"""
|
||||||
Return the most basic action with the object name set.
|
Return the most basic action with the object name set.
|
||||||
|
|
||||||
|
``category``
|
||||||
|
The category the action should be listed in the shortcut dialog. If you
|
||||||
|
not wish, that this action is added to the shortcut dialog, then do not
|
||||||
|
state any.
|
||||||
"""
|
"""
|
||||||
action = QtGui.QAction(parent)
|
action = QtGui.QAction(parent)
|
||||||
action.setObjectName(name)
|
action.setObjectName(name)
|
||||||
|
if category is not None:
|
||||||
|
action_list = ActionList.get_instance()
|
||||||
|
action_list.add_action(action, category)
|
||||||
return action
|
return action
|
||||||
|
|
||||||
def checkable_action(parent, name, checked=None):
|
def checkable_action(parent, name, checked=None, category=None):
|
||||||
"""
|
"""
|
||||||
Return a standard action with the checkable attribute set.
|
Return a standard action with the checkable attribute set.
|
||||||
"""
|
"""
|
||||||
action = base_action(parent, name)
|
action = base_action(parent, name, category)
|
||||||
action.setCheckable(True)
|
action.setCheckable(True)
|
||||||
if checked is not None:
|
if checked is not None:
|
||||||
action.setChecked(checked)
|
action.setChecked(checked)
|
||||||
return action
|
return action
|
||||||
|
|
||||||
def icon_action(parent, name, icon, checked=None):
|
def icon_action(parent, name, icon, checked=None, category=None):
|
||||||
"""
|
"""
|
||||||
Return a standard action with an icon.
|
Return a standard action with an icon.
|
||||||
"""
|
"""
|
||||||
if checked is not None:
|
if checked is not None:
|
||||||
action = checkable_action(parent, name, checked)
|
action = checkable_action(parent, name, checked, category)
|
||||||
else:
|
else:
|
||||||
action = base_action(parent, name)
|
action = base_action(parent, name, category)
|
||||||
action.setIcon(build_icon(icon))
|
action.setIcon(build_icon(icon))
|
||||||
return action
|
return action
|
||||||
|
|
||||||
def shortcut_action(parent, text, shortcuts, function):
|
def shortcut_action(parent, name, shortcuts, function, icon=None, checked=None,
|
||||||
|
category=None, context=QtCore.Qt.WindowShortcut):
|
||||||
"""
|
"""
|
||||||
Return a shortcut enabled action.
|
Return a shortcut enabled action.
|
||||||
"""
|
"""
|
||||||
action = QtGui.QAction(text, parent)
|
action = QtGui.QAction(parent)
|
||||||
|
action.setObjectName(name)
|
||||||
|
if icon is not None:
|
||||||
|
action.setIcon(build_icon(icon))
|
||||||
|
if checked is not None:
|
||||||
|
action.setCheckable(True)
|
||||||
|
action.setChecked(checked)
|
||||||
|
if shortcuts:
|
||||||
action.setShortcuts(shortcuts)
|
action.setShortcuts(shortcuts)
|
||||||
action.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
|
action.setShortcutContext(context)
|
||||||
QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered()'), function)
|
action_list = ActionList.get_instance()
|
||||||
|
action_list.add_action(action, category)
|
||||||
|
QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered(bool)'), function)
|
||||||
|
return action
|
||||||
|
|
||||||
|
def context_menu_action(base, icon, text, slot, shortcuts=None, category=None,
|
||||||
|
context=QtCore.Qt.WidgetShortcut):
|
||||||
|
"""
|
||||||
|
Utility method to help build context menus.
|
||||||
|
|
||||||
|
``base``
|
||||||
|
The parent menu to add this menu item to
|
||||||
|
|
||||||
|
``icon``
|
||||||
|
An icon for this action
|
||||||
|
|
||||||
|
``text``
|
||||||
|
The text to display for this action
|
||||||
|
|
||||||
|
``slot``
|
||||||
|
The code to run when this action is triggered
|
||||||
|
|
||||||
|
``shortcuts``
|
||||||
|
The action's shortcuts.
|
||||||
|
|
||||||
|
``category``
|
||||||
|
The category the shortcut should be listed in the shortcut dialog. If
|
||||||
|
left to ``None``, then the action will be hidden in the shortcut dialog.
|
||||||
|
|
||||||
|
``context``
|
||||||
|
The context the shortcut is valid.
|
||||||
|
"""
|
||||||
|
action = QtGui.QAction(text, base)
|
||||||
|
if icon:
|
||||||
|
action.setIcon(build_icon(icon))
|
||||||
|
QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered(bool)'), slot)
|
||||||
|
if shortcuts is not None:
|
||||||
|
action.setShortcuts(shortcuts)
|
||||||
|
action.setShortcutContext(context)
|
||||||
|
action_list = ActionList.get_instance()
|
||||||
|
action_list.add_action(action)
|
||||||
|
base.addAction(action)
|
||||||
|
return action
|
||||||
|
|
||||||
|
def context_menu(base, icon, text):
|
||||||
|
"""
|
||||||
|
Utility method to help build context menus.
|
||||||
|
|
||||||
|
``base``
|
||||||
|
The parent object to add this menu to
|
||||||
|
|
||||||
|
``icon``
|
||||||
|
An icon for this menu
|
||||||
|
|
||||||
|
``text``
|
||||||
|
The text to display for this menu
|
||||||
|
"""
|
||||||
|
action = QtGui.QMenu(text, base)
|
||||||
|
action.setIcon(build_icon(icon))
|
||||||
|
return action
|
||||||
|
|
||||||
|
def context_menu_separator(base):
|
||||||
|
"""
|
||||||
|
Add a separator to a context menu
|
||||||
|
|
||||||
|
``base``
|
||||||
|
The menu object to add the separator to
|
||||||
|
"""
|
||||||
|
action = QtGui.QAction(u'', base)
|
||||||
|
action.setSeparator(True)
|
||||||
|
base.addAction(action)
|
||||||
return action
|
return action
|
||||||
|
|
||||||
def add_widget_completer(cache, widget):
|
def add_widget_completer(cache, widget):
|
||||||
|
@ -305,8 +438,25 @@ def create_valign_combo(form, parent, layout):
|
||||||
verticalLabel.setText(translate('OpenLP.Ui', '&Vertical Align:'))
|
verticalLabel.setText(translate('OpenLP.Ui', '&Vertical Align:'))
|
||||||
form.verticalComboBox = QtGui.QComboBox(parent)
|
form.verticalComboBox = QtGui.QComboBox(parent)
|
||||||
form.verticalComboBox.setObjectName(u'VerticalComboBox')
|
form.verticalComboBox.setObjectName(u'VerticalComboBox')
|
||||||
form.verticalComboBox.addItem(UiStrings.Top)
|
form.verticalComboBox.addItem(UiStrings().Top)
|
||||||
form.verticalComboBox.addItem(UiStrings.Middle)
|
form.verticalComboBox.addItem(UiStrings().Middle)
|
||||||
form.verticalComboBox.addItem(UiStrings.Bottom)
|
form.verticalComboBox.addItem(UiStrings().Bottom)
|
||||||
verticalLabel.setBuddy(form.verticalComboBox)
|
verticalLabel.setBuddy(form.verticalComboBox)
|
||||||
layout.addRow(verticalLabel, form.verticalComboBox)
|
layout.addRow(verticalLabel, form.verticalComboBox)
|
||||||
|
|
||||||
|
def find_and_set_in_combo_box(combo_box, value_to_find):
|
||||||
|
"""
|
||||||
|
Find a string in a combo box and set it as the selected item if present
|
||||||
|
|
||||||
|
``combo_box``
|
||||||
|
The combo box to check for selected items
|
||||||
|
|
||||||
|
``value_to_find``
|
||||||
|
The value to find
|
||||||
|
"""
|
||||||
|
index = combo_box.findText(value_to_find,
|
||||||
|
QtCore.Qt.MatchExactly)
|
||||||
|
if index == -1:
|
||||||
|
# Not Found.
|
||||||
|
index = 0
|
||||||
|
combo_box.setCurrentIndex(index)
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
|
|
@ -5,10 +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, Meinert Jordan, Armin Köhler, Andreas Preikschat, #
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
# Christian Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon #
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
# Tibble, Carsten Tinggaard, Frode Woldsund #
|
# 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 #
|
# 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 #
|
||||||
|
|