forked from openlp/openlp
Even more pep8 fixes and removed the old htmlbuilder.
This commit is contained in:
parent
27f37df7e8
commit
5ac8bbc3f2
@ -24,7 +24,6 @@ The :mod:`lib` module contains most of the components and libraries that make
|
|||||||
OpenLP work.
|
OpenLP work.
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import re
|
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
@ -1,817 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# OpenLP - Open Source Lyrics Projection #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# Copyright (c) 2008-2018 OpenLP Developers #
|
|
||||||
# --------------------------------------------------------------------------- #
|
|
||||||
# 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 #
|
|
||||||
###############################################################################
|
|
||||||
r"""
|
|
||||||
This module is responsible for generating the HTML for :class:`~openlp.core.ui.maindisplay`. The ``build_html`` function
|
|
||||||
is the function which has to be called from outside. The generated and returned HTML will look similar to this::
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>OpenLP Display</title>
|
|
||||||
<style>
|
|
||||||
*{
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
background-color: #000000;
|
|
||||||
}
|
|
||||||
.size {
|
|
||||||
position: absolute;
|
|
||||||
left: 0px;
|
|
||||||
top: 0px;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
#black {
|
|
||||||
z-index: 8;
|
|
||||||
background-color: black;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
#bgimage {
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
#image {
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
#videobackboard {
|
|
||||||
z-index:3;
|
|
||||||
background-color: #000000;
|
|
||||||
}
|
|
||||||
#video {
|
|
||||||
background-color: #000000;
|
|
||||||
z-index:4;
|
|
||||||
}
|
|
||||||
|
|
||||||
#flash {
|
|
||||||
z-index:5;
|
|
||||||
}
|
|
||||||
|
|
||||||
#alert {
|
|
||||||
position: absolute;
|
|
||||||
left: 0px;
|
|
||||||
top: 0px;
|
|
||||||
z-index: 10;
|
|
||||||
width: 100%;
|
|
||||||
vertical-align: bottom;
|
|
||||||
font-family: DejaVu Sans;
|
|
||||||
font-size: 40pt;
|
|
||||||
color: #ffffff;
|
|
||||||
background-color: #660000;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
#footer {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 6;
|
|
||||||
|
|
||||||
left: 10px;
|
|
||||||
bottom: 0px;
|
|
||||||
width: 1580px;
|
|
||||||
font-family: Nimbus Sans L;
|
|
||||||
font-size: 12pt;
|
|
||||||
color: #FFFFFF;
|
|
||||||
text-align: left;
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
}
|
|
||||||
/* lyric css */
|
|
||||||
|
|
||||||
.lyricstable {
|
|
||||||
z-index: 5;
|
|
||||||
position: absolute;
|
|
||||||
display: table;
|
|
||||||
left: 10px; top: 0px;
|
|
||||||
}
|
|
||||||
.lyricscell {
|
|
||||||
display: table-cell;
|
|
||||||
word-wrap: break-word;
|
|
||||||
-webkit-transition: opacity 0.4s ease;
|
|
||||||
white-space:pre-wrap; word-wrap: break-word; text-align: left; vertical-align: top; font-family: Nimbus
|
|
||||||
Sans L; font-size: 40pt; color: #FFFFFF; line-height: 100%; margin: 0;padding: 0; padding-bottom: 0;
|
|
||||||
padding-left: 4px; width: 1580px; height: 810px;
|
|
||||||
}
|
|
||||||
.lyricsmain {
|
|
||||||
-webkit-text-stroke: 0.125em #000000; -webkit-text-fill-color: #FFFFFF; text-shadow: #000000 5px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
sup {
|
|
||||||
font-size: 0.6em;
|
|
||||||
vertical-align: top;
|
|
||||||
position: relative;
|
|
||||||
top: -0.3em;
|
|
||||||
}
|
|
||||||
/* Chords css */
|
|
||||||
.chordline {
|
|
||||||
line-height: 1.0em;
|
|
||||||
}
|
|
||||||
.chordline span.chord span {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.chordline span.chord span strong {
|
|
||||||
position: absolute;
|
|
||||||
top: -0.8em;
|
|
||||||
left: 0;
|
|
||||||
font-size: 75%;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: normal;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.firstchordline {
|
|
||||||
line-height: 1.0em;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script>
|
|
||||||
var timer = null;
|
|
||||||
var transition = false;
|
|
||||||
|
|
||||||
function show_video(state, path, volume, loop, variable_value){
|
|
||||||
// Sometimes video.currentTime stops slightly short of video.duration and video.ended is intermittent!
|
|
||||||
|
|
||||||
var video = document.getElementById('video');
|
|
||||||
if(volume != null){
|
|
||||||
video.volume = volume;
|
|
||||||
}
|
|
||||||
switch(state){
|
|
||||||
case 'load':
|
|
||||||
video.src = 'file:///' + path;
|
|
||||||
if(loop == true) {
|
|
||||||
video.loop = true;
|
|
||||||
}
|
|
||||||
video.load();
|
|
||||||
break;
|
|
||||||
case 'play':
|
|
||||||
video.play();
|
|
||||||
break;
|
|
||||||
case 'pause':
|
|
||||||
video.pause();
|
|
||||||
break;
|
|
||||||
case 'stop':
|
|
||||||
show_video('pause');
|
|
||||||
video.currentTime = 0;
|
|
||||||
break;
|
|
||||||
case 'close':
|
|
||||||
show_video('stop');
|
|
||||||
video.src = '';
|
|
||||||
break;
|
|
||||||
case 'length':
|
|
||||||
return video.duration;
|
|
||||||
case 'current_time':
|
|
||||||
return video.currentTime;
|
|
||||||
case 'seek':
|
|
||||||
video.currentTime = variable_value;
|
|
||||||
break;
|
|
||||||
case 'isEnded':
|
|
||||||
return video.ended;
|
|
||||||
case 'setVisible':
|
|
||||||
video.style.visibility = variable_value;
|
|
||||||
break;
|
|
||||||
case 'setBackBoard':
|
|
||||||
var back = document.getElementById('videobackboard');
|
|
||||||
back.style.visibility = variable_value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFlashMovieObject(movieName)
|
|
||||||
{
|
|
||||||
if (window.document[movieName]){
|
|
||||||
return window.document[movieName];
|
|
||||||
}
|
|
||||||
if (document.embeds && document.embeds[movieName]){
|
|
||||||
return document.embeds[movieName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_flash(state, path, volume, variable_value){
|
|
||||||
var text = document.getElementById('flash');
|
|
||||||
var flashMovie = getFlashMovieObject("OpenLPFlashMovie");
|
|
||||||
var src = "src = 'file:///" + path + "'";
|
|
||||||
var view_parm = " wmode='opaque'" + " width='100%%'" + " height='100%%'";
|
|
||||||
var swf_parm = " name='OpenLPFlashMovie'" + " autostart='true' loop='false' play='true'" +
|
|
||||||
" hidden='false' swliveconnect='true' allowscriptaccess='always'" + " volume='" + volume + "'";
|
|
||||||
|
|
||||||
switch(state){
|
|
||||||
case 'load':
|
|
||||||
text.innerHTML = "<embed " + src + view_parm + swf_parm + "/>";
|
|
||||||
flashMovie = getFlashMovieObject("OpenLPFlashMovie");
|
|
||||||
flashMovie.Play();
|
|
||||||
break;
|
|
||||||
case 'play':
|
|
||||||
flashMovie.Play();
|
|
||||||
break;
|
|
||||||
case 'pause':
|
|
||||||
flashMovie.StopPlay();
|
|
||||||
break;
|
|
||||||
case 'stop':
|
|
||||||
flashMovie.StopPlay();
|
|
||||||
tempHtml = text.innerHTML;
|
|
||||||
text.innerHTML = '';
|
|
||||||
text.innerHTML = tempHtml;
|
|
||||||
break;
|
|
||||||
case 'close':
|
|
||||||
flashMovie.StopPlay();
|
|
||||||
text.innerHTML = '';
|
|
||||||
break;
|
|
||||||
case 'length':
|
|
||||||
return flashMovie.TotalFrames();
|
|
||||||
case 'current_time':
|
|
||||||
return flashMovie.CurrentFrame();
|
|
||||||
case 'seek':
|
|
||||||
// flashMovie.GotoFrame(variable_value);
|
|
||||||
break;
|
|
||||||
case 'isEnded':
|
|
||||||
//TODO check flash end
|
|
||||||
return false;
|
|
||||||
case 'setVisible':
|
|
||||||
text.style.visibility = variable_value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 update_css(align, font, size, color, bgcolor){
|
|
||||||
var text = document.getElementById('alert');
|
|
||||||
text.style.fontSize = size + "pt";
|
|
||||||
text.style.fontFamily = font;
|
|
||||||
text.style.color = color;
|
|
||||||
text.style.backgroundColor = bgcolor;
|
|
||||||
switch(align)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function show_image(src){
|
|
||||||
var img = document.getElementById('image');
|
|
||||||
img.src = src;
|
|
||||||
if(src == '')
|
|
||||||
img.style.display = 'none';
|
|
||||||
else
|
|
||||||
img.style.display = 'block';
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_blank(state){
|
|
||||||
var black = 'none';
|
|
||||||
var lyrics = '';
|
|
||||||
switch(state){
|
|
||||||
case 'theme':
|
|
||||||
lyrics = 'hidden';
|
|
||||||
break;
|
|
||||||
case 'black':
|
|
||||||
black = 'block';
|
|
||||||
break;
|
|
||||||
case 'desktop':
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
document.getElementById('black').style.display = black;
|
|
||||||
document.getElementById('lyricsmain').style.visibility = lyrics;
|
|
||||||
document.getElementById('image').style.visibility = lyrics;
|
|
||||||
document.getElementById('footer').style.visibility = lyrics;
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_footer(footertext){
|
|
||||||
document.getElementById('footer').innerHTML = footertext;
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_text(new_text){
|
|
||||||
var match = /-webkit-text-fill-color:[^;"]+/gi;
|
|
||||||
if(timer != null)
|
|
||||||
clearTimeout(timer);
|
|
||||||
/*
|
|
||||||
QtWebkit bug with outlines and justify causing outline alignment
|
|
||||||
problems. (Bug 859950) Surround each word with a <span> to workaround,
|
|
||||||
but only in this scenario.
|
|
||||||
*/
|
|
||||||
var txt = document.getElementById('lyricsmain');
|
|
||||||
if(window.getComputedStyle(txt).textAlign == 'justify'){
|
|
||||||
if(window.getComputedStyle(txt).webkitTextStrokeWidth != '0px'){
|
|
||||||
new_text = new_text.replace(/(\s| )+(?![^<]*>)/g,
|
|
||||||
function(match) {
|
|
||||||
return '</span>' + match + '<span>';
|
|
||||||
});
|
|
||||||
new_text = '<span>' + new_text + '</span>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
text_fade('lyricsmain', new_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
function text_fade(id, new_text){
|
|
||||||
/*
|
|
||||||
Show the text.
|
|
||||||
*/
|
|
||||||
var text = document.getElementById(id);
|
|
||||||
if(text == null) return;
|
|
||||||
if(!transition){
|
|
||||||
text.innerHTML = new_text;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Fade text out. 0.1 to minimize the time "nothing" is shown on the screen.
|
|
||||||
text.style.opacity = '0.1';
|
|
||||||
// Fade new text in after the old text has finished fading out.
|
|
||||||
timer = window.setTimeout(function(){_show_text(text, new_text)}, 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _show_text(text, new_text) {
|
|
||||||
/*
|
|
||||||
Helper function to show the new_text delayed.
|
|
||||||
*/
|
|
||||||
text.innerHTML = new_text;
|
|
||||||
text.style.opacity = '1';
|
|
||||||
// Wait until the text is completely visible. We want to save the timer id, to be able to call
|
|
||||||
// clearTimeout(timer) when the text has changed before finishing fading.
|
|
||||||
timer = window.setTimeout(function(){timer = null;}, 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_text_completed(){
|
|
||||||
return (timer == null);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<img id="bgimage" class="size" style="display:none;" />
|
|
||||||
<img id="image" class="size" style="display:none;" />
|
|
||||||
|
|
||||||
<div id="videobackboard" class="size" style="visibility:hidden"></div>
|
|
||||||
<video id="video" class="size" style="visibility:hidden" autobuffer preload></video>
|
|
||||||
|
|
||||||
<div id="flash" class="size" style="visibility:hidden"></div>
|
|
||||||
|
|
||||||
<div id="alert" style="visibility:hidden"></div>
|
|
||||||
|
|
||||||
<div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"></div></div>
|
|
||||||
<div id="footer" class="footer"></div>
|
|
||||||
<div id="black" class="size"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
import logging
|
|
||||||
from string import Template
|
|
||||||
|
|
||||||
from PyQt5 import QtWebEngine
|
|
||||||
|
|
||||||
from openlp.core.common.settings import Settings
|
|
||||||
from openlp.core.lib.theme import BackgroundGradientType, BackgroundType, HorizontalType, VerticalType
|
|
||||||
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
HTML_SRC = Template(r"""
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>OpenLP Display</title>
|
|
||||||
<style>
|
|
||||||
*{
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
${bg_css};
|
|
||||||
}
|
|
||||||
.size {
|
|
||||||
position: absolute;
|
|
||||||
left: 0px;
|
|
||||||
top: 0px;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
#black {
|
|
||||||
z-index: 8;
|
|
||||||
background-color: black;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
#bgimage {
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
#image {
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
${css_additions}
|
|
||||||
#footer {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 6;
|
|
||||||
${footer_css}
|
|
||||||
}
|
|
||||||
/* lyric css */${lyrics_css}
|
|
||||||
sup {
|
|
||||||
font-size: 0.6em;
|
|
||||||
vertical-align: top;
|
|
||||||
position: relative;
|
|
||||||
top: -0.3em;
|
|
||||||
}
|
|
||||||
/* Chords css */${chords_css}
|
|
||||||
</style>
|
|
||||||
<script>
|
|
||||||
var timer = null;
|
|
||||||
var transition = ${transitions};
|
|
||||||
${js_additions}
|
|
||||||
|
|
||||||
function show_image(src){
|
|
||||||
var img = document.getElementById('image');
|
|
||||||
img.src = src;
|
|
||||||
if(src == '')
|
|
||||||
img.style.display = 'none';
|
|
||||||
else
|
|
||||||
img.style.display = 'block';
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_blank(state){
|
|
||||||
var black = 'none';
|
|
||||||
var lyrics = '';
|
|
||||||
switch(state){
|
|
||||||
case 'theme':
|
|
||||||
lyrics = 'hidden';
|
|
||||||
break;
|
|
||||||
case 'black':
|
|
||||||
black = 'block';
|
|
||||||
break;
|
|
||||||
case 'desktop':
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
document.getElementById('black').style.display = black;
|
|
||||||
document.getElementById('lyricsmain').style.visibility = lyrics;
|
|
||||||
document.getElementById('image').style.visibility = lyrics;
|
|
||||||
document.getElementById('footer').style.visibility = lyrics;
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_footer(footertext){
|
|
||||||
document.getElementById('footer').innerHTML = footertext;
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_text(new_text){
|
|
||||||
var match = /-webkit-text-fill-color:[^;\"]+/gi;
|
|
||||||
if(timer != null)
|
|
||||||
clearTimeout(timer);
|
|
||||||
/*
|
|
||||||
QtWebkit bug with outlines and justify causing outline alignment
|
|
||||||
problems. (Bug 859950) Surround each word with a <span> to workaround,
|
|
||||||
but only in this scenario.
|
|
||||||
*/
|
|
||||||
var txt = document.getElementById('lyricsmain');
|
|
||||||
if(window.getComputedStyle(txt).textAlign == 'justify'){
|
|
||||||
if(window.getComputedStyle(txt).webkitTextStrokeWidth != '0px'){
|
|
||||||
new_text = new_text.replace(/(\s| )+(?![^<]*>)/g,
|
|
||||||
function(match) {
|
|
||||||
return '</span>' + match + '<span>';
|
|
||||||
});
|
|
||||||
new_text = '<span>' + new_text + '</span>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
text_fade('lyricsmain', new_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
function text_fade(id, new_text){
|
|
||||||
/*
|
|
||||||
Show the text.
|
|
||||||
*/
|
|
||||||
var text = document.getElementById(id);
|
|
||||||
if(text == null) return;
|
|
||||||
if(!transition){
|
|
||||||
text.innerHTML = new_text;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Fade text out. 0.1 to minimize the time "nothing" is shown on the screen.
|
|
||||||
text.style.opacity = '0.1';
|
|
||||||
// Fade new text in after the old text has finished fading out.
|
|
||||||
timer = window.setTimeout(function(){_show_text(text, new_text)}, 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _show_text(text, new_text) {
|
|
||||||
/*
|
|
||||||
Helper function to show the new_text delayed.
|
|
||||||
*/
|
|
||||||
text.innerHTML = new_text;
|
|
||||||
text.style.opacity = '1';
|
|
||||||
// Wait until the text is completely visible. We want to save the timer id, to be able to call
|
|
||||||
// clearTimeout(timer) when the text has changed before finishing fading.
|
|
||||||
timer = window.setTimeout(function(){timer = null;}, 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_text_completed(){
|
|
||||||
return (timer == null);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<img id="bgimage" class="size" ${bg_image} />
|
|
||||||
<img id="image" class="size" ${image} />
|
|
||||||
${html_additions}
|
|
||||||
<div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"></div></div>
|
|
||||||
<div id="footer" class="footer"></div>
|
|
||||||
<div id="black" class="size"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
""")
|
|
||||||
|
|
||||||
LYRICS_SRC = Template("""
|
|
||||||
.lyricstable {
|
|
||||||
z-index: 5;
|
|
||||||
position: absolute;
|
|
||||||
display: table;
|
|
||||||
${stable}
|
|
||||||
}
|
|
||||||
.lyricscell {
|
|
||||||
display: table-cell;
|
|
||||||
word-wrap: break-word;
|
|
||||||
-webkit-transition: opacity 0.4s ease;
|
|
||||||
${lyrics}
|
|
||||||
}
|
|
||||||
.lyricsmain {
|
|
||||||
${main}
|
|
||||||
}
|
|
||||||
""")
|
|
||||||
|
|
||||||
FOOTER_SRC = Template("""
|
|
||||||
left: ${left}px;
|
|
||||||
bottom: ${bottom}px;
|
|
||||||
width: ${width}px;
|
|
||||||
font-family: ${family};
|
|
||||||
font-size: ${size}pt;
|
|
||||||
color: ${color};
|
|
||||||
text-align: left;
|
|
||||||
white-space: ${space};
|
|
||||||
""")
|
|
||||||
|
|
||||||
LYRICS_FORMAT_SRC = Template("""
|
|
||||||
${justify}word-wrap: break-word;
|
|
||||||
text-align: ${align};
|
|
||||||
vertical-align: ${valign};
|
|
||||||
font-family: ${font};
|
|
||||||
font-size: ${size}pt;
|
|
||||||
color: ${color};
|
|
||||||
line-height: ${line}%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
padding-bottom: ${bottom};
|
|
||||||
padding-left: ${left}px;
|
|
||||||
width: ${width}px;
|
|
||||||
height: ${height}px;${font_style}${font_weight}
|
|
||||||
""")
|
|
||||||
|
|
||||||
CHORDS_FORMAT = Template("""
|
|
||||||
.chordline {
|
|
||||||
line-height: ${chord_line_height};
|
|
||||||
}
|
|
||||||
.chordline span.chord span {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.chordline span.chord span strong {
|
|
||||||
position: absolute;
|
|
||||||
top: -0.8em;
|
|
||||||
left: 0;
|
|
||||||
font-size: 75%;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: normal;
|
|
||||||
display: ${chords_display};
|
|
||||||
}
|
|
||||||
.firstchordline {
|
|
||||||
line-height: ${first_chord_line_height};
|
|
||||||
}
|
|
||||||
.ws {
|
|
||||||
display: ${chords_display};
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}""")
|
|
||||||
|
|
||||||
|
|
||||||
def build_html(item, screen, is_live, background, image=None, plugins=None):
|
|
||||||
"""
|
|
||||||
Build the full web paged structure for display
|
|
||||||
|
|
||||||
:param item: Service Item to be displayed
|
|
||||||
:param screen: Current display information
|
|
||||||
:param is_live: Item is going live, rather than preview/theme building
|
|
||||||
:param background: Theme background image - bytes
|
|
||||||
:param image: Image media item - bytes
|
|
||||||
:param plugins: The List of available plugins
|
|
||||||
"""
|
|
||||||
width = screen['size'].width()
|
|
||||||
height = screen['size'].height()
|
|
||||||
theme_data = item.theme_data
|
|
||||||
# Image generated and poked in
|
|
||||||
if background:
|
|
||||||
bgimage_src = 'src="data:image/png;base64,{image}"'.format(image=background)
|
|
||||||
elif item.bg_image_bytes:
|
|
||||||
bgimage_src = 'src="data:image/png;base64,{image}"'.format(image=item.bg_image_bytes)
|
|
||||||
else:
|
|
||||||
bgimage_src = 'style="display:none;"'
|
|
||||||
if image:
|
|
||||||
image_src = 'src="data:image/png;base64,{image}"'.format(image=image)
|
|
||||||
else:
|
|
||||||
image_src = 'style="display:none;"'
|
|
||||||
css_additions = ''
|
|
||||||
js_additions = ''
|
|
||||||
html_additions = ''
|
|
||||||
if plugins:
|
|
||||||
for plugin in plugins:
|
|
||||||
css_additions += plugin.get_display_css()
|
|
||||||
js_additions += plugin.get_display_javascript()
|
|
||||||
html_additions += plugin.get_display_html()
|
|
||||||
return HTML_SRC.substitute(bg_css=build_background_css(item, width),
|
|
||||||
css_additions=css_additions,
|
|
||||||
footer_css=build_footer_css(item, height),
|
|
||||||
lyrics_css=build_lyrics_css(item),
|
|
||||||
transitions='true' if (theme_data and
|
|
||||||
theme_data.display_slide_transition and
|
|
||||||
is_live) else 'false',
|
|
||||||
js_additions=js_additions,
|
|
||||||
bg_image=bgimage_src,
|
|
||||||
image=image_src,
|
|
||||||
html_additions=html_additions,
|
|
||||||
chords_css=build_chords_css())
|
|
||||||
|
|
||||||
|
|
||||||
def build_background_css(item, width):
|
|
||||||
"""
|
|
||||||
Build the background css
|
|
||||||
|
|
||||||
:param item: Service Item containing theme and location information
|
|
||||||
:param width:
|
|
||||||
"""
|
|
||||||
width = int(width) // 2
|
|
||||||
theme = item.theme_data
|
|
||||||
background = 'background-color: black'
|
|
||||||
if theme:
|
|
||||||
if theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
|
|
||||||
background = ''
|
|
||||||
elif theme.background_type == BackgroundType.to_string(BackgroundType.Solid):
|
|
||||||
background = 'background-color: {theme}'.format(theme=theme.background_color)
|
|
||||||
else:
|
|
||||||
if theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Horizontal):
|
|
||||||
background = 'background: -webkit-gradient(linear, left top, left bottom, from({start}), to({end})) ' \
|
|
||||||
'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
|
|
||||||
elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.LeftTop):
|
|
||||||
background = 'background: -webkit-gradient(linear, left top, right bottom, from({start}), to({end})) ' \
|
|
||||||
'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
|
|
||||||
elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.LeftBottom):
|
|
||||||
background = 'background: -webkit-gradient(linear, left bottom, right top, from({start}), to({end})) ' \
|
|
||||||
'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
|
|
||||||
elif theme.background_direction == BackgroundGradientType.to_string(BackgroundGradientType.Vertical):
|
|
||||||
background = 'background: -webkit-gradient(linear, left top, right top, from({start}), to({end})) ' \
|
|
||||||
'fixed'.format(start=theme.background_start_color, end=theme.background_end_color)
|
|
||||||
else:
|
|
||||||
background = 'background: -webkit-gradient(radial, {width} 50%, 100, {width} 50%, {width}, ' \
|
|
||||||
'from({start}), to({end})) fixed'.format(width=width,
|
|
||||||
start=theme.background_start_color,
|
|
||||||
end=theme.background_end_color)
|
|
||||||
return background
|
|
||||||
|
|
||||||
|
|
||||||
def build_lyrics_css(item):
|
|
||||||
"""
|
|
||||||
Build the lyrics display css
|
|
||||||
|
|
||||||
:param item: Service Item containing theme and location information
|
|
||||||
"""
|
|
||||||
theme_data = item.theme_data
|
|
||||||
lyricstable = ''
|
|
||||||
lyrics = ''
|
|
||||||
lyricsmain = ''
|
|
||||||
if theme_data and item.main:
|
|
||||||
lyricstable = 'left: {left}px; top: {top}px;'.format(left=item.main.x(), top=item.main.y())
|
|
||||||
lyrics = build_lyrics_format_css(theme_data, item.main.width(), item.main.height())
|
|
||||||
lyricsmain += build_lyrics_outline_css(theme_data)
|
|
||||||
if theme_data.font_main_shadow:
|
|
||||||
lyricsmain += ' text-shadow: {theme} {shadow}px ' \
|
|
||||||
'{shadow}px;'.format(theme=theme_data.font_main_shadow_color,
|
|
||||||
shadow=theme_data.font_main_shadow_size)
|
|
||||||
return LYRICS_SRC.substitute(stable=lyricstable, lyrics=lyrics, main=lyricsmain)
|
|
||||||
|
|
||||||
|
|
||||||
def build_lyrics_outline_css(theme_data):
|
|
||||||
"""
|
|
||||||
Build the css which controls the theme outline. Also used by renderer for splitting verses
|
|
||||||
|
|
||||||
:param theme_data: Object containing theme information
|
|
||||||
"""
|
|
||||||
if theme_data.font_main_outline:
|
|
||||||
size = float(theme_data.font_main_outline_size) / 16
|
|
||||||
fill_color = theme_data.font_main_color
|
|
||||||
outline_color = theme_data.font_main_outline_color
|
|
||||||
return ' -webkit-text-stroke: {size}em {color}; -webkit-text-fill-color: {fill}; '.format(size=size,
|
|
||||||
color=outline_color,
|
|
||||||
fill=fill_color)
|
|
||||||
return ''
|
|
||||||
|
|
||||||
|
|
||||||
def build_lyrics_format_css(theme_data, width, height):
|
|
||||||
"""
|
|
||||||
Build the css which controls the theme format. Also used by renderer for splitting verses
|
|
||||||
|
|
||||||
:param theme_data: Object containing theme information
|
|
||||||
:param width: Width of the lyrics block
|
|
||||||
:param height: Height of the lyrics block
|
|
||||||
"""
|
|
||||||
align = HorizontalType.Names[theme_data.display_horizontal_align]
|
|
||||||
valign = VerticalType.Names[theme_data.display_vertical_align]
|
|
||||||
left_margin = (int(theme_data.font_main_outline_size) * 2) if theme_data.font_main_outline else 0
|
|
||||||
# fix tag incompatibilities
|
|
||||||
justify = '' if (theme_data.display_horizontal_align == HorizontalType.Justify) else ' white-space: pre-wrap;\n'
|
|
||||||
padding_bottom = '0.5em' if (theme_data.display_vertical_align == VerticalType.Bottom) else '0'
|
|
||||||
return LYRICS_FORMAT_SRC.substitute(justify=justify,
|
|
||||||
align=align,
|
|
||||||
valign=valign,
|
|
||||||
font=theme_data.font_main_name,
|
|
||||||
size=theme_data.font_main_size,
|
|
||||||
color=theme_data.font_main_color,
|
|
||||||
line='{line:d}'.format(line=100 + int(theme_data.font_main_line_adjustment)),
|
|
||||||
bottom=padding_bottom,
|
|
||||||
left=left_margin,
|
|
||||||
width=width,
|
|
||||||
height=height,
|
|
||||||
font_style='\n font-style: italic;' if theme_data.font_main_italics else '',
|
|
||||||
font_weight='\n font-weight: bold;' if theme_data.font_main_bold else '')
|
|
||||||
|
|
||||||
|
|
||||||
def build_footer_css(item, height):
|
|
||||||
"""
|
|
||||||
Build the display of the item footer
|
|
||||||
|
|
||||||
:param item: Service Item to be processed.
|
|
||||||
:param height:
|
|
||||||
"""
|
|
||||||
theme = item.theme_data
|
|
||||||
if not theme or not item.footer:
|
|
||||||
return ''
|
|
||||||
bottom = height - int(item.footer.y()) - int(item.footer.height())
|
|
||||||
whitespace = 'normal' if Settings().value('themes/wrap footer') else 'nowrap'
|
|
||||||
return FOOTER_SRC.substitute(left=item.footer.x(), bottom=bottom, width=item.footer.width(),
|
|
||||||
family=theme.font_footer_name, size=theme.font_footer_size,
|
|
||||||
color=theme.font_footer_color, space=whitespace)
|
|
||||||
|
|
||||||
|
|
||||||
def build_chords_css():
|
|
||||||
if Settings().value('songs/enable chords') and Settings().value('songs/mainview chords'):
|
|
||||||
chord_line_height = '2.0em'
|
|
||||||
chords_display = 'inline'
|
|
||||||
first_chord_line_height = '2.1em'
|
|
||||||
else:
|
|
||||||
chord_line_height = '1.0em'
|
|
||||||
chords_display = 'none'
|
|
||||||
first_chord_line_height = '1.0em'
|
|
||||||
return CHORDS_FORMAT.substitute(chord_line_height=chord_line_height, chords_display=chords_display,
|
|
||||||
first_chord_line_height=first_chord_line_height)
|
|
@ -30,7 +30,7 @@ from lxml import etree, objectify
|
|||||||
from openlp.core.common import de_hump
|
from openlp.core.common import de_hump
|
||||||
from openlp.core.common.applocation import AppLocation
|
from openlp.core.common.applocation import AppLocation
|
||||||
from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder
|
from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder
|
||||||
from openlp.core.common.path import Path, str_to_path
|
from openlp.core.common.path import Path
|
||||||
from openlp.core.display.screens import ScreenList
|
from openlp.core.display.screens import ScreenList
|
||||||
from openlp.core.lib import get_text_file_string, str_to_bool
|
from openlp.core.lib import get_text_file_string, str_to_bool
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ import platform
|
|||||||
from PyQt5 import QtCore, QtWidgets
|
from PyQt5 import QtCore, QtWidgets
|
||||||
|
|
||||||
from openlp.core.common.i18n import UiStrings, translate
|
from openlp.core.common.i18n import UiStrings, translate
|
||||||
from openlp.core.common.registry import Registry
|
# from openlp.core.common.registry import Registry
|
||||||
from openlp.core.common.settings import Settings
|
from openlp.core.common.settings import Settings
|
||||||
from openlp.core.lib.settingstab import SettingsTab
|
from openlp.core.lib.settingstab import SettingsTab
|
||||||
from openlp.core.lib.ui import create_button
|
from openlp.core.lib.ui import create_button
|
||||||
|
@ -36,7 +36,7 @@ from openlp.core.ui.advancedtab import AdvancedTab
|
|||||||
from openlp.core.ui.generaltab import GeneralTab
|
from openlp.core.ui.generaltab import GeneralTab
|
||||||
from openlp.core.ui.screenstab import ScreensTab
|
from openlp.core.ui.screenstab import ScreensTab
|
||||||
from openlp.core.ui.themestab import ThemesTab
|
from openlp.core.ui.themestab import ThemesTab
|
||||||
from openlp.core.ui.media.playertab import PlayerTab
|
# from openlp.core.ui.media.playertab import PlayerTab
|
||||||
from openlp.core.ui.settingsdialog import Ui_SettingsDialog
|
from openlp.core.ui.settingsdialog import Ui_SettingsDialog
|
||||||
|
|
||||||
|
|
||||||
|
@ -295,7 +295,7 @@ class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, R
|
|||||||
for plugin in self.plugin_manager.plugins:
|
for plugin in self.plugin_manager.plugins:
|
||||||
if plugin.uses_theme(old_theme_name):
|
if plugin.uses_theme(old_theme_name):
|
||||||
plugin.rename_theme(old_theme_name, new_theme_name)
|
plugin.rename_theme(old_theme_name, new_theme_name)
|
||||||
self.renderer.set_theme(self.get_theme_data(new_theme_data))
|
self.renderer.set_theme(self.get_theme_data(new_theme_name))
|
||||||
self.load_themes()
|
self.load_themes()
|
||||||
|
|
||||||
def on_copy_theme(self, field=None):
|
def on_copy_theme(self, field=None):
|
||||||
|
@ -30,10 +30,8 @@ from openlp.core.state import State
|
|||||||
from openlp.core.api.http import register_endpoint
|
from openlp.core.api.http import register_endpoint
|
||||||
from openlp.core.common.i18n import translate
|
from openlp.core.common.i18n import translate
|
||||||
from openlp.core.ui.icons import UiIcons
|
from openlp.core.ui.icons import UiIcons
|
||||||
from openlp.core.common.path import Path
|
|
||||||
from openlp.core.lib import build_icon
|
from openlp.core.lib import build_icon
|
||||||
from openlp.core.lib.plugin import Plugin, StringContent
|
from openlp.core.lib.plugin import Plugin, StringContent
|
||||||
from openlp.core.ui.icons import UiIcons
|
|
||||||
from openlp.plugins.media.endpoint import api_media_endpoint, media_endpoint
|
from openlp.plugins.media.endpoint import api_media_endpoint, media_endpoint
|
||||||
from openlp.plugins.media.lib.mediaitem import MediaMediaItem
|
from openlp.plugins.media.lib.mediaitem import MediaMediaItem
|
||||||
from openlp.plugins.media.lib.mediatab import MediaTab
|
from openlp.plugins.media.lib.mediatab import MediaTab
|
||||||
|
@ -27,7 +27,7 @@ import faulthandler
|
|||||||
import multiprocessing
|
import multiprocessing
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from OpenGL import GL
|
# from OpenGL import GL
|
||||||
|
|
||||||
from openlp.core.app import main
|
from openlp.core.app import main
|
||||||
from openlp.core.common import is_macosx, is_win
|
from openlp.core.common import is_macosx, is_win
|
||||||
|
@ -57,7 +57,7 @@ import webbrowser
|
|||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from getpass import getpass
|
from getpass import getpass
|
||||||
|
|
||||||
from lxml import etree, objectify
|
from lxml import objectify
|
||||||
from PyQt5 import QtCore
|
from PyQt5 import QtCore
|
||||||
|
|
||||||
SERVER_URL = 'http://www.transifex.com/api/2/project/openlp/resource/openlp-26x/'
|
SERVER_URL = 'http://www.transifex.com/api/2/project/openlp/resource/openlp-26x/'
|
||||||
|
@ -30,7 +30,6 @@ from openlp.core.state import State
|
|||||||
# sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock()
|
# sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock()
|
||||||
|
|
||||||
from openlp.core.api.endpoint.controller import controller_direction, controller_text
|
from openlp.core.api.endpoint.controller import controller_direction, controller_text
|
||||||
from openlp.core.display.render import Renderer
|
|
||||||
from openlp.core.common.registry import Registry
|
from openlp.core.common.registry import Registry
|
||||||
from openlp.core.display.screens import ScreenList
|
from openlp.core.display.screens import ScreenList
|
||||||
from openlp.core.lib.serviceitem import ServiceItem
|
from openlp.core.lib.serviceitem import ServiceItem
|
||||||
|
@ -1,461 +0,0 @@
|
|||||||
"""
|
|
||||||
Package to test the openlp.core.lib.htmlbuilder module.
|
|
||||||
"""
|
|
||||||
from unittest import TestCase
|
|
||||||
from unittest.mock import MagicMock, patch
|
|
||||||
|
|
||||||
from PyQt5 import QtCore
|
|
||||||
|
|
||||||
from openlp.core.common.settings import Settings
|
|
||||||
from openlp.core.lib.htmlbuilder import build_background_css, build_chords_css, build_footer_css, build_html, \
|
|
||||||
build_lyrics_css, build_lyrics_format_css, build_lyrics_outline_css
|
|
||||||
from openlp.core.lib.theme import HorizontalType, VerticalType
|
|
||||||
from tests.helpers.testmixin import TestMixin
|
|
||||||
|
|
||||||
|
|
||||||
HTML = r"""
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>OpenLP Display</title>
|
|
||||||
<style>
|
|
||||||
*{
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
;
|
|
||||||
}
|
|
||||||
.size {
|
|
||||||
position: absolute;
|
|
||||||
left: 0px;
|
|
||||||
top: 0px;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
#black {
|
|
||||||
z-index: 8;
|
|
||||||
background-color: black;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
#bgimage {
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
#image {
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
plugin CSS
|
|
||||||
#footer {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 6;
|
|
||||||
dummy: dummy;
|
|
||||||
}
|
|
||||||
/* lyric css */
|
|
||||||
sup {
|
|
||||||
font-size: 0.6em;
|
|
||||||
vertical-align: top;
|
|
||||||
position: relative;
|
|
||||||
top: -0.3em;
|
|
||||||
}
|
|
||||||
/* Chords css */
|
|
||||||
.chordline {
|
|
||||||
line-height: 1.0em;
|
|
||||||
}
|
|
||||||
.chordline span.chord span {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.chordline span.chord span strong {
|
|
||||||
position: absolute;
|
|
||||||
top: -0.8em;
|
|
||||||
left: 0;
|
|
||||||
font-size: 75%;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: normal;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
.firstchordline {
|
|
||||||
line-height: 1.0em;
|
|
||||||
}
|
|
||||||
.ws {
|
|
||||||
display: none;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script>
|
|
||||||
var timer = null;
|
|
||||||
var transition = false;
|
|
||||||
plugin JS
|
|
||||||
|
|
||||||
function show_image(src){
|
|
||||||
var img = document.getElementById('image');
|
|
||||||
img.src = src;
|
|
||||||
if(src == '')
|
|
||||||
img.style.display = 'none';
|
|
||||||
else
|
|
||||||
img.style.display = 'block';
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_blank(state){
|
|
||||||
var black = 'none';
|
|
||||||
var lyrics = '';
|
|
||||||
switch(state){
|
|
||||||
case 'theme':
|
|
||||||
lyrics = 'hidden';
|
|
||||||
break;
|
|
||||||
case 'black':
|
|
||||||
black = 'block';
|
|
||||||
break;
|
|
||||||
case 'desktop':
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
document.getElementById('black').style.display = black;
|
|
||||||
document.getElementById('lyricsmain').style.visibility = lyrics;
|
|
||||||
document.getElementById('image').style.visibility = lyrics;
|
|
||||||
document.getElementById('footer').style.visibility = lyrics;
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_footer(footertext){
|
|
||||||
document.getElementById('footer').innerHTML = footertext;
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_text(new_text){
|
|
||||||
var match = /-webkit-text-fill-color:[^;\"]+/gi;
|
|
||||||
if(timer != null)
|
|
||||||
clearTimeout(timer);
|
|
||||||
/*
|
|
||||||
QtWebkit bug with outlines and justify causing outline alignment
|
|
||||||
problems. (Bug 859950) Surround each word with a <span> to workaround,
|
|
||||||
but only in this scenario.
|
|
||||||
*/
|
|
||||||
var txt = document.getElementById('lyricsmain');
|
|
||||||
if(window.getComputedStyle(txt).textAlign == 'justify'){
|
|
||||||
if(window.getComputedStyle(txt).webkitTextStrokeWidth != '0px'){
|
|
||||||
new_text = new_text.replace(/(\s| )+(?![^<]*>)/g,
|
|
||||||
function(match) {
|
|
||||||
return '</span>' + match + '<span>';
|
|
||||||
});
|
|
||||||
new_text = '<span>' + new_text + '</span>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
text_fade('lyricsmain', new_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
function text_fade(id, new_text){
|
|
||||||
/*
|
|
||||||
Show the text.
|
|
||||||
*/
|
|
||||||
var text = document.getElementById(id);
|
|
||||||
if(text == null) return;
|
|
||||||
if(!transition){
|
|
||||||
text.innerHTML = new_text;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Fade text out. 0.1 to minimize the time "nothing" is shown on the screen.
|
|
||||||
text.style.opacity = '0.1';
|
|
||||||
// Fade new text in after the old text has finished fading out.
|
|
||||||
timer = window.setTimeout(function(){_show_text(text, new_text)}, 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
function _show_text(text, new_text) {
|
|
||||||
/*
|
|
||||||
Helper function to show the new_text delayed.
|
|
||||||
*/
|
|
||||||
text.innerHTML = new_text;
|
|
||||||
text.style.opacity = '1';
|
|
||||||
// Wait until the text is completely visible. We want to save the timer id, to be able to call
|
|
||||||
// clearTimeout(timer) when the text has changed before finishing fading.
|
|
||||||
timer = window.setTimeout(function(){timer = null;}, 400);
|
|
||||||
}
|
|
||||||
|
|
||||||
function show_text_completed(){
|
|
||||||
return (timer == null);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<img id="bgimage" class="size" style="display:none;" />
|
|
||||||
<img id="image" class="size" style="display:none;" />
|
|
||||||
plugin HTML
|
|
||||||
<div class="lyricstable"><div id="lyricsmain" style="opacity:1" class="lyricscell lyricsmain"></div></div>
|
|
||||||
<div id="footer" class="footer"></div>
|
|
||||||
<div id="black" class="size"></div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
"""
|
|
||||||
BACKGROUND_CSS_RADIAL = 'background: -webkit-gradient(radial, 5 50%, 100, 5 50%, 5, from(#000000), to(#FFFFFF)) fixed'
|
|
||||||
LYRICS_CSS = """
|
|
||||||
.lyricstable {
|
|
||||||
z-index: 5;
|
|
||||||
position: absolute;
|
|
||||||
display: table;
|
|
||||||
left: 10px; top: 20px;
|
|
||||||
}
|
|
||||||
.lyricscell {
|
|
||||||
display: table-cell;
|
|
||||||
word-wrap: break-word;
|
|
||||||
-webkit-transition: opacity 0.4s ease;
|
|
||||||
lyrics_format_css
|
|
||||||
}
|
|
||||||
.lyricsmain {
|
|
||||||
text-shadow: #000000 5px 5px;
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
LYRICS_OUTLINE_CSS = ' -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-color: #FFFFFF; '
|
|
||||||
LYRICS_FORMAT_CSS = """
|
|
||||||
word-wrap: break-word;
|
|
||||||
text-align: justify;
|
|
||||||
vertical-align: bottom;
|
|
||||||
font-family: Arial;
|
|
||||||
font-size: 40pt;
|
|
||||||
color: #FFFFFF;
|
|
||||||
line-height: 108%;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
padding-bottom: 0.5em;
|
|
||||||
padding-left: 2px;
|
|
||||||
width: 1580px;
|
|
||||||
height: 810px;
|
|
||||||
font-style: italic;
|
|
||||||
font-weight: bold;
|
|
||||||
"""
|
|
||||||
FOOTER_CSS_BASE = """
|
|
||||||
left: 10px;
|
|
||||||
bottom: 0px;
|
|
||||||
width: 1260px;
|
|
||||||
font-family: Arial;
|
|
||||||
font-size: 12pt;
|
|
||||||
color: #FFFFFF;
|
|
||||||
text-align: left;
|
|
||||||
white-space: %s;
|
|
||||||
"""
|
|
||||||
FOOTER_CSS = FOOTER_CSS_BASE % ('nowrap')
|
|
||||||
FOOTER_CSS_WRAP = FOOTER_CSS_BASE % ('normal')
|
|
||||||
FOOTER_CSS_INVALID = ''
|
|
||||||
CHORD_CSS_ENABLED = """
|
|
||||||
.chordline {
|
|
||||||
line-height: 2.0em;
|
|
||||||
}
|
|
||||||
.chordline span.chord span {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
.chordline span.chord span strong {
|
|
||||||
position: absolute;
|
|
||||||
top: -0.8em;
|
|
||||||
left: 0;
|
|
||||||
font-size: 75%;
|
|
||||||
font-weight: normal;
|
|
||||||
line-height: normal;
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
.firstchordline {
|
|
||||||
line-height: 2.1em;
|
|
||||||
}
|
|
||||||
.ws {
|
|
||||||
display: inline;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
}"""
|
|
||||||
|
|
||||||
__default_settings__ = {
|
|
||||||
'songs/mainview chords': False,
|
|
||||||
'songs/enable chords': True
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class Htmbuilder(TestCase, TestMixin):
|
|
||||||
"""
|
|
||||||
Test the functions in the Htmlbuilder module
|
|
||||||
"""
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create the UI
|
|
||||||
"""
|
|
||||||
self.build_settings()
|
|
||||||
Settings().extend_default_settings(__default_settings__)
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
"""
|
|
||||||
Delete all the C++ objects at the end so that we don't have a segfault
|
|
||||||
"""
|
|
||||||
self.destroy_settings()
|
|
||||||
|
|
||||||
def test_build_html(self):
|
|
||||||
"""
|
|
||||||
Test the build_html() function
|
|
||||||
"""
|
|
||||||
# GIVEN: Mocked arguments and function.
|
|
||||||
with patch('openlp.core.lib.htmlbuilder.build_background_css') as mocked_build_background_css, \
|
|
||||||
patch('openlp.core.lib.htmlbuilder.build_footer_css') as mocked_build_footer_css, \
|
|
||||||
patch('openlp.core.lib.htmlbuilder.build_lyrics_css') as mocked_build_lyrics_css:
|
|
||||||
# Mocked function.
|
|
||||||
mocked_build_background_css.return_value = ''
|
|
||||||
mocked_build_footer_css.return_value = 'dummy: dummy;'
|
|
||||||
mocked_build_lyrics_css.return_value = ''
|
|
||||||
# Mocked arguments.
|
|
||||||
item = MagicMock()
|
|
||||||
item.bg_image_bytes = None
|
|
||||||
screen = MagicMock()
|
|
||||||
is_live = False
|
|
||||||
background = None
|
|
||||||
plugin = MagicMock()
|
|
||||||
plugin.get_display_css.return_value = 'plugin CSS'
|
|
||||||
plugin.get_display_javascript.return_value = 'plugin JS'
|
|
||||||
plugin.get_display_html.return_value = 'plugin HTML'
|
|
||||||
plugins = [plugin]
|
|
||||||
|
|
||||||
# WHEN: Create the html.
|
|
||||||
html = build_html(item, screen, is_live, background, plugins=plugins)
|
|
||||||
|
|
||||||
# THEN: The returned html should match.
|
|
||||||
assert html == HTML, 'The returned html should match'
|
|
||||||
|
|
||||||
def test_build_background_css_radial(self):
|
|
||||||
"""
|
|
||||||
Test the build_background_css() function with a radial background
|
|
||||||
"""
|
|
||||||
# GIVEN: Mocked arguments.
|
|
||||||
item = MagicMock()
|
|
||||||
item.theme_data.background_start_color = '#000000'
|
|
||||||
item.theme_data.background_end_color = '#FFFFFF'
|
|
||||||
width = 10
|
|
||||||
|
|
||||||
# WHEN: Create the css.
|
|
||||||
css = build_background_css(item, width)
|
|
||||||
|
|
||||||
# THEN: The returned css should match.
|
|
||||||
assert BACKGROUND_CSS_RADIAL == css, 'The background css should be equal.'
|
|
||||||
|
|
||||||
def test_build_lyrics_css(self):
|
|
||||||
"""
|
|
||||||
Test the build_lyrics_css() function
|
|
||||||
"""
|
|
||||||
# GIVEN: Mocked method and arguments.
|
|
||||||
with patch('openlp.core.lib.htmlbuilder.build_lyrics_format_css') as mocked_build_lyrics_format_css, \
|
|
||||||
patch('openlp.core.lib.htmlbuilder.build_lyrics_outline_css') as mocked_build_lyrics_outline_css:
|
|
||||||
mocked_build_lyrics_format_css.return_value = 'lyrics_format_css'
|
|
||||||
mocked_build_lyrics_outline_css.return_value = ''
|
|
||||||
item = MagicMock()
|
|
||||||
item.main = QtCore.QRect(10, 20, 10, 20)
|
|
||||||
item.theme_data.font_main_shadow = True
|
|
||||||
item.theme_data.font_main_shadow_color = '#000000'
|
|
||||||
item.theme_data.font_main_shadow_size = 5
|
|
||||||
|
|
||||||
# WHEN: Create the css.
|
|
||||||
css = build_lyrics_css(item)
|
|
||||||
|
|
||||||
# THEN: The css should be equal.
|
|
||||||
assert LYRICS_CSS == css, 'The lyrics css should be equal.'
|
|
||||||
|
|
||||||
def test_build_lyrics_outline_css(self):
|
|
||||||
"""
|
|
||||||
Test the build_lyrics_outline_css() function
|
|
||||||
"""
|
|
||||||
# GIVEN: The mocked theme data.
|
|
||||||
theme_data = MagicMock()
|
|
||||||
theme_data.font_main_outline = True
|
|
||||||
theme_data.font_main_outline_size = 2
|
|
||||||
theme_data.font_main_color = '#FFFFFF'
|
|
||||||
theme_data.font_main_outline_color = '#000000'
|
|
||||||
|
|
||||||
# WHEN: Create the css.
|
|
||||||
css = build_lyrics_outline_css(theme_data)
|
|
||||||
|
|
||||||
# THEN: The css should be equal.
|
|
||||||
assert LYRICS_OUTLINE_CSS == css, 'The outline css should be equal.'
|
|
||||||
|
|
||||||
def test_build_lyrics_format_css(self):
|
|
||||||
"""
|
|
||||||
Test the build_lyrics_format_css() function
|
|
||||||
"""
|
|
||||||
# GIVEN: Mocked arguments.
|
|
||||||
theme_data = MagicMock()
|
|
||||||
theme_data.display_horizontal_align = HorizontalType.Justify
|
|
||||||
theme_data.display_vertical_align = VerticalType.Bottom
|
|
||||||
theme_data.font_main_name = 'Arial'
|
|
||||||
theme_data.font_main_size = 40
|
|
||||||
theme_data.font_main_color = '#FFFFFF'
|
|
||||||
theme_data.font_main_italics = True
|
|
||||||
theme_data.font_main_bold = True
|
|
||||||
theme_data.font_main_line_adjustment = 8
|
|
||||||
width = 1580
|
|
||||||
height = 810
|
|
||||||
|
|
||||||
# WHEN: Get the css.
|
|
||||||
css = build_lyrics_format_css(theme_data, width, height)
|
|
||||||
|
|
||||||
# THEN: They should be equal.
|
|
||||||
assert LYRICS_FORMAT_CSS == css, 'The lyrics format css should be equal.'
|
|
||||||
|
|
||||||
def test_build_footer_css(self):
|
|
||||||
"""
|
|
||||||
Test the build_footer_css() function
|
|
||||||
"""
|
|
||||||
# GIVEN: Create a theme.
|
|
||||||
item = MagicMock()
|
|
||||||
item.footer = QtCore.QRect(10, 921, 1260, 103)
|
|
||||||
item.theme_data.font_footer_name = 'Arial'
|
|
||||||
item.theme_data.font_footer_size = 12
|
|
||||||
item.theme_data.font_footer_color = '#FFFFFF'
|
|
||||||
height = 1024
|
|
||||||
|
|
||||||
# WHEN: create the css with default settings.
|
|
||||||
css = build_footer_css(item, height)
|
|
||||||
|
|
||||||
# THEN: THE css should be the same.
|
|
||||||
assert FOOTER_CSS == css, 'The footer strings should be equal.'
|
|
||||||
|
|
||||||
def test_build_footer_css_wrap(self):
|
|
||||||
"""
|
|
||||||
Test the build_footer_css() function
|
|
||||||
"""
|
|
||||||
# GIVEN: Create a theme.
|
|
||||||
item = MagicMock()
|
|
||||||
item.footer = QtCore.QRect(10, 921, 1260, 103)
|
|
||||||
item.theme_data.font_footer_name = 'Arial'
|
|
||||||
item.theme_data.font_footer_size = 12
|
|
||||||
item.theme_data.font_footer_color = '#FFFFFF'
|
|
||||||
height = 1024
|
|
||||||
|
|
||||||
# WHEN: Settings say that footer should wrap
|
|
||||||
Settings().setValue('themes/wrap footer', True)
|
|
||||||
css = build_footer_css(item, height)
|
|
||||||
|
|
||||||
# THEN: Footer should wrap
|
|
||||||
assert FOOTER_CSS_WRAP == css, 'The footer strings should be equal.'
|
|
||||||
|
|
||||||
def test_build_footer_invalid(self):
|
|
||||||
"""
|
|
||||||
Test the build_footer_css() function
|
|
||||||
"""
|
|
||||||
# GIVEN: Create a theme.
|
|
||||||
css = []
|
|
||||||
item = MagicMock()
|
|
||||||
item.theme_data = None
|
|
||||||
item.footer = 'FAIL'
|
|
||||||
height = 1024
|
|
||||||
|
|
||||||
# WHEN: Settings say that footer should wrap
|
|
||||||
css.append(build_footer_css(item, height))
|
|
||||||
item.theme_data = 'TEST'
|
|
||||||
item.footer = None
|
|
||||||
css.append(build_footer_css(item, height))
|
|
||||||
|
|
||||||
# THEN: Footer should wrap
|
|
||||||
assert FOOTER_CSS_INVALID == css[0], 'The footer strings should be blank.'
|
|
||||||
assert FOOTER_CSS_INVALID == css[1], 'The footer strings should be blank.'
|
|
||||||
|
|
||||||
def test_build_chords_css(self):
|
|
||||||
"""
|
|
||||||
Test the build_chords_css() function
|
|
||||||
"""
|
|
||||||
# GIVEN: A setting that activates chords on the mainview
|
|
||||||
Settings().setValue('songs/enable chords', True)
|
|
||||||
Settings().setValue('songs/mainview chords', True)
|
|
||||||
|
|
||||||
# WHEN: Building the chord CSS
|
|
||||||
chord_css = build_chords_css()
|
|
||||||
|
|
||||||
# THEN: The build css should look as expected
|
|
||||||
assert CHORD_CSS_ENABLED == chord_css, 'The chord CSS should look as expected'
|
|
@ -30,7 +30,6 @@ from PyQt5 import QtCore, QtGui
|
|||||||
from openlp.core.common.path import Path
|
from openlp.core.common.path import Path
|
||||||
from openlp.core.lib import build_icon, check_item_selected, create_separated_list, create_thumb, \
|
from openlp.core.lib import build_icon, check_item_selected, create_separated_list, create_thumb, \
|
||||||
get_text_file_string, image_to_byte, resize_image, str_to_bool, validate_thumb
|
get_text_file_string, image_to_byte, resize_image, str_to_bool, validate_thumb
|
||||||
from openlp.core.lib.formattingtags import FormattingTags
|
|
||||||
from tests.utils.constants import RESOURCE_PATH
|
from tests.utils.constants import RESOURCE_PATH
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,13 +27,12 @@ from pathlib import Path
|
|||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
|
|
||||||
from PyQt5 import QtGui, QtCore, QtWidgets
|
from PyQt5 import QtCore, QtWidgets
|
||||||
|
|
||||||
from openlp.core.state import State
|
from openlp.core.state import State
|
||||||
from openlp.core.common.i18n import UiStrings
|
from openlp.core.common.i18n import UiStrings
|
||||||
from openlp.core.common.registry import Registry
|
from openlp.core.common.registry import Registry
|
||||||
from openlp.core.display.screens import ScreenList
|
from openlp.core.display.screens import ScreenList
|
||||||
from openlp.core.lib.plugin import PluginStatus
|
|
||||||
from openlp.core.ui.mainwindow import MainWindow
|
from openlp.core.ui.mainwindow import MainWindow
|
||||||
from tests.helpers.testmixin import TestMixin
|
from tests.helpers.testmixin import TestMixin
|
||||||
from tests.utils.constants import TEST_RESOURCES_PATH
|
from tests.utils.constants import TEST_RESOURCES_PATH
|
||||||
|
@ -30,7 +30,7 @@ from unittest.mock import MagicMock, call, patch
|
|||||||
from PyQt5 import QtGui
|
from PyQt5 import QtGui
|
||||||
|
|
||||||
from openlp.core.common.i18n import UiStrings
|
from openlp.core.common.i18n import UiStrings
|
||||||
from openlp.core.lib import ImageSource
|
# from openlp.core.lib import ImageSource
|
||||||
from openlp.core.widgets.views import ListPreviewWidget, ListWidgetWithDnD, TreeWidgetWithDnD, handle_mime_data_urls
|
from openlp.core.widgets.views import ListPreviewWidget, ListWidgetWithDnD, TreeWidgetWithDnD, handle_mime_data_urls
|
||||||
from openlp.core.ui.icons import UiIcons
|
from openlp.core.ui.icons import UiIcons
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user