This commit is contained in:
Raoul Snyman 2010-09-04 19:53:10 +02:00
commit a44ae94de7
19 changed files with 1168 additions and 530 deletions

View File

@ -39,40 +39,40 @@ log = logging.getLogger(__name__)
html_expands = []
html_expands.append({u'desc':u'Red', u'start tag':u'{r}', \
u'start html':u'<font color=red>', \
u'end tag':u'{/r}', u'end html':u'</font>', \
u'start html':u'<span style="-webkit-text-fill-color:red">', \
u'end tag':u'{/r}', u'end html':u'</span>', \
u'protected':False})
html_expands.append({u'desc':u'Black', u'start tag':u'{b}', \
u'start html':u'<font color=black>', \
u'end tag':u'{/b}', u'end html':u'</font>', \
u'start html':u'<span style="-webkit-text-fill-color:black">', \
u'end tag':u'{/b}', u'end html':u'</span>', \
u'protected':False})
html_expands.append({u'desc':u'Blue', u'start tag':u'{bl}', \
u'start html':u'<font color=blue>', \
u'end tag':u'{/bl}', u'end html':u'</font>', \
u'start html':u'<span style="-webkit-text-fill-color:blue">', \
u'end tag':u'{/bl}', u'end html':u'</span>', \
u'protected':False})
html_expands.append({u'desc':u'Yellow', u'start tag':u'{y}', \
u'start html':u'<font color=yellow>', \
u'end tag':u'{/y}', u'end html':u'</font>', \
u'start html':u'<span style="-webkit-text-fill-color:yellow">', \
u'end tag':u'{/y}', u'end html':u'</span>', \
u'protected':False})
html_expands.append({u'desc':u'Green', u'start tag':u'{g}', \
u'start html':u'<font color=green>', \
u'end tag':u'{/g}', u'end html':u'</font>', \
u'start html':u'<span style="-webkit-text-fill-color:green">', \
u'end tag':u'{/g}', u'end html':u'</span>', \
u'protected':False})
html_expands.append({u'desc':u'Pink', u'start tag':u'{pk}', \
u'start html':u'<font color=#CC33CC>', \
u'end tag':u'{/pk}', u'end html':u'</font>', \
u'start html':u'<span style="-webkit-text-fill-color:#CC33CC">', \
u'end tag':u'{/pk}', u'end html':u'</span>', \
u'protected':False})
html_expands.append({u'desc':u'Orange', u'start tag':u'{o}', \
u'start html':u'<font color=#CC0033>', \
u'end tag':u'{/o}', u'end html':u'</font>', \
u'start html':u'<span style="-webkit-text-fill-color:#CC0033">', \
u'end tag':u'{/o}', u'end html':u'</span>', \
u'protected':False})
html_expands.append({u'desc':u'Purple', u'start tag':u'{pp}', \
u'start html':u'<font color=#9900FF>', \
u'end tag':u'{/pp}', u'end html':u'</font>', \
u'start html':u'<span style="-webkit-text-fill-color:#9900FF">', \
u'end tag':u'{/pp}', u'end html':u'</span>', \
u'protected':False})
html_expands.append({u'desc':u'White', u'start tag':u'{w}', \
u'start html':u'<font color=white>', \
u'end tag':u'{/w}', u'end html':u'</font>', \
u'start html':u'<span style="-webkit-text-fill-color:white">', \
u'end tag':u'{/w}', u'end html':u'</span>', \
u'protected':False})
html_expands.append({u'desc':u'Superscript', u'start tag':u'{su}', \
u'start html':u'<sup>', \
@ -316,6 +316,7 @@ def expand_tags(text):
text = text.replace(tag[u'end tag'], tag[u'end html'])
return text
from spelltextedit import SpellTextEdit
from eventreceiver import Receiver
from settingsmanager import SettingsManager
from plugin import PluginStatus, Plugin
@ -324,7 +325,8 @@ from settingstab import SettingsTab
from serviceitem import ServiceItem
from serviceitem import ServiceItemType
from serviceitem import ItemCapabilities
from htmlbuilder import build_html
from htmlbuilder import build_html, build_lyrics_format_css, \
build_lyrics_outline_css
from toolbar import OpenLPToolbar
from dockwidget import OpenLPDockWidget
from theme import ThemeLevel, ThemeXML

View File

@ -24,8 +24,13 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import logging
from PyQt4 import QtWebKit
from openlp.core.lib import image_to_byte
log = logging.getLogger(__name__)
HTMLSRC = u"""
<html>
<head>
@ -37,9 +42,9 @@ HTMLSRC = u"""
border: 0;
}
body {
background-color: black;
%s;
}
.dim {
.size {
position: absolute;
left: 0px;
top: 0px;
@ -51,6 +56,9 @@ body {
background-color: black;
display: none;
}
#image {
z-index:1;
}
#video {
z-index:2;
}
@ -136,8 +144,12 @@ body {
}
document.getElementById('black').style.display = black;
document.getElementById('lyricsmain').style.visibility = lyrics;
document.getElementById('lyricsoutline').style.visibility = lyrics;
document.getElementById('lyricsshadow').style.visibility = lyrics;
outline = document.getElementById('lyricsoutline')
if(outline!=null)
outline.style.visibility = lyrics;
shadow = document.getElementById('lyricsshadow')
if(shadow!=null)
shadow.style.visibility = lyrics;
document.getElementById('footer').style.visibility = lyrics;
var vid = document.getElementById('video');
if(vid.src != ''){
@ -156,7 +168,7 @@ body {
return 0;
}
if(position == ''){
position = window.getComputedStyle(text, '').verticalAlign;
position = getComputedStyle(text, '').verticalAlign;
}
switch(position)
{
@ -181,116 +193,67 @@ body {
}
function show_text(newtext){
var text1 = document.getElementById('lyricsmain');
var texto1 = document.getElementById('lyricsoutline');
var texts1 = document.getElementById('lyricsshadow');
if(!transition){
text1.innerHTML = newtext;
texto1.innerHTML = newtext;
texts1.innerHTML = newtext;
return;
}
var text2 = document.getElementById('lyricsmain2');
var texto2 = document.getElementById('lyricsoutline2');
var texts2 = document.getElementById('lyricsshadow2');
if((text2.style.opacity == '')||(parseFloat(text2.style.opacity) < 0.5))
{
text2.innerHTML = text1.innerHTML;
text2.style.opacity = text1.style.opacity;
texto2.innerHTML = text1.innerHTML;
texto2.style.opacity = text1.style.opacity;
texts2.innerHTML = text1.innerHTML;
texts2.style.opacity = text1.style.opacity;
}
text1.style.opacity = 0;
text1.innerHTML = newtext;
texto1.style.opacity = 0;
texto1.innerHTML = newtext;
texts1.style.opacity = 0;
texts1.innerHTML = newtext;
// For performance reasons, we'll not animate the shadow for now
texts2.style.opacity = 0;
if(timer != null)
clearTimeout(timer);
timer = setTimeout('text_fade()', 50);
text_fade('lyricsmain', newtext);
text_fade('lyricsoutline', newtext);
text_fade('lyricsshadow', newtext);
if(text_opacity()==1) return;
timer = setTimeout(function(){
show_text(newtext);
}, 100);
}
function text_fade(){
var text1 = document.getElementById('lyricsmain');
var texto1 = document.getElementById('lyricsoutline');
var texts1 = document.getElementById('lyricsshadow');
var text2 = document.getElementById('lyricsmain2');
var texto2 = document.getElementById('lyricsoutline2');
var texts2 = document.getElementById('lyricsshadow2');
if(parseFloat(text1.style.opacity) < 1){
text1.style.opacity = parseFloat(text1.style.opacity) + 0.1;
texto1.style.opacity = parseFloat(texto1.style.opacity) + 0.1;
// Don't animate shadow (performance)
//texts1.style.opacity = parseFloat(texts1.style.opacity) + 0.1;
function text_fade(id, newtext){
/*
Using -webkit-transition: opacity 1s linear; would have been preferred
but it isn't currently quick enough when animating multiple layers of
large areas of large text. Therefore do it manually as best we can.
Hopefully in the future we can revisit and do more interesting
transitions using -webkit-transition and -webkit-transform.
However we need to ensure interrupted transitions (quickly change 2
slides) still looks pretty and is zippy.
*/
var text = document.getElementById(id);
if(text==null) return;
if(!transition){
text.innerHTML = newtext;
return;
}
if(parseFloat(text2.style.opacity) > 0){
text2.style.opacity = parseFloat(text2.style.opacity) - 0.1;
texto2.style.opacity = parseFloat(texto2.style.opacity) - 0.1;
// Don't animate shadow (performance)
//texts2.style.opacity = parseFloat(texts2.style.opacity) - 0.1;
}
if((parseFloat(text1.style.opacity) < 1) ||
(parseFloat(text2.style.opacity) > 0)){
t = setTimeout('text_fade()', 50);
if(newtext==text.innerHTML){
text.style.opacity = parseFloat(text.style.opacity) + 0.3;
if(text.style.opacity>0.7)
text.style.opacity = 1;
} else {
text1.style.opacity = 1;
texto1.style.opacity = 1;
texts1.style.opacity = 1;
text2.style.opacity = 0;
texto2.style.opacity = 0;
texts2.style.opacity = 0;
text.style.opacity = parseFloat(text.style.opacity) - 0.3;
if(text.style.opacity<=0.1){
text.innerHTML = newtext;
}
}
}
function text_opacity(){
var text = document.getElementById('lyricsmain');
return getComputedStyle(text, '').opacity;
}
function show_text_complete(){
return (document.getElementById('lyricsmain').style.opacity == 1);
return (text_opacity()==1);
}
</script>
</head>
<body>
<!--
Using tables, rather than div's to make use of the vertical-align style that
doesn't work on div's. This avoids the need to do positioning manually which
could get messy when changing verses esp. with transitions
Would prefer to use a single table and make use of -webkit-text-fill-color
-webkit-text-stroke and text-shadow styles, but they have problems working/
co-operating in qwebkit. https://bugs.webkit.org/show_bug.cgi?id=43187
Therefore one table for text, one for outline and one for shadow.
-->
<table class="lyricstable lyricscommon">
<tr><td id="lyricsmain" class="lyrics"></td></tr>
</table>
<table class="lyricsoutlinetable lyricscommon">
<tr><td id="lyricsoutline" class="lyricsoutline lyrics"></td></tr>
</table>
<table class="lyricsshadowtable lyricscommon">
<tr><td id="lyricsshadow" class="lyricsshadow lyrics"></td></tr>
</table>
<table class="lyricstable lyricscommon">
<tr><td id="lyricsmain2" class="lyrics"></td></tr>
</table>
<table class="lyricsoutlinetable lyricscommon">
<tr><td id="lyricsoutline2" class="lyricsoutline lyrics"></td></tr>
</table>
<table class="lyricsshadowtable lyricscommon">
<tr><td id="lyricsshadow2" class="lyricsshadow lyrics"></td></tr>
</table>
<div id="alert" style="visibility:hidden;"></div>
<img id="image" class="size" src="%s" />
<video id="video" class="size"></video>
%s
<div id="footer" class="footer"></div>
<video class="dim" id="video"></video>
<div class="dim" id="black"></div>
<img class="dim" id="image" src="%s" />
<div id="black" class="size"></div>
<div id="alert" style="visibility:hidden;"></div>
</body>
</html>
"""
def build_html(item, screen, alert):
def build_html(item, screen, alert, islive):
"""
Build the full web paged structure for display
@ -300,92 +263,245 @@ def build_html(item, screen, alert):
Current display information
`alert`
Alert display display information
`islive`
Item is going live, rather than preview/theme building
"""
width = screen[u'size'].width()
height = screen[u'size'].height()
theme = item.themedata
webkitvers = webkit_version()
if item.bg_frame:
image = u'data:image/png;base64,%s' % image_to_byte(item.bg_frame)
else:
image = u''
html = HTMLSRC % (width, height,
build_alert(alert, width),
build_footer(item),
build_lyrics(item),
u'true' if theme and theme.display_slideTransition \
html = HTMLSRC % (build_background_css(item, width, height),
width, height,
build_alert_css(alert, width),
build_footer_css(item),
build_lyrics_css(item, webkitvers),
u'true' if theme and theme.display_slideTransition and islive \
else u'false',
image)
image,
build_lyrics_html(item, webkitvers))
return html
def build_lyrics(item):
def webkit_version():
"""
Build the video display div
Return the Webkit version in use.
Note method added relatively recently, so return 0 if prior to this
"""
try:
webkitvers = float(QtWebKit.qWebKitVersion())
log.debug(u'Webkit version = %s' % webkitvers)
except AttributeError:
webkitvers = 0
return webkitvers
def build_background_css(item, width, height):
"""
Build the background css
`item`
Service Item containing theme and location information
"""
width = int(width) / 2
theme = item.themedata
background = u'background-color: black'
if theme:
if theme.background_type == u'solid':
background = u'background-color: %s' % theme.background_color
else:
if theme.background_direction == u'horizontal':
background = \
u'background: ' \
u'-webkit-gradient(linear, left top, left bottom, ' \
'from(%s), to(%s))' % (theme.background_startColor,
theme.background_endColor)
elif theme.background_direction == u'vertical':
background = \
u'background: -webkit-gradient(linear, left top, ' \
u'right top, from(%s), to(%s))' % \
(theme.background_startColor, theme.background_endColor)
else:
background = \
u'background: -webkit-gradient(radial, %s 50%%, 100, %s ' \
u'50%%, %s, from(%s), to(%s))' % (width, width, width,
theme.background_startColor, theme.background_endColor)
return background
def build_lyrics_css(item, webkitvers):
"""
Build the lyrics display css
`item`
Service Item containing theme and location information
`webkitvers`
The version of qtwebkit we're using
"""
style = """
.lyricscommon { position: absolute; %s }
.lyricstable { z-index:4; %s }
.lyricsoutlinetable { z-index:3; %s }
.lyricsshadowtable { z-index:2; %s }
.lyrics { %s }
.lyricsoutline { %s }
.lyricsshadow { %s }
.lyricstable {
z-index:4;
position: absolute;
display: table;
%s
}
.lyricscell {
display:table-cell;
word-wrap: break-word;
%s
}
.lyricsmain {
%s
}
.lyricsoutline {
%s
}
.lyricsshadow {
%s
}
"""
theme = item.themedata
lyricscommon = u''
lyricstable = u''
outlinetable = u''
shadowtable = u''
lyrics = u''
outline = u'display: none;'
shadow = u'display: none;'
lyricsmain = u''
outline = u''
shadow = u''
if theme:
lyricscommon = u'width: %spx; height: %spx; word-wrap: break-word; ' \
u'font-family: %s; font-size: %spx; color: %s; line-height: %d%%;' \
% (item.main.width(), item.main.height(), theme.font_main_name,
theme.font_main_proportion, theme.font_main_color,
100 + int(theme.font_main_line_adjustment))
lyricstable = u'left: %spx; top: %spx;' % \
(item.main.x(), item.main.y())
outlinetable = u'left: %spx; top: %spx;' % \
(item.main.x(), item.main.y())
shadowtable = u'left: %spx; top: %spx;' % \
(item.main.x() + float(theme.display_shadow_size),
item.main.y() + float(theme.display_shadow_size))
align = u''
if theme.display_horizontalAlign == 2:
align = u'text-align:center;'
elif theme.display_horizontalAlign == 1:
align = u'text-align:right;'
lyrics = build_lyrics_format_css(theme, item.main.width(),
item.main.height())
# For performance reasons we want to show as few DIV's as possible,
# especially when animating/transitions.
# However some bugs in older versions of qtwebkit mean we need to
# perform workarounds and add extra divs. Only do these when needed.
#
# Before 533.3 the webkit-text-fill colour wasn't displayed, only the
# stroke (outline) color. So put stroke layer underneath the main text.
#
# Before 534.4 the webkit-text-stroke was sometimes out of alignment
# with the fill, or normal text. letter-spacing=1 is workaround
# https://bugs.webkit.org/show_bug.cgi?id=44403
#
# Before 534.4 the text-shadow didn't get displayed when
# webkit-text-stroke was used. So use an offset text layer underneath.
# https://bugs.webkit.org/show_bug.cgi?id=19728
if webkitvers >= 533.3:
lyricsmain += build_lyrics_outline_css(theme)
else:
align = u'text-align:left;'
if theme.display_verticalAlign == 2:
valign = u'vertical-align:bottom;'
elif theme.display_verticalAlign == 1:
valign = u'vertical-align:middle;'
else:
valign = u'vertical-align:top;'
lyrics = u'%s %s' % (align, valign)
if theme.display_outline:
lyricscommon += u' letter-spacing: 1px;'
outline = u'-webkit-text-stroke: %sem %s; ' % \
(float(theme.display_outline_size) / 16,
theme.display_outline_color)
if theme.display_shadow:
shadow = u'-webkit-text-stroke: %sem %s; ' \
u'-webkit-text-fill-color: %s; ' % \
(float(theme.display_outline_size) / 16,
theme.display_shadow_color, theme.display_shadow_color)
else:
if theme.display_shadow:
shadow = u'color: %s;' % (theme.display_shadow_color)
lyrics_html = style % (lyricscommon, lyricstable, outlinetable,
shadowtable, lyrics, outline, shadow)
return lyrics_html
outline = build_lyrics_outline_css(theme)
if theme.display_shadow:
if theme.display_outline and webkitvers < 534.3:
shadow = u'padding-left: %spx; padding-top: %spx ' % \
(theme.display_shadow_size, theme.display_shadow_size)
shadow += build_lyrics_outline_css(theme, True)
else:
lyricsmain += u' text-shadow: %s %spx %spx;' % \
(theme.display_shadow_color, theme.display_shadow_size,
theme.display_shadow_size)
lyrics_css = style % (lyricstable, lyrics, lyricsmain, outline, shadow)
return lyrics_css
def build_lyrics_outline_css(theme, is_shadow=False):
"""
Build the css which controls the theme outline
Also used by renderer for splitting verses
def build_footer(item):
`theme`
Object containing theme information
`is_shadow`
If true, use the shadow colors instead
"""
if theme.display_outline:
size = float(theme.display_outline_size) / 16
if is_shadow:
fill_color = theme.display_shadow_color
outline_color = theme.display_shadow_color
else:
fill_color = theme.font_main_color
outline_color = theme.display_outline_color
return u' -webkit-text-stroke: %sem %s; ' \
u'-webkit-text-fill-color: %s; ' % (size, outline_color, fill_color)
else:
return u''
def build_lyrics_format_css(theme, width, height):
"""
Build the css which controls the theme format
Also used by renderer for splitting verses
`theme`
Object containing theme information
`width`
Width of the lyrics block
`height`
Height of the lyrics block
"""
if theme.display_horizontalAlign == 2:
align = u'center'
elif theme.display_horizontalAlign == 1:
align = u'right'
else:
align = u'left'
if theme.display_verticalAlign == 2:
valign = u'bottom'
elif theme.display_verticalAlign == 1:
valign = u'middle'
else:
valign = u'top'
lyrics = u'white-space:pre-wrap; word-wrap: break-word; ' \
'text-align: %s; vertical-align: %s; font-family: %s; ' \
'font-size: %spt; color: %s; line-height: %d%%; ' \
'margin:0; padding:0; width: %spx; height: %spx; ' % \
(align, valign, theme.font_main_name, theme.font_main_proportion,
theme.font_main_color, 100 + int(theme.font_main_line_adjustment),
width, height)
if theme.display_outline:
if webkit_version() < 534.3:
lyrics += u' letter-spacing: 1px;'
if theme.font_main_italics:
lyrics += u' font-style:italic; '
if theme.font_main_weight == u'Bold':
lyrics += u' font-weight:bold; '
return lyrics
def build_lyrics_html(item, webkitvers):
"""
Build the HTML required to show the lyrics
`item`
Service Item containing theme and location information
`webkitvers`
The version of qtwebkit we're using
"""
# Bugs in some versions of QtWebKit mean we sometimes need additional
# divs for outline and shadow, since the CSS doesn't work.
# To support vertical alignment middle and bottom, nested div's using
# display:table/display:table-cell are required for each lyric block.
lyrics = u''
theme = item.themedata
if webkitvers < 534.4 and theme and theme.display_outline:
lyrics += u'<div class="lyricstable">' \
u'<div id="lyricsshadow" style="opacity:1" ' \
u'class="lyricscell lyricsshadow"></div></div>'
if webkitvers < 533.3:
lyrics += u'<div class="lyricstable">' \
u'<div id="lyricsoutline" style="opacity:1" ' \
u'class="lyricscell lyricsoutline"></div></div>'
lyrics += u'<div class="lyricstable">' \
u'<div id="lyricsmain" style="opacity:1" ' \
u'class="lyricscell lyricsmain"></div></div>'
return lyrics
def build_footer_css(item):
"""
Build the display of the item footer
@ -398,7 +514,7 @@ def build_footer(item):
width: %spx;
height: %spx;
font-family: %s;
font-size: %spx;
font-size: %spt;
color: %s;
text-align: %s;
"""
@ -416,7 +532,7 @@ def build_footer(item):
theme.font_footer_proportion, theme.font_footer_color, align)
return lyrics_html
def build_alert(alertTab, width):
def build_alert_css(alertTab, width):
"""
Build the display of the footer
@ -424,10 +540,10 @@ def build_alert(alertTab, width):
Details from the Alert tab for fonts etc
"""
style = """
width: %s;
width: %spx;
vertical-align: %s;
font-family: %s;
font-size: %spx;
font-size: %spt;
color: %s;
background-color: %s;
"""

View File

@ -29,9 +29,10 @@ format it for the output display.
"""
import logging
from PyQt4 import QtGui, QtCore
from PyQt4 import QtGui, QtCore, QtWebKit
from openlp.core.lib import resize_image, expand_tags
from openlp.core.lib import resize_image, expand_tags, \
build_lyrics_format_css, build_lyrics_outline_css
log = logging.getLogger(__name__)
@ -47,26 +48,13 @@ class Renderer(object):
Initialise the renderer.
"""
self._rect = None
self._debug = False
self._display_shadow_size_footer = 0
self._display_outline_size_footer = 0
self.theme_name = None
self._theme = None
self._bg_image_filename = None
self.frame = None
self.frame_opaque = None
self.bg_frame = None
self.bg_image = None
def set_debug(self, debug):
"""
Set the debug mode of the renderer.
``debug``
The debug mode.
"""
self._debug = debug
def set_theme(self, theme):
"""
Set the theme to be used.
@ -155,42 +143,35 @@ class Renderer(object):
for verse in verses_text:
lines = verse.split(u'\n')
for line in lines:
text.append(line)
doc = QtGui.QTextDocument()
doc.setPageSize(QtCore.QSizeF(self._rect.width(), self._rect.height()))
df = doc.defaultFont()
df.setPixelSize(self._theme.font_main_proportion)
df.setFamily(self._theme.font_main_name)
main_weight = 50
if self._theme.font_main_weight == u'Bold':
main_weight = 75
df.setWeight(main_weight)
doc.setDefaultFont(df)
layout = doc.documentLayout()
text.append(line)
web = QtWebKit.QWebView()
web.resize(self._rect.width(), self._rect.height())
web.setVisible(False)
frame = web.page().mainFrame()
# Adjust width and height to account for shadow. outline done in css
width = self._rect.width() - int(self._theme.display_shadow_size)
height = self._rect.height() - int(self._theme.display_shadow_size)
shell = u'<html><head><style>#main {%s %s}</style><body>' \
u'<div id="main">' % \
(build_lyrics_format_css(self._theme, width, height),
build_lyrics_outline_css(self._theme))
formatted = []
if self._theme.font_main_weight == u'Bold' and \
self._theme.font_main_italics:
shell = u'{p}{st}{it}%s{/it}{/st}{/p}'
elif self._theme.font_main_weight == u'Bold' and \
not self._theme.font_main_italics:
shell = u'{p}{st}%s{/st}{/p}'
elif self._theme.font_main_italics:
shell = u'{p}{it}%s{/it}{/p}'
else:
shell = u'{p}%s{/p}'
temp_text = u''
old_html_text = u''
html_text = u''
styled_text = u''
js_height = 'document.getElementById("main").scrollHeight'
for line in text:
# mark line ends
temp_text = temp_text + line + line_end
html_text = shell % expand_tags(temp_text)
doc.setHtml(html_text)
# Text too long so gone to next mage
if layout.pageCount() != 1:
formatted.append(shell % old_html_text)
temp_text = line
old_html_text = temp_text
formatted.append(shell % old_html_text)
styled_line = expand_tags(line) + line_end
styled_text += styled_line
html = shell + styled_text + u'</div></body></html>'
web.setHtml(html)
# Text too long so go to next page
text_height = int(frame.evaluateJavaScript(js_height).toString())
if text_height > height:
formatted.append(html_text)
html_text = u''
styled_text = styled_line
html_text += line + line_end
formatted.append(html_text)
log.debug(u'format_slide - End')
return formatted
@ -203,44 +184,47 @@ class Renderer(object):
self.bg_frame = QtGui.QImage(self.frame.width(),
self.frame.height(), QtGui.QImage.Format_ARGB32_Premultiplied)
log.debug(u'render background %s start', self._theme.background_type)
painter = QtGui.QPainter()
painter.begin(self.bg_frame)
if self._theme.background_type == u'solid':
painter.fillRect(self.frame.rect(),
QtGui.QColor(self._theme.background_color))
self.bg_frame = None
# painter.fillRect(self.frame.rect(),
# QtGui.QColor(self._theme.background_color))
elif self._theme.background_type == u'gradient':
self.bg_frame = None
# gradient
gradient = None
if self._theme.background_direction == u'horizontal':
w = int(self.frame.width()) / 2
# vertical
gradient = QtGui.QLinearGradient(w, 0, w, self.frame.height())
elif self._theme.background_direction == u'vertical':
h = int(self.frame.height()) / 2
# Horizontal
gradient = QtGui.QLinearGradient(0, h, self.frame.width(), h)
else:
w = int(self.frame.width()) / 2
h = int(self.frame.height()) / 2
# Circular
gradient = QtGui.QRadialGradient(w, h, w)
gradient.setColorAt(0,
QtGui.QColor(self._theme.background_startColor))
gradient.setColorAt(1,
QtGui.QColor(self._theme.background_endColor))
painter.setBrush(QtGui.QBrush(gradient))
rect_path = QtGui.QPainterPath()
max_x = self.frame.width()
max_y = self.frame.height()
rect_path.moveTo(0, 0)
rect_path.lineTo(0, max_y)
rect_path.lineTo(max_x, max_y)
rect_path.lineTo(max_x, 0)
rect_path.closeSubpath()
painter.drawPath(rect_path)
# gradient = None
# if self._theme.background_direction == u'horizontal':
# w = int(self.frame.width()) / 2
# # vertical
# gradient = QtGui.QLinearGradient(w, 0, w, self.frame.height())
# elif self._theme.background_direction == u'vertical':
# h = int(self.frame.height()) / 2
# # Horizontal
# gradient = QtGui.QLinearGradient(0, h, self.frame.width(), h)
# else:
# w = int(self.frame.width()) / 2
# h = int(self.frame.height()) / 2
# # Circular
# gradient = QtGui.QRadialGradient(w, h, w)
# gradient.setColorAt(0,
# QtGui.QColor(self._theme.background_startColor))
# gradient.setColorAt(1,
# QtGui.QColor(self._theme.background_endColor))
# painter.setBrush(QtGui.QBrush(gradient))
# rect_path = QtGui.QPainterPath()
# max_x = self.frame.width()
# max_y = self.frame.height()
# rect_path.moveTo(0, 0)
# rect_path.lineTo(0, max_y)
# rect_path.lineTo(max_x, max_y)
# rect_path.lineTo(max_x, 0)
# rect_path.closeSubpath()
# painter.drawPath(rect_path)
# painter.end()
elif self._theme.background_type == u'image':
# image
painter = QtGui.QPainter()
painter.begin(self.bg_frame)
painter.fillRect(self.frame.rect(), QtCore.Qt.black)
if self.bg_image:
painter.drawImage(0, 0, self.bg_image)
painter.end()
painter.end()

View File

@ -93,6 +93,7 @@ class RenderManager(object):
"""
self.global_theme = global_theme
self.theme_level = theme_level
self.themedata = None
def set_service_theme(self, service_theme):
"""
@ -102,6 +103,7 @@ class RenderManager(object):
The service-level theme to be set.
"""
self.service_theme = service_theme
self.themedata = None
def set_override_theme(self, theme, overrideLevels=False):
"""
@ -111,6 +113,10 @@ class RenderManager(object):
``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
@ -137,6 +143,7 @@ class RenderManager(object):
if self.theme != self.renderer.theme_name or self.themedata 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.themedata = theme
else:

View File

@ -160,9 +160,9 @@ class ServiceItem(object):
self.themedata = self.render_manager.renderer._theme
for slide in self._raw_frames:
before = time.time()
formated = self.render_manager \
formatted = self.render_manager \
.format_slide(slide[u'raw_slide'], line_break)
for page in formated:
for page in formatted:
self._display_frames.append(
{u'title': clean_tags(page),
u'text': clean_tags(page.rstrip()),

View File

@ -0,0 +1,155 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Meinert Jordan, 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 re
import sys
try:
import enchant
enchant_available = True
except ImportError:
enchant_available = False
# based on code from
# http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/
from PyQt4 import QtCore, QtGui
from openlp.core.lib import html_expands, translate, context_menu_action
class SpellTextEdit(QtGui.QPlainTextEdit):
def __init__(self, *args):
QtGui.QPlainTextEdit.__init__(self, *args)
# Default dictionary based on the current locale.
if enchant_available:
self.dict = enchant.Dict()
self.highlighter = Highlighter(self.document())
self.highlighter.setDict(self.dict)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.RightButton:
# Rewrite the mouse event to a left button event so the cursor is
# moved to the location of the pointer.
event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress,
event.pos(), QtCore.Qt.LeftButton, QtCore.Qt.LeftButton,
QtCore.Qt.NoModifier)
QtGui.QPlainTextEdit.mousePressEvent(self, event)
def contextMenuEvent(self, event):
popup_menu = self.createStandardContextMenu()
# Select the word under the cursor.
cursor = self.textCursor()
# only select text if not already selected
if not cursor.hasSelection():
cursor.select(QtGui.QTextCursor.WordUnderCursor)
self.setTextCursor(cursor)
# Check if the selected word is misspelled and offer spelling
# suggestions if it is.
if enchant_available and self.textCursor().hasSelection():
text = unicode(self.textCursor().selectedText())
if not self.dict.check(text):
spell_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
'Spelling Suggestions'))
for word in self.dict.suggest(text):
action = SpellAction(word, spell_menu)
action.correct.connect(self.correctWord)
spell_menu.addAction(action)
# Only add the spelling suggests to the menu if there are
# suggestions.
if len(spell_menu.actions()) != 0:
popup_menu.insertSeparator(popup_menu.actions()[0])
popup_menu.insertMenu(popup_menu.actions()[0], spell_menu)
tag_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
'Formatting Tags'))
for html in html_expands:
action = SpellAction( html[u'desc'], tag_menu)
action.correct.connect(self.htmlTag)
tag_menu.addAction(action)
popup_menu.insertSeparator(popup_menu.actions()[0])
popup_menu.insertMenu(popup_menu.actions()[0], tag_menu)
popup_menu.exec_(event.globalPos())
def correctWord(self, word):
"""
Replaces the selected text with word.
"""
cursor = self.textCursor()
cursor.beginEditBlock()
cursor.removeSelectedText()
cursor.insertText(word)
cursor.endEditBlock()
def htmlTag(self, tag):
"""
Replaces the selected text with word.
"""
for html in html_expands:
if tag == html[u'desc']:
cursor = self.textCursor()
if self.textCursor().hasSelection():
text = cursor.selectedText()
cursor.beginEditBlock()
cursor.removeSelectedText()
cursor.insertText(html[u'start tag'])
cursor.insertText(text)
cursor.insertText(html[u'end tag'])
cursor.endEditBlock()
else:
cursor = self.textCursor()
cursor.insertText(html[u'start tag'])
cursor.insertText(html[u'end tag'])
class Highlighter(QtGui.QSyntaxHighlighter):
WORDS = u'(?iu)[\w\']+'
def __init__(self, *args):
QtGui.QSyntaxHighlighter.__init__(self, *args)
self.dict = None
def setDict(self, dict):
self.dict = dict
def highlightBlock(self, text):
if not self.dict:
return
text = unicode(text)
format = QtGui.QTextCharFormat()
format.setUnderlineColor(QtCore.Qt.red)
format.setUnderlineStyle(QtGui.QTextCharFormat.SpellCheckUnderline)
for word_object in re.finditer(self.WORDS, text):
if not self.dict.check(word_object.group()):
self.setFormat(word_object.start(),
word_object.end() - word_object.start(), format)
class SpellAction(QtGui.QAction):
"""
A special QAction that returns the text in a signal.
"""
correct = QtCore.pyqtSignal(unicode)
def __init__(self, *args):
QtGui.QAction.__init__(self, *args)
self.triggered.connect(lambda x: self.correct.emit(
unicode(self.text())))

View File

@ -27,141 +27,6 @@
The :mod:`ui` module provides the core user interface for OpenLP
"""
# http://john.nachtimwald.com/2009/08/22/qplaintextedit-with-in-line-spell-check/
import re
import sys
try:
import enchant
enchant_available = True
except ImportError:
enchant_available = False
from PyQt4 import QtCore, QtGui
from openlp.core.lib import html_expands, translate, context_menu_action
class SpellTextEdit(QtGui.QPlainTextEdit):
def __init__(self, *args):
QtGui.QPlainTextEdit.__init__(self, *args)
# Default dictionary based on the current locale.
if enchant_available:
self.dict = enchant.Dict()
self.highlighter = Highlighter(self.document())
self.highlighter.setDict(self.dict)
def mousePressEvent(self, event):
if event.button() == QtCore.Qt.RightButton:
# Rewrite the mouse event to a left button event so the cursor is
# moved to the location of the pointer.
event = QtGui.QMouseEvent(QtCore.QEvent.MouseButtonPress,
event.pos(), QtCore.Qt.LeftButton, QtCore.Qt.LeftButton,
QtCore.Qt.NoModifier)
QtGui.QPlainTextEdit.mousePressEvent(self, event)
def contextMenuEvent(self, event):
popup_menu = self.createStandardContextMenu()
# Select the word under the cursor.
cursor = self.textCursor()
cursor.select(QtGui.QTextCursor.WordUnderCursor)
self.setTextCursor(cursor)
# Check if the selected word is misspelled and offer spelling
# suggestions if it is.
if enchant_available and self.textCursor().hasSelection():
text = unicode(self.textCursor().selectedText())
if not self.dict.check(text):
spell_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
'Spelling Suggestions'))
for word in self.dict.suggest(text):
action = SpellAction(word, spell_menu)
action.correct.connect(self.correctWord)
spell_menu.addAction(action)
# Only add the spelling suggests to the menu if there are
# suggestions.
if len(spell_menu.actions()) != 0:
popup_menu.insertSeparator(popup_menu.actions()[0])
popup_menu.insertMenu(popup_menu.actions()[0], spell_menu)
tag_menu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
'Formatting Tags'))
for html in html_expands:
action = SpellAction( html[u'desc'], tag_menu)
action.correct.connect(self.htmlTag)
tag_menu.addAction(action)
popup_menu.insertSeparator(popup_menu.actions()[0])
popup_menu.insertMenu(popup_menu.actions()[0], tag_menu)
popup_menu.exec_(event.globalPos())
def correctWord(self, word):
"""
Replaces the selected text with word.
"""
cursor = self.textCursor()
cursor.beginEditBlock()
cursor.removeSelectedText()
cursor.insertText(word)
cursor.endEditBlock()
def htmlTag(self, tag):
"""
Replaces the selected text with word.
"""
for html in html_expands:
if tag == html[u'desc']:
cursor = self.textCursor()
if self.textCursor().hasSelection():
text = cursor.selectedText()
cursor.beginEditBlock()
cursor.removeSelectedText()
cursor.insertText(html[u'start tag'])
cursor.insertText(text)
cursor.insertText(html[u'end tag'])
cursor.endEditBlock()
else:
cursor = self.textCursor()
cursor.insertText(html[u'start tag'])
cursor.insertText(html[u'end tag'])
class Highlighter(QtGui.QSyntaxHighlighter):
WORDS = u'(?iu)[\w\']+'
def __init__(self, *args):
QtGui.QSyntaxHighlighter.__init__(self, *args)
self.dict = None
def setDict(self, dict):
self.dict = dict
def highlightBlock(self, text):
if not self.dict:
return
text = unicode(text)
format = QtGui.QTextCharFormat()
format.setUnderlineColor(QtCore.Qt.red)
format.setUnderlineStyle(QtGui.QTextCharFormat.SpellCheckUnderline)
for word_object in re.finditer(self.WORDS, text):
if not self.dict.check(word_object.group()):
self.setFormat(word_object.start(),
word_object.end() - word_object.start(), format)
class SpellAction(QtGui.QAction):
"""
A special QAction that returns the text in a signal.
"""
correct = QtCore.pyqtSignal(unicode)
def __init__(self, *args):
QtGui.QAction.__init__(self, *args)
self.triggered.connect(lambda x: self.correct.emit(
unicode(self.text())))
class HideMode(object):
"""
This is basically an enumeration class which specifies the mode of a Bible.

View File

@ -115,8 +115,11 @@ class MainDisplay(DisplayWidget):
self.screen = self.screens.current
self.setVisible(False)
self.setGeometry(self.screen[u'size'])
self.webView = QtWebKit.QWebView(self)
self.webView.setGeometry(0, 0, self.screen[u'size'].width(), \
self.scene = QtGui.QGraphicsScene()
self.setScene(self.scene)
self.webView = QtWebKit.QGraphicsWebView()
self.scene.addItem(self.webView)
self.webView.resize(self.screen[u'size'].width(), \
self.screen[u'size'].height())
self.page = self.webView.page()
self.frame = self.page.mainFrame()
@ -135,7 +138,7 @@ class MainDisplay(DisplayWidget):
painter_image = QtGui.QPainter()
painter_image.begin(self.black)
painter_image.fillRect(self.black.rect(), QtCore.Qt.black)
#Build the initial frame.
# Build the initial frame.
initialFrame = QtGui.QImage(
self.screens.current[u'size'].width(),
self.screens.current[u'size'].height(),
@ -152,12 +155,12 @@ class MainDisplay(DisplayWidget):
splash_image)
serviceItem = ServiceItem()
serviceItem.bg_frame = initialFrame
self.webView.setHtml(build_html(serviceItem, self.screen, \
self.parent.alertTab))
self.webView.setHtml(build_html(serviceItem, self.screen,
self.parent.alertTab, self.isLive))
self.initialFrame = True
self.show()
# To display or not to display?
if not self.screen[u'primary']:
self.show()
self.primary = False
else:
self.primary = True
@ -297,13 +300,14 @@ class MainDisplay(DisplayWidget):
Generates a preview of the image displayed.
"""
log.debug(u'preview for %s', self.isLive)
# Wait for the fade to finish before geting the preview.
# Important otherwise preview will have incorrect text if at all !
if self.serviceItem.themedata and \
self.serviceItem.themedata.display_slideTransition:
while self.frame.evaluateJavaScript(u'show_text_complete()') \
.toString() == u'false':
Receiver.send_message(u'openlp_process_events')
if self.isLive:
# Wait for the fade to finish before geting the preview.
# Important otherwise preview will have incorrect text if at all !
if self.serviceItem.themedata and \
self.serviceItem.themedata.display_slideTransition:
while self.frame.evaluateJavaScript(u'show_text_complete()') \
.toString() == u'false':
Receiver.send_message(u'openlp_process_events')
# Wait for the webview to update before geting the preview.
# Important otherwise first preview will miss the background !
while not self.loaded:
@ -318,9 +322,6 @@ class MainDisplay(DisplayWidget):
# Make display show up if in single screen mode
if self.isLive:
self.setVisible(True)
# save preview for debugging
if log.isEnabledFor(logging.DEBUG):
preview.save(u'temp.png', u'png')
return preview
def buildHtml(self, serviceItem):
@ -332,7 +333,8 @@ class MainDisplay(DisplayWidget):
self.loaded = False
self.initialFrame = False
self.serviceItem = serviceItem
html = build_html(self.serviceItem, self.screen, self.parent.alertTab)
html = build_html(self.serviceItem, self.screen, self.parent.alertTab,\
self.isLive)
self.webView.setHtml(html)
if serviceItem.foot_text and serviceItem.foot_text:
self.footer(serviceItem.foot_text)

View File

@ -574,7 +574,7 @@ class ServiceManager(QtGui.QWidget):
* An osd which is a pickle of the service items
* All image, presentation and video files needed to run the service.
"""
log.debug(u'onSaveService')
log.debug(u'onSaveService %s' % quick)
if not quick or self.isNew:
filename = QtGui.QFileDialog.getSaveFileName(self,
translate('OpenLP.ServiceManager', 'Save Service'),
@ -755,6 +755,7 @@ class ServiceManager(QtGui.QWidget):
"""
Set the theme for the current service
"""
log.debug(u'onThemeComboBoxSelected')
self.service_theme = unicode(self.themeComboBox.currentText())
self.parent.RenderManager.set_service_theme(self.service_theme)
QtCore.QSettings().setValue(
@ -767,6 +768,7 @@ class ServiceManager(QtGui.QWidget):
The theme may have changed in the settings dialog so make
sure the theme combo box is in the correct state.
"""
log.debug(u'themeChange')
if self.parent.RenderManager.theme_level == ThemeLevel.Global:
self.toolbar.actions[u'ThemeLabel'].setVisible(False)
self.toolbar.actions[u'ThemeWidget'].setVisible(False)
@ -779,6 +781,7 @@ class ServiceManager(QtGui.QWidget):
Rebuild the service list as things have changed and a
repaint is the easiest way to do this.
"""
log.debug(u'regenerateServiceItems')
# force reset of renderer as theme data has changed
self.parent.RenderManager.themedata = None
if self.serviceItems:
@ -800,6 +803,7 @@ class ServiceManager(QtGui.QWidget):
``item``
Service Item to be added
"""
log.debug(u'addServiceItem')
sitem = self.findServiceItem()[0]
item.render()
if replace:

View File

@ -295,7 +295,7 @@ class ThemeManager(QtGui.QWidget):
path = unicode(path)
if path:
SettingsManager.set_last_dir(self.settingsSection, path, 1)
themePath = os.path.join(path, theme + u'.thz')
themePath = os.path.join(path, theme + u'.otz')
zip = None
try:
zip = zipfile.ZipFile(themePath, u'w')

View File

@ -51,9 +51,9 @@ class CSVBible(BibleDB):
if u'booksfile' not in kwargs:
raise KeyError(u'You have to supply a file to import books from.')
self.booksfile = kwargs[u'booksfile']
if u'versesfile' not in kwargs:
if u'versefile' not in kwargs:
raise KeyError(u'You have to supply a file to import verses from.')
self.versesfile = kwargs[u'versesfile']
self.versesfile = kwargs[u'versefile']
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'bibles_stop_import'), self.stop_import)

View File

@ -47,7 +47,6 @@ class BibleListView(BaseListWithDnD):
self.parent().onListViewResize(event.size().width(),
event.size().width())
class BibleMediaItem(MediaManagerItem):
"""
This is the custom media manager item for Bibles.
@ -466,19 +465,27 @@ class BibleMediaItem(MediaManagerItem):
def generateSlideData(self, service_item, item=None):
"""
Generates and formats the slides for the service item.
Generates and formats the slides for the service item as well as the
service item's title.
"""
log.debug(u'generating slide data')
items = self.listView.selectedIndexes()
if len(items) == 0:
return False
has_dual_bible = False
bible_text = u''
old_chapter = u''
raw_footer = []
raw_slides = []
service_item.add_capability(ItemCapabilities.AllowsPreview)
service_item.add_capability(ItemCapabilities.AllowsLoop)
service_item.add_capability(ItemCapabilities.AllowsAdditions)
for item in items:
bitem = self.listView.item(item.row())
reference = bitem.data(QtCore.Qt.UserRole)
if isinstance(reference, QtCore.QVariant):
reference = reference.toPyObject()
dual_bible = self._decodeQtObject(reference, 'dual_bible')
if dual_bible:
has_dual_bible = True
break
# Let's loop through the main lot, and assemble our verses.
for item in items:
bitem = self.listView.item(item.row())
@ -491,7 +498,7 @@ class BibleMediaItem(MediaManagerItem):
bible = self._decodeQtObject(reference, 'bible')
version = self._decodeQtObject(reference, 'version')
copyright = self._decodeQtObject(reference, 'copyright')
#permission = self._decodeQtObject(reference, 'permission')
permission = self._decodeQtObject(reference, 'permission')
text = self._decodeQtObject(reference, 'text')
dual_bible = self._decodeQtObject(reference, 'dual_bible')
if dual_bible:
@ -499,70 +506,57 @@ class BibleMediaItem(MediaManagerItem):
'dual_version')
dual_copyright = self._decodeQtObject(reference,
'dual_copyright')
#dual_permission = self._decodeQtObject(reference,
# 'dual_permission')
dual_permission = self._decodeQtObject(reference,
'dual_permission')
dual_text = self._decodeQtObject(reference, 'dual_text')
if self.parent.settings_tab.display_style == 1:
verse_text = self.formatVerse(old_chapter, chapter, verse,
u'{su}(', u'){/su}')
elif self.parent.settings_tab.display_style == 2:
verse_text = self.formatVerse(old_chapter, chapter, verse,
u'{su}{', u'}{/su}')
elif self.parent.settings_tab.display_style == 3:
verse_text = self.formatVerse(old_chapter, chapter, verse,
u'{su}[', u']{/su}')
else:
verse_text = self.formatVerse(old_chapter, chapter, verse,
u'{su}', u'{/su}')
old_chapter = chapter
footer = u'%s (%s %s)' % (book, version, copyright)
# If not found add to footer
verse_text = self.formatVerse(old_chapter, chapter, verse)
footer = u'%s (%s %s %s)' % (book, version, copyright, permission)
if footer not in raw_footer:
raw_footer.append(footer)
if dual_bible:
footer = u'%s (%s %s)' % (book, dual_version,
dual_copyright)
# If not found add second version and copyright to footer.
if footer not in raw_footer:
raw_footer.append(footer)
bible_text = u'%s %s \n\n %s %s' % (verse_text, text,
verse_text, dual_text)
raw_slides.append(bible_text)
bible_text = u''
else:
# If we are 'Verse Per Line' then force a new line.
if self.parent.settings_tab.layout_style == 1:
text = text + u'\n'
else:
# split the line but do not replace line breaks in renderer
service_item.add_capability(ItemCapabilities.NoLineBreaks)
text = text + u'\n'
bible_text = u'%s %s %s' % (bible_text, verse_text, text)
# If we are 'Verse Per Slide' then create a new slide.
if self.parent.settings_tab.layout_style == 0:
raw_slides.append(bible_text)
bible_text = u''
# If we are not 'Verse Per Slide' we have to make sure, that we
# add more verses.
else:
if item.row() < len(items) - 1:
bitem = items[item.row() + 1]
reference = bitem.data(QtCore.Qt.UserRole)
if isinstance(reference, QtCore.QVariant):
reference = reference.toPyObject()
bible_new = self._decodeQtObject(reference, 'bible')
dual_bible_new = self._decodeQtObject(reference,
'dual_bible')
if dual_bible_new:
raw_slides.append(bible_text)
bible_text = u''
elif bible != bible_new:
raw_slides.append(bible_text)
bible_text = u''
else:
if has_dual_bible:
if dual_bible:
footer = u'%s (%s %s %s)' % (book, dual_version,
dual_copyright, dual_permission)
if footer not in raw_footer:
raw_footer.append(footer)
# If there is an old bible_text we have to add it.
if bible_text:
raw_slides.append(bible_text)
bible_text = u''
# service item title
bible_text = u'%s %s\n\n%s %s' % (verse_text, text,
verse_text, dual_text)
raw_slides.append(bible_text)
bible_text = u''
elif self.parent.settings_tab.layout_style == 0:
bible_text = u'%s %s' % (verse_text, text)
raw_slides.append(bible_text)
bible_text = u''
else:
bible_text = u'%s %s %s\n' % (bible_text, verse_text, text)
# If we are 'Verse Per Slide' then create a new slide.
elif self.parent.settings_tab.layout_style == 0:
bible_text = u'%s %s' % (verse_text, text)
raw_slides.append(bible_text)
bible_text = u''
# If we are 'Verse Per Line' then force a new line.
elif self.parent.settings_tab.layout_style == 1:
bible_text = u'%s %s %s\n' % (bible_text, verse_text, text)
# We have to be 'Continuous'.
else:
bible_text = u'%s %s %s\n' % (bible_text, verse_text, text)
old_chapter = chapter
# If there are no more items we check whether we have to add bible_text.
if bible_text:
raw_slides.append(bible_text)
bible_text = u''
# Service Item: Capabilities
if self.parent.settings_tab.layout_style == 2 and not has_dual_bible:
# split the line but do not replace line breaks in renderer
service_item.add_capability(ItemCapabilities.NoLineBreaks)
service_item.add_capability(ItemCapabilities.AllowsPreview)
service_item.add_capability(ItemCapabilities.AllowsLoop)
service_item.add_capability(ItemCapabilities.AllowsAdditions)
# Service Item: Title
if not service_item.title:
if dual_bible:
service_item.title = u'%s (%s, %s) %s' % (book, version,
@ -573,7 +567,7 @@ class BibleMediaItem(MediaManagerItem):
translate('BiblesPlugin.MediaItem', 'etc')) == -1:
service_item.title = u'%s, %s' % (service_item.title,
translate('BiblesPlugin.MediaItem', 'etc'))
# item theme
# Service Item: Theme
if len(self.parent.settings_tab.bible_theme) == 0:
service_item.theme = None
else:
@ -587,14 +581,20 @@ class BibleMediaItem(MediaManagerItem):
service_item.raw_footer = raw_footer
return True
def formatVerse(self, old_chapter, chapter, verse, opening, closing):
verse_text = opening
if old_chapter != chapter:
verse_text += chapter + u':'
elif not self.parent.settings_tab.show_new_chapters:
verse_text += chapter + u':'
verse_text += verse
verse_text += closing
def formatVerse(self, old_chapter, chapter, verse):
if not self.parent.settings_tab.show_new_chapters or \
old_chapter != chapter:
verse_text = chapter + u':' + verse
else:
verse_text = verse
if self.parent.settings_tab.display_style == 1:
verse_text = u'{su}(' + verse_text + u'){/su}'
elif self.parent.settings_tab.display_style == 2:
verse_text = u'{su}{' + verse_text + u'}{/su}'
elif self.parent.settings_tab.display_style == 3:
verse_text = u'{su}[' + verse_text + u']{/su}'
else:
verse_text = u'{su}' + verse_text + u'{/su}'
return verse_text
def reloadBibles(self):
@ -639,14 +639,14 @@ class BibleMediaItem(MediaManagerItem):
for i in range(int(range_from), int(range_to) + 1):
combo.addItem(unicode(i))
def displayResults(self, bible, dual_bible=None):
def displayResults(self, bible, dual_bible=u''):
"""
Displays the search results in the media manager. All data needed for
further action is saved for/in each row.
"""
version = self.parent.manager.get_meta_data(bible, u'Version')
copyright = self.parent.manager.get_meta_data(bible, u'Copyright')
#permission = self.parent.manager.get_meta_data(bible, u'Permissions')
permission = self.parent.manager.get_meta_data(bible, u'Permissions')
if dual_bible:
dual_version = self.parent.manager.get_meta_data(dual_bible,
u'Version')
@ -654,43 +654,41 @@ class BibleMediaItem(MediaManagerItem):
u'Copyright')
dual_permission = self.parent.manager.get_meta_data(dual_bible,
u'Permissions')
if dual_permission:
dual_permission = dual_permission.value
else:
if not dual_permission:
dual_permission = u''
# We count the number of rows which are maybe already present.
start_count = self.listView.count()
for count, verse in enumerate(self.search_results):
if dual_bible:
vdict = {
'book':QtCore.QVariant(verse.book.name),
'chapter':QtCore.QVariant(verse.chapter),
'verse':QtCore.QVariant(verse.verse),
'bible':QtCore.QVariant(bible),
'version':QtCore.QVariant(version.value),
'copyright':QtCore.QVariant(copyright.value),
#'permission':QtCore.QVariant(permission.value),
'text':QtCore.QVariant(verse.text),
'dual_bible':QtCore.QVariant(dual_bible),
'dual_version':QtCore.QVariant(dual_version.value),
'dual_copyright':QtCore.QVariant(dual_copyright.value),
#'dual_permission':QtCore.QVariant(dual_permission),
'dual_text':QtCore.QVariant(
'book': QtCore.QVariant(verse.book.name),
'chapter': QtCore.QVariant(verse.chapter),
'verse': QtCore.QVariant(verse.verse),
'bible': QtCore.QVariant(bible),
'version': QtCore.QVariant(version.value),
'copyright': QtCore.QVariant(copyright.value),
'permission': QtCore.QVariant(permission.value),
'text': QtCore.QVariant(verse.text),
'dual_bible': QtCore.QVariant(dual_bible),
'dual_version': QtCore.QVariant(dual_version.value),
'dual_copyright': QtCore.QVariant(dual_copyright.value),
'dual_permission': QtCore.QVariant(dual_permission.value),
'dual_text': QtCore.QVariant(
self.dual_search_results[count].text)
}
bible_text = u' %s %d:%d (%s, %s)' % (verse.book.name,
verse.chapter, verse.verse, version.value, dual_version.value)
else:
vdict = {
'book':QtCore.QVariant(verse.book.name),
'chapter':QtCore.QVariant(verse.chapter),
'verse':QtCore.QVariant(verse.verse),
'bible':QtCore.QVariant(bible),
'version':QtCore.QVariant(version.value),
'copyright':QtCore.QVariant(copyright.value),
#'permission':QtCore.QVariant(permission.value),
'text':QtCore.QVariant(verse.text),
'dual_bible':QtCore.QVariant(dual_bible)
'book': QtCore.QVariant(verse.book.name),
'chapter': QtCore.QVariant(verse.chapter),
'verse': QtCore.QVariant(verse.verse),
'bible': QtCore.QVariant(bible),
'version': QtCore.QVariant(version.value),
'copyright': QtCore.QVariant(copyright.value),
'permission': QtCore.QVariant(permission.value),
'text': QtCore.QVariant(verse.text),
'dual_bible': QtCore.QVariant(dual_bible)
}
bible_text = u' %s %d:%d (%s)' % (verse.book.name,
verse.chapter, verse.verse, version.value)

View File

@ -26,8 +26,7 @@
from PyQt4 import QtCore, QtGui
from openlp.core.lib import build_icon, translate
from openlp.core.ui import SpellTextEdit
from openlp.core.lib import build_icon, translate, SpellTextEdit
class Ui_CustomEditDialog(object):
def setupUi(self, customEditDialog):

View File

@ -26,8 +26,7 @@
from PyQt4 import QtCore, QtGui
from openlp.core.lib import build_icon, translate
from openlp.core.ui import SpellTextEdit
from openlp.core.lib import build_icon, translate, SpellTextEdit
from openlp.plugins.songs.lib import VerseType
class Ui_EditVerseDialog(object):

View File

@ -83,6 +83,12 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
QtCore.QObject.connect(self.wordsOfWorshipRemoveButton,
QtCore.SIGNAL(u'clicked()'),
self.onWordsOfWorshipRemoveButtonClicked)
QtCore.QObject.connect(self.ccliAddButton,
QtCore.SIGNAL(u'clicked()'),
self.onCCLIAddButtonClicked)
QtCore.QObject.connect(self.ccliRemoveButton,
QtCore.SIGNAL(u'clicked()'),
self.onCCLIRemoveButtonClicked)
QtCore.QObject.connect(self.songsOfFellowshipAddButton,
QtCore.SIGNAL(u'clicked()'),
self.onSongsOfFellowshipAddButtonClicked)
@ -160,7 +166,7 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
self.openSongAddButton.setFocus()
return False
elif source_format == SongFormat.WordsOfWorship:
if self.wordsOfWorshipListWidget.count() == 0:
if self.wordsOfWorshipFileListWidget.count() == 0:
QtGui.QMessageBox.critical(self,
translate('SongsPlugin.ImportWizardForm',
'No Words of Worship Files Selected'),
@ -277,6 +283,16 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
def onWordsOfWorshipRemoveButtonClicked(self):
self.removeSelectedItems(self.wordsOfWorshipFileListWidget)
def onCCLIAddButtonClicked(self):
self.getFiles(
translate('SongsPlugin.ImportWizardForm',
'Select CCLI Files'),
self.ccliFileListWidget
)
def onCCLIRemoveButtonClicked(self):
self.removeSelectedItems(self.ccliFileListWidget)
def onSongsOfFellowshipAddButtonClicked(self):
self.getFiles(
translate('SongsPlugin.ImportWizardForm',
@ -315,6 +331,7 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
pass
def setDefaults(self):
self.restart()
self.formatComboBox.setCurrentIndex(0)
self.openLP2FilenameEdit.setText(u'')
self.openLP1FilenameEdit.setText(u'')

View File

@ -0,0 +1,310 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
# Carsten Tinggaard, Frode Woldsund, Derek Scotney #
# --------------------------------------------------------------------------- #
# 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
import os
import chardet
import codecs
from songimport import SongImport
log = logging.getLogger(__name__)
class CCLIFileImportError(Exception):
pass
class CCLIFileImport(SongImport):
"""
The :class:`CCLIFileImport` class provides OpenLP with the
ability to import CCLI SongSelect song files in both .txt and
.usr formats. See http://www.ccli.com
"""
def __init__(self, manager, **kwargs):
"""
Initialise the import.
``manager``
The song manager for the running OpenLP installation.
``filenames``
The files to be imported.
"""
SongImport.__init__(self, manager)
if u'filenames' in kwargs:
self.filenames = kwargs[u'filenames']
log.debug(self.filenames)
else:
raise KeyError(u'Keyword argument "filenames" not supplied.')
def do_import(self):
"""
Import either a .usr or a .txt SongSelect file
"""
log.debug(u'Starting CCLI File Import')
song_total = len(self.filenames)
self.import_wizard.importProgressBar.setMaximum(song_total)
song_count = 1
for filename in self.filenames:
self.import_wizard.incrementProgressBar(
u'Importing song %s of %s' % (song_count, song_total))
filename = unicode(filename)
log.debug(u'Importing CCLI File: %s', filename)
lines = []
if os.path.isfile(filename):
detect_file = open(filename, u'r')
details = chardet.detect(detect_file.read(2048))
detect_file.close()
infile = codecs.open(filename, u'r', details['encoding'])
lines = infile.readlines()
ext = os.path.splitext(filename)[1]
if ext.lower() == ".usr":
log.info(u'SongSelect .usr format file found %s: ' , filename)
self.do_import_usr_file(lines)
elif ext.lower() == ".txt":
log.info(u'SongSelect .txt format file found %s: ', filename)
self.do_import_txt_file(lines)
else:
log.info(u'Extension %s is not valid', filename)
pass
song_count += 1
if self.stop_import_flag:
return False
return True
def do_import_usr_file(self, textList):
"""
The :method:`do_import_usr_file` method provides OpenLP
with the ability to import CCLI SongSelect songs in
*USR* file format
``textList``
An array of strings containing the usr file content.
**SongSelect .usr file format**
``[File]``
USR file format first line
``Type=``
Indicates the file type
e.g. *Type=SongSelect Import File*
``Version=3.0``
File format version
``[S A2672885]``
Contains the CCLI Song number e.g. *2672885*
``Title=``
Contains the song title (e.g. *Title=Above All*)
``Author=``
Contains a | delimited list of the song authors
e.g. *Author=LeBlanc, Lenny | Baloche, Paul*
``Copyright=``
Contains a | delimited list of the song copyrights
e.g. Copyright=1999 Integrity's Hosanna! Music |
LenSongs Publishing (Verwaltet von Gerth Medien
Musikverlag)
``Admin=``
Contains the song administrator
e.g. *Admin=Gerth Medien Musikverlag*
``Themes=``
Contains a /t delimited list of the song themes
e.g. *Themes=Cross/tKingship/tMajesty/tRedeemer*
``Keys=``
Contains the keys in which the music is played??
e.g. *Keys=A*
``Fields=``
Contains a list of the songs fields in order /t delimited
e.g. *Fields=Vers 1/tVers 2/tChorus 1/tAndere 1*
``Words=``
Contains the songs various lyrics in order as shown by the
*Fields* description
e.g. *Words=Above all powers....* [/n = CR, /n/t = CRLF]
"""
log.debug(u'USR file text: %s', textList)
lyrics = []
self.set_defaults()
for line in textList:
if line.startswith(u'Title='):
song_name = line[6:].strip()
elif line.startswith(u'Author='):
song_author = line[7:].strip()
elif line.startswith(u'Copyright='):
song_copyright = line[10:].strip()
elif line.startswith(u'[S A'):
song_ccli = line[4:-3].strip()
elif line.startswith(u'Fields='):
#Fields contain single line indicating verse, chorus, etc,
#/t delimited, same as with words field. store seperately
#and process at end.
song_fields = line[7:].strip()
elif line.startswith(u'Words='):
song_words = line[6:].strip()
#Unhandled usr keywords:Type,Version,Admin,Themes,Keys
#Process Fields and words sections
field_list = song_fields.split(u'/t')
words_list = song_words.split(u'/t')
for counter in range(0, len(field_list)):
if field_list[counter].startswith(u'Ver'):
verse_type = u'V'
elif field_list[counter].startswith(u'Ch'):
verse_type = u'C'
elif field_list[counter].startswith(u'Br'):
verse_type = u'B'
else: #Other
verse_type = u'O'
verse_text = unicode(words_list[counter])
verse_text = verse_text.replace("/n", "\n")
if len(verse_text) > 0:
self.add_verse(verse_text, verse_type);
#Handle multiple authors
author_list = song_author.split(u'/')
if len(author_list) < 2:
author_list = song_author.split(u'|')
for author in author_list:
seperated = author.split(u',')
self.add_author(seperated[1].strip() + " " + seperated[0].strip())
self.title = song_name
self.copyright = song_copyright
self.ccli_number = song_ccli
self.finish()
def do_import_txt_file(self, textList):
"""
The :method:`do_import_txt_file` method provides OpenLP
with the ability to import CCLI SongSelect songs in
*TXT* file format
``textList``
An array of strings containing the txt file content.
**SongSelect .txt file format**
``Song Title``
Contains the song title
<Empty line>
``Title of following verse/chorus and number``
e.g. Verse 1, Chorus 1
``Verse/Chorus lyrics``
<Empty line>
<Empty line>
``Title of next verse/chorus (repeats)``
``Verse/Chorus lyrics``
<Empty line>
<Empty line>
``Song CCLI Number``
e.g. CCLI Number (e.g.CCLI-Liednummer: 2672885)
``Song Copyright``
e.g. © 1999 Integrity's Hosanna! Music | LenSongs Publishing
``Song Authors``
e.g. Lenny LeBlanc | Paul Baloche
``Licencing info``
e.g. For use solely with the SongSelect Terms of Use.
All rights Reserved. www.ccli.com
``CCLI Licence number of user``
e.g. CCL-Liedlizenznummer: 14 / CCLI License No. 14
"""
log.debug(u'TXT file text: %s', textList)
self.set_defaults()
line_number = 0
verse_text = u''
song_comments = u''
song_copyright = u'';
verse_start = False
for line in textList:
clean_line = line.strip()
if not clean_line:
if line_number==0:
continue
elif verse_start:
if verse_text:
self.add_verse(verse_text, verse_type)
verse_text = ''
verse_start = False
else:
#line_number=0, song title
if line_number==0:
song_name = clean_line
line_number += 1
#line_number=1, verses
elif line_number==1:
#line_number=1, ccli number, first line after verses
if clean_line.startswith(u'CCLI'):
line_number += 1
ccli_parts = clean_line.split(' ')
song_ccli = ccli_parts[len(ccli_parts)-1]
elif not verse_start:
# We have the verse descriptor
verse_desc_parts = clean_line.split(' ')
if len(verse_desc_parts) == 2:
if verse_desc_parts[0].startswith(u'Ver'):
verse_type = u'V'
elif verse_desc_parts[0].startswith(u'Ch'):
verse_type = u'C'
elif verse_desc_parts[0].startswith(u'Br'):
verse_type = u'B'
else:
verse_type = u'O'
verse_number = verse_desc_parts[1]
else:
verse_type = u'O'
verse_number = 1
verse_start = True
else:
# We have verse content or the start of the
# last part. Add l so as to keep the CRLF
verse_text = verse_text + line
else:
#line_number=2, copyright
if line_number==2:
line_number += 1
song_copyright = clean_line
#n=3, authors
elif line_number==3:
line_number += 1
song_author = clean_line
#line_number=4, comments lines before last line
elif (line_number==4) and (not clean_line.startswith(u'CCL')):
song_comments = song_comments + clean_line
# split on known separators
author_list = song_author.split(u'/')
if len(author_list) < 2:
author_list = song_author.split(u'|')
#Clean spaces before and after author names
for author_name in author_list:
self.add_author(author_name.strip())
self.title = song_name
self.copyright = song_copyright
self.ccli_number = song_ccli
self.comments = song_comments
self.finish()

View File

@ -29,6 +29,8 @@ from olpimport import OpenLPSongImport
try:
from sofimport import SofImport
from oooimport import OooImport
from cclifileimport import CCLIFileImport
from wowimport import WowImport
except ImportError:
pass
@ -63,8 +65,12 @@ class SongFormat(object):
return OpenSongImport
elif format == SongFormat.SongsOfFellowship:
return SofImport
elif format == SongFormat.WordsOfWorship:
return WowImport
elif format == SongFormat.Generic:
return OooImport
elif format == SongFormat.CCLI:
return CCLIFileImport
# else:
return None

View File

@ -76,7 +76,7 @@ class SongImport(QtCore.QObject):
'SongsPlugin.SongImport', 'copyright'))
self.copyright_symbol = unicode(translate(
'SongsPlugin.SongImport', '\xa9'))
def stop_import(self):
"""
Sets the flag for importers to stop their import
@ -129,13 +129,13 @@ class SongImport(QtCore.QObject):
def process_verse_text(self, text):
lines = text.split(u'\n')
if text.lower().find(self.copyright_string) >= 0 \
or text.lower().find(self.copyright_symbol) >= 0:
if text.lower().find(COPYRIGHT_STRING) >= 0 \
or text.lower().find(COPYRIGHT_SYMBOL) >= 0:
copyright_found = False
for line in lines:
if (copyright_found or
line.lower().find(self.copyright_string) >= 0 or
line.lower().find(self.copyright_symbol) >= 0):
line.lower().find(COPYRIGHT_STRING) >= 0 or
line.lower().find(COPYRIGHT_SYMBOL) >= 0):
copyright_found = True
self.add_copyright(line)
else:
@ -300,6 +300,7 @@ class SongImport(QtCore.QObject):
topic = Topic.populate(name=topictext)
song.topics.append(topic)
self.manager.save_object(song)
self.setDefaults()
def print_song(self):
"""

View File

@ -0,0 +1,173 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2010 Raoul Snyman #
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
# Gorven, Scott Guerrieri, Meinert Jordan, 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 #
###############################################################################
"""
The :mod:`wowimport` module provides the functionality for importing Words of
Worship songs into the OpenLP database.
"""
import os
import logging
from openlp.plugins.songs.lib.songimport import SongImport
BLOCK_TYPES = (u'V', u'C', u'B')
log = logging.getLogger(__name__)
class WowImport(SongImport):
"""
The :class:`WowImport` class provides the ability to import song files from
Words of Worship.
Words Of Worship Song File Format
`````````````````````````````````
The Words Of Worship song file format is as follows:
* The song title is the file name minus the extension.
* The song has a header, a number of blocks, followed by footer containing
the author and the copyright.
* A block can be a verse, chorus or bridge.
File Header:
Bytes are counted from one, i.e. the first byte is byte 1. These bytes,
up to the 56 byte, can change but no real meaning has been found. The
56th byte specifies how many blocks there are. The first block starts
with byte 83 after the "CSongDoc::CBlock" declaration.
Blocks:
Each block has a starting header, some lines of text, and an ending
footer. Each block starts with 4 bytes, the first byte specifies how
many lines are in that block, the next three bytes are null bytes.
Each block ends with 4 bytes, the first of which defines what type of
block it is, and the rest which are null bytes:
* ``NUL`` (\x00) - Verse
* ``SOH`` (\x01) - Chorus
* ``STX`` (\x02) - Bridge
Blocks are seperated by two bytes. The first byte is ``SOH`` (\x01),
and the second byte is ```` (\x80).
Lines:
Each line starts with a byte which specifies how long that line is,
the line text, and ends with a null byte.
Footer:
The footer follows on after the last block, the first byte specifies
the length of the author text, followed by the author text, if
this byte is null, then there is no author text. The byte after the
author text specifies the length of the copyright text, followed
by the copyright text.
The file is ended with four null bytes.
Valid extensions for a Words of Worship song file are:
* .wsg
* .wow-song
"""
def __init__(self, master_manager, **kwargs):
"""
Initialise the import.
``master_manager``
The song manager for the running OpenLP installation.
"""
SongImport.__init__(self, master_manager)
self.master_manager = master_manager
if kwargs.has_key(u'filename'):
self.import_source = kwargs[u'filename']
if kwargs.has_key(u'filenames'):
self.import_source = kwargs[u'filenames']
log.debug(self.import_source)
def do_import(self):
"""
Recieve a single file, or a list of files to import.
"""
if isinstance(self.import_source, list):
self.import_wizard.importProgressBar.setMaximum(
len(self.import_source))
for file in self.import_source:
# TODO: check that it is a valid words of worship file (could
# check header for WoW File Song Word)
self.author = u''
self.copyright= u''
# Get the song title
self.file_name = os.path.split(file)[1]
self.import_wizard.incrementProgressBar(
"Importing %s" % (self.file_name), 0)
self.title = self.file_name.rpartition(u'.')[0]
self.songData = open(file, 'rb')
# Seek to byte which stores number of blocks in the song
self.songData.seek(56)
self.no_of_blocks = ord(self.songData.read(1))
# Seek to the beging of the first block
self.songData.seek(82)
for block in range(self.no_of_blocks):
self.lines_to_read = ord(self.songData.read(1))
# Skip 3 nulls to the beginnig of the 1st line
self.songData.seek(3, os.SEEK_CUR)
self.block_text = u''
while self.lines_to_read:
self.length_of_line = ord(self.songData.read(1))
self.line_text = unicode(
self.songData.read(self.length_of_line), u'cp1252')
self.songData.seek(1, os.SEEK_CUR)
if self.block_text != u'':
self.block_text += u'\n'
self.block_text += self.line_text
self.lines_to_read -= 1
self.block_type = BLOCK_TYPES[ord(self.songData.read(1))]
# Skip 3 nulls at the end of the block
self.songData.seek(3, os.SEEK_CUR)
# Blocks are seperated by 2 bytes, skip them, but not if
# this is the last block!
if (block + 1) < self.no_of_blocks:
self.songData.seek(2, os.SEEK_CUR)
self.add_verse(self.block_text, self.block_type)
# Now to extact the author
self.author_length = ord(self.songData.read(1))
if self.author_length != 0:
self.author = unicode(
self.songData.read(self.author_length), u'cp1252')
# Finally the copyright
self.copyright_length = ord(self.songData.read(1))
if self.copyright_length != 0:
self.copyright = unicode(
self.songData.read(self.copyright_length), u'cp1252')
self.parse_author(self.author)
self.add_copyright(self.copyright)
self.songData.close()
self.finish()
self.import_wizard.incrementProgressBar(
"Importing %s" % (self.file_name))
return True