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.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>', \

View File

@ -24,6 +24,8 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from PyQt4 import QtWebKit
from openlp.core.lib import image_to_byte
HTMLSRC = u"""
@ -39,7 +41,7 @@ HTMLSRC = u"""
body {
background-color: black;
}
.dim {
.size {
position: absolute;
left: 0px;
top: 0px;
@ -51,6 +53,9 @@ body {
background-color: black;
display: none;
}
#image {
z-index:1;
}
#video {
z-index:2;
}
@ -136,8 +141,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 +165,7 @@ body {
return 0;
}
if(position == ''){
position = window.getComputedStyle(text, '').verticalAlign;
position = getComputedStyle(text, '').verticalAlign;
}
switch(position)
{
@ -181,111 +190,62 @@ 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>
"""
@ -300,7 +260,13 @@ def build_html(item, screen, alert, islive):
Current display information
`alert`
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()
height = screen[u'size'].height()
theme = item.themedata
@ -309,83 +275,144 @@ def build_html(item, screen, alert, islive):
else:
image = u''
html = HTMLSRC % (width, height,
build_alert(alert, width),
build_footer(item),
build_lyrics(item),
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 build_lyrics_css(item, webkitvers):
"""
Build the video display div
Build the video 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: %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;' % \
(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;'
align = u'center'
elif theme.display_horizontalAlign == 1:
align = u'text-align:right;'
align = u'right'
else:
align = u'text-align:left;'
align = u'left'
if theme.display_verticalAlign == 2:
valign = u'vertical-align:bottom;'
valign = u'bottom'
elif theme.display_verticalAlign == 1:
valign = u'vertical-align:middle;'
valign = u'middle'
else:
valign = u'vertical-align:top;'
lyrics = u'%s %s' % (align, valign)
valign = u'top'
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:
lyricscommon += u' letter-spacing: 1px;'
outline = u'-webkit-text-stroke: %sem %s; ' % \
if webkitvers < 534.3:
lyrics += u' letter-spacing: 1px;'
outline = u' -webkit-text-stroke: %sem %s; ' \
'-webkit-text-fill-color: %s; ' % \
(float(theme.display_outline_size) / 16,
theme.display_outline_color)
if theme.display_shadow:
theme.display_outline_color, theme.font_main_color)
if webkitvers >= 533.3:
lyricsmain += outline
if theme.display_shadow and webkitvers < 534.3:
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,
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
theme.display_shadow_color, theme.display_shadow_color,
theme.display_shadow_size, theme.display_shadow_size)
if theme.display_shadow and \
(not theme.display_outline or webkitvers >= 534.3):
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_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
@ -416,7 +443,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,7 +451,7 @@ 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: %spt;

View File

@ -47,26 +47,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.
@ -159,7 +146,7 @@ class Renderer(object):
doc = QtGui.QTextDocument()
doc.setPageSize(QtCore.QSizeF(self._rect.width(), self._rect.height()))
df = doc.defaultFont()
df.setPixelSize(self._theme.font_main_proportion)
df.setPointSize(self._theme.font_main_proportion)
df.setFamily(self._theme.font_main_name)
main_weight = 50
if self._theme.font_main_weight == u'Bold':
@ -188,7 +175,7 @@ class Renderer(object):
# Text too long so gone to next mage
if layout.pageCount() != 1:
formatted.append(shell % old_html_text)
temp_text = line
temp_text = line + line_end
old_html_text = temp_text
formatted.append(shell % old_html_text)
log.debug(u'format_slide - 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

@ -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()
@ -319,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):

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

@ -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 has_dual_bible:
if dual_bible:
footer = u'%s (%s %s)' % (book, dual_version,
dual_copyright)
# If not found add second version and copyright to footer.
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''
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.
if self.parent.settings_tab.layout_style == 1:
text = text + u'\n'
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)
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:
raw_slides.append(bible_text)
bible_text = u''
# service item title
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,9 +654,7 @@ 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()
@ -669,12 +667,12 @@ class BibleMediaItem(MediaManagerItem):
'bible': QtCore.QVariant(bible),
'version': QtCore.QVariant(version.value),
'copyright': QtCore.QVariant(copyright.value),
#'permission':QtCore.QVariant(permission.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_permission': QtCore.QVariant(dual_permission.value),
'dual_text': QtCore.QVariant(
self.dual_search_results[count].text)
}
@ -688,7 +686,7 @@ class BibleMediaItem(MediaManagerItem):
'bible': QtCore.QVariant(bible),
'version': QtCore.QVariant(version.value),
'copyright': QtCore.QVariant(copyright.value),
#'permission':QtCore.QVariant(permission.value),
'permission': QtCore.QVariant(permission.value),
'text': QtCore.QVariant(verse.text),
'dual_bible': QtCore.QVariant(dual_bible)
}

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)
@ -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',

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:
from sofimport import SofImport
from oooimport import OooImport
from cclifileimport import CCLIFileImport
from wowimport import WowImport
except ImportError:
pass
@ -68,6 +69,8 @@ class SongFormat(object):
return WowImport
elif format == SongFormat.Generic:
return OooImport
elif format == SongFormat.CCLI:
return CCLIFileImport
# else:
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
as necessary
"""
COPYRIGHT_STRING = unicode(translate(
'SongsPlugin.SongImport', 'copyright'))
COPYRIGHT_SYMBOL = unicode(translate(
'SongsPlugin.SongImport', '\xa9'))
def __init__(self, manager):
"""
Initialise and create defaults for properties
@ -58,11 +52,11 @@ class SongImport(QtCore.QObject):
"""
self.manager = manager
self.stop_import_flag = False
self.set_defaults()
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'songs_stop_import'), self.stop_import)
self.setDefaults()
def setDefaults(self):
def set_defaults(self):
self.title = u''
self.song_number = u''
self.alternate_title = u''
@ -78,6 +72,10 @@ class SongImport(QtCore.QObject):
self.verses = []
self.versecount = 0
self.choruscount = 0
self.copyright_string = unicode(translate(
'SongsPlugin.SongImport', 'copyright'))
self.copyright_symbol = unicode(translate(
'SongsPlugin.SongImport', '\xa9'))
def stop_import(self):
"""
@ -163,8 +161,7 @@ class SongImport(QtCore.QObject):
def parse_author(self, text):
"""
Add the author. OpenLP stores them individually so split by 'and', '&'
and comma.
However need to check for 'Mr and Mrs Smith' and turn it to
and comma. However need to check for 'Mr and Mrs Smith' and turn it to
'Mr Smith' and 'Mrs Smith'.
"""
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
"""
if len(self.authors) == 0:
if not self.authors:
self.authors.append(u'Author unknown')
self.commit_song()