Head r1006

This commit is contained in:
Jon Tibble 2010-09-03 21:08:38 +01:00
commit 9fa6039557
12 changed files with 648 additions and 299 deletions

View File

@ -39,40 +39,40 @@ log = logging.getLogger(__name__)
html_expands = [] html_expands = []
html_expands.append({u'desc':u'Red', u'start tag':u'{r}', \ html_expands.append({u'desc':u'Red', u'start tag':u'{r}', \
u'start html':u'<font color=red>', \ u'start html':u'<span style="-webkit-text-fill-color:red">', \
u'end tag':u'{/r}', u'end html':u'</font>', \ u'end tag':u'{/r}', u'end html':u'</span>', \
u'protected':False}) u'protected':False})
html_expands.append({u'desc':u'Black', u'start tag':u'{b}', \ html_expands.append({u'desc':u'Black', u'start tag':u'{b}', \
u'start html':u'<font color=black>', \ u'start html':u'<span style="-webkit-text-fill-color:black">', \
u'end tag':u'{/b}', u'end html':u'</font>', \ u'end tag':u'{/b}', u'end html':u'</span>', \
u'protected':False}) u'protected':False})
html_expands.append({u'desc':u'Blue', u'start tag':u'{bl}', \ html_expands.append({u'desc':u'Blue', u'start tag':u'{bl}', \
u'start html':u'<font color=blue>', \ u'start html':u'<span style="-webkit-text-fill-color:blue">', \
u'end tag':u'{/bl}', u'end html':u'</font>', \ u'end tag':u'{/bl}', u'end html':u'</span>', \
u'protected':False}) u'protected':False})
html_expands.append({u'desc':u'Yellow', u'start tag':u'{y}', \ html_expands.append({u'desc':u'Yellow', u'start tag':u'{y}', \
u'start html':u'<font color=yellow>', \ u'start html':u'<span style="-webkit-text-fill-color:yellow">', \
u'end tag':u'{/y}', u'end html':u'</font>', \ u'end tag':u'{/y}', u'end html':u'</span>', \
u'protected':False}) u'protected':False})
html_expands.append({u'desc':u'Green', u'start tag':u'{g}', \ html_expands.append({u'desc':u'Green', u'start tag':u'{g}', \
u'start html':u'<font color=green>', \ u'start html':u'<span style="-webkit-text-fill-color:green">', \
u'end tag':u'{/g}', u'end html':u'</font>', \ u'end tag':u'{/g}', u'end html':u'</span>', \
u'protected':False}) u'protected':False})
html_expands.append({u'desc':u'Pink', u'start tag':u'{pk}', \ html_expands.append({u'desc':u'Pink', u'start tag':u'{pk}', \
u'start html':u'<font color=#CC33CC>', \ u'start html':u'<span style="-webkit-text-fill-color:#CC33CC">', \
u'end tag':u'{/pk}', u'end html':u'</font>', \ u'end tag':u'{/pk}', u'end html':u'</span>', \
u'protected':False}) u'protected':False})
html_expands.append({u'desc':u'Orange', u'start tag':u'{o}', \ html_expands.append({u'desc':u'Orange', u'start tag':u'{o}', \
u'start html':u'<font color=#CC0033>', \ u'start html':u'<span style="-webkit-text-fill-color:#CC0033">', \
u'end tag':u'{/o}', u'end html':u'</font>', \ u'end tag':u'{/o}', u'end html':u'</span>', \
u'protected':False}) u'protected':False})
html_expands.append({u'desc':u'Purple', u'start tag':u'{pp}', \ html_expands.append({u'desc':u'Purple', u'start tag':u'{pp}', \
u'start html':u'<font color=#9900FF>', \ u'start html':u'<span style="-webkit-text-fill-color:#9900FF">', \
u'end tag':u'{/pp}', u'end html':u'</font>', \ u'end tag':u'{/pp}', u'end html':u'</span>', \
u'protected':False}) u'protected':False})
html_expands.append({u'desc':u'White', u'start tag':u'{w}', \ html_expands.append({u'desc':u'White', u'start tag':u'{w}', \
u'start html':u'<font color=white>', \ u'start html':u'<span style="-webkit-text-fill-color:white">', \
u'end tag':u'{/w}', u'end html':u'</font>', \ u'end tag':u'{/w}', u'end html':u'</span>', \
u'protected':False}) u'protected':False})
html_expands.append({u'desc':u'Superscript', u'start tag':u'{su}', \ html_expands.append({u'desc':u'Superscript', u'start tag':u'{su}', \
u'start html':u'<sup>', \ u'start html':u'<sup>', \

View File

@ -24,6 +24,8 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
from PyQt4 import QtWebKit
from openlp.core.lib import image_to_byte from openlp.core.lib import image_to_byte
HTMLSRC = u""" HTMLSRC = u"""
@ -39,7 +41,7 @@ HTMLSRC = u"""
body { body {
background-color: black; background-color: black;
} }
.dim { .size {
position: absolute; position: absolute;
left: 0px; left: 0px;
top: 0px; top: 0px;
@ -51,6 +53,9 @@ body {
background-color: black; background-color: black;
display: none; display: none;
} }
#image {
z-index:1;
}
#video { #video {
z-index:2; z-index:2;
} }
@ -136,8 +141,12 @@ body {
} }
document.getElementById('black').style.display = black; document.getElementById('black').style.display = black;
document.getElementById('lyricsmain').style.visibility = lyrics; document.getElementById('lyricsmain').style.visibility = lyrics;
document.getElementById('lyricsoutline').style.visibility = lyrics; outline = document.getElementById('lyricsoutline')
document.getElementById('lyricsshadow').style.visibility = lyrics; if(outline!=null)
outline.style.visibility = lyrics;
shadow = document.getElementById('lyricsshadow')
if(shadow!=null)
shadow.style.visibility = lyrics;
document.getElementById('footer').style.visibility = lyrics; document.getElementById('footer').style.visibility = lyrics;
var vid = document.getElementById('video'); var vid = document.getElementById('video');
if(vid.src != ''){ if(vid.src != ''){
@ -156,7 +165,7 @@ body {
return 0; return 0;
} }
if(position == ''){ if(position == ''){
position = window.getComputedStyle(text, '').verticalAlign; position = getComputedStyle(text, '').verticalAlign;
} }
switch(position) switch(position)
{ {
@ -181,111 +190,62 @@ body {
} }
function show_text(newtext){ 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) if(timer != null)
clearTimeout(timer); 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(){ function text_fade(id, newtext){
var text1 = document.getElementById('lyricsmain'); /*
var texto1 = document.getElementById('lyricsoutline'); Using -webkit-transition: opacity 1s linear; would have been preferred
var texts1 = document.getElementById('lyricsshadow'); but it isn't currently quick enough when animating multiple layers of
var text2 = document.getElementById('lyricsmain2'); large areas of large text. Therefore do it manually as best we can.
var texto2 = document.getElementById('lyricsoutline2'); Hopefully in the future we can revisit and do more interesting
var texts2 = document.getElementById('lyricsshadow2'); transitions using -webkit-transition and -webkit-transform.
if(parseFloat(text1.style.opacity) < 1){ However we need to ensure interrupted transitions (quickly change 2
text1.style.opacity = parseFloat(text1.style.opacity) + 0.1; slides) still looks pretty and is zippy.
texto1.style.opacity = parseFloat(texto1.style.opacity) + 0.1; */
// Don't animate shadow (performance) var text = document.getElementById(id);
//texts1.style.opacity = parseFloat(texts1.style.opacity) + 0.1; if(text==null) return;
if(!transition){
text.innerHTML = newtext;
return;
} }
if(parseFloat(text2.style.opacity) > 0){ if(newtext==text.innerHTML){
text2.style.opacity = parseFloat(text2.style.opacity) - 0.1; text.style.opacity = parseFloat(text.style.opacity) + 0.3;
texto2.style.opacity = parseFloat(texto2.style.opacity) - 0.1; if(text.style.opacity>0.7)
// Don't animate shadow (performance) text.style.opacity = 1;
//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);
} else { } else {
text1.style.opacity = 1; text.style.opacity = parseFloat(text.style.opacity) - 0.3;
texto1.style.opacity = 1; if(text.style.opacity<=0.1){
texts1.style.opacity = 1; text.innerHTML = newtext;
text2.style.opacity = 0;
texto2.style.opacity = 0;
texts2.style.opacity = 0;
} }
} }
}
function text_opacity(){
var text = document.getElementById('lyricsmain');
return getComputedStyle(text, '').opacity;
}
function show_text_complete(){ function show_text_complete(){
return (document.getElementById('lyricsmain').style.opacity == 1); return (text_opacity()==1);
} }
</script> </script>
</head> </head>
<body> <body>
<!-- <img id="image" class="size" src="%s" />
Using tables, rather than div's to make use of the vertical-align style that <video id="video" class="size"></video>
doesn't work on div's. This avoids the need to do positioning manually which %s
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>
<div id="footer" class="footer"></div> <div id="footer" class="footer"></div>
<video class="dim" id="video"></video> <div id="black" class="size"></div>
<div class="dim" id="black"></div> <div id="alert" style="visibility:hidden;"></div>
<img class="dim" id="image" src="%s" />
</body> </body>
</html> </html>
""" """
@ -300,7 +260,13 @@ def build_html(item, screen, alert, islive):
Current display information Current display information
`alert` `alert`
Alert display display information Alert display display information
`islive`
Item is going live, rather than preview/theme building
""" """
try:
webkitvers = float(QtWebKit.qWebKitVersion())
except AttributeError:
webkitvers = 0
width = screen[u'size'].width() width = screen[u'size'].width()
height = screen[u'size'].height() height = screen[u'size'].height()
theme = item.themedata theme = item.themedata
@ -309,83 +275,144 @@ def build_html(item, screen, alert, islive):
else: else:
image = u'' image = u''
html = HTMLSRC % (width, height, html = HTMLSRC % (width, height,
build_alert(alert, width), build_alert_css(alert, width),
build_footer(item), build_footer_css(item),
build_lyrics(item), build_lyrics_css(item, webkitvers),
u'true' if theme and theme.display_slideTransition and islive \ u'true' if theme and theme.display_slideTransition and islive \
else u'false', else u'false',
image) image,
build_lyrics_html(item, webkitvers))
return html return html
def build_lyrics(item): def build_lyrics_css(item, webkitvers):
""" """
Build the video display div Build the video display css
`item` `item`
Service Item containing theme and location information Service Item containing theme and location information
`webkitvers`
The version of qtwebkit we're using
""" """
style = """ style = """
.lyricscommon { position: absolute; %s } .lyricstable {
.lyricstable { z-index:4; %s } z-index:4;
.lyricsoutlinetable { z-index:3; %s } position: absolute;
.lyricsshadowtable { z-index:2; %s } display: table;
.lyrics { %s } %s
.lyricsoutline { %s } }
.lyricsshadow { %s } .lyricscell {
display:table-cell;
word-wrap: break-word;
%s
}
.lyricsmain {
%s
}
.lyricsoutline {
%s
}
.lyricsshadow {
%s
}
""" """
theme = item.themedata theme = item.themedata
lyricscommon = u''
lyricstable = u'' lyricstable = u''
outlinetable = u''
shadowtable = u''
lyrics = u'' lyrics = u''
outline = u'display: none;' lyricsmain = u''
shadow = u'display: none;' outline = u''
shadow = u''
if theme: if theme:
lyricscommon = u'width: %spx; height: %spx; word-wrap: break-word; ' \
u'font-family: %s; font-size: %spt; 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;' % \ lyricstable = u'left: %spx; top: %spx;' % \
(item.main.x(), item.main.y()) (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: if theme.display_horizontalAlign == 2:
align = u'text-align:center;' align = u'center'
elif theme.display_horizontalAlign == 1: elif theme.display_horizontalAlign == 1:
align = u'text-align:right;' align = u'right'
else: else:
align = u'text-align:left;' align = u'left'
if theme.display_verticalAlign == 2: if theme.display_verticalAlign == 2:
valign = u'vertical-align:bottom;' valign = u'bottom'
elif theme.display_verticalAlign == 1: elif theme.display_verticalAlign == 1:
valign = u'vertical-align:middle;' valign = u'middle'
else: else:
valign = u'vertical-align:top;' valign = u'top'
lyrics = u'%s %s' % (align, valign) lyrics = u'width: %spx; height: %spx; text-align: %s; ' \
'vertical-align: %s; font-family: %s; font-size: %spt; ' \
'color: %s; line-height: %d%%;' % \
(item.main.width(), item.main.height(), align, valign,
theme.font_main_name, theme.font_main_proportion,
theme.font_main_color, 100 + int(theme.font_main_line_adjustment))
# 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 theme.display_outline: if theme.display_outline:
lyricscommon += u' letter-spacing: 1px;' if webkitvers < 534.3:
outline = u'-webkit-text-stroke: %sem %s; ' % \ lyrics += u' letter-spacing: 1px;'
outline = u' -webkit-text-stroke: %sem %s; ' \
'-webkit-text-fill-color: %s; ' % \
(float(theme.display_outline_size) / 16, (float(theme.display_outline_size) / 16,
theme.display_outline_color) theme.display_outline_color, theme.font_main_color)
if theme.display_shadow: if webkitvers >= 533.3:
lyricsmain += outline
if theme.display_shadow and webkitvers < 534.3:
shadow = u'-webkit-text-stroke: %sem %s; ' \ shadow = u'-webkit-text-stroke: %sem %s; ' \
u'-webkit-text-fill-color: %s; ' % \ u'-webkit-text-fill-color: %s; ' \
u' padding-left: %spx; padding-top: %spx' % \
(float(theme.display_outline_size) / 16, (float(theme.display_outline_size) / 16,
theme.display_shadow_color, theme.display_shadow_color) theme.display_shadow_color, theme.display_shadow_color,
else: theme.display_shadow_size, theme.display_shadow_size)
if theme.display_shadow: if theme.display_shadow and \
shadow = u'color: %s;' % (theme.display_shadow_color) (not theme.display_outline or webkitvers >= 534.3):
lyrics_html = style % (lyricscommon, lyricstable, outlinetable, lyricsmain += u' text-shadow: %s %spx %spx;' % \
shadowtable, lyrics, outline, shadow) (theme.display_shadow_color, theme.display_shadow_size,
return lyrics_html theme.display_shadow_size)
lyrics_css = style % (lyricstable, lyrics, lyricsmain, outline, shadow)
return lyrics_css
def build_footer(item): 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 Build the display of the item footer
@ -416,7 +443,7 @@ def build_footer(item):
theme.font_footer_proportion, theme.font_footer_color, align) theme.font_footer_proportion, theme.font_footer_color, align)
return lyrics_html return lyrics_html
def build_alert(alertTab, width): def build_alert_css(alertTab, width):
""" """
Build the display of the footer Build the display of the footer
@ -424,7 +451,7 @@ def build_alert(alertTab, width):
Details from the Alert tab for fonts etc Details from the Alert tab for fonts etc
""" """
style = """ style = """
width: %s; width: %spx;
vertical-align: %s; vertical-align: %s;
font-family: %s; font-family: %s;
font-size: %spt; font-size: %spt;

View File

@ -47,26 +47,13 @@ class Renderer(object):
Initialise the renderer. Initialise the renderer.
""" """
self._rect = None self._rect = None
self._debug = False
self._display_shadow_size_footer = 0
self._display_outline_size_footer = 0
self.theme_name = None self.theme_name = None
self._theme = None self._theme = None
self._bg_image_filename = None self._bg_image_filename = None
self.frame = None self.frame = None
self.frame_opaque = None
self.bg_frame = None self.bg_frame = None
self.bg_image = 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): def set_theme(self, theme):
""" """
Set the theme to be used. Set the theme to be used.
@ -159,7 +146,7 @@ class Renderer(object):
doc = QtGui.QTextDocument() doc = QtGui.QTextDocument()
doc.setPageSize(QtCore.QSizeF(self._rect.width(), self._rect.height())) doc.setPageSize(QtCore.QSizeF(self._rect.width(), self._rect.height()))
df = doc.defaultFont() df = doc.defaultFont()
df.setPixelSize(self._theme.font_main_proportion) df.setPointSize(self._theme.font_main_proportion)
df.setFamily(self._theme.font_main_name) df.setFamily(self._theme.font_main_name)
main_weight = 50 main_weight = 50
if self._theme.font_main_weight == u'Bold': if self._theme.font_main_weight == u'Bold':
@ -188,7 +175,7 @@ class Renderer(object):
# Text too long so gone to next mage # Text too long so gone to next mage
if layout.pageCount() != 1: if layout.pageCount() != 1:
formatted.append(shell % old_html_text) formatted.append(shell % old_html_text)
temp_text = line temp_text = line + line_end
old_html_text = temp_text old_html_text = temp_text
formatted.append(shell % old_html_text) formatted.append(shell % old_html_text)
log.debug(u'format_slide - End') log.debug(u'format_slide - End')

View File

@ -93,6 +93,7 @@ class RenderManager(object):
""" """
self.global_theme = global_theme self.global_theme = global_theme
self.theme_level = theme_level self.theme_level = theme_level
self.themedata = None
def set_service_theme(self, service_theme): def set_service_theme(self, service_theme):
""" """
@ -102,6 +103,7 @@ class RenderManager(object):
The service-level theme to be set. The service-level theme to be set.
""" """
self.service_theme = service_theme self.service_theme = service_theme
self.themedata = None
def set_override_theme(self, theme, overrideLevels=False): def set_override_theme(self, theme, overrideLevels=False):
""" """
@ -111,6 +113,10 @@ class RenderManager(object):
``theme`` ``theme``
The name of the song-level theme. None means the service The name of the song-level theme. None means the service
item wants to use the given value. 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) log.debug(u'set override theme to %s', theme)
theme_level = self.theme_level theme_level = self.theme_level
@ -137,6 +143,7 @@ class RenderManager(object):
if self.theme != self.renderer.theme_name or self.themedata is None \ if self.theme != self.renderer.theme_name or self.themedata is None \
or overrideLevels: or overrideLevels:
log.debug(u'theme is now %s', self.theme) log.debug(u'theme is now %s', self.theme)
# Force the theme to be the one passed in.
if overrideLevels: if overrideLevels:
self.themedata = theme self.themedata = theme
else: else:

View File

@ -115,8 +115,11 @@ class MainDisplay(DisplayWidget):
self.screen = self.screens.current self.screen = self.screens.current
self.setVisible(False) self.setVisible(False)
self.setGeometry(self.screen[u'size']) self.setGeometry(self.screen[u'size'])
self.webView = QtWebKit.QWebView(self) self.scene = QtGui.QGraphicsScene()
self.webView.setGeometry(0, 0, self.screen[u'size'].width(), \ 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.screen[u'size'].height())
self.page = self.webView.page() self.page = self.webView.page()
self.frame = self.page.mainFrame() self.frame = self.page.mainFrame()
@ -319,9 +322,6 @@ class MainDisplay(DisplayWidget):
# Make display show up if in single screen mode # Make display show up if in single screen mode
if self.isLive: if self.isLive:
self.setVisible(True) self.setVisible(True)
# save preview for debugging
if log.isEnabledFor(logging.DEBUG):
preview.save(u'temp.png', u'png')
return preview return preview
def buildHtml(self, serviceItem): def buildHtml(self, serviceItem):

View File

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

View File

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

View File

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

View File

@ -83,6 +83,12 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
QtCore.QObject.connect(self.wordsOfWorshipRemoveButton, QtCore.QObject.connect(self.wordsOfWorshipRemoveButton,
QtCore.SIGNAL(u'clicked()'), QtCore.SIGNAL(u'clicked()'),
self.onWordsOfWorshipRemoveButtonClicked) 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.QObject.connect(self.songsOfFellowshipAddButton,
QtCore.SIGNAL(u'clicked()'), QtCore.SIGNAL(u'clicked()'),
self.onSongsOfFellowshipAddButtonClicked) self.onSongsOfFellowshipAddButtonClicked)
@ -277,6 +283,16 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
def onWordsOfWorshipRemoveButtonClicked(self): def onWordsOfWorshipRemoveButtonClicked(self):
self.removeSelectedItems(self.wordsOfWorshipFileListWidget) 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): def onSongsOfFellowshipAddButtonClicked(self):
self.getFiles( self.getFiles(
translate('SongsPlugin.ImportWizardForm', translate('SongsPlugin.ImportWizardForm',

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,7 @@ from olpimport import OpenLPSongImport
try: try:
from sofimport import SofImport from sofimport import SofImport
from oooimport import OooImport from oooimport import OooImport
from cclifileimport import CCLIFileImport
from wowimport import WowImport from wowimport import WowImport
except ImportError: except ImportError:
pass pass
@ -68,6 +69,8 @@ class SongFormat(object):
return WowImport return WowImport
elif format == SongFormat.Generic: elif format == SongFormat.Generic:
return OooImport return OooImport
elif format == SongFormat.CCLI:
return CCLIFileImport
# else: # else:
return None return None

View File

@ -43,12 +43,6 @@ class SongImport(QtCore.QObject):
whether the authors etc already exist and add them or refer to them whether the authors etc already exist and add them or refer to them
as necessary as necessary
""" """
COPYRIGHT_STRING = unicode(translate(
'SongsPlugin.SongImport', 'copyright'))
COPYRIGHT_SYMBOL = unicode(translate(
'SongsPlugin.SongImport', '\xa9'))
def __init__(self, manager): def __init__(self, manager):
""" """
Initialise and create defaults for properties Initialise and create defaults for properties
@ -58,11 +52,11 @@ class SongImport(QtCore.QObject):
""" """
self.manager = manager self.manager = manager
self.stop_import_flag = False self.stop_import_flag = False
self.set_defaults()
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'songs_stop_import'), self.stop_import) QtCore.SIGNAL(u'songs_stop_import'), self.stop_import)
self.setDefaults()
def setDefaults(self): def set_defaults(self):
self.title = u'' self.title = u''
self.song_number = u'' self.song_number = u''
self.alternate_title = u'' self.alternate_title = u''
@ -78,6 +72,10 @@ class SongImport(QtCore.QObject):
self.verses = [] self.verses = []
self.versecount = 0 self.versecount = 0
self.choruscount = 0 self.choruscount = 0
self.copyright_string = unicode(translate(
'SongsPlugin.SongImport', 'copyright'))
self.copyright_symbol = unicode(translate(
'SongsPlugin.SongImport', '\xa9'))
def stop_import(self): def stop_import(self):
""" """
@ -163,8 +161,7 @@ class SongImport(QtCore.QObject):
def parse_author(self, text): def parse_author(self, text):
""" """
Add the author. OpenLP stores them individually so split by 'and', '&' Add the author. OpenLP stores them individually so split by 'and', '&'
and comma. and comma. However need to check for 'Mr and Mrs Smith' and turn it to
However need to check for 'Mr and Mrs Smith' and turn it to
'Mr Smith' and 'Mrs Smith'. 'Mr Smith' and 'Mrs Smith'.
""" """
for author in text.split(u','): for author in text.split(u','):
@ -241,7 +238,7 @@ class SongImport(QtCore.QObject):
""" """
All fields have been set to this song. Write it away All fields have been set to this song. Write it away
""" """
if len(self.authors) == 0: if not self.authors:
self.authors.append(u'Author unknown') self.authors.append(u'Author unknown')
self.commit_song() self.commit_song()