forked from openlp/openlp
Head1054
This commit is contained in:
commit
f62ac45a29
28
openlp.pyw
28
openlp.pyw
@ -147,6 +147,9 @@ class OpenLP(QtGui.QApplication):
|
||||
return self.exec_()
|
||||
|
||||
def hookException(self, exctype, value, traceback):
|
||||
if not hasattr(self, u'mainWindow'):
|
||||
log.exception(''.join(format_exception(exctype, value, traceback)))
|
||||
return
|
||||
if not hasattr(self, u'exceptionForm'):
|
||||
self.exceptionForm = ExceptionForm(self.mainWindow)
|
||||
self.exceptionForm.exceptionTextEdit.setPlainText(
|
||||
@ -159,18 +162,18 @@ def main():
|
||||
the PyQt4 Application.
|
||||
"""
|
||||
# Set up command line options.
|
||||
usage = u'Usage: %prog [options] [qt-options]'
|
||||
usage = 'Usage: %prog [options] [qt-options]'
|
||||
parser = OptionParser(usage=usage)
|
||||
parser.add_option("-l", "--log-level", dest="loglevel",
|
||||
default="warning", metavar="LEVEL",
|
||||
help="Set logging to LEVEL level. Valid values are "
|
||||
"\"debug\", \"info\", \"warning\".")
|
||||
parser.add_option("-p", "--portable", dest="portable",
|
||||
action="store_true",
|
||||
help="Specify if this should be run as a portable app, "
|
||||
"off a USB flash drive.")
|
||||
parser.add_option("-s", "--style", dest="style",
|
||||
help="Set the Qt4 style (passed directly to Qt4).")
|
||||
parser.add_option('-e', '--no-error-form', dest='no_error_form',
|
||||
action='store_true', help='Disable the error notification form.')
|
||||
parser.add_option('-l', '--log-level', dest='loglevel',
|
||||
default='warning', metavar='LEVEL', help='Set logging to LEVEL '
|
||||
'level. Valid values are "debug", "info", "warning".')
|
||||
parser.add_option('-p', '--portable', dest='portable',
|
||||
action='store_true', help='Specify if this should be run as a '
|
||||
'portable app, off a USB flash drive (not implemented).')
|
||||
parser.add_option('-s', '--style', dest='style',
|
||||
help='Set the Qt4 style (passed directly to Qt4).')
|
||||
# Set up logging
|
||||
log_path = AppLocation.get_directory(AppLocation.CacheDir)
|
||||
if not os.path.exists(log_path):
|
||||
@ -203,7 +206,8 @@ def main():
|
||||
language = LanguageManager.get_language()
|
||||
appTranslator = LanguageManager.get_translator(language)
|
||||
app.installTranslator(appTranslator)
|
||||
sys.excepthook = app.hookException
|
||||
if not options.no_error_form:
|
||||
sys.excepthook = app.hookException
|
||||
sys.exit(app.run())
|
||||
|
||||
if __name__ == u'__main__':
|
||||
|
@ -38,63 +38,48 @@ log = logging.getLogger(__name__)
|
||||
# TODO make external and configurable in alpha 4 via a settings dialog
|
||||
html_expands = []
|
||||
|
||||
html_expands.append({u'desc':u'Red', u'start tag':u'{r}', \
|
||||
u'start html':u'<span style="-webkit-text-fill-color:red">', \
|
||||
u'end tag':u'{/r}', u'end html':u'</span>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'Black', u'start tag':u'{b}', \
|
||||
u'start html':u'<span style="-webkit-text-fill-color:black">', \
|
||||
u'end tag':u'{/b}', u'end html':u'</span>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'Blue', u'start tag':u'{bl}', \
|
||||
u'start html':u'<span style="-webkit-text-fill-color:blue">', \
|
||||
u'end tag':u'{/bl}', u'end html':u'</span>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'Yellow', u'start tag':u'{y}', \
|
||||
u'start html':u'<span style="-webkit-text-fill-color:yellow">', \
|
||||
u'end tag':u'{/y}', u'end html':u'</span>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'Green', u'start tag':u'{g}', \
|
||||
u'start html':u'<span style="-webkit-text-fill-color:green">', \
|
||||
u'end tag':u'{/g}', u'end html':u'</span>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'Pink', u'start tag':u'{pk}', \
|
||||
u'start html':u'<span style="-webkit-text-fill-color:#CC33CC">', \
|
||||
u'end tag':u'{/pk}', u'end html':u'</span>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'Orange', u'start tag':u'{o}', \
|
||||
u'start html':u'<span style="-webkit-text-fill-color:#CC0033">', \
|
||||
u'end tag':u'{/o}', u'end html':u'</span>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'Purple', u'start tag':u'{pp}', \
|
||||
u'start html':u'<span style="-webkit-text-fill-color:#9900FF">', \
|
||||
u'end tag':u'{/pp}', u'end html':u'</span>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'White', u'start tag':u'{w}', \
|
||||
u'start html':u'<span style="-webkit-text-fill-color:white">', \
|
||||
u'end tag':u'{/w}', u'end html':u'</span>', \
|
||||
u'protected':False})
|
||||
html_expands.append({u'desc':u'Superscript', u'start tag':u'{su}', \
|
||||
u'start html':u'<sup>', \
|
||||
u'end tag':u'{/su}', u'end html':u'</sup>', \
|
||||
u'protected':True})
|
||||
html_expands.append({u'desc':u'Subscript', u'start tag':u'{sb}', \
|
||||
u'start html':u'<sub>', \
|
||||
u'end tag':u'{/sb}', u'end html':u'</sub>', \
|
||||
u'protected':True})
|
||||
html_expands.append({u'desc':u'Paragraph', u'start tag':u'{p}', \
|
||||
u'start html':u'<p>', \
|
||||
u'end tag':u'{/p}', u'end html':u'</p>', \
|
||||
u'protected':True})
|
||||
html_expands.append({u'desc':u'Bold', u'start tag':u'{st}', \
|
||||
u'start html':u'<strong>', \
|
||||
u'end tag':u'{/st}', \
|
||||
u'end html':u'</strong>', \
|
||||
u'protected':True})
|
||||
html_expands.append({u'desc':u'Italics', u'start tag':u'{it}', \
|
||||
u'start html':u'<em>', \
|
||||
u'end tag':u'{/it}', u'end html':u'</em>', \
|
||||
u'protected':True})
|
||||
html_expands.append({u'desc':u'Red', u'start tag':u'{r}',
|
||||
u'start html':u'<span style="-webkit-text-fill-color:red">',
|
||||
u'end tag':u'{/r}', u'end html':u'</span>', u'protected':False})
|
||||
html_expands.append({u'desc':u'Black', u'start tag':u'{b}',
|
||||
u'start html':u'<span style="-webkit-text-fill-color:black">',
|
||||
u'end tag':u'{/b}', u'end html':u'</span>', u'protected':False})
|
||||
html_expands.append({u'desc':u'Blue', u'start tag':u'{bl}',
|
||||
u'start html':u'<span style="-webkit-text-fill-color:blue">',
|
||||
u'end tag':u'{/bl}', u'end html':u'</span>', u'protected':False})
|
||||
html_expands.append({u'desc':u'Yellow', u'start tag':u'{y}',
|
||||
u'start html':u'<span style="-webkit-text-fill-color:yellow">',
|
||||
u'end tag':u'{/y}', u'end html':u'</span>', u'protected':False})
|
||||
html_expands.append({u'desc':u'Green', u'start tag':u'{g}',
|
||||
u'start html':u'<span style="-webkit-text-fill-color:green">',
|
||||
u'end tag':u'{/g}', u'end html':u'</span>', u'protected':False})
|
||||
html_expands.append({u'desc':u'Pink', u'start tag':u'{pk}',
|
||||
u'start html':u'<span style="-webkit-text-fill-color:#CC33CC">',
|
||||
u'end tag':u'{/pk}', u'end html':u'</span>', u'protected':False})
|
||||
html_expands.append({u'desc':u'Orange', u'start tag':u'{o}',
|
||||
u'start html':u'<span style="-webkit-text-fill-color:#CC0033">',
|
||||
u'end tag':u'{/o}', u'end html':u'</span>', u'protected':False})
|
||||
html_expands.append({u'desc':u'Purple', u'start tag':u'{pp}',
|
||||
u'start html':u'<span style="-webkit-text-fill-color:#9900FF">',
|
||||
u'end tag':u'{/pp}', u'end html':u'</span>', u'protected':False})
|
||||
html_expands.append({u'desc':u'White', u'start tag':u'{w}',
|
||||
u'start html':u'<span style="-webkit-text-fill-color:white">',
|
||||
u'end tag':u'{/w}', u'end html':u'</span>', u'protected':False})
|
||||
html_expands.append({u'desc':u'Superscript', u'start tag':u'{su}',
|
||||
u'start html':u'<sup>', u'end tag':u'{/su}', u'end html':u'</sup>',
|
||||
u'protected':True})
|
||||
html_expands.append({u'desc':u'Subscript', u'start tag':u'{sb}',
|
||||
u'start html':u'<sub>', u'end tag':u'{/sb}', u'end html':u'</sub>',
|
||||
u'protected':True})
|
||||
html_expands.append({u'desc':u'Paragraph', u'start tag':u'{p}',
|
||||
u'start html':u'<p>', u'end tag':u'{/p}', u'end html':u'</p>',
|
||||
u'protected':True})
|
||||
html_expands.append({u'desc':u'Bold', u'start tag':u'{st}',
|
||||
u'start html':u'<strong>', u'end tag':u'{/st}', u'end html':u'</strong>',
|
||||
u'protected':True})
|
||||
html_expands.append({u'desc':u'Italics', u'start tag':u'{it}',
|
||||
u'start html':u'<em>', u'end tag':u'{/it}', u'end html':u'</em>',
|
||||
u'protected':True})
|
||||
|
||||
def translate(context, text, comment=None):
|
||||
"""
|
||||
@ -235,6 +220,7 @@ def image_to_byte(image):
|
||||
``image``
|
||||
The image to converted.
|
||||
"""
|
||||
log.debug(u'image_to_byte')
|
||||
byte_array = QtCore.QByteArray()
|
||||
# use buffer to store pixmap into byteArray
|
||||
buffie = QtCore.QBuffer(byte_array)
|
||||
@ -264,6 +250,7 @@ def resize_image(image, width, height, background=QtCore.Qt.black):
|
||||
The background colour defaults to black.
|
||||
|
||||
"""
|
||||
log.debug(u'resize_image')
|
||||
preview = QtGui.QImage(image)
|
||||
if not preview.isNull():
|
||||
# Only resize if different size
|
||||
|
@ -27,8 +27,6 @@
|
||||
import logging
|
||||
from PyQt4 import QtWebKit
|
||||
|
||||
from openlp.core.lib import image_to_byte
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
HTMLSRC = u"""
|
||||
@ -60,7 +58,10 @@ body {
|
||||
#image {
|
||||
z-index:1;
|
||||
}
|
||||
#video {
|
||||
#video1 {
|
||||
z-index:2;
|
||||
}
|
||||
#video2 {
|
||||
z-index:2;
|
||||
}
|
||||
#alert {
|
||||
@ -81,38 +82,86 @@ body {
|
||||
</style>
|
||||
<script language="javascript">
|
||||
var timer = null;
|
||||
var video_timer = null;
|
||||
var current_video = '1';
|
||||
var transition = %s;
|
||||
|
||||
function show_video(state, path, volume, loop){
|
||||
var vid = document.getElementById('video');
|
||||
if(path != null)
|
||||
vid.src = path;
|
||||
if(loop != null){
|
||||
if(loop)
|
||||
vid.loop = 'loop';
|
||||
else
|
||||
vid.loop = '';
|
||||
// Note, the preferred method for looping would be to use the
|
||||
// video tag loop attribute.
|
||||
// But QtWebKit doesn't support this. Neither does it support the
|
||||
// onended event, hence the setInterval()
|
||||
// In addition, setting the currentTime attribute to zero to restart
|
||||
// the video raises an INDEX_SIZE_ERROR: DOM Exception 1
|
||||
// To complicate it further, sometimes vid.currentTime stops
|
||||
// slightly short of vid.duration and vid.ended is intermittent!
|
||||
//
|
||||
// Note, currently the background may go black between loops. Not
|
||||
// desirable. Need to investigate using two <video>'s, and hiding/
|
||||
// preloading one, and toggle between the two when looping.
|
||||
|
||||
if(current_video=='1'){
|
||||
var vid = document.getElementById('video1');
|
||||
var vid2 = document.getElementById('video2');
|
||||
} else {
|
||||
var vid = document.getElementById('video2');
|
||||
var vid2 = document.getElementById('video1');
|
||||
}
|
||||
if(volume != null){
|
||||
vid.volume = volume;
|
||||
vid2.volume = volume;
|
||||
}
|
||||
switch(state){
|
||||
case 'init':
|
||||
vid.src = path;
|
||||
vid2.src = path;
|
||||
if(loop == null) loop = false;
|
||||
vid.looping = loop;
|
||||
vid2.looping = loop;
|
||||
vid.load();
|
||||
break;
|
||||
case 'load':
|
||||
vid2.style.visibility = 'hidden';
|
||||
vid2.load();
|
||||
break;
|
||||
case 'play':
|
||||
vid.play();
|
||||
vid.style.display = 'block';
|
||||
vid.style.visibility = 'visible';
|
||||
if(vid.looping){
|
||||
video_timer = setInterval(
|
||||
function() {
|
||||
show_video('poll');
|
||||
}, 200);
|
||||
}
|
||||
break;
|
||||
case 'pause':
|
||||
if(video_timer!=null){
|
||||
clearInterval(video_timer);
|
||||
video_timer = null;
|
||||
}
|
||||
vid.pause();
|
||||
vid.style.display = 'block';
|
||||
break;
|
||||
case 'stop':
|
||||
vid.pause();
|
||||
vid.style.display = 'none';
|
||||
show_video('pause');
|
||||
vid.style.visibility = 'hidden';
|
||||
break;
|
||||
case 'poll':
|
||||
if(vid.ended||vid.currentTime+0.2>vid.duration)
|
||||
show_video('swap');
|
||||
break;
|
||||
case 'swap':
|
||||
show_video('pause');
|
||||
if(current_video=='1')
|
||||
current_video = '2';
|
||||
else
|
||||
current_video = '1';
|
||||
show_video('play');
|
||||
show_video('load');
|
||||
break;
|
||||
case 'close':
|
||||
vid.pause();
|
||||
vid.style.display = 'none';
|
||||
show_video('stop');
|
||||
vid.src = '';
|
||||
vid2.src = '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -245,8 +294,11 @@ body {
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<img id="image" class="size" src="%s" />
|
||||
<video id="video" class="size"></video>
|
||||
<img id="image" class="size" %s />
|
||||
<video id="video1" class="size" style="visibility:hidden" autobuffer preload>
|
||||
</video>
|
||||
<video id="video2" class="size" style="visibility:hidden" autobuffer preload>
|
||||
</video>
|
||||
%s
|
||||
<div id="footer" class="footer"></div>
|
||||
<div id="black" class="size"></div>
|
||||
@ -272,10 +324,10 @@ def build_html(item, screen, alert, islive):
|
||||
height = screen[u'size'].height()
|
||||
theme = item.themedata
|
||||
webkitvers = webkit_version()
|
||||
if item.bg_frame:
|
||||
image = u'data:image/png;base64,%s' % image_to_byte(item.bg_frame)
|
||||
if item.bg_image_bytes:
|
||||
image = u'src="data:image/png;base64,%s"' % item.bg_image_bytes
|
||||
else:
|
||||
image = u''
|
||||
image = u'style="display:none;"'
|
||||
html = HTMLSRC % (build_background_css(item, width, height),
|
||||
width, height,
|
||||
build_alert_css(alert, width),
|
||||
@ -397,8 +449,10 @@ def build_lyrics_css(item, webkitvers):
|
||||
outline = build_lyrics_outline_css(theme)
|
||||
if theme.display_shadow:
|
||||
if theme.display_outline and webkitvers < 534.3:
|
||||
shadow = u'padding-left: %spx; padding-top: %spx ' % \
|
||||
(theme.display_shadow_size, theme.display_shadow_size)
|
||||
shadow = u'padding-left: %spx; padding-top: %spx;' % \
|
||||
(int(theme.display_shadow_size) +
|
||||
(int(theme.display_outline_size) * 2),
|
||||
theme.display_shadow_size)
|
||||
shadow += build_lyrics_outline_css(theme, True)
|
||||
else:
|
||||
lyricsmain += u' text-shadow: %s %spx %spx;' % \
|
||||
@ -458,13 +512,17 @@ def build_lyrics_format_css(theme, width, height):
|
||||
valign = u'middle'
|
||||
else:
|
||||
valign = u'top'
|
||||
if theme.display_outline:
|
||||
left_margin = int(theme.display_outline_size) * 2
|
||||
else:
|
||||
left_margin = 0
|
||||
lyrics = u'white-space:pre-wrap; word-wrap: break-word; ' \
|
||||
'text-align: %s; vertical-align: %s; font-family: %s; ' \
|
||||
'font-size: %spt; color: %s; line-height: %d%%; ' \
|
||||
'margin:0; padding:0; width: %spx; height: %spx; ' % \
|
||||
'font-size: %spt; color: %s; line-height: %d%%; margin:0;' \
|
||||
'padding:0; padding-left:%spx; width: %spx; height: %spx; ' % \
|
||||
(align, valign, theme.font_main_name, theme.font_main_proportion,
|
||||
theme.font_main_color, 100 + int(theme.font_main_line_adjustment),
|
||||
width, height)
|
||||
left_margin, width, height)
|
||||
if theme.display_outline:
|
||||
if webkit_version() < 534.3:
|
||||
lyrics += u' letter-spacing: 1px;'
|
||||
|
@ -432,7 +432,7 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
raise NotImplementedError(u'MediaManagerItem.onDeleteClick needs to '
|
||||
u'be defined by the plugin')
|
||||
|
||||
def generateSlideData(self, service_item, item):
|
||||
def generateSlideData(self, service_item, item=None):
|
||||
raise NotImplementedError(u'MediaManagerItem.generateSlideData needs '
|
||||
u'to be defined by the plugin')
|
||||
|
||||
|
@ -32,7 +32,8 @@ import logging
|
||||
from PyQt4 import QtGui, QtCore, QtWebKit
|
||||
|
||||
from openlp.core.lib import resize_image, expand_tags, \
|
||||
build_lyrics_format_css, build_lyrics_outline_css
|
||||
build_lyrics_format_css, build_lyrics_outline_css, image_to_byte
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -54,6 +55,7 @@ class Renderer(object):
|
||||
self.frame = None
|
||||
self.bg_frame = None
|
||||
self.bg_image = None
|
||||
self.bg_image_bytes = None
|
||||
|
||||
def set_theme(self, theme):
|
||||
"""
|
||||
@ -66,15 +68,12 @@ class Renderer(object):
|
||||
self._theme = theme
|
||||
self.bg_frame = None
|
||||
self.bg_image = None
|
||||
self.bg_image_bytes = None
|
||||
self._bg_image_filename = None
|
||||
self.theme_name = theme.theme_name
|
||||
if theme.background_type == u'image':
|
||||
if theme.background_filename:
|
||||
self._bg_image_filename = unicode(theme.background_filename)
|
||||
if self.frame:
|
||||
self.bg_image = resize_image(self._bg_image_filename,
|
||||
self.frame.width(),
|
||||
self.frame.height())
|
||||
|
||||
def set_text_rectangle(self, rect_main, rect_footer):
|
||||
"""
|
||||
@ -89,6 +88,22 @@ class Renderer(object):
|
||||
log.debug(u'set_text_rectangle %s , %s' % (rect_main, rect_footer))
|
||||
self._rect = rect_main
|
||||
self._rect_footer = rect_footer
|
||||
self.page_width = self._rect.width()
|
||||
self.page_height = self._rect.height()
|
||||
if self._theme.display_shadow:
|
||||
self.page_width -= int(self._theme.display_shadow_size)
|
||||
self.page_height -= int(self._theme.display_shadow_size)
|
||||
self.web = QtWebKit.QWebView()
|
||||
self.web.setVisible(False)
|
||||
self.web.resize(self.page_width, self.page_height)
|
||||
self.web_frame = self.web.page().mainFrame()
|
||||
# Adjust width and height to account for shadow. outline done in css
|
||||
self.page_shell = u'<html><head><style>' \
|
||||
u'*{margin: 0; padding: 0; border: 0;} '\
|
||||
u'#main {position:absolute; top:0px; %s %s}</style><body>' \
|
||||
u'<div id="main">' % \
|
||||
(build_lyrics_format_css(self._theme, self.page_width,
|
||||
self.page_height), build_lyrics_outline_css(self._theme))
|
||||
|
||||
def set_frame_dest(self, frame_width, frame_height):
|
||||
"""
|
||||
@ -110,15 +125,18 @@ class Renderer(object):
|
||||
self.frame.width(), self.frame.height())
|
||||
if self._theme.background_type == u'image':
|
||||
self.bg_frame = QtGui.QImage(self.frame.width(),
|
||||
self.frame.height(), QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
self.frame.height(),
|
||||
QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
painter = QtGui.QPainter()
|
||||
painter.begin(self.bg_frame)
|
||||
painter.fillRect(self.frame.rect(), QtCore.Qt.black)
|
||||
if self.bg_image:
|
||||
painter.drawImage(0, 0, self.bg_image)
|
||||
painter.end()
|
||||
self.bg_image_bytes = image_to_byte(self.bg_frame)
|
||||
else:
|
||||
self.bg_frame = None
|
||||
self.bg_image_bytes = None
|
||||
|
||||
def format_slide(self, words, line_break):
|
||||
"""
|
||||
@ -139,29 +157,16 @@ class Renderer(object):
|
||||
lines = verse.split(u'\n')
|
||||
for line in lines:
|
||||
text.append(line)
|
||||
web = QtWebKit.QWebView()
|
||||
web.resize(self._rect.width(), self._rect.height())
|
||||
web.setVisible(False)
|
||||
frame = web.page().mainFrame()
|
||||
# Adjust width and height to account for shadow. outline done in css
|
||||
width = self._rect.width() - int(self._theme.display_shadow_size)
|
||||
height = self._rect.height() - int(self._theme.display_shadow_size)
|
||||
shell = u'<html><head><style>#main {%s %s}</style><body>' \
|
||||
u'<div id="main">' % \
|
||||
(build_lyrics_format_css(self._theme, width, height),
|
||||
build_lyrics_outline_css(self._theme))
|
||||
formatted = []
|
||||
html_text = u''
|
||||
styled_text = u''
|
||||
js_height = 'document.getElementById("main").scrollHeight'
|
||||
for line in text:
|
||||
styled_line = expand_tags(line) + line_end
|
||||
styled_text += styled_line
|
||||
html = shell + styled_text + u'</div></body></html>'
|
||||
web.setHtml(html)
|
||||
html = self.page_shell + styled_text + u'</div></body></html>'
|
||||
self.web.setHtml(html)
|
||||
# Text too long so go to next page
|
||||
text_height = int(frame.evaluateJavaScript(js_height).toString())
|
||||
if text_height > height:
|
||||
if self.web_frame.contentsSize().height() > self.page_height:
|
||||
formatted.append(html_text)
|
||||
html_text = u''
|
||||
styled_text = styled_line
|
||||
|
@ -223,7 +223,6 @@ class RenderManager(object):
|
||||
The words to go on the slides.
|
||||
"""
|
||||
log.debug(u'format slide')
|
||||
self.build_text_rectangle(self.themedata)
|
||||
return self.renderer.format_slide(words, line_break)
|
||||
|
||||
def calculate_default(self, screen):
|
||||
|
@ -97,7 +97,7 @@ class ServiceItem(object):
|
||||
self.themedata = None
|
||||
self.main = None
|
||||
self.footer = None
|
||||
self.bg_frame = None
|
||||
self.bg_image_bytes = None
|
||||
|
||||
def _new_item(self):
|
||||
"""
|
||||
@ -145,7 +145,7 @@ class ServiceItem(object):
|
||||
"""
|
||||
log.debug(u'Render called')
|
||||
self._display_frames = []
|
||||
self.bg_frame = None
|
||||
self.bg_image_bytes = None
|
||||
line_break = True
|
||||
if self.is_capable(ItemCapabilities.NoLineBreaks):
|
||||
line_break = False
|
||||
@ -156,7 +156,7 @@ class ServiceItem(object):
|
||||
theme = self.theme
|
||||
self.main, self.footer = \
|
||||
self.render_manager.set_override_theme(theme, useOverride)
|
||||
self.bg_frame = self.render_manager.renderer.bg_frame
|
||||
self.bg_image_bytes = self.render_manager.renderer.bg_image_bytes
|
||||
self.themedata = self.render_manager.renderer._theme
|
||||
for slide in self._raw_frames:
|
||||
before = time.time()
|
||||
|
@ -28,6 +28,7 @@ import re
|
||||
import sys
|
||||
try:
|
||||
import enchant
|
||||
from enchant import DictNotFoundError
|
||||
enchant_available = True
|
||||
except ImportError:
|
||||
enchant_available = False
|
||||
@ -43,7 +44,10 @@ class SpellTextEdit(QtGui.QPlainTextEdit):
|
||||
QtGui.QPlainTextEdit.__init__(self, *args)
|
||||
# Default dictionary based on the current locale.
|
||||
if enchant_available:
|
||||
self.dict = enchant.Dict()
|
||||
try:
|
||||
self.dict = enchant.Dict()
|
||||
except DictNotFoundError:
|
||||
self.dict = enchant.Dict(u'en_US')
|
||||
self.highlighter = Highlighter(self.document())
|
||||
self.highlighter.setDict(self.dict)
|
||||
|
||||
|
@ -199,18 +199,18 @@ class Ui_AboutDialog(object):
|
||||
'Preamble\n'
|
||||
'\n'
|
||||
'The licenses for most software are designed to take away your '
|
||||
'freedom to share and change it. By contrast, the GNU General '
|
||||
'freedom to share and change it. By contrast, the GNU General '
|
||||
'Public License is intended to guarantee your freedom to share '
|
||||
'and change free software--to make sure the software is free for '
|
||||
'all its users. This General Public License applies to most of '
|
||||
'all its users. This General Public License applies to most of '
|
||||
'the Free Software Foundation\'s software and to any other '
|
||||
'program whose authors commit to using it. (Some other Free '
|
||||
'program whose authors commit to using it. (Some other Free '
|
||||
'Software Foundation software is covered by the GNU Lesser '
|
||||
'General Public License instead.) You can apply it to your '
|
||||
'General Public License instead.) You can apply it to your '
|
||||
'programs, too.\n'
|
||||
'\n'
|
||||
'When we speak of free software, we are referring to freedom, not '
|
||||
'price. Our General Public Licenses are designed to make sure '
|
||||
'price. Our General Public Licenses are designed to make sure '
|
||||
'that you have the freedom to distribute copies of free software '
|
||||
'(and charge for this service if you wish), that you receive '
|
||||
'source code or can get it if you want it, that you can change '
|
||||
@ -225,8 +225,8 @@ class Ui_AboutDialog(object):
|
||||
'\n'
|
||||
'For example, if you distribute copies of such a program, whether '
|
||||
'gratis or for a fee, you must give the recipients all the rights '
|
||||
'that you have. You must make sure that they, too, receive or '
|
||||
'can get the source code. And you must show them these terms so '
|
||||
'that you have. You must make sure that they, too, receive or '
|
||||
'can get the source code. And you must show them these terms so '
|
||||
'they know their rights.\n'
|
||||
'\n'
|
||||
'We protect your rights with two steps: (1) copyright the '
|
||||
@ -235,15 +235,15 @@ class Ui_AboutDialog(object):
|
||||
'\n'
|
||||
'Also, for each author\'s protection and ours, we want to make '
|
||||
'certain that everyone understands that there is no warranty for '
|
||||
'this free software. If the software is modified by someone else '
|
||||
'this free software. If the software is modified by someone else '
|
||||
'and passed on, we want its recipients to know that what they '
|
||||
'have is not the original, so that any problems introduced by '
|
||||
'others will not reflect on the original authors\' reputations.\n'
|
||||
'\n'
|
||||
'Finally, any free program is threatened constantly by software '
|
||||
'patents. We wish to avoid the danger that redistributors of a '
|
||||
'patents. We wish to avoid the danger that redistributors of a '
|
||||
'free program will individually obtain patent licenses, in effect '
|
||||
'making the program proprietary. To prevent this, we have made '
|
||||
'making the program proprietary. To prevent this, we have made '
|
||||
'it clear that any patent must be licensed for everyone\'s free '
|
||||
'use or not licensed at all.\n'
|
||||
'\n'
|
||||
@ -255,17 +255,17 @@ class Ui_AboutDialog(object):
|
||||
'\n'
|
||||
'0. This License applies to any program or other work which '
|
||||
'contains a notice placed by the copyright holder saying it may '
|
||||
'be distributed under the terms of this General Public License. '
|
||||
'be distributed under the terms of this General Public License. '
|
||||
'The "Program", below, refers to any such program or work, and a '
|
||||
'"work based on the Program" means either the Program or any '
|
||||
'derivative work under copyright law: that is to say, a work '
|
||||
'containing the Program or a portion of it, either verbatim or '
|
||||
'with modifications and/or translated into another language. '
|
||||
'with modifications and/or translated into another language. '
|
||||
'(Hereinafter, translation is included without limitation in the '
|
||||
'term "modification".) Each licensee is addressed as "you".\n'
|
||||
'term "modification".) Each licensee is addressed as "you".\n'
|
||||
'\n'
|
||||
'Activities other than copying, distribution and modification are '
|
||||
'not covered by this License; they are outside its scope. The '
|
||||
'not covered by this License; they are outside its scope. The '
|
||||
'act of running the Program is not restricted, and the output '
|
||||
'from the Program is covered only if its contents constitute a '
|
||||
'work based on the Program (independent of having been made by '
|
||||
@ -305,17 +305,17 @@ class Ui_AboutDialog(object):
|
||||
'notice that there is no warranty (or else, saying that you '
|
||||
'provide a warranty) and that users may redistribute the program '
|
||||
'under these conditions, and telling the user how to view a copy '
|
||||
'of this License. (Exception: if the Program itself is '
|
||||
'of this License. (Exception: if the Program itself is '
|
||||
'interactive but does not normally print such an announcement, '
|
||||
'your work based on the Program is not required to print an '
|
||||
'announcement.)\n'
|
||||
'\n'
|
||||
'These requirements apply to the modified work as a whole. If '
|
||||
'These requirements apply to the modified work as a whole. If '
|
||||
'identifiable sections of that work are not derived from the '
|
||||
'Program, and can be reasonably considered independent and '
|
||||
'separate works in themselves, then this License, and its terms, '
|
||||
'do not apply to those sections when you distribute them as '
|
||||
'separate works. But when you distribute the same sections as '
|
||||
'separate works. But when you distribute the same sections as '
|
||||
'part of a whole which is a work based on the Program, the '
|
||||
'distribution of the whole must be on the terms of this License, '
|
||||
'whose permissions for other licensees extend to the entire '
|
||||
@ -350,17 +350,17 @@ class Ui_AboutDialog(object):
|
||||
'medium customarily used for software interchange; or,\n'
|
||||
'\n'
|
||||
'c) Accompany it with the information you received as to the '
|
||||
'offer to distribute corresponding source code. (This '
|
||||
'offer to distribute corresponding source code. (This '
|
||||
'alternative is allowed only for noncommercial distribution and '
|
||||
'only if you received the program in object code or executable '
|
||||
'form with such an offer, in accord with Subsection b above.)\n'
|
||||
'\n'
|
||||
'The source code for a work means the preferred form of the work '
|
||||
'for making modifications to it. For an executable work, '
|
||||
'for making modifications to it. For an executable work, '
|
||||
'complete source code means all the source code for all modules '
|
||||
'it contains, plus any associated interface definition files, '
|
||||
'plus the scripts used to control compilation and installation of '
|
||||
'the executable. However, as a special exception, the source '
|
||||
'the executable. However, as a special exception, the source '
|
||||
'code distributed need not include anything that is normally '
|
||||
'distributed (in either source or binary form) with the major '
|
||||
'components (compiler, kernel, and so on) of the operating system '
|
||||
@ -374,7 +374,7 @@ class Ui_AboutDialog(object):
|
||||
'not compelled to copy the source along with the object code.\n'
|
||||
'\n'
|
||||
'4. You may not copy, modify, sublicense, or distribute the '
|
||||
'Program except as expressly provided under this License. Any '
|
||||
'Program except as expressly provided under this License. Any '
|
||||
'attempt otherwise to copy, modify, sublicense or distribute the '
|
||||
'Program is void, and will automatically terminate your rights '
|
||||
'under this License. However, parties who have received copies, '
|
||||
@ -383,10 +383,10 @@ class Ui_AboutDialog(object):
|
||||
'compliance.\n'
|
||||
'\n'
|
||||
'5. You are not required to accept this License, since you have '
|
||||
'not signed it. However, nothing else grants you permission to '
|
||||
'modify or distribute the Program or its derivative works. These '
|
||||
'not signed it. However, nothing else grants you permission to '
|
||||
'modify or distribute the Program or its derivative works. These '
|
||||
'actions are prohibited by law if you do not accept this '
|
||||
'License. Therefore, by modifying or distributing the Program '
|
||||
'License. Therefore, by modifying or distributing the Program '
|
||||
'(or any work based on the Program), you indicate your acceptance '
|
||||
'of this License to do so, and all its terms and conditions for '
|
||||
'copying, distributing or modifying the Program or works based on '
|
||||
@ -395,7 +395,7 @@ class Ui_AboutDialog(object):
|
||||
'6. Each time you redistribute the Program (or any work based on '
|
||||
'the Program), the recipient automatically receives a license '
|
||||
'from the original licensor to copy, distribute or modify the '
|
||||
'Program subject to these terms and conditions. You may not '
|
||||
'Program subject to these terms and conditions. You may not '
|
||||
'impose any further restrictions on the recipients\' exercise of '
|
||||
'the rights granted herein. You are not responsible for enforcing '
|
||||
'compliance by third parties to this License.\n'
|
||||
@ -405,10 +405,10 @@ class Ui_AboutDialog(object):
|
||||
'patent issues), conditions are imposed on you (whether by court '
|
||||
'order, agreement or otherwise) that contradict the conditions of '
|
||||
'this License, they do not excuse you from the conditions of this '
|
||||
'License. If you cannot distribute so as to satisfy '
|
||||
'License. If you cannot distribute so as to satisfy '
|
||||
'simultaneously your obligations under this License and any other '
|
||||
'pertinent obligations, then as a consequence you may not '
|
||||
'distribute the Program at all. For example, if a patent license '
|
||||
'distribute the Program at all. For example, if a patent license '
|
||||
'would not permit royalty-free redistribution of the Program by '
|
||||
'all those who receive copies directly or indirectly through you, '
|
||||
'then the only way you could satisfy both it and this License '
|
||||
@ -423,7 +423,7 @@ class Ui_AboutDialog(object):
|
||||
'any patents or other property right claims or to contest '
|
||||
'validity of any such claims; this section has the sole purpose '
|
||||
'of protecting the integrity of the free software distribution '
|
||||
'system, which is implemented by public license practices. Many '
|
||||
'system, which is implemented by public license practices. Many '
|
||||
'people have made generous contributions to the wide range of '
|
||||
'software distributed through that system in reliance on '
|
||||
'consistent application of that system; it is up to the '
|
||||
@ -439,29 +439,29 @@ class Ui_AboutDialog(object):
|
||||
'interfaces, the original copyright holder who places the Program '
|
||||
'under this License may add an explicit geographical distribution '
|
||||
'limitation excluding those countries, so that distribution is '
|
||||
'permitted only in or among countries not thus excluded. In such '
|
||||
'permitted only in or among countries not thus excluded. In such '
|
||||
'case, this License incorporates the limitation as if written in '
|
||||
'the body of this License.\n'
|
||||
'\n'
|
||||
'9. The Free Software Foundation may publish revised and/or new '
|
||||
'versions of the General Public License from time to time. Such '
|
||||
'versions of the General Public License from time to time. Such '
|
||||
'new versions will be similar in spirit to the present version, '
|
||||
'but may differ in detail to address new problems or concerns.\n'
|
||||
'\n'
|
||||
'Each version is given a distinguishing version number. If the '
|
||||
'Each version is given a distinguishing version number. If the '
|
||||
'Program specifies a version number of this License which applies '
|
||||
'to it and \"any later version\', you have the option of '
|
||||
'to it and "any later version", you have the option of '
|
||||
'following the terms and conditions either of that version or of '
|
||||
'any later version published by the Free Software Foundation. If '
|
||||
'any later version published by the Free Software Foundation. If '
|
||||
'the Program does not specify a version number of this License, '
|
||||
'you may choose any version ever published by the Free Software '
|
||||
'Foundation.\n'
|
||||
'\n'
|
||||
'10. If you wish to incorporate parts of the Program into other '
|
||||
'free programs whose distribution conditions are different, write '
|
||||
'to the author to ask for permission. For software which is '
|
||||
'to the author to ask for permission. For software which is '
|
||||
'copyrighted by the Free Software Foundation, write to the Free '
|
||||
'Software Foundation; we sometimes make exceptions for this. Our '
|
||||
'Software Foundation; we sometimes make exceptions for this. Our '
|
||||
'decision will be guided by the two goals of preserving the free '
|
||||
'status of all derivatives of our free software and of promoting '
|
||||
'the sharing and reuse of software generally.\n'
|
||||
@ -470,12 +470,12 @@ class Ui_AboutDialog(object):
|
||||
'\n'
|
||||
'11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO '
|
||||
'WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE '
|
||||
'LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT '
|
||||
'LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT '
|
||||
'HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT '
|
||||
'WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, '
|
||||
'BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY '
|
||||
'AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE '
|
||||
'QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE '
|
||||
'AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE '
|
||||
'QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE '
|
||||
'PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY '
|
||||
'SERVICING, REPAIR OR CORRECTION.\n'
|
||||
'\n'
|
||||
@ -499,7 +499,7 @@ class Ui_AboutDialog(object):
|
||||
'this is to make it free software which everyone can redistribute '
|
||||
'and change under these terms.\n'
|
||||
'\n'
|
||||
'To do so, attach the following notices to the program. It is '
|
||||
'To do so, attach the following notices to the program. It is '
|
||||
'safest to attach them to the start of each source file to most '
|
||||
'effectively convey the exclusion of warranty; and each file '
|
||||
'should have at least the "copyright" line and a pointer to where '
|
||||
@ -507,7 +507,7 @@ class Ui_AboutDialog(object):
|
||||
'\n'
|
||||
'<one line to give the program\'s name and a brief idea of what '
|
||||
'it does.>\n'
|
||||
'Copyright (C) <year> <name of author>\n'
|
||||
'Copyright (C) <year> <name of author>\n'
|
||||
'\n'
|
||||
'This program is free software; you can redistribute it and/or '
|
||||
'modify it under the terms of the GNU General Public License as '
|
||||
@ -516,7 +516,7 @@ class Ui_AboutDialog(object):
|
||||
'\n'
|
||||
'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 '
|
||||
'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the '
|
||||
'GNU General Public License for more details.\n'
|
||||
'\n'
|
||||
'You should have received a copy of the GNU General Public '
|
||||
@ -537,14 +537,14 @@ class Ui_AboutDialog(object):
|
||||
'under certain conditions; type "show c" for details.\n'
|
||||
'\n'
|
||||
'The hypothetical commands "show w" and "show c" should show '
|
||||
'the appropriate parts of the General Public License. Of course, '
|
||||
'the appropriate parts of the General Public License. Of course, '
|
||||
'the commands you use may be called something other than "show '
|
||||
'w" and "show c"; they could even be mouse-clicks or menu items--'
|
||||
'whatever suits your program.\n'
|
||||
'\n'
|
||||
'You should also get your employer (if you work as a programmer) '
|
||||
'or your school, if any, to sign a "copyright disclaimer" for the '
|
||||
'program, if necessary. Here is a sample; alter the names:\n'
|
||||
'program, if necessary. Here is a sample; alter the names:\n'
|
||||
'\n'
|
||||
'Yoyodyne, Inc., hereby disclaims all copyright interest in the '
|
||||
'program "Gnomovision" (which makes passes at compilers) written '
|
||||
@ -554,9 +554,9 @@ class Ui_AboutDialog(object):
|
||||
'Ty Coon, President of Vice\n'
|
||||
'\n'
|
||||
'This General Public License does not permit incorporating your '
|
||||
'program into proprietary programs. If your program is a '
|
||||
'program into proprietary programs. If your program is a '
|
||||
'subroutine library, you may consider it more useful to permit '
|
||||
'linking proprietary applications with the library. If this is '
|
||||
'linking proprietary applications with the library. If this is '
|
||||
'what you want to do, use the GNU Lesser General Public License '
|
||||
'instead of this License.'))
|
||||
self.aboutNotebook.setTabText(
|
||||
@ -565,3 +565,4 @@ class Ui_AboutDialog(object):
|
||||
self.contributeButton.setText(translate('OpenLP.AboutForm',
|
||||
'Contribute'))
|
||||
self.closeButton.setText(translate('OpenLP.AboutForm', 'Close'))
|
||||
|
||||
|
@ -73,7 +73,7 @@ class Ui_ExceptionDialog(object):
|
||||
|
||||
def retranslateUi(self, exceptionDialog):
|
||||
exceptionDialog.setWindowTitle(
|
||||
translate('OpenLP.ExceptionDialog', 'Error Occured'))
|
||||
translate('OpenLP.ExceptionDialog', 'Error Occurred'))
|
||||
self.messageLabel.setText(translate('OpenLP.ExceptionDialog', 'Oops! '
|
||||
'OpenLP hit a problem, and couldn\'t recover. The text in the box '
|
||||
'below contains information that might be helpful to the OpenLP '
|
||||
|
@ -99,6 +99,7 @@ class MainDisplay(DisplayWidget):
|
||||
self.alertTab = None
|
||||
self.hide_mode = None
|
||||
self.setWindowTitle(u'OpenLP Display')
|
||||
self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;')
|
||||
self.setWindowFlags(QtCore.Qt.FramelessWindowHint |
|
||||
QtCore.Qt.WindowStaysOnTopHint)
|
||||
if self.isLive:
|
||||
@ -116,12 +117,18 @@ class MainDisplay(DisplayWidget):
|
||||
self.screen = self.screens.current
|
||||
self.setVisible(False)
|
||||
self.setGeometry(self.screen[u'size'])
|
||||
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())
|
||||
try:
|
||||
self.webView = QtWebKit.QGraphicsWebView()
|
||||
self.scene = QtGui.QGraphicsScene(self)
|
||||
self.setScene(self.scene)
|
||||
self.scene.addItem(self.webView)
|
||||
self.webView.setGeometry(QtCore.QRectF(0, 0,
|
||||
self.screen[u'size'].width(), self.screen[u'size'].height()))
|
||||
except AttributeError:
|
||||
# QGraphicsWebView a recent addition, so fall back to QWebView
|
||||
self.webView = QtWebKit.QWebView(self)
|
||||
self.webView.setGeometry(0, 0,
|
||||
self.screen[u'size'].width(), self.screen[u'size'].height())
|
||||
self.page = self.webView.page()
|
||||
self.frame = self.page.mainFrame()
|
||||
QtCore.QObject.connect(self.webView,
|
||||
@ -157,7 +164,7 @@ class MainDisplay(DisplayWidget):
|
||||
- splash_image.height()) / 2,
|
||||
splash_image)
|
||||
serviceItem = ServiceItem()
|
||||
serviceItem.bg_frame = initialFrame
|
||||
serviceItem.bg_image_bytes = image_to_byte(initialFrame)
|
||||
self.webView.setHtml(build_html(serviceItem, self.screen,
|
||||
self.parent.alertTab, self.isLive))
|
||||
self.initialFrame = True
|
||||
@ -286,7 +293,7 @@ class MainDisplay(DisplayWidget):
|
||||
"""
|
||||
log.debug(u'video')
|
||||
self.loaded = True
|
||||
js = u'show_video("play", "%s", %s, true);' % \
|
||||
js = u'show_video("init", "%s", %s, true); show_video("play");' % \
|
||||
(videoPath.replace(u'\\', u'\\\\'), str(float(volume)/float(10)))
|
||||
self.frame.evaluateJavaScript(js)
|
||||
return self.preview()
|
||||
@ -303,6 +310,10 @@ class MainDisplay(DisplayWidget):
|
||||
Generates a preview of the image displayed.
|
||||
"""
|
||||
log.debug(u'preview for %s', self.isLive)
|
||||
# We must have a service item to preview
|
||||
if not hasattr(self, u'serviceItem'):
|
||||
return
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
if self.isLive:
|
||||
# Wait for the fade to finish before geting the preview.
|
||||
# Important otherwise preview will have incorrect text if at all !
|
||||
@ -315,6 +326,12 @@ class MainDisplay(DisplayWidget):
|
||||
# Important otherwise first preview will miss the background !
|
||||
while not self.loaded:
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
# if was hidden keep it hidden
|
||||
if self.isLive:
|
||||
self.setVisible(True)
|
||||
# if was hidden keep it hidden
|
||||
if self.hide_mode and self.isLive:
|
||||
self.hideDisplay(self.hide_mode)
|
||||
preview = QtGui.QImage(self.screen[u'size'].width(),
|
||||
self.screen[u'size'].height(),
|
||||
QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
@ -322,9 +339,6 @@ class MainDisplay(DisplayWidget):
|
||||
painter.setRenderHint(QtGui.QPainter.Antialiasing)
|
||||
self.frame.render(painter)
|
||||
painter.end()
|
||||
# Make display show up if in single screen mode
|
||||
if self.isLive:
|
||||
self.setVisible(True)
|
||||
return preview
|
||||
|
||||
def buildHtml(self, serviceItem):
|
||||
@ -336,9 +350,11 @@ class MainDisplay(DisplayWidget):
|
||||
self.loaded = False
|
||||
self.initialFrame = False
|
||||
self.serviceItem = serviceItem
|
||||
html = build_html(self.serviceItem, self.screen, self.parent.alertTab,\
|
||||
html = build_html(self.serviceItem, self.screen, self.parent.alertTab,
|
||||
self.isLive)
|
||||
log.debug(u'buildHtml - pre setHtml')
|
||||
self.webView.setHtml(html)
|
||||
log.debug(u'buildHtml - post setHtml')
|
||||
if serviceItem.foot_text and serviceItem.foot_text:
|
||||
self.footer(serviceItem.foot_text)
|
||||
# if was hidden keep it hidden
|
||||
|
@ -250,7 +250,7 @@ class Ui_MainWindow(object):
|
||||
self.LanguageGroup = QtGui.QActionGroup(MainWindow)
|
||||
qmList = LanguageManager.get_qm_list()
|
||||
savedLanguage = LanguageManager.get_language()
|
||||
self.AutoLanguageItem.setChecked(LanguageManager.AutoLanguage)
|
||||
self.AutoLanguageItem.setChecked(LanguageManager.auto_language)
|
||||
for key in sorted(qmList.keys()):
|
||||
languageItem = QtGui.QAction(MainWindow)
|
||||
languageItem.setObjectName(key)
|
||||
@ -258,7 +258,7 @@ class Ui_MainWindow(object):
|
||||
if qmList[key] == savedLanguage:
|
||||
languageItem.setChecked(True)
|
||||
add_actions(self.LanguageGroup, [languageItem])
|
||||
self.LanguageGroup.setDisabled(LanguageManager.AutoLanguage)
|
||||
self.LanguageGroup.setDisabled(LanguageManager.auto_language)
|
||||
self.ToolsAddToolItem = QtGui.QAction(MainWindow)
|
||||
self.ToolsAddToolItem.setIcon(build_icon(u':/tools/tools_add.png'))
|
||||
self.ToolsAddToolItem.setObjectName(u'ToolsAddToolItem')
|
||||
@ -575,7 +575,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
QtCore.SIGNAL(u'toggled(bool)'), self.setAutoLanguage)
|
||||
self.LanguageGroup.triggered.connect(LanguageManager.set_language)
|
||||
QtCore.QObject.connect(self.ModeDefaultItem,
|
||||
QtCore.SIGNAL(u'triggered()'), self.setViewMode)
|
||||
QtCore.SIGNAL(u'triggered()'), self.onModeDefaultItemClicked)
|
||||
QtCore.QObject.connect(self.ModeSetupItem,
|
||||
QtCore.SIGNAL(u'triggered()'), self.onModeSetupItemClicked)
|
||||
QtCore.QObject.connect(self.ModeLiveItem,
|
||||
@ -640,7 +640,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
|
||||
def setAutoLanguage(self, value):
|
||||
self.LanguageGroup.setDisabled(value)
|
||||
LanguageManager.AutoLanguage = value
|
||||
LanguageManager.auto_language = value
|
||||
LanguageManager.set_language(self.LanguageGroup.checkedAction())
|
||||
|
||||
def versionNotice(self, version):
|
||||
@ -670,6 +670,16 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
self.generalSettingsSection + u'/auto open',
|
||||
QtCore.QVariant(False)).toBool():
|
||||
self.ServiceManagerContents.onLoadService(True)
|
||||
view_mode = QtCore.QSettings().value(u'%s/view mode' % \
|
||||
self.generalSettingsSection, u'default')
|
||||
if view_mode == u'default':
|
||||
self.ModeDefaultItem.setChecked(True)
|
||||
elif view_mode == u'setup':
|
||||
self.setViewMode(True, True, False, True, False)
|
||||
self.ModeSetupItem.setChecked(True)
|
||||
elif view_mode == u'live':
|
||||
self.setViewMode(False, True, False, False, True)
|
||||
self.ModeLiveItem.setChecked(True)
|
||||
|
||||
def blankCheck(self):
|
||||
"""
|
||||
@ -677,8 +687,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
Triggered by delay thread.
|
||||
"""
|
||||
settings = QtCore.QSettings()
|
||||
settings.beginGroup(self.generalSettingsSection)
|
||||
if settings.value(u'screen blank', QtCore.QVariant(False)).toBool():
|
||||
if settings.value(u'%s/screen blank' % self.generalSettingsSection,
|
||||
QtCore.QVariant(False)).toBool():
|
||||
self.LiveController.mainDisplaySetBackground()
|
||||
if settings.value(u'blank warning',
|
||||
QtCore.QVariant(False)).toBool():
|
||||
@ -687,7 +697,6 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
'OpenLP Main Display Blanked'),
|
||||
translate('OpenLP.MainWindow',
|
||||
'The Main Display has been blanked out'))
|
||||
settings.endGroup()
|
||||
|
||||
def onHelpWebSiteClicked(self):
|
||||
"""
|
||||
@ -716,16 +725,31 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
"""
|
||||
self.settingsForm.exec_()
|
||||
|
||||
def onModeDefaultItemClicked(self):
|
||||
"""
|
||||
Put OpenLP into "Default" view mode.
|
||||
"""
|
||||
settings = QtCore.QSettings()
|
||||
settings.setValue(u'%s/view mode' % self.generalSettingsSection,
|
||||
u'default')
|
||||
self.setViewMode(True, True, True, True, True)
|
||||
|
||||
def onModeSetupItemClicked(self):
|
||||
"""
|
||||
Put OpenLP into "Setup" view mode.
|
||||
"""
|
||||
settings = QtCore.QSettings()
|
||||
settings.setValue(u'%s/view mode' % self.generalSettingsSection,
|
||||
u'setup')
|
||||
self.setViewMode(True, True, False, True, False)
|
||||
|
||||
def onModeLiveItemClicked(self):
|
||||
"""
|
||||
Put OpenLP into "Live" view mode.
|
||||
"""
|
||||
settings = QtCore.QSettings()
|
||||
settings.setValue(u'%s/view mode' % self.generalSettingsSection,
|
||||
u'live')
|
||||
self.setViewMode(False, True, False, False, True)
|
||||
|
||||
def setViewMode(self, media=True, service=True, theme=True, preview=True,
|
||||
|
@ -93,6 +93,7 @@ class Ui_PluginViewDialog(object):
|
||||
self.pluginListButtonBox.setStandardButtons(QtGui.QDialogButtonBox.Ok)
|
||||
self.pluginListButtonBox.setObjectName(u'pluginListButtonBox')
|
||||
self.pluginLayout.addWidget(self.pluginListButtonBox)
|
||||
self.versionNumberLabel.setText(u'')
|
||||
self.retranslateUi(pluginViewDialog)
|
||||
QtCore.QObject.connect(self.pluginListButtonBox,
|
||||
QtCore.SIGNAL(u'accepted()'), pluginViewDialog.close)
|
||||
@ -105,8 +106,6 @@ class Ui_PluginViewDialog(object):
|
||||
translate('OpenLP.PluginForm', 'Plugin Details'))
|
||||
self.versionLabel.setText(
|
||||
translate('OpenLP.PluginForm', 'Version:'))
|
||||
self.versionNumberLabel.setText(
|
||||
translate('OpenLP.PluginForm', 'TextLabel'))
|
||||
self.aboutLabel.setText(
|
||||
translate('OpenLP.PluginForm', 'About:'))
|
||||
self.statusLabel.setText(
|
||||
@ -115,3 +114,4 @@ class Ui_PluginViewDialog(object):
|
||||
translate('OpenLP.PluginForm', 'Active'))
|
||||
self.statusComboBox.setItemText(1,
|
||||
translate('OpenLP.PluginForm', 'Inactive'))
|
||||
|
||||
|
@ -279,7 +279,8 @@ class ServiceManager(QtGui.QWidget):
|
||||
self.editAction.setVisible(False)
|
||||
self.maintainAction.setVisible(False)
|
||||
self.notesAction.setVisible(False)
|
||||
if serviceItem[u'service_item'].is_capable(ItemCapabilities.AllowsEdit):
|
||||
if serviceItem[u'service_item'].is_capable(ItemCapabilities.AllowsEdit) \
|
||||
and hasattr(serviceItem[u'service_item'], u'editId'):
|
||||
self.editAction.setVisible(True)
|
||||
if serviceItem[u'service_item']\
|
||||
.is_capable(ItemCapabilities.AllowsMaintain):
|
||||
@ -382,20 +383,20 @@ class ServiceManager(QtGui.QWidget):
|
||||
serviceIterator = QtGui.QTreeWidgetItemIterator(self.serviceManagerList)
|
||||
tempItem = None
|
||||
setLastItem = False
|
||||
while serviceIterator:
|
||||
if serviceIterator.isSelected() and tempItem is None:
|
||||
while serviceIterator.value():
|
||||
if serviceIterator.value().isSelected() and tempItem is None:
|
||||
setLastItem = True
|
||||
serviceIterator.setSelected(False)
|
||||
if serviceIterator.isSelected():
|
||||
#We are on the first record
|
||||
serviceIterator.value().setSelected(False)
|
||||
if serviceIterator.value().isSelected():
|
||||
# We are on the first record
|
||||
if tempItem:
|
||||
tempItem.setSelected(True)
|
||||
serviceIterator.setSelected(False)
|
||||
serviceIterator.value().setSelected(False)
|
||||
else:
|
||||
tempItem = serviceIterator
|
||||
lastItem = serviceIterator
|
||||
++serviceIterator
|
||||
#Top Item was selected so set the last one
|
||||
tempItem = serviceIterator.value()
|
||||
lastItem = serviceIterator.value()
|
||||
serviceIterator += 1
|
||||
# Top Item was selected so set the last one
|
||||
if setLastItem:
|
||||
lastItem.setSelected(True)
|
||||
|
||||
@ -405,16 +406,18 @@ class ServiceManager(QtGui.QWidget):
|
||||
Called by the down arrow
|
||||
"""
|
||||
serviceIterator = QtGui.QTreeWidgetItemIterator(self.serviceManagerList)
|
||||
firstItem = serviceIterator
|
||||
firstItem = None
|
||||
setSelected = False
|
||||
while serviceIterator:
|
||||
while serviceIterator.value():
|
||||
if not firstItem:
|
||||
firstItem = serviceIterator.value()
|
||||
if setSelected:
|
||||
setSelected = False
|
||||
serviceIterator.setSelected(True)
|
||||
elif serviceIterator.isSelected():
|
||||
serviceIterator.setSelected(False)
|
||||
serviceIterator.value().setSelected(True)
|
||||
elif serviceIterator.value() and serviceIterator.value().isSelected():
|
||||
serviceIterator.value().setSelected(False)
|
||||
setSelected = True
|
||||
++serviceIterator
|
||||
serviceIterator += 1
|
||||
if setSelected:
|
||||
firstItem.setSelected(True)
|
||||
|
||||
@ -556,7 +559,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
QtCore.QVariant(item[u'order']))
|
||||
for count, frame in enumerate(serviceitem.get_frames()):
|
||||
treewidgetitem1 = QtGui.QTreeWidgetItem(treewidgetitem)
|
||||
text = frame[u'title']
|
||||
text = frame[u'title'].replace(u'\n', u' ')
|
||||
treewidgetitem1.setText(0, text[:40])
|
||||
treewidgetitem1.setData(0, QtCore.Qt.UserRole,
|
||||
QtCore.QVariant(count))
|
||||
@ -632,6 +635,8 @@ class ServiceManager(QtGui.QWidget):
|
||||
|
||||
def onLoadService(self, lastService=False):
|
||||
if lastService:
|
||||
if not self.parent.recentFiles:
|
||||
return
|
||||
filename = self.parent.recentFiles[0]
|
||||
else:
|
||||
filename = QtGui.QFileDialog.getOpenFileName(
|
||||
@ -881,7 +886,8 @@ class ServiceManager(QtGui.QWidget):
|
||||
QtGui.QMessageBox.critical(self,
|
||||
translate('OpenLP.ServiceManager', 'Missing Display Handler'),
|
||||
translate('OpenLP.ServiceManager', 'Your item cannot be '
|
||||
'displayed as there is no handler to display it'))
|
||||
'displayed as the plugin required to display it is missing '
|
||||
'or inactive'))
|
||||
|
||||
def remoteEdit(self):
|
||||
"""
|
||||
|
@ -209,7 +209,8 @@ class SlideController(QtGui.QWidget):
|
||||
self.Toolbar.addToolbarSeparator(u'Close Separator')
|
||||
self.Toolbar.addToolbarButton(
|
||||
u'Edit Song', u':/general/general_edit.png',
|
||||
translate('OpenLP.SlideController', 'Edit and re-preview Song'),
|
||||
translate('OpenLP.SlideController',
|
||||
'Edit and reload song preview'),
|
||||
self.onEditSong)
|
||||
if isLive:
|
||||
self.Toolbar.addToolbarSeparator(u'Loop Separator')
|
||||
@ -269,11 +270,11 @@ class SlideController(QtGui.QWidget):
|
||||
if isLive:
|
||||
self.SongMenu = QtGui.QToolButton(self.Toolbar)
|
||||
self.SongMenu.setText(translate('OpenLP.SlideController',
|
||||
'Go to'))
|
||||
'Go To'))
|
||||
self.SongMenu.setPopupMode(QtGui.QToolButton.InstantPopup)
|
||||
self.Toolbar.addToolbarWidget(u'Song Menu', self.SongMenu)
|
||||
self.SongMenu.setMenu(QtGui.QMenu(
|
||||
translate('OpenLP.SlideController', 'Go to'),
|
||||
translate('OpenLP.SlideController', 'Go To'),
|
||||
self.Toolbar))
|
||||
self.Toolbar.makeWidgetsInvisible([u'Song Menu'])
|
||||
# Screen preview area
|
||||
@ -636,9 +637,9 @@ class SlideController(QtGui.QWidget):
|
||||
"""
|
||||
if not self.serviceItem:
|
||||
return
|
||||
Receiver.send_message(u'%s_first' % self.serviceItem.name.lower(),
|
||||
[self.serviceItem, self.isLive])
|
||||
if self.serviceItem.is_command():
|
||||
Receiver.send_message(u'%s_first' % self.serviceItem.name.lower(),
|
||||
[self.serviceItem, self.isLive])
|
||||
self.updatePreview()
|
||||
else:
|
||||
self.PreviewListWidget.selectRow(0)
|
||||
@ -651,9 +652,9 @@ class SlideController(QtGui.QWidget):
|
||||
index = int(message[0])
|
||||
if not self.serviceItem:
|
||||
return
|
||||
Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
|
||||
[self.serviceItem, self.isLive, index])
|
||||
if self.serviceItem.is_command():
|
||||
Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
|
||||
[self.serviceItem, self.isLive, index])
|
||||
self.updatePreview()
|
||||
else:
|
||||
self.PreviewListWidget.selectRow(index)
|
||||
@ -768,9 +769,9 @@ class SlideController(QtGui.QWidget):
|
||||
row = self.PreviewListWidget.currentRow()
|
||||
self.selectedRow = 0
|
||||
if row > -1 and row < self.PreviewListWidget.rowCount():
|
||||
Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
|
||||
[self.serviceItem, self.isLive, row])
|
||||
if self.serviceItem.is_command() and self.isLive:
|
||||
Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
|
||||
[self.serviceItem, self.isLive, row])
|
||||
self.updatePreview()
|
||||
else:
|
||||
frame, raw_html = self.serviceItem.get_rendered_frame(row)
|
||||
|
@ -242,14 +242,14 @@ class ThemeManager(QtGui.QWidget):
|
||||
QtGui.QMessageBox.critical(self,
|
||||
translate('OpenLP.ThemeManager', 'Error'),
|
||||
unicode(translate('OpenLP.ThemeManager',
|
||||
'Theme %s is use in %s plugin.')) % \
|
||||
'Theme %s is used in the %s plugin.')) % \
|
||||
(theme, plugin.name))
|
||||
return
|
||||
if unicode(self.serviceComboBox.currentText()) == theme:
|
||||
QtGui.QMessageBox.critical(self,
|
||||
translate('OpenLP.ThemeManager', 'Error'),
|
||||
unicode(translate('OpenLP.ThemeManager',
|
||||
'Theme %s is use by the service manager.')) % theme)
|
||||
'Theme %s is used by the service manager.')) % theme)
|
||||
return
|
||||
row = self.themeListWidget.row(item)
|
||||
self.themeListWidget.takeItem(row)
|
||||
@ -573,7 +573,7 @@ class ThemeManager(QtGui.QWidget):
|
||||
translate('OpenLP.ThemeManager', 'Theme Exists'),
|
||||
translate('OpenLP.ThemeManager',
|
||||
'A theme with this name already '
|
||||
'exists. Would you like to overwrite it?'),
|
||||
'exists. Would you like to overwrite it?'),
|
||||
(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
|
||||
QtGui.QMessageBox.No)
|
||||
if self.saveThemeName != u'':
|
||||
|
@ -102,6 +102,7 @@ class AppLocation(object):
|
||||
PluginsDir = 4
|
||||
VersionDir = 5
|
||||
CacheDir = 6
|
||||
LanguageDir = 7
|
||||
|
||||
@staticmethod
|
||||
def get_directory(dir_type=1):
|
||||
@ -112,7 +113,11 @@ class AppLocation(object):
|
||||
The directory type you want, for instance the data directory.
|
||||
"""
|
||||
if dir_type == AppLocation.AppDir:
|
||||
return os.path.abspath(os.path.split(sys.argv[0])[0])
|
||||
if hasattr(sys, u'frozen') and sys.frozen == 1:
|
||||
app_path = os.path.abspath(os.path.split(sys.argv[0])[0])
|
||||
else:
|
||||
app_path = os.path.split(openlp.__file__)[0]
|
||||
return app_path
|
||||
elif dir_type == AppLocation.ConfigDir:
|
||||
if sys.platform == u'win32':
|
||||
path = os.path.join(os.getenv(u'APPDATA'), u'openlp')
|
||||
@ -169,6 +174,13 @@ class AppLocation(object):
|
||||
except ImportError:
|
||||
path = os.path.join(os.getenv(u'HOME'), u'.openlp')
|
||||
return path
|
||||
if dir_type == AppLocation.LanguageDir:
|
||||
if hasattr(sys, u'frozen') and sys.frozen == 1:
|
||||
app_path = os.path.abspath(os.path.split(sys.argv[0])[0])
|
||||
else:
|
||||
app_path = os.path.split(openlp.__file__)[0]
|
||||
return os.path.join(app_path, u'i18n')
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_data_path():
|
||||
|
@ -35,14 +35,14 @@ from PyQt4 import QtCore, QtGui
|
||||
from openlp.core.utils import AppLocation
|
||||
from openlp.core.lib import translate
|
||||
|
||||
log = logging.getLogger()
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class LanguageManager(object):
|
||||
"""
|
||||
Helper for Language selection
|
||||
"""
|
||||
__qmList__ = None
|
||||
AutoLanguage = False
|
||||
__qm_list__ = {}
|
||||
auto_language = False
|
||||
|
||||
@staticmethod
|
||||
def get_translator(language):
|
||||
@ -52,12 +52,11 @@ class LanguageManager(object):
|
||||
``language``
|
||||
The language to load into the translator
|
||||
"""
|
||||
if LanguageManager.AutoLanguage:
|
||||
if LanguageManager.auto_language:
|
||||
language = QtCore.QLocale.system().name()
|
||||
lang_path = AppLocation.get_directory(AppLocation.AppDir)
|
||||
lang_path = os.path.join(lang_path, u'resources', u'i18n')
|
||||
lang_path = AppLocation.get_directory(AppLocation.LanguageDir)
|
||||
app_translator = QtCore.QTranslator()
|
||||
if app_translator.load("openlp_" + language, lang_path):
|
||||
if app_translator.load(language, lang_path):
|
||||
return app_translator
|
||||
|
||||
@staticmethod
|
||||
@ -65,8 +64,8 @@ class LanguageManager(object):
|
||||
"""
|
||||
Find all available language files in this OpenLP install
|
||||
"""
|
||||
trans_dir = AppLocation.get_directory(AppLocation.AppDir)
|
||||
trans_dir = QtCore.QDir(os.path.join(trans_dir, u'resources', u'i18n'))
|
||||
trans_dir = QtCore.QDir(AppLocation.get_directory(
|
||||
AppLocation.LanguageDir))
|
||||
file_names = trans_dir.entryList(QtCore.QStringList("*.qm"),
|
||||
QtCore.QDir.Files, QtCore.QDir.Name)
|
||||
for name in file_names:
|
||||
@ -96,7 +95,7 @@ class LanguageManager(object):
|
||||
log.info(u'Language file: \'%s\' Loaded from conf file' % language)
|
||||
reg_ex = QtCore.QRegExp("^\[(.*)\]")
|
||||
if reg_ex.exactMatch(language):
|
||||
LanguageManager.AutoLanguage = True
|
||||
LanguageManager.auto_language = True
|
||||
language = reg_ex.cap(1)
|
||||
return language
|
||||
|
||||
@ -108,9 +107,12 @@ class LanguageManager(object):
|
||||
``action``
|
||||
The language menu option
|
||||
"""
|
||||
action_name = u'%s' % action.objectName()
|
||||
if action is None:
|
||||
action_name = u'en'
|
||||
else:
|
||||
action_name = u'%s' % action.objectName()
|
||||
qm_list = LanguageManager.get_qm_list()
|
||||
if LanguageManager.AutoLanguage:
|
||||
if LanguageManager.auto_language:
|
||||
language = u'[%s]' % qm_list[action_name]
|
||||
else:
|
||||
language = u'%s' % qm_list[action_name]
|
||||
@ -127,20 +129,18 @@ class LanguageManager(object):
|
||||
"""
|
||||
Initialise the list of available translations
|
||||
"""
|
||||
LanguageManager.__qmList__ = {}
|
||||
LanguageManager.__qm_list__ = {}
|
||||
qm_files = LanguageManager.find_qm_files()
|
||||
for i, qmf in enumerate(qm_files):
|
||||
reg_ex = QtCore.QRegExp("^.*openlp_(.*).qm")
|
||||
if reg_ex.exactMatch(qmf):
|
||||
lang_name = reg_ex.cap(1)
|
||||
LanguageManager.__qmList__[u'%#2i %s' % (i+1,
|
||||
LanguageManager.language_name(qmf))] = lang_name
|
||||
for counter, qmf in enumerate(qm_files):
|
||||
name = unicode(qmf).split(u'.')[0]
|
||||
LanguageManager.__qm_list__[u'%#2i %s' % (counter + 1,
|
||||
LanguageManager.language_name(qmf))] = name
|
||||
|
||||
@staticmethod
|
||||
def get_qm_list():
|
||||
"""
|
||||
Return the list of available translations
|
||||
"""
|
||||
if LanguageManager.__qmList__ is None:
|
||||
if not LanguageManager.__qm_list__:
|
||||
LanguageManager.init_qm_list()
|
||||
return LanguageManager.__qmList__
|
||||
return LanguageManager.__qm_list__
|
||||
|
@ -40,7 +40,7 @@ class AlertsPlugin(Plugin):
|
||||
log.info(u'Alerts Plugin loaded')
|
||||
|
||||
def __init__(self, plugin_helpers):
|
||||
Plugin.__init__(self, u'Alerts', u'1.9.2', plugin_helpers)
|
||||
Plugin.__init__(self, u'Alerts', u'1.9.3', plugin_helpers)
|
||||
self.weight = -3
|
||||
self.icon = build_icon(u':/plugins/plugin_alerts.png')
|
||||
self.alertsmanager = AlertsManager(self)
|
||||
|
@ -37,7 +37,7 @@ class BiblePlugin(Plugin):
|
||||
log.info(u'Bible Plugin loaded')
|
||||
|
||||
def __init__(self, plugin_helpers):
|
||||
Plugin.__init__(self, u'Bibles', u'1.9.2', plugin_helpers)
|
||||
Plugin.__init__(self, u'Bibles', u'1.9.3', plugin_helpers)
|
||||
self.weight = -9
|
||||
self.icon_path = u':/plugins/plugin_bibles.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
|
@ -126,29 +126,29 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
|
||||
if self.field(u'osis_location').toString() == u'':
|
||||
QtGui.QMessageBox.critical(self,
|
||||
translate('BiblesPlugin.ImportWizardForm',
|
||||
'Invalid Bible Location'),
|
||||
'Invalid Bible Location'),
|
||||
translate('BiblesPlugin.ImportWizardForm',
|
||||
'You need to specify a file to import your '
|
||||
'Bible from.'))
|
||||
'You need to specify a file to import your '
|
||||
'Bible from.'))
|
||||
self.OSISLocationEdit.setFocus()
|
||||
return False
|
||||
elif self.field(u'source_format').toInt()[0] == BibleFormat.CSV:
|
||||
if self.field(u'csv_booksfile').toString() == u'':
|
||||
QtGui.QMessageBox.critical(self,
|
||||
translate('BiblesPlugin.ImportWizardForm',
|
||||
'Invalid Books File'),
|
||||
'Invalid Books File'),
|
||||
translate('BiblesPlugin.ImportWizardForm',
|
||||
'You need to specify a file with books of '
|
||||
'the Bible to use in the import.'))
|
||||
'You need to specify a file with books of '
|
||||
'the Bible to use in the import.'))
|
||||
self.BooksLocationEdit.setFocus()
|
||||
return False
|
||||
elif self.field(u'csv_versefile').toString() == u'':
|
||||
QtGui.QMessageBox.critical(self,
|
||||
translate('BiblesPlugin.ImportWizardForm',
|
||||
'Invalid Verse File'),
|
||||
'Invalid Verse File'),
|
||||
translate('BiblesPlugin.ImportWizardForm',
|
||||
'You need to specify a file of Bible '
|
||||
'verses to import.'))
|
||||
'You need to specify a file of Bible '
|
||||
'verses to import.'))
|
||||
self.CsvVerseLocationEdit.setFocus()
|
||||
return False
|
||||
elif self.field(u'source_format').toInt()[0] == \
|
||||
@ -156,10 +156,10 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
|
||||
if self.field(u'opensong_file').toString() == u'':
|
||||
QtGui.QMessageBox.critical(self,
|
||||
translate('BiblesPlugin.ImportWizardForm',
|
||||
'Invalid OpenSong Bible'),
|
||||
'Invalid OpenSong Bible'),
|
||||
translate('BiblesPlugin.ImportWizardForm',
|
||||
'You need to specify an OpenSong Bible '
|
||||
'file to import.'))
|
||||
'You need to specify an OpenSong Bible '
|
||||
'file to import.'))
|
||||
self.OpenSongFileEdit.setFocus()
|
||||
return False
|
||||
return True
|
||||
@ -171,29 +171,26 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
|
||||
if license_version == u'':
|
||||
QtGui.QMessageBox.critical(self,
|
||||
translate('BiblesPlugin.ImportWizardForm',
|
||||
'Empty Version Name'),
|
||||
'Empty Version Name'),
|
||||
translate('BiblesPlugin.ImportWizardForm',
|
||||
'You need to specify a version name for your '
|
||||
'Bible.'))
|
||||
'You need to specify a version name for your Bible.'))
|
||||
self.VersionNameEdit.setFocus()
|
||||
return False
|
||||
elif license_copyright == u'':
|
||||
QtGui.QMessageBox.critical(self,
|
||||
translate('BiblesPlugin.ImportWizardForm',
|
||||
'Empty Copyright'),
|
||||
'Empty Copyright'),
|
||||
translate('BiblesPlugin.ImportWizardForm',
|
||||
'You need to set a copyright for your Bible! '
|
||||
'Bibles in the Public Domain need to be marked as '
|
||||
'such.'))
|
||||
'You need to set a copyright for your Bible. '
|
||||
'Bibles in the Public Domain need to be marked as such.'))
|
||||
self.CopyrightEdit.setFocus()
|
||||
return False
|
||||
elif self.manager.exists(license_version):
|
||||
QtGui.QMessageBox.critical(self,
|
||||
translate('BiblesPlugin.ImportWizardForm', 'Bible Exists'),
|
||||
translate('BiblesPlugin.ImportWizardForm',
|
||||
'Bible Exists'),
|
||||
translate('BiblesPlugin.ImportWizardForm',
|
||||
'This Bible already exists! Please import '
|
||||
'a different Bible or first delete the existing one.'))
|
||||
'This Bible already exists. Please import '
|
||||
'a different Bible or first delete the existing one.'))
|
||||
self.VersionNameEdit.setFocus()
|
||||
return False
|
||||
return True
|
||||
@ -290,6 +287,9 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
|
||||
def setDefaults(self):
|
||||
settings = QtCore.QSettings()
|
||||
settings.beginGroup(self.bibleplugin.settingsSection)
|
||||
self.restart()
|
||||
self.finishButton.setVisible(False)
|
||||
self.cancelButton.setVisible(True)
|
||||
self.setField(u'source_format', QtCore.QVariant(0))
|
||||
self.setField(u'osis_location', QtCore.QVariant(''))
|
||||
self.setField(u'csv_booksfile', QtCore.QVariant(''))
|
||||
@ -434,18 +434,16 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
|
||||
unicode(self.field(u'proxy_username').toString()),
|
||||
proxy_password=unicode(self.field(u'proxy_password').toString())
|
||||
)
|
||||
success = importer.do_import()
|
||||
if success:
|
||||
if importer.do_import():
|
||||
self.manager.save_meta_data(license_version, license_version,
|
||||
license_copyright, license_permission)
|
||||
self.manager.reload_bibles()
|
||||
self.ImportProgressLabel.setText(
|
||||
translate('BiblesPlugin.ImportWizardForm',
|
||||
'Finished import.'))
|
||||
translate('BiblesPlugin.ImportWizardForm', 'Finished import.'))
|
||||
else:
|
||||
self.ImportProgressLabel.setText(
|
||||
translate('BiblesPlugin.ImportWizardForm',
|
||||
'Your Bible import failed.'))
|
||||
'Your Bible import failed.'))
|
||||
delete_database(self.bibleplugin.settingsSection, importer.file)
|
||||
|
||||
def postImport(self):
|
||||
|
@ -353,7 +353,7 @@ class BibleDB(QtCore.QObject, Manager):
|
||||
QtGui.QMessageBox.information(self.bible_plugin.mediaItem,
|
||||
translate('BiblesPlugin.BibleDB', 'Book not found'),
|
||||
translate('BiblesPlugin.BibleDB', 'The book you requested '
|
||||
'could not be found in this bible. Please check your '
|
||||
'could not be found in this bible. Please check your '
|
||||
'spelling and that this is a complete bible not just '
|
||||
'one testament.'))
|
||||
return verse_list
|
||||
|
@ -246,7 +246,7 @@ class BibleManager(object):
|
||||
translate('BiblesPlugin.BibleManager',
|
||||
'Scripture Reference Error'),
|
||||
translate('BiblesPlugin.BibleManager', 'Your scripture '
|
||||
'reference is either not supported by OpenLP or invalid. '
|
||||
'reference is either not supported by OpenLP or is invalid. '
|
||||
'Please make sure your reference conforms to one of the '
|
||||
'following patterns:\n\n'
|
||||
'Book Chapter\n'
|
||||
|
@ -65,12 +65,6 @@ class BibleMediaItem(MediaManagerItem):
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'bibles_load_list'), self.reloadBibles)
|
||||
|
||||
def _decodeQtObject(self, listobj, key):
|
||||
obj = listobj[QtCore.QString(key)]
|
||||
if isinstance(obj, QtCore.QVariant):
|
||||
obj = obj.toPyObject()
|
||||
return unicode(obj)
|
||||
|
||||
def requiredIcons(self):
|
||||
MediaManagerItem.requiredIcons(self)
|
||||
self.hasImportIcon = True
|
||||
@ -376,9 +370,6 @@ class BibleMediaItem(MediaManagerItem):
|
||||
def onSearchProgressShow(self):
|
||||
self.SearchProgress.setVisible(True)
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
#self.SearchProgress.setMinimum(0)
|
||||
#self.SearchProgress.setMaximum(2)
|
||||
#self.SearchProgress.setValue(1)
|
||||
|
||||
def onSearchProgressHide(self):
|
||||
self.SearchProgress.setVisible(False)
|
||||
@ -408,18 +399,28 @@ class BibleMediaItem(MediaManagerItem):
|
||||
self.reloadBibles()
|
||||
|
||||
def onAdvancedFromVerse(self):
|
||||
frm = self.AdvancedFromVerse.currentText()
|
||||
self.adjustComboBox(frm, self.verses, self.AdvancedToVerse)
|
||||
|
||||
def onAdvancedToChapter(self):
|
||||
frm = unicode(self.AdvancedFromChapter.currentText())
|
||||
to = unicode(self.AdvancedToChapter.currentText())
|
||||
if frm != to:
|
||||
frm = int(self.AdvancedFromVerse.currentText())
|
||||
chapter_frm = int(self.AdvancedFromChapter.currentText())
|
||||
chapter_to = int(self.AdvancedToChapter.currentText())
|
||||
if chapter_frm == chapter_to:
|
||||
bible = unicode(self.AdvancedVersionComboBox.currentText())
|
||||
book = unicode(self.AdvancedBookComboBox.currentText())
|
||||
# get the verse count for new chapter
|
||||
verses = self.parent.manager.get_verse_count(bible, book, int(to))
|
||||
verses = self.parent.manager.get_verse_count(bible, book, chapter_to)
|
||||
self.adjustComboBox(frm, verses, self.AdvancedToVerse)
|
||||
|
||||
def onAdvancedToChapter(self):
|
||||
chapter_frm = int(self.AdvancedFromChapter.currentText())
|
||||
chapter_to = int(self.AdvancedToChapter.currentText())
|
||||
bible = unicode(self.AdvancedVersionComboBox.currentText())
|
||||
book = unicode(self.AdvancedBookComboBox.currentText())
|
||||
verses = self.parent.manager.get_verse_count(bible, book, chapter_to)
|
||||
if chapter_frm != chapter_to:
|
||||
self.adjustComboBox(1, verses, self.AdvancedToVerse)
|
||||
else:
|
||||
frm = int(self.AdvancedFromVerse.currentText())
|
||||
to = int(self.AdvancedToVerse.currentText())
|
||||
if to < frm:
|
||||
self.adjustComboBox(frm, verses, self.AdvancedToVerse)
|
||||
|
||||
def onAdvancedSearchButton(self):
|
||||
log.debug(u'Advanced Search Button pressed')
|
||||
@ -438,32 +439,72 @@ class BibleMediaItem(MediaManagerItem):
|
||||
dual_bible, versetext)
|
||||
if self.ClearAdvancedSearchComboBox.currentIndex() == 0:
|
||||
self.listView.clear()
|
||||
self.displayResults(bible, dual_bible)
|
||||
if self.listView.count() != 0:
|
||||
bitem = self.listView.item(0)
|
||||
item_dual_bible = self._decodeQtObject(bitem, 'dual_bible')
|
||||
if item_dual_bible and dual_bible or not item_dual_bible and \
|
||||
not dual_bible:
|
||||
self.displayResults(bible, dual_bible)
|
||||
elif QtGui.QMessageBox.critical(self,
|
||||
translate('BiblePlugin.MediaItem', 'Error'),
|
||||
translate('BiblePlugin.MediaItem', 'You cannot combine single '
|
||||
'and dual bible verses. Do you want to delete your search '
|
||||
'results and start a new search?'),
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
|
||||
QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.Yes:
|
||||
self.listView.clear()
|
||||
self.displayResults(bible, dual_bible)
|
||||
else:
|
||||
self.displayResults(bible, dual_bible)
|
||||
|
||||
def onAdvancedFromChapter(self):
|
||||
bible = unicode(self.AdvancedVersionComboBox.currentText())
|
||||
book = unicode(self.AdvancedBookComboBox.currentText())
|
||||
cf = int(self.AdvancedFromChapter.currentText())
|
||||
self.adjustComboBox(cf, self.chapters_from, self.AdvancedToChapter)
|
||||
# get the verse count for new chapter
|
||||
vse = self.parent.manager.get_verse_count(bible, book, cf)
|
||||
self.adjustComboBox(1, vse, self.AdvancedFromVerse)
|
||||
self.adjustComboBox(1, vse, self.AdvancedToVerse)
|
||||
chapter_frm = int(self.AdvancedFromChapter.currentText())
|
||||
self.adjustComboBox(chapter_frm, self.chapters_from,
|
||||
self.AdvancedToChapter)
|
||||
verse = self.parent.manager.get_verse_count(bible, book, chapter_frm)
|
||||
self.adjustComboBox(1, verse, self.AdvancedToVerse)
|
||||
self.adjustComboBox(1, verse, self.AdvancedFromVerse)
|
||||
|
||||
def onQuickSearchButton(self):
|
||||
log.debug(u'Quick Search Button pressed')
|
||||
bible = unicode(self.QuickVersionComboBox.currentText())
|
||||
dual_bible = unicode(self.QuickSecondBibleComboBox.currentText())
|
||||
text = unicode(self.QuickSearchEdit.text())
|
||||
if self.ClearQuickSearchComboBox.currentIndex() == 0:
|
||||
self.listView.clear()
|
||||
self.search_results = self.parent.manager.get_verses(bible, text)
|
||||
if dual_bible:
|
||||
self.dual_search_results = self.parent.manager.get_verses(
|
||||
dual_bible, text)
|
||||
if self.search_results:
|
||||
if self.ClearQuickSearchComboBox.currentIndex() == 0:
|
||||
self.listView.clear()
|
||||
if self.listView.count() != 0 and self.search_results:
|
||||
bitem = self.listView.item(0)
|
||||
item_dual_bible = self._decodeQtObject(bitem, 'dual_bible')
|
||||
if item_dual_bible and dual_bible or not item_dual_bible and \
|
||||
not dual_bible:
|
||||
self.displayResults(bible, dual_bible)
|
||||
elif QtGui.QMessageBox.critical(self,
|
||||
translate('BiblePlugin.MediaItem', 'Error'),
|
||||
translate('BiblePlugin.MediaItem', 'You cannot combine single '
|
||||
'and dual bible verses. Do you want to delete your search '
|
||||
'results and start a new search?'),
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
|
||||
QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.Yes:
|
||||
self.listView.clear()
|
||||
self.displayResults(bible, dual_bible)
|
||||
elif self.search_results:
|
||||
self.displayResults(bible, dual_bible)
|
||||
|
||||
def _decodeQtObject(self, bitem, key):
|
||||
reference = bitem.data(QtCore.Qt.UserRole)
|
||||
if isinstance(reference, QtCore.QVariant):
|
||||
reference = reference.toPyObject()
|
||||
obj = reference[QtCore.QString(key)]
|
||||
if isinstance(obj, QtCore.QVariant):
|
||||
obj = obj.toPyObject()
|
||||
return unicode(obj)
|
||||
|
||||
def generateSlideData(self, service_item, item=None):
|
||||
"""
|
||||
Generates and formats the slides for the service item as well as the
|
||||
@ -473,67 +514,40 @@ class BibleMediaItem(MediaManagerItem):
|
||||
items = self.listView.selectedIndexes()
|
||||
if len(items) == 0:
|
||||
return False
|
||||
has_dual_bible = False
|
||||
bible_text = u''
|
||||
old_chapter = u''
|
||||
raw_footer = []
|
||||
raw_slides = []
|
||||
raw_title = []
|
||||
first_item = True
|
||||
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())
|
||||
reference = bitem.data(QtCore.Qt.UserRole)
|
||||
if isinstance(reference, QtCore.QVariant):
|
||||
reference = reference.toPyObject()
|
||||
book = self._decodeQtObject(reference, 'book')
|
||||
chapter = self._decodeQtObject(reference, 'chapter')
|
||||
verse = self._decodeQtObject(reference, 'verse')
|
||||
bible = self._decodeQtObject(reference, 'bible')
|
||||
version = self._decodeQtObject(reference, 'version')
|
||||
copyright = self._decodeQtObject(reference, 'copyright')
|
||||
permission = self._decodeQtObject(reference, 'permission')
|
||||
text = self._decodeQtObject(reference, 'text')
|
||||
dual_bible = self._decodeQtObject(reference, 'dual_bible')
|
||||
if dual_bible:
|
||||
dual_version = self._decodeQtObject(reference,
|
||||
'dual_version')
|
||||
dual_copyright = self._decodeQtObject(reference,
|
||||
'dual_copyright')
|
||||
dual_permission = self._decodeQtObject(reference,
|
||||
'dual_permission')
|
||||
dual_text = self._decodeQtObject(reference, 'dual_text')
|
||||
book = self._decodeQtObject(bitem, 'book')
|
||||
chapter = int(self._decodeQtObject(bitem, 'chapter'))
|
||||
verse = int(self._decodeQtObject(bitem, 'verse'))
|
||||
bible = self._decodeQtObject(bitem, 'bible')
|
||||
version = self._decodeQtObject(bitem, 'version')
|
||||
copyright = self._decodeQtObject(bitem, 'copyright')
|
||||
permission = self._decodeQtObject(bitem, 'permission')
|
||||
text = self._decodeQtObject(bitem, 'text')
|
||||
dual_bible = self._decodeQtObject(bitem, 'dual_bible')
|
||||
dual_version = self._decodeQtObject(bitem, 'dual_version')
|
||||
dual_copyright = self._decodeQtObject(bitem, 'dual_copyright')
|
||||
dual_permission = self._decodeQtObject(bitem, 'dual_permission')
|
||||
dual_text = self._decodeQtObject(bitem, 'dual_text')
|
||||
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 %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 dual_bible:
|
||||
footer = u'%s (%s %s %s)' % (book, dual_version, dual_copyright,
|
||||
dual_permission)
|
||||
if footer not in raw_footer:
|
||||
raw_footer.append(footer)
|
||||
bible_text = u'%s %s\n\n%s %s' % (verse_text, text, verse_text,
|
||||
dual_text)
|
||||
raw_slides.append(bible_text)
|
||||
bible_text = u''
|
||||
# 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)
|
||||
@ -545,29 +559,31 @@ class BibleMediaItem(MediaManagerItem):
|
||||
# We have to be 'Continuous'.
|
||||
else:
|
||||
bible_text = u'%s %s %s\n' % (bible_text, verse_text, text)
|
||||
if first_item:
|
||||
start_item = item
|
||||
first_item = False
|
||||
elif self.checkTitle(item, old_item):
|
||||
raw_title.append(self.formatTitle(start_item, old_item))
|
||||
start_item = item
|
||||
old_item = item
|
||||
old_chapter = chapter
|
||||
raw_title.append(self.formatTitle(start_item, item))
|
||||
# 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
|
||||
if self.parent.settings_tab.layout_style == 2 and not dual_bible:
|
||||
# Split the line but do not replace line breaks in renderer.
|
||||
service_item.add_capability(ItemCapabilities.NoLineBreaks)
|
||||
service_item.add_capability(ItemCapabilities.AllowsPreview)
|
||||
service_item.add_capability(ItemCapabilities.AllowsLoop)
|
||||
service_item.add_capability(ItemCapabilities.AllowsAdditions)
|
||||
# Service Item: Title
|
||||
if not service_item.title:
|
||||
if dual_bible:
|
||||
service_item.title = u'%s (%s, %s) %s' % (book, version,
|
||||
dual_version, verse_text)
|
||||
for title in raw_title:
|
||||
if not service_item.title:
|
||||
service_item.title = title
|
||||
else:
|
||||
service_item.title = u'%s (%s) %s' % (book, version, verse_text)
|
||||
elif service_item.title.find(
|
||||
translate('BiblesPlugin.MediaItem', 'etc')) == -1:
|
||||
service_item.title = u'%s, %s' % (service_item.title,
|
||||
translate('BiblesPlugin.MediaItem', 'etc'))
|
||||
service_item.title += u', ' + title
|
||||
# Service Item: Theme
|
||||
if len(self.parent.settings_tab.bible_theme) == 0:
|
||||
service_item.theme = None
|
||||
@ -582,12 +598,80 @@ class BibleMediaItem(MediaManagerItem):
|
||||
service_item.raw_footer = raw_footer
|
||||
return True
|
||||
|
||||
def formatTitle(self, start_item, old_item):
|
||||
"""
|
||||
This methode is called, when we have to change the title, because
|
||||
we are at the end of a verse range. E. g. if we want to add
|
||||
Genesis 1:1-6 as well as Daniel 2:14.
|
||||
"""
|
||||
old_bitem = self.listView.item(old_item.row())
|
||||
old_chapter = int(self._decodeQtObject(old_bitem, 'chapter'))
|
||||
old_verse = int(self._decodeQtObject(old_bitem, 'verse'))
|
||||
start_bitem = self.listView.item(start_item.row())
|
||||
start_book = self._decodeQtObject(start_bitem, 'book')
|
||||
start_chapter = int(self._decodeQtObject(start_bitem, 'chapter'))
|
||||
start_verse = int(self._decodeQtObject(start_bitem, 'verse'))
|
||||
start_bible = self._decodeQtObject(start_bitem, 'bible')
|
||||
start_dual_bible = self._decodeQtObject(start_bitem, 'dual_bible')
|
||||
if start_dual_bible:
|
||||
if start_verse == old_verse and start_chapter == old_chapter:
|
||||
title = u'%s %s:%s (%s, %s)' % (start_book, start_chapter,
|
||||
start_verse, start_bible, start_dual_bible)
|
||||
elif start_chapter == old_chapter:
|
||||
title = u'%s %s:%s-%s (%s, %s)' % (start_book, start_chapter,
|
||||
start_verse, old_verse, start_bible, start_dual_bible)
|
||||
else:
|
||||
title = u'%s %s:%s-%s:%s (%s, %s)' % (start_book, start_chapter,
|
||||
start_verse, old_chapter, old_verse, start_bible,
|
||||
start_dual_bible)
|
||||
else:
|
||||
if start_verse == old_verse and start_chapter == old_chapter:
|
||||
title = u'%s %s:%s (%s)' % (start_book, start_chapter,
|
||||
start_verse, start_bible)
|
||||
elif start_chapter == old_chapter:
|
||||
title = u'%s %s:%s-%s (%s)' % (start_book, start_chapter,
|
||||
start_verse, old_verse, start_bible)
|
||||
else:
|
||||
title = u'%s %s:%s-%s:%s (%s)' % (start_book, start_chapter,
|
||||
start_verse, old_chapter, old_verse, start_bible)
|
||||
return title
|
||||
|
||||
def checkTitle(self, item, old_item):
|
||||
"""
|
||||
This methode checks if we are at the end of an verse range. If that is
|
||||
the case, we return True, else False. E. g. if we added Genesis 1:1-6,
|
||||
but the next verse is Daniel 2:14.
|
||||
"""
|
||||
bitem = self.listView.item(item.row())
|
||||
book = self._decodeQtObject(bitem, 'book')
|
||||
chapter = int(self._decodeQtObject(bitem, 'chapter'))
|
||||
verse = int(self._decodeQtObject(bitem, 'verse'))
|
||||
bible = self._decodeQtObject(bitem, 'bible')
|
||||
dual_bible = self._decodeQtObject(bitem, 'dual_bible')
|
||||
old_bitem = self.listView.item(old_item.row())
|
||||
old_book = self._decodeQtObject(old_bitem, 'book')
|
||||
old_chapter = int(self._decodeQtObject(old_bitem, 'chapter'))
|
||||
old_verse = int(self._decodeQtObject(old_bitem, 'verse'))
|
||||
old_bible = self._decodeQtObject(old_bitem, 'bible')
|
||||
old_dual_bible = self._decodeQtObject(old_bitem, 'dual_bible')
|
||||
if old_bible != bible or old_dual_bible != dual_bible or \
|
||||
old_book != book:
|
||||
return True
|
||||
elif old_verse + 1 != verse and old_chapter == chapter:
|
||||
return True
|
||||
elif old_chapter + 1 == chapter and (verse != 1 or
|
||||
old_verse != self.parent.manager.get_verse_count(
|
||||
old_bible, old_book, old_chapter)):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
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
|
||||
verse_text = u'%s:%s' % (chapter, verse)
|
||||
else:
|
||||
verse_text = verse
|
||||
verse_text = u'%s' % 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:
|
||||
@ -678,7 +762,8 @@ class BibleMediaItem(MediaManagerItem):
|
||||
self.dual_search_results[count].text)
|
||||
}
|
||||
bible_text = u' %s %d:%d (%s, %s)' % (verse.book.name,
|
||||
verse.chapter, verse.verse, version.value, dual_version.value)
|
||||
verse.chapter, verse.verse, version.value,
|
||||
dual_version.value)
|
||||
else:
|
||||
vdict = {
|
||||
'book': QtCore.QVariant(verse.book.name),
|
||||
@ -689,14 +774,15 @@ class BibleMediaItem(MediaManagerItem):
|
||||
'copyright': QtCore.QVariant(copyright.value),
|
||||
'permission': QtCore.QVariant(permission.value),
|
||||
'text': QtCore.QVariant(verse.text),
|
||||
'dual_bible': QtCore.QVariant(dual_bible)
|
||||
'dual_bible': QtCore.QVariant(u''),
|
||||
'dual_version': QtCore.QVariant(u''),
|
||||
'dual_copyright': QtCore.QVariant(u''),
|
||||
'dual_permission': QtCore.QVariant(u''),
|
||||
'dual_text': QtCore.QVariant(u'')
|
||||
}
|
||||
bible_text = u' %s %d:%d (%s)' % (verse.book.name,
|
||||
verse.chapter, verse.verse, version.value)
|
||||
# set the row title
|
||||
bible_verse = QtGui.QListWidgetItem(bible_text)
|
||||
#bible_verse.setData(QtCore.Qt.UserRole,
|
||||
# QtCore.QVariant(bible_text))
|
||||
bible_verse.setData(QtCore.Qt.UserRole, QtCore.QVariant(vdict))
|
||||
self.listView.addItem(bible_verse)
|
||||
row = self.listView.setCurrentRow(count + start_count)
|
||||
|
@ -89,7 +89,7 @@ class OpenSongBible(BibleDB):
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
self.wizard.incrementProgressBar(
|
||||
QtCore.QString('%s %s %s' % (
|
||||
translate('BiblesPlugin.Opensong', 'Importing'), \
|
||||
translate('BiblesPlugin.Opensong', 'Importing'),
|
||||
db_book.name, chapter.attrib[u'n'])))
|
||||
self.session.commit()
|
||||
except IOError:
|
||||
|
@ -47,7 +47,7 @@ class CustomPlugin(Plugin):
|
||||
log.info(u'Custom Plugin loaded')
|
||||
|
||||
def __init__(self, plugin_helpers):
|
||||
Plugin.__init__(self, u'Custom', u'1.9.2', plugin_helpers)
|
||||
Plugin.__init__(self, u'Custom', u'1.9.3', plugin_helpers)
|
||||
self.weight = -5
|
||||
self.custommanager = Manager(u'custom', init_schema)
|
||||
self.edit_custom_form = EditCustomForm(self.custommanager)
|
||||
|
@ -35,7 +35,7 @@ class ImagePlugin(Plugin):
|
||||
log.info(u'Image Plugin loaded')
|
||||
|
||||
def __init__(self, plugin_helpers):
|
||||
Plugin.__init__(self, u'Images', u'1.9.2', plugin_helpers)
|
||||
Plugin.__init__(self, u'Images', u'1.9.3', plugin_helpers)
|
||||
self.weight = -7
|
||||
self.icon_path = u':/plugins/plugin_images.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
|
@ -37,7 +37,7 @@ class MediaPlugin(Plugin):
|
||||
log.info(u'%s MediaPlugin loaded', __name__)
|
||||
|
||||
def __init__(self, plugin_helpers):
|
||||
Plugin.__init__(self, u'Media', u'1.9.2', plugin_helpers)
|
||||
Plugin.__init__(self, u'Media', u'1.9.3', plugin_helpers)
|
||||
self.weight = -6
|
||||
self.icon_path = u':/plugins/plugin_media.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
|
@ -74,6 +74,7 @@ class ImpressController(PresentationController):
|
||||
self.process = None
|
||||
self.desktop = None
|
||||
self.manager = None
|
||||
self.uno_connection_type = u'pipe' #u'socket'
|
||||
|
||||
def check_available(self):
|
||||
"""
|
||||
@ -98,7 +99,14 @@ class ImpressController(PresentationController):
|
||||
self.manager._FlagAsMethod(u'Bridge_GetValueObject')
|
||||
else:
|
||||
# -headless
|
||||
cmd = u'openoffice.org -nologo -norestore -minimized -invisible -nofirststartwizard -accept="socket,host=localhost,port=2002;urp;"'
|
||||
if self.uno_connection_type == u'pipe':
|
||||
cmd = u'openoffice.org -nologo -norestore -minimized ' \
|
||||
+ u'-invisible -nofirststartwizard ' \
|
||||
+ u'-accept=pipe,name=openlp_pipe;urp;'
|
||||
else:
|
||||
cmd = u'openoffice.org -nologo -norestore -minimized ' \
|
||||
+ u'-invisible -nofirststartwizard ' \
|
||||
+ u'-accept=socket,host=localhost,port=2002;urp;'
|
||||
self.process = QtCore.QProcess()
|
||||
self.process.startDetached(cmd)
|
||||
self.process.waitForStarted()
|
||||
@ -120,8 +128,14 @@ class ImpressController(PresentationController):
|
||||
while ctx is None and loop < 3:
|
||||
try:
|
||||
log.debug(u'get UNO Desktop Openoffice - resolve')
|
||||
ctx = resolver.resolve(u'uno:socket,host=localhost,port=2002;'
|
||||
u'urp;StarOffice.ComponentContext')
|
||||
if self.uno_connection_type == u'pipe':
|
||||
ctx = resolver.resolve(u'uno:' \
|
||||
+ u'pipe,name=openlp_pipe;' \
|
||||
+ u'urp;StarOffice.ComponentContext')
|
||||
else:
|
||||
ctx = resolver.resolve(u'uno:' \
|
||||
+ u'socket,host=localhost,port=2002;' \
|
||||
+ u'urp;StarOffice.ComponentContext')
|
||||
except:
|
||||
log.exception(u'Unable to find running instance ')
|
||||
self.start_process()
|
||||
|
@ -212,7 +212,7 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
self, translate('PresentationPlugin.MediaItem',
|
||||
'Unsupported File'),
|
||||
translate('PresentationPlugin.MediaItem',
|
||||
'This type of presentation is not supported'))
|
||||
'This type of presentation is not supported.'))
|
||||
continue
|
||||
item_name = QtGui.QListWidgetItem(filename)
|
||||
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
|
||||
|
@ -51,7 +51,7 @@ class PresentationPlugin(Plugin):
|
||||
"""
|
||||
log.debug(u'Initialised')
|
||||
self.controllers = {}
|
||||
Plugin.__init__(self, u'Presentations', u'1.9.2', plugin_helpers)
|
||||
Plugin.__init__(self, u'Presentations', u'1.9.3', plugin_helpers)
|
||||
self.weight = -8
|
||||
self.icon_path = u':/plugins/plugin_presentations.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
|
0
openlp/plugins/remotes/html/jquery.js
vendored
Executable file → Normal file
0
openlp/plugins/remotes/html/jquery.js
vendored
Executable file → Normal file
@ -38,7 +38,7 @@ class RemotesPlugin(Plugin):
|
||||
"""
|
||||
remotes constructor
|
||||
"""
|
||||
Plugin.__init__(self, u'Remotes', u'1.9.2', plugin_helpers)
|
||||
Plugin.__init__(self, u'Remotes', u'1.9.3', plugin_helpers)
|
||||
self.icon = build_icon(u':/plugins/plugin_remote.png')
|
||||
self.weight = -1
|
||||
self.server = None
|
||||
|
@ -97,8 +97,7 @@ class AuthorsForm(QtGui.QDialog, Ui_AuthorsDialog):
|
||||
self, translate('SongsPlugin.AuthorsForm', 'Error'),
|
||||
translate('SongsPlugin.AuthorsForm',
|
||||
'You have not set a display name for the '
|
||||
'author, would you like me to combine the first and '
|
||||
'last names for you?'),
|
||||
'author, combine the first and last names?'),
|
||||
QtGui.QMessageBox.StandardButtons(
|
||||
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
|
||||
) == QtGui.QMessageBox.Yes:
|
||||
|
@ -258,10 +258,11 @@ class Ui_EditSongDialog(object):
|
||||
self.TopicBookLayout.addWidget(self.TopicGroupBox)
|
||||
self.SongBookGroup = QtGui.QGroupBox(self.TopicBookWidget)
|
||||
self.SongBookGroup.setObjectName(u'SongBookGroup')
|
||||
self.SongbookLayout = QtGui.QGridLayout(self.SongBookGroup)
|
||||
self.SongbookLayout = QtGui.QFormLayout(self.SongBookGroup)
|
||||
self.SongbookLayout.setMargin(8)
|
||||
self.SongbookLayout.setSpacing(8)
|
||||
self.SongbookLayout.setObjectName(u'SongbookLayout')
|
||||
self.SongbookNameLabel = QtGui.QLabel(self.SongBookGroup)
|
||||
self.SongbookCombo = QtGui.QComboBox(self.SongBookGroup)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.MinimumExpanding,
|
||||
QtGui.QSizePolicy.Fixed)
|
||||
@ -272,13 +273,11 @@ class Ui_EditSongDialog(object):
|
||||
self.SongbookCombo.setEditable(True)
|
||||
self.SongbookCombo.setSizePolicy(sizePolicy)
|
||||
self.SongbookCombo.setObjectName(u'SongbookCombo')
|
||||
self.SongbookLayout.addWidget(self.SongbookCombo, 0, 0, 1, 1)
|
||||
self.SongbookLayout.addRow(self.SongbookNameLabel, self.SongbookCombo)
|
||||
self.songBookNumberLabel = QtGui.QLabel(self.SongBookGroup)
|
||||
self.SongbookLayout.addWidget(self.songBookNumberLabel, 0, 1, 1, 1)
|
||||
self.songBookNumberEdit = QtGui.QLineEdit(self.SongBookGroup)
|
||||
self.songBookNumberLabel.setBuddy(self.songBookNumberEdit)
|
||||
self.songBookNumberEdit.setMaximumWidth(35)
|
||||
self.SongbookLayout.addWidget(self.songBookNumberEdit, 0, 2, 1, 1)
|
||||
self.SongbookLayout.addRow(self.songBookNumberLabel,
|
||||
self.songBookNumberEdit)
|
||||
self.TopicBookLayout.addWidget(self.SongBookGroup)
|
||||
self.AuthorsTabLayout.addWidget(self.TopicBookWidget)
|
||||
self.SongTabWidget.addTab(self.AuthorsTab, u'')
|
||||
@ -446,8 +445,10 @@ class Ui_EditSongDialog(object):
|
||||
translate('SongsPlugin.EditSongForm', 'R&emove'))
|
||||
self.SongBookGroup.setTitle(
|
||||
translate('SongsPlugin.EditSongForm', 'Song Book'))
|
||||
self.SongbookNameLabel.setText(translate('SongsPlugin.EditSongForm',
|
||||
'Book:'))
|
||||
self.songBookNumberLabel.setText(translate('SongsPlugin.EditSongForm',
|
||||
'Song No.:'))
|
||||
'Number:'))
|
||||
self.SongTabWidget.setTabText(
|
||||
self.SongTabWidget.indexOf(self.AuthorsTab),
|
||||
translate('SongsPlugin.EditSongForm',
|
||||
|
@ -621,6 +621,10 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
self.close()
|
||||
|
||||
def saveSong(self):
|
||||
"""
|
||||
Get all the data from the widgets on the form, and then save it to the
|
||||
database.
|
||||
"""
|
||||
self.song.title = unicode(self.TitleEditItem.text())
|
||||
self.song.alternate_title = unicode(self.AlternativeEdit.text())
|
||||
self.song.copyright = unicode(self.CopyrightEditItem.text())
|
||||
@ -646,6 +650,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
self.song.topics.append(self.songmanager.get_object(Topic,
|
||||
topicId))
|
||||
self.songmanager.save_object(self.song)
|
||||
self.song = None
|
||||
return True
|
||||
return False
|
||||
|
||||
|
@ -57,6 +57,15 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
self.registerFields()
|
||||
self.finishButton = self.button(QtGui.QWizard.FinishButton)
|
||||
self.cancelButton = self.button(QtGui.QWizard.CancelButton)
|
||||
if not SongFormat.get_availability(SongFormat.OpenLP1):
|
||||
self.openLP1DisabledWidget.setVisible(True)
|
||||
self.openLP1ImportWidget.setVisible(False)
|
||||
if not SongFormat.get_availability(SongFormat.SongsOfFellowship):
|
||||
self.songsOfFellowshipDisabledWidget.setVisible(True)
|
||||
self.songsOfFellowshipImportWidget.setVisible(False)
|
||||
if not SongFormat.get_availability(SongFormat.Generic):
|
||||
self.genericDisabledWidget.setVisible(True)
|
||||
self.genericImportWidget.setVisible(False)
|
||||
self.plugin = plugin
|
||||
QtCore.QObject.connect(self.openLP2BrowseButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
@ -64,12 +73,12 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
QtCore.QObject.connect(self.openLP1BrowseButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onOpenLP1BrowseButtonClicked)
|
||||
QtCore.QObject.connect(self.openLyricsAddButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onOpenLyricsAddButtonClicked)
|
||||
QtCore.QObject.connect(self.openLyricsRemoveButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onOpenLyricsRemoveButtonClicked)
|
||||
#QtCore.QObject.connect(self.openLyricsAddButton,
|
||||
# QtCore.SIGNAL(u'clicked()'),
|
||||
# self.onOpenLyricsAddButtonClicked)
|
||||
#QtCore.QObject.connect(self.openLyricsRemoveButton,
|
||||
# QtCore.SIGNAL(u'clicked()'),
|
||||
# self.onOpenLyricsRemoveButtonClicked)
|
||||
QtCore.QObject.connect(self.openSongAddButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onOpenSongAddButtonClicked)
|
||||
@ -100,6 +109,9 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
QtCore.QObject.connect(self.genericRemoveButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onGenericRemoveButtonClicked)
|
||||
QtCore.QObject.connect(self.ewBrowseButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onEWBrowseButtonClicked)
|
||||
QtCore.QObject.connect(self.cancelButton,
|
||||
QtCore.SIGNAL(u'clicked(bool)'),
|
||||
self.onCancelButtonClicked)
|
||||
@ -145,15 +157,16 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
self.openLP1BrowseButton.setFocus()
|
||||
return False
|
||||
elif source_format == SongFormat.OpenLyrics:
|
||||
if self.openLyricsFileListWidget.count() == 0:
|
||||
QtGui.QMessageBox.critical(self,
|
||||
translate('SongsPlugin.ImportWizardForm',
|
||||
'No OpenLyrics Files Selected'),
|
||||
translate('SongsPlugin.ImportWizardForm',
|
||||
'You need to add at least one OpenLyrics '
|
||||
'song file to import from.'))
|
||||
self.openLyricsAddButton.setFocus()
|
||||
return False
|
||||
#if self.openLyricsFileListWidget.count() == 0:
|
||||
# QtGui.QMessageBox.critical(self,
|
||||
# translate('SongsPlugin.ImportWizardForm',
|
||||
# 'No OpenLyrics Files Selected'),
|
||||
# translate('SongsPlugin.ImportWizardForm',
|
||||
# 'You need to add at least one OpenLyrics '
|
||||
# 'song file to import from.'))
|
||||
# self.openLyricsAddButton.setFocus()
|
||||
# return False
|
||||
return False
|
||||
elif source_format == SongFormat.OpenSong:
|
||||
if self.openSongFileListWidget.count() == 0:
|
||||
QtGui.QMessageBox.critical(self,
|
||||
@ -204,6 +217,16 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
'presentation file to import from.'))
|
||||
self.genericAddButton.setFocus()
|
||||
return False
|
||||
elif source_format == SongFormat.EasyWorship:
|
||||
if self.ewFilenameEdit.text().isEmpty():
|
||||
QtGui.QMessageBox.critical(self,
|
||||
translate('SongsPlugin.ImportWizardForm',
|
||||
'No EasyWorship Song Database Selected'),
|
||||
translate('SongsPlugin.ImportWizardForm',
|
||||
'You need to select an EasyWorship song database '
|
||||
'file to import from.'))
|
||||
self.ewBrowseButton.setFocus()
|
||||
return False
|
||||
return True
|
||||
elif self.currentId() == 2:
|
||||
# Progress page
|
||||
@ -252,15 +275,15 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
self.openLP1FilenameEdit
|
||||
)
|
||||
|
||||
def onOpenLyricsAddButtonClicked(self):
|
||||
self.getFiles(
|
||||
translate('SongsPlugin.ImportWizardForm',
|
||||
'Select OpenLyrics Files'),
|
||||
self.openLyricsFileListWidget
|
||||
)
|
||||
#def onOpenLyricsAddButtonClicked(self):
|
||||
# self.getFiles(
|
||||
# translate('SongsPlugin.ImportWizardForm',
|
||||
# 'Select OpenLyrics Files'),
|
||||
# self.openLyricsFileListWidget
|
||||
# )
|
||||
|
||||
def onOpenLyricsRemoveButtonClicked(self):
|
||||
self.removeSelectedItems(self.openLyricsFileListWidget)
|
||||
#def onOpenLyricsRemoveButtonClicked(self):
|
||||
# self.removeSelectedItems(self.openLyricsFileListWidget)
|
||||
|
||||
def onOpenSongAddButtonClicked(self):
|
||||
self.getFiles(
|
||||
@ -312,6 +335,13 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
def onGenericRemoveButtonClicked(self):
|
||||
self.removeSelectedItems(self.genericFileListWidget)
|
||||
|
||||
def onEWBrowseButtonClicked(self):
|
||||
self.getFileName(
|
||||
translate('SongsPlugin.ImportWizardForm',
|
||||
'Select EasyWorship Database File'),
|
||||
self.ewFilenameEdit
|
||||
)
|
||||
|
||||
def onCancelButtonClicked(self, checked):
|
||||
"""
|
||||
Stop the import on pressing the cancel button.
|
||||
@ -331,15 +361,18 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
|
||||
def setDefaults(self):
|
||||
self.restart()
|
||||
self.finishButton.setVisible(False)
|
||||
self.cancelButton.setVisible(True)
|
||||
self.formatComboBox.setCurrentIndex(0)
|
||||
self.openLP2FilenameEdit.setText(u'')
|
||||
self.openLP1FilenameEdit.setText(u'')
|
||||
self.openLyricsFileListWidget.clear()
|
||||
#self.openLyricsFileListWidget.clear()
|
||||
self.openSongFileListWidget.clear()
|
||||
self.wordsOfWorshipFileListWidget.clear()
|
||||
self.ccliFileListWidget.clear()
|
||||
self.songsOfFellowshipFileListWidget.clear()
|
||||
self.genericFileListWidget.clear()
|
||||
self.ewFilenameEdit.setText(u'')
|
||||
#self.csvFilenameEdit.setText(u'')
|
||||
|
||||
def incrementProgressBar(self, status_text, increment=1):
|
||||
@ -410,6 +443,11 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
|
||||
importer = self.plugin.importSongs(SongFormat.Generic,
|
||||
filenames=self.getListOfFiles(self.genericFileListWidget)
|
||||
)
|
||||
elif source_format == SongFormat.EasyWorship:
|
||||
# Import an OpenLP 2.0 database
|
||||
importer = self.plugin.importSongs(SongFormat.EasyWorship,
|
||||
filename=unicode(self.ewFilenameEdit.text())
|
||||
)
|
||||
success = importer.do_import()
|
||||
if success:
|
||||
# reload songs
|
||||
|
@ -96,6 +96,7 @@ class Ui_SongImportWizard(object):
|
||||
self.formatComboBox.addItem(u'')
|
||||
self.formatComboBox.addItem(u'')
|
||||
self.formatComboBox.addItem(u'')
|
||||
self.formatComboBox.addItem(u'')
|
||||
# self.formatComboBox.addItem(u'')
|
||||
self.formatLayout.addWidget(self.formatComboBox)
|
||||
self.formatSpacer = QtGui.QSpacerItem(40, 20,
|
||||
@ -131,26 +132,43 @@ class Ui_SongImportWizard(object):
|
||||
# openlp.org 1.x
|
||||
self.openLP1Page = QtGui.QWidget()
|
||||
self.openLP1Page.setObjectName(u'openLP1Page')
|
||||
self.openLP1Layout = QtGui.QFormLayout(self.openLP1Page)
|
||||
self.openLP1Layout = QtGui.QVBoxLayout(self.openLP1Page)
|
||||
self.openLP1Layout.setMargin(0)
|
||||
self.openLP1Layout.setSpacing(8)
|
||||
self.openLP1Layout.setSpacing(0)
|
||||
self.openLP1Layout.setObjectName(u'openLP1Layout')
|
||||
self.openLP1FilenameLabel = QtGui.QLabel(self.openLP1Page)
|
||||
self.openLP1DisabledWidget = QtGui.QWidget(self.openLP1Page)
|
||||
self.openLP1DisabledLayout = QtGui.QVBoxLayout(self.openLP1DisabledWidget)
|
||||
self.openLP1DisabledLayout.setMargin(0)
|
||||
self.openLP1DisabledLayout.setSpacing(8)
|
||||
self.openLP1DisabledLayout.setObjectName(u'openLP1DisabledLayout')
|
||||
self.openLP1DisabledLabel = QtGui.QLabel(self.openLP1DisabledWidget)
|
||||
self.openLP1DisabledLabel.setWordWrap(True)
|
||||
self.openLP1DisabledLabel.setObjectName(u'openLP1DisabledLabel')
|
||||
self.openLP1DisabledLayout.addWidget(self.openLP1DisabledLabel)
|
||||
self.openLP1DisabledWidget.setVisible(False)
|
||||
self.openLP1Layout.addWidget(self.openLP1DisabledWidget)
|
||||
self.openLP1ImportWidget = QtGui.QWidget(self.openLP1Page)
|
||||
self.openLP1ImportLayout = QtGui.QFormLayout(self.openLP1ImportWidget)
|
||||
self.openLP1ImportLayout.setMargin(0)
|
||||
self.openLP1ImportLayout.setSpacing(8)
|
||||
self.openLP1ImportLayout.setObjectName(u'openLP1ImportLayout')
|
||||
self.openLP1FilenameLabel = QtGui.QLabel(self.openLP1ImportWidget)
|
||||
self.openLP1FilenameLabel.setObjectName(u'openLP1FilenameLabel')
|
||||
self.openLP1Layout.setWidget(0, QtGui.QFormLayout.LabelRole,
|
||||
self.openLP1ImportLayout.setWidget(0, QtGui.QFormLayout.LabelRole,
|
||||
self.openLP1FilenameLabel)
|
||||
self.openLP1FileLayout = QtGui.QHBoxLayout()
|
||||
self.openLP1FileLayout.setSpacing(8)
|
||||
self.openLP1FileLayout.setObjectName(u'openLP1FileLayout')
|
||||
self.openLP1FilenameEdit = QtGui.QLineEdit(self.openLP1Page)
|
||||
self.openLP1FilenameEdit = QtGui.QLineEdit(self.openLP1ImportWidget)
|
||||
self.openLP1FilenameEdit.setObjectName(u'openLP1FilenameEdit')
|
||||
self.openLP1FileLayout.addWidget(self.openLP1FilenameEdit)
|
||||
self.openLP1BrowseButton = QtGui.QToolButton(self.openLP1Page)
|
||||
self.openLP1BrowseButton = QtGui.QToolButton(self.openLP1ImportWidget)
|
||||
self.openLP1BrowseButton.setIcon(openIcon)
|
||||
self.openLP1BrowseButton.setObjectName(u'openLP1BrowseButton')
|
||||
self.openLP1FileLayout.addWidget(self.openLP1BrowseButton)
|
||||
self.openLP1Layout.setLayout(0, QtGui.QFormLayout.FieldRole,
|
||||
self.openLP1ImportLayout.setLayout(0, QtGui.QFormLayout.FieldRole,
|
||||
self.openLP1FileLayout)
|
||||
self.openLP1Layout.addWidget(self.openLP1ImportWidget)
|
||||
self.formatStackedWidget.addWidget(self.openLP1Page)
|
||||
# OpenLyrics
|
||||
self.openLyricsPage = QtGui.QWidget()
|
||||
@ -159,26 +177,31 @@ class Ui_SongImportWizard(object):
|
||||
self.openLyricsLayout.setSpacing(8)
|
||||
self.openLyricsLayout.setMargin(0)
|
||||
self.openLyricsLayout.setObjectName(u'OpenLyricsLayout')
|
||||
self.openLyricsFileListWidget = QtGui.QListWidget(self.openLyricsPage)
|
||||
self.openLyricsFileListWidget.setSelectionMode(
|
||||
QtGui.QAbstractItemView.ExtendedSelection)
|
||||
self.openLyricsFileListWidget.setObjectName(u'OpenLyricsFileListWidget')
|
||||
self.openLyricsLayout.addWidget(self.openLyricsFileListWidget)
|
||||
self.openLyricsButtonLayout = QtGui.QHBoxLayout()
|
||||
self.openLyricsButtonLayout.setSpacing(8)
|
||||
self.openLyricsButtonLayout.setObjectName(u'OpenLyricsButtonLayout')
|
||||
self.openLyricsAddButton = QtGui.QPushButton(self.openLyricsPage)
|
||||
self.openLyricsAddButton.setIcon(openIcon)
|
||||
self.openLyricsAddButton.setObjectName(u'OpenLyricsAddButton')
|
||||
self.openLyricsButtonLayout.addWidget(self.openLyricsAddButton)
|
||||
self.openLyricsButtonSpacer = QtGui.QSpacerItem(40, 20,
|
||||
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
|
||||
self.openLyricsButtonLayout.addItem(self.openLyricsButtonSpacer)
|
||||
self.openLyricsRemoveButton = QtGui.QPushButton(self.openLyricsPage)
|
||||
self.openLyricsRemoveButton.setIcon(deleteIcon)
|
||||
self.openLyricsRemoveButton.setObjectName(u'OpenLyricsRemoveButton')
|
||||
self.openLyricsButtonLayout.addWidget(self.openLyricsRemoveButton)
|
||||
self.openLyricsLayout.addLayout(self.openLyricsButtonLayout)
|
||||
self.openLyricsDisabledLabel = QtGui.QLabel(self.openLyricsPage)
|
||||
self.openLyricsDisabledLabel.setWordWrap(True)
|
||||
self.openLyricsDisabledLabel.setObjectName(u'openLyricsDisabledLabel')
|
||||
self.openLyricsLayout.addWidget(self.openLyricsDisabledLabel)
|
||||
# Commented out for future use.
|
||||
#self.openLyricsFileListWidget = QtGui.QListWidget(self.openLyricsPage)
|
||||
#self.openLyricsFileListWidget.setSelectionMode(
|
||||
# QtGui.QAbstractItemView.ExtendedSelection)
|
||||
#self.openLyricsFileListWidget.setObjectName(u'OpenLyricsFileListWidget')
|
||||
#self.openLyricsLayout.addWidget(self.openLyricsFileListWidget)
|
||||
#self.openLyricsButtonLayout = QtGui.QHBoxLayout()
|
||||
#self.openLyricsButtonLayout.setSpacing(8)
|
||||
#self.openLyricsButtonLayout.setObjectName(u'OpenLyricsButtonLayout')
|
||||
#self.openLyricsAddButton = QtGui.QPushButton(self.openLyricsPage)
|
||||
#self.openLyricsAddButton.setIcon(openIcon)
|
||||
#self.openLyricsAddButton.setObjectName(u'OpenLyricsAddButton')
|
||||
#self.openLyricsButtonLayout.addWidget(self.openLyricsAddButton)
|
||||
#self.openLyricsButtonSpacer = QtGui.QSpacerItem(40, 20,
|
||||
# QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
|
||||
#self.openLyricsButtonLayout.addItem(self.openLyricsButtonSpacer)
|
||||
#self.openLyricsRemoveButton = QtGui.QPushButton(self.openLyricsPage)
|
||||
#self.openLyricsRemoveButton.setIcon(deleteIcon)
|
||||
#self.openLyricsRemoveButton.setObjectName(u'OpenLyricsRemoveButton')
|
||||
#self.openLyricsButtonLayout.addWidget(self.openLyricsRemoveButton)
|
||||
#self.openLyricsLayout.addLayout(self.openLyricsButtonLayout)
|
||||
self.formatStackedWidget.addWidget(self.openLyricsPage)
|
||||
# Open Song
|
||||
self.openSongPage = QtGui.QWidget()
|
||||
@ -277,22 +300,52 @@ class Ui_SongImportWizard(object):
|
||||
self.songsOfFellowshipLayout = QtGui.QVBoxLayout(
|
||||
self.songsOfFellowshipPage)
|
||||
self.songsOfFellowshipLayout.setMargin(0)
|
||||
self.songsOfFellowshipLayout.setSpacing(8)
|
||||
self.songsOfFellowshipLayout.setSpacing(0)
|
||||
self.songsOfFellowshipLayout.setObjectName(u'songsOfFellowshipLayout')
|
||||
self.songsOfFellowshipFileListWidget = QtGui.QListWidget(
|
||||
self.songsOfFellowshipDisabledWidget = QtGui.QWidget(
|
||||
self.songsOfFellowshipPage)
|
||||
self.songsOfFellowshipDisabledWidget.setVisible(False)
|
||||
self.songsOfFellowshipDisabledWidget.setObjectName(
|
||||
u'songsOfFellowshipDisabledWidget')
|
||||
self.songsOfFellowshipDisabledLayout = QtGui.QVBoxLayout(
|
||||
self.songsOfFellowshipDisabledWidget)
|
||||
self.songsOfFellowshipDisabledLayout.setMargin(0)
|
||||
self.songsOfFellowshipDisabledLayout.setSpacing(8)
|
||||
self.songsOfFellowshipDisabledLayout.setObjectName(
|
||||
u'songsOfFellowshipDisabledLayout')
|
||||
self.songsOfFellowshipDisabledLabel = QtGui.QLabel(
|
||||
self.songsOfFellowshipDisabledWidget)
|
||||
self.songsOfFellowshipDisabledLabel.setWordWrap(True)
|
||||
self.songsOfFellowshipDisabledLabel.setObjectName(
|
||||
u'songsOfFellowshipDisabledLabel')
|
||||
self.songsOfFellowshipDisabledLayout.addWidget(
|
||||
self.songsOfFellowshipDisabledLabel)
|
||||
self.songsOfFellowshipLayout.addWidget(
|
||||
self.songsOfFellowshipDisabledWidget)
|
||||
self.songsOfFellowshipImportWidget = QtGui.QWidget(
|
||||
self.songsOfFellowshipPage)
|
||||
self.songsOfFellowshipImportWidget.setObjectName(
|
||||
u'songsOfFellowshipImportWidget')
|
||||
self.songsOfFellowshipImportLayout = QtGui.QVBoxLayout(
|
||||
self.songsOfFellowshipImportWidget)
|
||||
self.songsOfFellowshipImportLayout.setMargin(0)
|
||||
self.songsOfFellowshipImportLayout.setSpacing(8)
|
||||
self.songsOfFellowshipImportLayout.setObjectName(
|
||||
u'songsOfFellowshipImportLayout')
|
||||
self.songsOfFellowshipFileListWidget = QtGui.QListWidget(
|
||||
self.songsOfFellowshipImportWidget)
|
||||
self.songsOfFellowshipFileListWidget.setSelectionMode(
|
||||
QtGui.QAbstractItemView.ExtendedSelection)
|
||||
self.songsOfFellowshipFileListWidget.setObjectName(
|
||||
u'songsOfFellowshipFileListWidget')
|
||||
self.songsOfFellowshipLayout.addWidget(
|
||||
self.songsOfFellowshipImportLayout.addWidget(
|
||||
self.songsOfFellowshipFileListWidget)
|
||||
self.songsOfFellowshipButtonLayout = QtGui.QHBoxLayout()
|
||||
self.songsOfFellowshipButtonLayout.setSpacing(8)
|
||||
self.songsOfFellowshipButtonLayout.setObjectName(
|
||||
u'songsOfFellowshipButtonLayout')
|
||||
self.songsOfFellowshipAddButton = QtGui.QPushButton(
|
||||
self.songsOfFellowshipPage)
|
||||
self.songsOfFellowshipImportWidget)
|
||||
self.songsOfFellowshipAddButton.setIcon(openIcon)
|
||||
self.songsOfFellowshipAddButton.setObjectName(
|
||||
u'songsOfFellowshipAddButton')
|
||||
@ -303,43 +356,88 @@ class Ui_SongImportWizard(object):
|
||||
self.songsOfFellowshipButtonLayout.addItem(
|
||||
self.songsOfFellowshipButtonSpacer)
|
||||
self.songsOfFellowshipRemoveButton = QtGui.QPushButton(
|
||||
self.songsOfFellowshipPage)
|
||||
self.songsOfFellowshipImportWidget)
|
||||
self.songsOfFellowshipRemoveButton.setIcon(deleteIcon)
|
||||
self.songsOfFellowshipRemoveButton.setObjectName(
|
||||
u'songsOfFellowshipRemoveButton')
|
||||
self.songsOfFellowshipButtonLayout.addWidget(
|
||||
self.songsOfFellowshipRemoveButton)
|
||||
self.songsOfFellowshipLayout.addLayout(
|
||||
self.songsOfFellowshipImportLayout.addLayout(
|
||||
self.songsOfFellowshipButtonLayout)
|
||||
self.songsOfFellowshipLayout.addWidget(
|
||||
self.songsOfFellowshipImportWidget)
|
||||
self.formatStackedWidget.addWidget(self.songsOfFellowshipPage)
|
||||
# Generic Document/Presentation import
|
||||
self.genericPage = QtGui.QWidget()
|
||||
self.genericPage.setObjectName(u'genericPage')
|
||||
self.genericLayout = QtGui.QVBoxLayout(self.genericPage)
|
||||
self.genericLayout.setMargin(0)
|
||||
self.genericLayout.setSpacing(8)
|
||||
self.genericLayout.setSpacing(0)
|
||||
self.genericLayout.setObjectName(u'genericLayout')
|
||||
self.genericFileListWidget = QtGui.QListWidget(self.genericPage)
|
||||
self.genericDisabledWidget = QtGui.QWidget(self.genericPage)
|
||||
self.genericDisabledWidget.setObjectName(u'genericDisabledWidget')
|
||||
self.genericDisabledLayout = QtGui.QVBoxLayout(self.genericDisabledWidget)
|
||||
self.genericDisabledLayout.setMargin(0)
|
||||
self.genericDisabledLayout.setSpacing(8)
|
||||
self.genericDisabledLayout.setObjectName(u'genericDisabledLayout')
|
||||
self.genericDisabledLabel = QtGui.QLabel(self.genericDisabledWidget)
|
||||
self.genericDisabledLabel.setWordWrap(True)
|
||||
self.genericDisabledLabel.setObjectName(u'genericDisabledLabel')
|
||||
self.genericDisabledWidget.setVisible(False)
|
||||
self.genericDisabledLayout.addWidget(self.genericDisabledLabel)
|
||||
self.genericLayout.addWidget(self.genericDisabledWidget)
|
||||
self.genericImportWidget = QtGui.QWidget(self.genericPage)
|
||||
self.genericImportWidget.setObjectName(u'genericImportWidget')
|
||||
self.genericImportLayout = QtGui.QVBoxLayout(self.genericImportWidget)
|
||||
self.genericImportLayout.setMargin(0)
|
||||
self.genericImportLayout.setSpacing(8)
|
||||
self.genericImportLayout.setObjectName(u'genericImportLayout')
|
||||
self.genericFileListWidget = QtGui.QListWidget(self.genericImportWidget)
|
||||
self.genericFileListWidget.setSelectionMode(
|
||||
QtGui.QAbstractItemView.ExtendedSelection)
|
||||
self.genericFileListWidget.setObjectName(u'genericFileListWidget')
|
||||
self.genericLayout.addWidget(self.genericFileListWidget)
|
||||
self.genericImportLayout.addWidget(self.genericFileListWidget)
|
||||
self.genericButtonLayout = QtGui.QHBoxLayout()
|
||||
self.genericButtonLayout.setSpacing(8)
|
||||
self.genericButtonLayout.setObjectName(u'genericButtonLayout')
|
||||
self.genericAddButton = QtGui.QPushButton(self.genericPage)
|
||||
self.genericAddButton = QtGui.QPushButton(self.genericImportWidget)
|
||||
self.genericAddButton.setIcon(openIcon)
|
||||
self.genericAddButton.setObjectName(u'genericAddButton')
|
||||
self.genericButtonLayout.addWidget(self.genericAddButton)
|
||||
self.genericButtonSpacer = QtGui.QSpacerItem(40, 20,
|
||||
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
|
||||
self.genericButtonLayout.addItem(self.genericButtonSpacer)
|
||||
self.genericRemoveButton = QtGui.QPushButton(self.genericPage)
|
||||
self.genericRemoveButton = QtGui.QPushButton(self.genericImportWidget)
|
||||
self.genericRemoveButton.setIcon(deleteIcon)
|
||||
self.genericRemoveButton.setObjectName(u'genericRemoveButton')
|
||||
self.genericButtonLayout.addWidget(self.genericRemoveButton)
|
||||
self.genericLayout.addLayout(self.genericButtonLayout)
|
||||
self.genericImportLayout.addLayout(self.genericButtonLayout)
|
||||
self.genericLayout.addWidget(self.genericImportWidget)
|
||||
self.formatStackedWidget.addWidget(self.genericPage)
|
||||
# EasyWorship
|
||||
self.ewPage = QtGui.QWidget()
|
||||
self.ewPage.setObjectName(u'ewPage')
|
||||
self.ewLayout = QtGui.QFormLayout(self.ewPage)
|
||||
self.ewLayout.setMargin(0)
|
||||
self.ewLayout.setSpacing(8)
|
||||
self.ewLayout.setObjectName(u'ewLayout')
|
||||
self.ewFilenameLabel = QtGui.QLabel(self.ewPage)
|
||||
self.ewFilenameLabel.setObjectName(u'ewFilenameLabel')
|
||||
self.ewLayout.setWidget(0, QtGui.QFormLayout.LabelRole,
|
||||
self.ewFilenameLabel)
|
||||
self.ewFileLayout = QtGui.QHBoxLayout()
|
||||
self.ewFileLayout.setSpacing(8)
|
||||
self.ewFileLayout.setObjectName(u'ewFileLayout')
|
||||
self.ewFilenameEdit = QtGui.QLineEdit(self.ewPage)
|
||||
self.ewFilenameEdit.setObjectName(u'ewFilenameEdit')
|
||||
self.ewFileLayout.addWidget(self.ewFilenameEdit)
|
||||
self.ewBrowseButton = QtGui.QToolButton(self.ewPage)
|
||||
self.ewBrowseButton.setIcon(openIcon)
|
||||
self.ewBrowseButton.setObjectName(u'ewBrowseButton')
|
||||
self.ewFileLayout.addWidget(self.ewBrowseButton)
|
||||
self.ewLayout.setLayout(0, QtGui.QFormLayout.FieldRole,
|
||||
self.ewFileLayout)
|
||||
self.formatStackedWidget.addWidget(self.ewPage)
|
||||
# Commented out for future use.
|
||||
# self.csvPage = QtGui.QWidget()
|
||||
# self.csvPage.setObjectName(u'CSVPage')
|
||||
@ -424,7 +522,9 @@ class Ui_SongImportWizard(object):
|
||||
self.formatComboBox.setItemText(7,
|
||||
translate('SongsPlugin.ImportWizardForm',
|
||||
'Generic Document/Presentation'))
|
||||
# self.formatComboBox.setItemText(8,
|
||||
self.formatComboBox.setItemText(8,
|
||||
translate('SongsPlugin.ImportWizardForm', 'EasyWorship'))
|
||||
# self.formatComboBox.setItemText(9,
|
||||
# translate('SongsPlugin.ImportWizardForm', 'CSV'))
|
||||
self.openLP2FilenameLabel.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Filename:'))
|
||||
@ -434,10 +534,20 @@ class Ui_SongImportWizard(object):
|
||||
translate('SongsPlugin.ImportWizardForm', 'Filename:'))
|
||||
self.openLP1BrowseButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Browse...'))
|
||||
self.openLyricsAddButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
|
||||
self.openLyricsRemoveButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
|
||||
self.openLP1DisabledLabel.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'The openlp.org 1.x '
|
||||
'importer has been disabled due to a missing Python module. If '
|
||||
'you want to use this importer, you will need to install the '
|
||||
'"python-sqlite" module.'))
|
||||
#self.openLyricsAddButton.setText(
|
||||
# translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
|
||||
#self.openLyricsRemoveButton.setText(
|
||||
# translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
|
||||
self.openLyricsDisabledLabel.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'The OpenLyrics '
|
||||
'importer has not yet been developed, but as you can see, we are '
|
||||
'still intending to do so. Hopefully it will be in the next '
|
||||
'release.'))
|
||||
self.openSongAddButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
|
||||
self.openSongRemoveButton.setText(
|
||||
@ -454,10 +564,22 @@ class Ui_SongImportWizard(object):
|
||||
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
|
||||
self.songsOfFellowshipRemoveButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
|
||||
self.songsOfFellowshipDisabledLabel.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'The Songs of '
|
||||
'Fellowship importer has been disabled because OpenLP cannot '
|
||||
'find OpenOffice.org on your computer.'))
|
||||
self.genericAddButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
|
||||
self.genericRemoveButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
|
||||
self.genericDisabledLabel.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'The generic document/'
|
||||
'presentation importer has been disabled because OpenLP cannot '
|
||||
'find OpenOffice.org on your computer.'))
|
||||
self.ewFilenameLabel.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Filename:'))
|
||||
self.ewBrowseButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Browse...'))
|
||||
# self.csvFilenameLabel.setText(
|
||||
# translate('SongsPlugin.ImportWizardForm', 'Filename:'))
|
||||
# self.csvBrowseButton.setText(
|
||||
|
@ -324,8 +324,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
|
||||
QtGui.QMessageBox.critical(self,
|
||||
translate('SongsPlugin.SongMaintenanceForm', 'Error'),
|
||||
translate('SongsPlugin.SongMaintenanceForm',
|
||||
'Could not save your modified author, because he '
|
||||
'already exists.'))
|
||||
'Could not save your modified author, because the '
|
||||
'author already exists.'))
|
||||
|
||||
def onTopicEditButtonClick(self):
|
||||
topic_id = self._getCurrentItemId(self.TopicsListWidget)
|
||||
|
@ -51,7 +51,7 @@ class TopicsForm(QtGui.QDialog, Ui_TopicsDialog):
|
||||
QtGui.QMessageBox.critical(
|
||||
self, translate('SongsPlugin.TopicsForm', 'Error'),
|
||||
translate('SongsPlugin.TopicsForm',
|
||||
'You need to type in a topic name!'))
|
||||
'You need to type in a topic name.'))
|
||||
self.NameEdit.setFocus()
|
||||
return False
|
||||
else:
|
||||
|
@ -57,7 +57,7 @@ class CCLIFileImport(SongImport):
|
||||
self.filenames = kwargs[u'filenames']
|
||||
log.debug(self.filenames)
|
||||
else:
|
||||
raise KeyError(u'Keyword argument "filenames" not supplied.')
|
||||
raise KeyError(u'Keyword argument "filenames" not supplied.')
|
||||
|
||||
def do_import(self):
|
||||
"""
|
||||
@ -66,11 +66,11 @@ class CCLIFileImport(SongImport):
|
||||
log.debug(u'Starting CCLI File Import')
|
||||
song_total = len(self.filenames)
|
||||
self.import_wizard.importProgressBar.setMaximum(song_total)
|
||||
song_count = 1
|
||||
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)
|
||||
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):
|
||||
@ -81,33 +81,34 @@ class CCLIFileImport(SongImport):
|
||||
lines = infile.readlines()
|
||||
ext = os.path.splitext(filename)[1]
|
||||
if ext.lower() == ".usr":
|
||||
log.info(u'SongSelect .usr format file found %s: ' , filename)
|
||||
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)
|
||||
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 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
|
||||
|
||||
*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
|
||||
Indicates the file type
|
||||
e.g. *Type=SongSelect Import File*
|
||||
``Version=3.0``
|
||||
File format version
|
||||
@ -116,7 +117,7 @@ class CCLIFileImport(SongImport):
|
||||
``Title=``
|
||||
Contains the song title (e.g. *Title=Above All*)
|
||||
``Author=``
|
||||
Contains a | delimited list of the song authors
|
||||
Contains a | delimited list of the song authors
|
||||
e.g. *Author=LeBlanc, Lenny | Baloche, Paul*
|
||||
``Copyright=``
|
||||
Contains a | delimited list of the song copyrights
|
||||
@ -136,7 +137,7 @@ class CCLIFileImport(SongImport):
|
||||
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
|
||||
Contains the songs various lyrics in order as shown by the
|
||||
*Fields* description
|
||||
e.g. *Words=Above all powers....* [/n = CR, /n/t = CRLF]
|
||||
"""
|
||||
@ -174,8 +175,8 @@ class CCLIFileImport(SongImport):
|
||||
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);
|
||||
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:
|
||||
@ -192,10 +193,10 @@ class CCLIFileImport(SongImport):
|
||||
"""
|
||||
The :method:`do_import_txt_file` method provides OpenLP
|
||||
with the ability to import CCLI SongSelect songs in
|
||||
*TXT* file format
|
||||
|
||||
*TXT* file format
|
||||
|
||||
``textList``
|
||||
An array of strings containing the txt file content.
|
||||
An array of strings containing the txt file content.
|
||||
|
||||
**SongSelect .txt file format**
|
||||
|
||||
@ -225,38 +226,38 @@ class CCLIFileImport(SongImport):
|
||||
e.g. CCLI Number (e.g.CCLI-Liednummer: 2672885)
|
||||
``Song Copyright``
|
||||
e.g. © 1999 Integrity's Hosanna! Music | LenSongs Publishing
|
||||
``Song Authors``
|
||||
``Song Authors``
|
||||
e.g. Lenny LeBlanc | Paul Baloche
|
||||
``Licencing info``
|
||||
e.g. For use solely with the SongSelect Terms of Use.
|
||||
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
|
||||
``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'';
|
||||
song_copyright = u''
|
||||
verse_start = False
|
||||
for line in textList:
|
||||
clean_line = line.strip()
|
||||
if not clean_line:
|
||||
if line_number==0:
|
||||
if line_number == 0:
|
||||
continue
|
||||
elif verse_start:
|
||||
if verse_text:
|
||||
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:
|
||||
if line_number == 0:
|
||||
song_name = clean_line
|
||||
line_number += 1
|
||||
#line_number=1, verses
|
||||
elif 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
|
||||
@ -285,15 +286,16 @@ class CCLIFileImport(SongImport):
|
||||
verse_text = verse_text + line
|
||||
else:
|
||||
#line_number=2, copyright
|
||||
if line_number==2:
|
||||
if line_number == 2:
|
||||
line_number += 1
|
||||
song_copyright = clean_line
|
||||
#n=3, authors
|
||||
elif line_number==3:
|
||||
#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')):
|
||||
#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'/')
|
||||
@ -307,4 +309,3 @@ class CCLIFileImport(SongImport):
|
||||
self.ccli_number = song_ccli
|
||||
self.comments = song_comments
|
||||
self.finish()
|
||||
|
||||
|
255
openlp/plugins/songs/lib/ewimport.py
Normal file
255
openlp/plugins/songs/lib/ewimport.py
Normal file
@ -0,0 +1,255 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
"""
|
||||
The :mod:`ewimport` module provides the functionality for importing
|
||||
EasyWorship song databases into the current installation database.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import struct
|
||||
|
||||
from songimport import SongImport
|
||||
|
||||
def strip_rtf(blob):
|
||||
depth = 0
|
||||
control = False
|
||||
clear_text = []
|
||||
control_word = []
|
||||
for c in blob:
|
||||
if control:
|
||||
# for delimiters, set control to False
|
||||
if c == '{':
|
||||
if len(control_word) > 0:
|
||||
depth += 1
|
||||
control = False
|
||||
elif c == '}':
|
||||
if len(control_word) > 0:
|
||||
depth -= 1
|
||||
control = False
|
||||
elif c == '\\':
|
||||
new_control = (len(control_word) > 0)
|
||||
control = False
|
||||
elif c.isspace():
|
||||
control = False
|
||||
else:
|
||||
control_word.append(c)
|
||||
if len(control_word) == 3 and control_word[0] == '\'':
|
||||
control = False
|
||||
if not control:
|
||||
if len(control_word) == 0:
|
||||
if c == '{' or c == '}' or c == '\\':
|
||||
clear_text.append(c)
|
||||
else:
|
||||
control_str = ''.join(control_word)
|
||||
if control_str == 'par' or control_str == 'line':
|
||||
clear_text.append(u'\n')
|
||||
elif control_str == 'tab':
|
||||
clear_text.append(u'\n')
|
||||
elif control_str[0] == '\'':
|
||||
# Really should take RTF character set into account but
|
||||
# for now assume ANSI (Windows-1252) and call it good
|
||||
s = chr(int(control_str[1:3], 16))
|
||||
clear_text.append(s.decode(u'windows-1252'))
|
||||
del control_word[:]
|
||||
if c == '\\' and new_control:
|
||||
control = True
|
||||
elif c == '{':
|
||||
depth += 1
|
||||
elif c == '}':
|
||||
depth -= 1
|
||||
elif depth > 2:
|
||||
continue
|
||||
elif c == '\n' or c == '\r':
|
||||
continue
|
||||
elif c == '\\':
|
||||
control = True
|
||||
else:
|
||||
clear_text.append(c)
|
||||
return u''.join(clear_text)
|
||||
|
||||
class FieldDescEntry:
|
||||
def __init__(self, name, type, size):
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.size = size
|
||||
|
||||
class EasyWorshipSongImport(SongImport):
|
||||
"""
|
||||
The :class:`EasyWorshipSongImport` class provides OpenLP with the
|
||||
ability to import EasyWorship song files.
|
||||
"""
|
||||
def __init__(self, manager, **kwargs):
|
||||
self.import_source = kwargs[u'filename']
|
||||
SongImport.__init__(self, manager)
|
||||
|
||||
def do_import(self):
|
||||
# Open the DB and MB files if they exist
|
||||
import_source_mb = self.import_source.replace('.DB', '.MB')
|
||||
if not os.path.isfile(self.import_source):
|
||||
return False
|
||||
if not os.path.isfile(import_source_mb):
|
||||
return False
|
||||
db_size = os.path.getsize(self.import_source)
|
||||
if db_size < 0x800:
|
||||
return False
|
||||
db_file = open(self.import_source, 'rb')
|
||||
self.memo_file = open(import_source_mb, 'rb')
|
||||
# Don't accept files that are clearly not paradox files
|
||||
record_size, header_size, block_size, first_block, num_fields \
|
||||
= struct.unpack('<hhxb8xh17xh', db_file.read(35))
|
||||
if header_size != 0x800 or block_size < 1 or block_size > 4:
|
||||
db_file.close()
|
||||
self.memo_file.close()
|
||||
return False
|
||||
# There does not appear to be a _reliable_ way of getting the number
|
||||
# of songs/records, so let's use file blocks for measuring progress.
|
||||
total_blocks = (db_size - header_size) / (block_size * 1024)
|
||||
self.import_wizard.importProgressBar.setMaximum(total_blocks)
|
||||
# Read the field description information
|
||||
db_file.seek(120)
|
||||
field_info = db_file.read(num_fields * 2)
|
||||
db_file.seek(4 + (num_fields * 4) + 261, os.SEEK_CUR)
|
||||
field_names = db_file.read(header_size - db_file.tell()).split('\0',
|
||||
num_fields)
|
||||
field_names.pop()
|
||||
field_descs = []
|
||||
for i,field_name in enumerate(field_names):
|
||||
field_type, field_size = struct.unpack_from('BB', field_info, i * 2)
|
||||
field_descs.append(FieldDescEntry(field_name, field_type,
|
||||
field_size))
|
||||
self.set_record_struct(field_descs)
|
||||
# Pick out the field description indexes we will need
|
||||
success = True
|
||||
try:
|
||||
fi_title = self.find_field(u'Title')
|
||||
fi_author = self.find_field(u'Author')
|
||||
fi_copy = self.find_field(u'Copyright')
|
||||
fi_admin = self.find_field(u'Administrator')
|
||||
fi_words = self.find_field(u'Words')
|
||||
fi_ccli = self.find_field(u'Song Number')
|
||||
except IndexError:
|
||||
# This is the wrong table
|
||||
success = False
|
||||
# Loop through each block of the file
|
||||
cur_block = first_block
|
||||
while cur_block != 0 and success:
|
||||
db_file.seek(header_size + ((cur_block - 1) * 1024 * block_size))
|
||||
cur_block, rec_count = struct.unpack('<h2xh', db_file.read(6))
|
||||
rec_count = (rec_count + record_size) / record_size
|
||||
# Loop through each record within the current block
|
||||
for i in range(rec_count):
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
break
|
||||
raw_record = db_file.read(record_size)
|
||||
self.fields = self.record_struct.unpack(raw_record)
|
||||
self.set_defaults()
|
||||
self.title = self.get_field(fi_title)
|
||||
self.import_wizard.incrementProgressBar(
|
||||
u'Importing "%s"...' % self.title, 0)
|
||||
self.copyright = self.get_field(fi_copy) + \
|
||||
u', Administered by ' + self.get_field(fi_admin)
|
||||
self.ccli_number = self.get_field(fi_ccli)
|
||||
# Format the lyrics
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
break
|
||||
words = self.get_field(fi_words)
|
||||
words = strip_rtf(words)
|
||||
for verse in words.split(u'\n\n'):
|
||||
self.add_verse(verse.strip(), u'V')
|
||||
# Split up the authors
|
||||
authors = self.get_field(fi_author)
|
||||
author_list = authors.split(u'/')
|
||||
if len(author_list) < 2:
|
||||
author_list = authors.split(u',')
|
||||
for author_name in author_list:
|
||||
self.add_author(author_name.strip())
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
break
|
||||
self.finish()
|
||||
if not self.stop_import_flag:
|
||||
self.import_wizard.incrementProgressBar(u'')
|
||||
db_file.close()
|
||||
self.memo_file.close()
|
||||
return success
|
||||
|
||||
def find_field(self, field_name):
|
||||
return [i for i,x in enumerate(self.field_descs) \
|
||||
if x.name == field_name][0]
|
||||
|
||||
def set_record_struct(self, field_descs):
|
||||
# Begin with empty field struct list
|
||||
fsl = ['>']
|
||||
for field_desc in field_descs:
|
||||
if field_desc.type == 1:
|
||||
# string
|
||||
fsl.append('%ds' % field_desc.size)
|
||||
elif field_desc.type == 3:
|
||||
# 16-bit int
|
||||
fsl.append('H')
|
||||
elif field_desc.type == 4:
|
||||
# 32-bit int
|
||||
fsl.append('I')
|
||||
elif field_desc.type == 9:
|
||||
# Logical
|
||||
fsl.append('B')
|
||||
elif field_desc.type == 0x0c:
|
||||
# Memo
|
||||
fsl.append('%ds' % field_desc.size)
|
||||
elif field_desc.type == 0x0d:
|
||||
# Blob
|
||||
fsl.append('%ds' % field_desc.size)
|
||||
elif field_desc.type == 0x15:
|
||||
# Timestamp
|
||||
fsl.append('Q')
|
||||
else:
|
||||
fsl.append('%ds' % field_desc.size)
|
||||
self.record_struct = struct.Struct(''.join(fsl))
|
||||
self.field_descs = field_descs
|
||||
|
||||
def get_field(self, field_desc_index):
|
||||
field = self.fields[field_desc_index]
|
||||
field_desc = self.field_descs[field_desc_index]
|
||||
# Check for 'blank' entries
|
||||
if isinstance(field, str):
|
||||
if len(field.rstrip('\0')) == 0:
|
||||
return u''
|
||||
elif field == 0:
|
||||
return 0
|
||||
# Format the field depending on the field type
|
||||
if field_desc.type == 1:
|
||||
# string
|
||||
return field.rstrip('\0').decode(u'windows-1252')
|
||||
elif field_desc.type == 3:
|
||||
# 16-bit int
|
||||
return field ^ 0x8000
|
||||
elif field_desc.type == 4:
|
||||
# 32-bit int
|
||||
return field ^ 0x80000000
|
||||
elif field_desc.type == 9:
|
||||
# Logical
|
||||
return (field ^ 0x80 == 1)
|
||||
elif field_desc.type == 0x0c or field_desc.type == 0x0d:
|
||||
# Memo or Blob
|
||||
sub_block, block_start, blob_size = \
|
||||
struct.unpack_from('<bhxi', field, len(field)-10)
|
||||
self.memo_file.seek(block_start * 256)
|
||||
memo_block_type, = struct.unpack('b', self.memo_file.read(1))
|
||||
if memo_block_type == 2:
|
||||
self.memo_file.seek(8, os.SEEK_CUR)
|
||||
elif memo_block_type == 3:
|
||||
if sub_block < 0 or sub_block > 63:
|
||||
return u'';
|
||||
self.memo_file.seek(11 + (5 * sub_block), os.SEEK_CUR)
|
||||
sub_block_start, = struct.unpack('B', self.memo_file.read(1))
|
||||
self.memo_file.seek((block_start * 256) +
|
||||
(sub_block_start * 16))
|
||||
else:
|
||||
return u'';
|
||||
return self.memo_file.read(blob_size)
|
||||
else:
|
||||
return 0
|
@ -26,14 +26,25 @@
|
||||
|
||||
from opensongimport import OpenSongImport
|
||||
from olpimport import OpenLPSongImport
|
||||
from olp1import import OpenLP1SongImport
|
||||
from wowimport import WowImport
|
||||
from cclifileimport import CCLIFileImport
|
||||
from ewimport import EasyWorshipSongImport
|
||||
# Imports that might fail
|
||||
try:
|
||||
from olp1import import OpenLP1SongImport
|
||||
has_openlp1 = True
|
||||
except ImportError:
|
||||
has_openlp1 = False
|
||||
try:
|
||||
from sofimport import SofImport
|
||||
from oooimport import OooImport
|
||||
from cclifileimport import CCLIFileImport
|
||||
from wowimport import WowImport
|
||||
has_sof = True
|
||||
except ImportError:
|
||||
pass
|
||||
has_sof = False
|
||||
try:
|
||||
from oooimport import OooImport
|
||||
has_ooo = True
|
||||
except ImportError:
|
||||
has_ooo = False
|
||||
|
||||
class SongFormat(object):
|
||||
"""
|
||||
@ -41,6 +52,7 @@ class SongFormat(object):
|
||||
plus a few helper functions to facilitate generic handling of song types
|
||||
for importing.
|
||||
"""
|
||||
_format_availability = {}
|
||||
Unknown = -1
|
||||
OpenLP2 = 0
|
||||
OpenLP1 = 1
|
||||
@ -50,7 +62,8 @@ class SongFormat(object):
|
||||
CCLI = 5
|
||||
SongsOfFellowship = 6
|
||||
Generic = 7
|
||||
CSV = 8
|
||||
#CSV = 8
|
||||
EasyWorship = 8
|
||||
|
||||
@staticmethod
|
||||
def get_class(format):
|
||||
@ -74,6 +87,8 @@ class SongFormat(object):
|
||||
return OooImport
|
||||
elif format == SongFormat.CCLI:
|
||||
return CCLIFileImport
|
||||
elif format == SongFormat.EasyWorship:
|
||||
return EasyWorshipSongImport
|
||||
# else:
|
||||
return None
|
||||
|
||||
@ -90,7 +105,20 @@ class SongFormat(object):
|
||||
SongFormat.WordsOfWorship,
|
||||
SongFormat.CCLI,
|
||||
SongFormat.SongsOfFellowship,
|
||||
SongFormat.Generic
|
||||
SongFormat.Generic,
|
||||
SongFormat.EasyWorship
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def set_availability(format, available):
|
||||
SongFormat._format_availability[format] = available
|
||||
|
||||
@staticmethod
|
||||
def get_availability(format):
|
||||
return SongFormat._format_availability.get(format, True)
|
||||
|
||||
SongFormat.set_availability(SongFormat.OpenLP1, has_openlp1)
|
||||
SongFormat.set_availability(SongFormat.SongsOfFellowship, has_sof)
|
||||
SongFormat.set_availability(SongFormat.Generic, has_ooo)
|
||||
|
||||
__all__ = [u'SongFormat']
|
||||
|
@ -28,10 +28,8 @@ The :mod:`olp1import` module provides the functionality for importing
|
||||
openlp.org 1.x song databases into the current installation database.
|
||||
"""
|
||||
import logging
|
||||
try:
|
||||
import sqlite
|
||||
except:
|
||||
pass
|
||||
import chardet
|
||||
import sqlite
|
||||
|
||||
from openlp.core.lib import translate
|
||||
from songimport import SongImport
|
||||
@ -43,6 +41,8 @@ class OpenLP1SongImport(SongImport):
|
||||
The :class:`OpenLP1SongImport` class provides OpenLP with the ability to
|
||||
import song databases from installations of openlp.org 1.x.
|
||||
"""
|
||||
last_encoding = u'windows-1252'
|
||||
|
||||
def __init__(self, manager, **kwargs):
|
||||
"""
|
||||
Initialise the import.
|
||||
@ -56,6 +56,34 @@ class OpenLP1SongImport(SongImport):
|
||||
SongImport.__init__(self, manager)
|
||||
self.import_source = kwargs[u'filename']
|
||||
|
||||
def decode_string(self, raw, guess):
|
||||
"""
|
||||
Use chardet to detect the encoding of the raw string, and convert it
|
||||
to unicode.
|
||||
|
||||
``raw``
|
||||
The raw bytestring to decode.
|
||||
``guess``
|
||||
What chardet guessed the encoding to be.
|
||||
"""
|
||||
if guess[u'confidence'] < 0.8:
|
||||
codec = u'windows-1252'
|
||||
else:
|
||||
codec = guess[u'encoding']
|
||||
try:
|
||||
decoded = unicode(raw, codec)
|
||||
self.last_encoding = codec
|
||||
except UnicodeDecodeError:
|
||||
log.exception(u'Error in detecting openlp.org 1.x database encoding.')
|
||||
try:
|
||||
decoded = unicode(raw, self.last_encoding)
|
||||
except UnicodeDecodeError:
|
||||
# possibly show an error form
|
||||
#self.import_wizard.showError(u'There was a problem '
|
||||
# u'detecting the encoding of a string')
|
||||
decoded = raw
|
||||
return decoded
|
||||
|
||||
def do_import(self):
|
||||
"""
|
||||
Run the import for an openlp.org 1.x song database.
|
||||
@ -63,6 +91,11 @@ class OpenLP1SongImport(SongImport):
|
||||
# Connect to the database
|
||||
connection = sqlite.connect(self.import_source)
|
||||
cursor = connection.cursor()
|
||||
# Determine if we're using a new or an old DB
|
||||
cursor.execute(u'SELECT name FROM sqlite_master '
|
||||
u'WHERE type = \'table\' AND name = \'tracks\'')
|
||||
table_list = cursor.fetchall()
|
||||
new_db = len(table_list) > 0
|
||||
# Count the number of records we need to import, for the progress bar
|
||||
cursor.execute(u'SELECT COUNT(songid) FROM songs')
|
||||
count = int(cursor.fetchone()[0])
|
||||
@ -71,9 +104,10 @@ class OpenLP1SongImport(SongImport):
|
||||
# "cache" our list of authors
|
||||
cursor.execute(u'SELECT authorid, authorname FROM authors')
|
||||
authors = cursor.fetchall()
|
||||
# "cache" our list of tracks
|
||||
cursor.execute(u'SELECT trackid, fulltrackname FROM tracks')
|
||||
tracks = cursor.fetchall()
|
||||
if new_db:
|
||||
# "cache" our list of tracks
|
||||
cursor.execute(u'SELECT trackid, fulltrackname FROM tracks')
|
||||
tracks = cursor.fetchall()
|
||||
# Import the songs
|
||||
cursor.execute(u'SELECT songid, songtitle, lyrics || \'\' AS lyrics, '
|
||||
u'copyrightinfo FROM songs')
|
||||
@ -84,9 +118,10 @@ class OpenLP1SongImport(SongImport):
|
||||
success = False
|
||||
break
|
||||
song_id = song[0]
|
||||
title = unicode(song[1], u'cp1252')
|
||||
lyrics = unicode(song[2], u'cp1252').replace(u'\r', u'')
|
||||
copyright = unicode(song[3], u'cp1252')
|
||||
guess = chardet.detect(song[2])
|
||||
title = self.decode_string(song[1], guess)
|
||||
lyrics = self.decode_string(song[2], guess).replace(u'\r', u'')
|
||||
copyright = self.decode_string(song[3], guess)
|
||||
self.import_wizard.incrementProgressBar(
|
||||
unicode(translate('SongsPlugin.ImportWizardForm',
|
||||
'Importing "%s"...')) % title)
|
||||
@ -102,15 +137,12 @@ class OpenLP1SongImport(SongImport):
|
||||
break
|
||||
for author in authors:
|
||||
if author[0] == author_id[0]:
|
||||
self.parse_author(unicode(author[1], u'cp1252'))
|
||||
self.parse_author(self.decode_string(author[1], guess))
|
||||
break
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
break
|
||||
cursor.execute(u'SELECT name FROM sqlite_master '
|
||||
u'WHERE type = \'table\' AND name = \'tracks\'')
|
||||
table_list = cursor.fetchall()
|
||||
if len(table_list) > 0:
|
||||
if new_db:
|
||||
cursor.execute(u'SELECT trackid FROM songtracks '
|
||||
u'WHERE songid = %s ORDER BY listindex' % song_id)
|
||||
track_ids = cursor.fetchall()
|
||||
@ -120,10 +152,11 @@ class OpenLP1SongImport(SongImport):
|
||||
break
|
||||
for track in tracks:
|
||||
if track[0] == track_id[0]:
|
||||
self.add_media_file(unicode(track[1], u'cp1252'))
|
||||
self.add_media_file(self.decode_string(track[1], guess))
|
||||
break
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
break
|
||||
self.finish()
|
||||
return success
|
||||
|
||||
|
@ -59,6 +59,7 @@ class OooImport(SongImport):
|
||||
self.document = None
|
||||
self.process_started = False
|
||||
self.filenames = kwargs[u'filenames']
|
||||
self.uno_connection_type = u'pipe' #u'socket'
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'song_stop_import'), self.stop_import)
|
||||
|
||||
@ -106,8 +107,14 @@ class OooImport(SongImport):
|
||||
loop = 0
|
||||
while ctx is None and loop < 5:
|
||||
try:
|
||||
ctx = resolver.resolve(u'uno:socket,host=localhost,' \
|
||||
+ 'port=2002;urp;StarOffice.ComponentContext')
|
||||
if self.uno_connection_type == u'pipe':
|
||||
ctx = resolver.resolve(u'uno:' \
|
||||
+ u'pipe,name=openlp_pipe;' \
|
||||
+ u'urp;StarOffice.ComponentContext')
|
||||
else:
|
||||
ctx = resolver.resolve(u'uno:' \
|
||||
+ u'socket,host=localhost,port=2002;' \
|
||||
+ u'urp;StarOffice.ComponentContext')
|
||||
except:
|
||||
pass
|
||||
self.start_ooo_process()
|
||||
@ -123,9 +130,14 @@ class OooImport(SongImport):
|
||||
self.manager._FlagAsMethod(u'Bridge_GetStruct')
|
||||
self.manager._FlagAsMethod(u'Bridge_GetValueObject')
|
||||
else:
|
||||
cmd = u'openoffice.org -nologo -norestore -minimized ' \
|
||||
+ u'-invisible -nofirststartwizard ' \
|
||||
+ '-accept="socket,host=localhost,port=2002;urp;"'
|
||||
if self.uno_connection_type == u'pipe':
|
||||
cmd = u'openoffice.org -nologo -norestore -minimized ' \
|
||||
+ u'-invisible -nofirststartwizard ' \
|
||||
+ u'-accept=pipe,name=openlp_pipe;urp;'
|
||||
else:
|
||||
cmd = u'openoffice.org -nologo -norestore -minimized ' \
|
||||
+ u'-invisible -nofirststartwizard ' \
|
||||
+ u'-accept=socket,host=localhost,port=2002;urp;'
|
||||
process = QtCore.QProcess()
|
||||
process.startDetached(cmd)
|
||||
process.waitForStarted()
|
||||
|
@ -29,7 +29,9 @@ import os
|
||||
from zipfile import ZipFile
|
||||
from lxml import objectify
|
||||
from lxml.etree import Error, LxmlError
|
||||
import re
|
||||
|
||||
from openlp.core.lib import translate
|
||||
from openlp.plugins.songs.lib.songimport import SongImport
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -112,12 +114,22 @@ class OpenSongImport(SongImport):
|
||||
|
||||
def do_import(self):
|
||||
"""
|
||||
Import either a single opensong file, or a zipfile containing multiple
|
||||
opensong files. If `self.commit` is set False, the import will not be
|
||||
committed to the database (useful for test scripts).
|
||||
Import either each of the files in self.filenames - each element of
|
||||
which can be either a single opensong file, or a zipfile containing
|
||||
multiple opensong files. If `self.commit` is set False, the
|
||||
import will not be committed to the database (useful for test scripts).
|
||||
"""
|
||||
success = True
|
||||
self.import_wizard.importProgressBar.setMaximum(len(self.filenames))
|
||||
numfiles = 0
|
||||
for filename in self.filenames:
|
||||
ext = os.path.splitext(filename)[1]
|
||||
if ext.lower() == u'.zip':
|
||||
z = ZipFile(filename, u'r')
|
||||
numfiles += len(z.infolist())
|
||||
else:
|
||||
numfiles += 1
|
||||
log.debug("Total number of files: %d", numfiles)
|
||||
self.import_wizard.importProgressBar.setMaximum(numfiles)
|
||||
for filename in self.filenames:
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
@ -126,9 +138,6 @@ class OpenSongImport(SongImport):
|
||||
if ext.lower() == u'.zip':
|
||||
log.debug(u'Zipfile found %s', filename)
|
||||
z = ZipFile(filename, u'r')
|
||||
self.import_wizard.importProgressBar.setMaximum(
|
||||
self.import_wizard.importProgressBar.maximum() +
|
||||
len(z.infolist()))
|
||||
for song in z.infolist():
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
@ -137,6 +146,7 @@ class OpenSongImport(SongImport):
|
||||
if parts[-1] == u'':
|
||||
#No final part => directory
|
||||
continue
|
||||
log.info(u'Zip importing %s', parts[-1])
|
||||
self.import_wizard.incrementProgressBar(
|
||||
unicode(translate('SongsPlugin.ImportWizardForm',
|
||||
'Importing %s...')) % parts[-1])
|
||||
@ -144,11 +154,11 @@ class OpenSongImport(SongImport):
|
||||
self.do_import_file(songfile)
|
||||
if self.commit:
|
||||
self.finish()
|
||||
self.set_defaults()
|
||||
if self.stop_import_flag:
|
||||
success = False
|
||||
break
|
||||
else:
|
||||
# not a zipfile
|
||||
log.info('Direct import %s', filename)
|
||||
self.import_wizard.incrementProgressBar(
|
||||
unicode(translate('SongsPlugin.ImportWizardForm',
|
||||
@ -157,9 +167,7 @@ class OpenSongImport(SongImport):
|
||||
self.do_import_file(file)
|
||||
if self.commit:
|
||||
self.finish()
|
||||
self.set_defaults()
|
||||
if not self.commit:
|
||||
self.finish()
|
||||
|
||||
return success
|
||||
|
||||
def do_import_file(self, file):
|
||||
@ -167,10 +175,10 @@ class OpenSongImport(SongImport):
|
||||
Process the OpenSong file - pass in a file-like object,
|
||||
not a filename
|
||||
"""
|
||||
self.authors = []
|
||||
self.set_defaults()
|
||||
try:
|
||||
tree = objectify.parse(file)
|
||||
except Error, LxmlError:
|
||||
except (Error, LxmlError):
|
||||
log.exception(u'Error parsing XML')
|
||||
return
|
||||
root = tree.getroot()
|
||||
@ -196,7 +204,6 @@ class OpenSongImport(SongImport):
|
||||
self.topics.append(unicode(root.alttheme))
|
||||
# data storage while importing
|
||||
verses = {}
|
||||
lyrics = unicode(root.lyrics)
|
||||
# keep track of a "default" verse order, in case none is specified
|
||||
our_verse_order = []
|
||||
verses_seen = {}
|
||||
@ -204,6 +211,7 @@ class OpenSongImport(SongImport):
|
||||
# erm, versetype!
|
||||
versetype = u'V'
|
||||
versenum = None
|
||||
lyrics = unicode(root.lyrics)
|
||||
for thisline in lyrics.split(u'\n'):
|
||||
# remove comments
|
||||
semicolon = thisline.find(u';')
|
||||
@ -218,16 +226,18 @@ class OpenSongImport(SongImport):
|
||||
continue
|
||||
# verse/chorus/etc. marker
|
||||
if thisline[0] == u'[':
|
||||
versetype = thisline[1].upper()
|
||||
if versetype.isdigit():
|
||||
versenum = versetype
|
||||
versetype = u'V'
|
||||
elif thisline[2] != u']':
|
||||
# there's a number to go with it - extract that as well
|
||||
right_bracket = thisline.find(u']')
|
||||
versenum = thisline[2:right_bracket]
|
||||
# drop the square brackets
|
||||
right_bracket = thisline.find(u']')
|
||||
content = thisline[1:right_bracket].upper()
|
||||
# have we got any digits? If so, versenumber is everything from the digits
|
||||
# to the end (even if there are some alpha chars on the end)
|
||||
match = re.match(u'(.*)(\d+.*)', content)
|
||||
if match is not None:
|
||||
versetype = match.group(1)
|
||||
versenum = match.group(2)
|
||||
else:
|
||||
# if there's no number, assume it's no.1
|
||||
# otherwise we assume number 1 and take the whole prefix as versetype
|
||||
versetype = content
|
||||
versenum = u'1'
|
||||
continue
|
||||
words = None
|
||||
@ -235,10 +245,10 @@ class OpenSongImport(SongImport):
|
||||
if thisline[0].isdigit():
|
||||
versenum = thisline[0]
|
||||
words = thisline[1:].strip()
|
||||
if words is None and \
|
||||
versenum is not None and \
|
||||
versetype is not None:
|
||||
if words is None:
|
||||
words = thisline
|
||||
if not versenum:
|
||||
versenum = u'1'
|
||||
if versenum is not None:
|
||||
versetag = u'%s%s' % (versetype, versenum)
|
||||
if not verses.has_key(versetype):
|
||||
@ -259,10 +269,13 @@ class OpenSongImport(SongImport):
|
||||
versetypes.sort()
|
||||
versetags = {}
|
||||
for versetype in versetypes:
|
||||
our_verse_type = versetype
|
||||
if our_verse_type == u'':
|
||||
our_verse_type = u'V'
|
||||
versenums = verses[versetype].keys()
|
||||
versenums.sort()
|
||||
for num in versenums:
|
||||
versetag = u'%s%s' % (versetype, num)
|
||||
versetag = u'%s%s' % (our_verse_type, num)
|
||||
lines = u'\n'.join(verses[versetype][num])
|
||||
self.verses.append([versetag, lines])
|
||||
# Keep track of what we have for error checking later
|
||||
@ -271,16 +284,23 @@ class OpenSongImport(SongImport):
|
||||
order = []
|
||||
if u'presentation' in fields and root.presentation != u'':
|
||||
order = unicode(root.presentation)
|
||||
order = order.split()
|
||||
# We make all the tags in the lyrics upper case, so match that here
|
||||
# and then split into a list on the whitespace
|
||||
order = order.upper().split()
|
||||
else:
|
||||
if len(our_verse_order) > 0:
|
||||
order = our_verse_order
|
||||
else:
|
||||
log.warn(u'No verse order available for %s, skipping.', self.title)
|
||||
log.warn(u'No verse order available for %s, skipping.',
|
||||
self.title)
|
||||
for tag in order:
|
||||
if len(tag) == 1:
|
||||
tag = tag + u'1' # Assume it's no.1 if it's not there
|
||||
if tag[0].isdigit():
|
||||
# Assume it's a verse if it has no prefix
|
||||
tag = u'V' + tag
|
||||
elif not re.search('\d+', tag):
|
||||
# Assume it's no.1 if there's no digits
|
||||
tag = tag + u'1'
|
||||
if not versetags.has_key(tag):
|
||||
log.warn(u'Got order %s but not in versetags, skipping', tag)
|
||||
log.info(u'Got order %s but not in versetags, dropping this item from presentation order', tag)
|
||||
else:
|
||||
self.verse_order_list.append(tag)
|
||||
|
@ -55,8 +55,12 @@ class SongImport(QtCore.QObject):
|
||||
self.set_defaults()
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'songs_stop_import'), self.stop_import)
|
||||
|
||||
def set_defaults(self):
|
||||
"""
|
||||
Create defaults for properties - call this before each song
|
||||
if importing many songs at once to ensure a clean beginning
|
||||
"""
|
||||
self.authors = []
|
||||
self.title = u''
|
||||
self.song_number = u''
|
||||
self.alternate_title = u''
|
||||
@ -71,13 +75,7 @@ class SongImport(QtCore.QObject):
|
||||
self.song_book_pub = u''
|
||||
self.verse_order_list = []
|
||||
self.verses = []
|
||||
self.versecount = 0
|
||||
self.choruscount = 0
|
||||
self.bridgecount = 0
|
||||
self.introcount = 0
|
||||
self.prechoruscount = 0
|
||||
self.endingcount = 0
|
||||
self.othercount = 0
|
||||
self.versecounts = {}
|
||||
self.copyright_string = unicode(translate(
|
||||
'SongsPlugin.SongImport', 'copyright'))
|
||||
self.copyright_symbol = unicode(translate(
|
||||
@ -198,7 +196,7 @@ class SongImport(QtCore.QObject):
|
||||
return
|
||||
self.media_files.append(filename)
|
||||
|
||||
def add_verse(self, verse, versetag=None):
|
||||
def add_verse(self, verse, versetag=u'V'):
|
||||
"""
|
||||
Add a verse. This is the whole verse, lines split by \n
|
||||
Verse tag can be V1/C1/B etc, or 'V' and 'C' (will count the verses/
|
||||
@ -210,33 +208,14 @@ class SongImport(QtCore.QObject):
|
||||
if oldverse.strip() == verse.strip():
|
||||
self.verse_order_list.append(oldversetag)
|
||||
return
|
||||
if versetag == u'V' or not versetag:
|
||||
self.versecount += 1
|
||||
versetag = u'V' + unicode(self.versecount)
|
||||
if versetag.startswith(u'C'):
|
||||
self.choruscount += 1
|
||||
if versetag == u'C':
|
||||
versetag += unicode(self.choruscount)
|
||||
if versetag.startswith(u'B'):
|
||||
self.bridgecount += 1
|
||||
if versetag == u'B':
|
||||
versetag += unicode(self.bridgecount)
|
||||
if versetag.startswith(u'I'):
|
||||
self.introcount += 1
|
||||
if versetag == u'I':
|
||||
versetag += unicode(self.introcount)
|
||||
if versetag.startswith(u'P'):
|
||||
self.prechoruscount += 1
|
||||
if versetag == u'P':
|
||||
versetag += unicode(self.prechoruscount)
|
||||
if versetag.startswith(u'E'):
|
||||
self.endingcount += 1
|
||||
if versetag == u'E':
|
||||
versetag += unicode(self.endingcount)
|
||||
if versetag.startswith(u'O'):
|
||||
self.othercount += 1
|
||||
if versetag == u'O':
|
||||
versetag += unicode(self.othercount)
|
||||
if versetag[0] in self.versecounts:
|
||||
self.versecounts[versetag[0]] += 1
|
||||
else:
|
||||
self.versecounts[versetag[0]] = 1
|
||||
if len(versetag) == 1:
|
||||
versetag += unicode(self.versecounts[versetag[0]])
|
||||
elif int(versetag[1:]) > self.versecounts[versetag[0]]:
|
||||
self.versecounts[versetag[0]] = int(versetag[1:])
|
||||
self.verses.append([versetag, verse.rstrip()])
|
||||
self.verse_order_list.append(versetag)
|
||||
if versetag.startswith(u'V') and self.contains_verse(u'C1'):
|
||||
@ -280,13 +259,16 @@ class SongImport(QtCore.QObject):
|
||||
"""
|
||||
Write the song and its fields to disk
|
||||
"""
|
||||
log.info(u'commiting song %s to database', self.title)
|
||||
song = Song()
|
||||
song.title = self.title
|
||||
song.search_title = self.remove_punctuation(self.title) \
|
||||
+ '@' + self.alternate_title
|
||||
song.song_number = self.song_number
|
||||
song.search_lyrics = u''
|
||||
verses_changed_to_other = {}
|
||||
sxml = SongXMLBuilder()
|
||||
other_count = 1
|
||||
for (versetag, versetext) in self.verses:
|
||||
if versetag[0] == u'C':
|
||||
versetype = VerseType.to_string(VerseType.Chorus)
|
||||
@ -301,10 +283,18 @@ class SongImport(QtCore.QObject):
|
||||
elif versetag[0] == u'E':
|
||||
versetype = VerseType.to_string(VerseType.Ending)
|
||||
else:
|
||||
newversetag = u'O%d' % other_count
|
||||
verses_changed_to_other[versetag] = newversetag
|
||||
other_count += 1
|
||||
versetype = VerseType.to_string(VerseType.Other)
|
||||
log.info(u'Versetype %s changing to %s' , versetag, newversetag)
|
||||
versetag = newversetag
|
||||
sxml.add_verse_to_lyrics(versetype, versetag[1:], versetext)
|
||||
song.search_lyrics += u' ' + self.remove_punctuation(versetext)
|
||||
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
||||
for i, current_verse_tag in enumerate(self.verse_order_list):
|
||||
if verses_changed_to_other.has_key(current_verse_tag):
|
||||
self.verse_order_list[i] = verses_changed_to_other[current_verse_tag]
|
||||
song.verse_order = u' '.join(self.verse_order_list)
|
||||
song.copyright = self.copyright
|
||||
song.comments = self.comments
|
||||
|
@ -1,10 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<song>
|
||||
<title>Martins Test</title>
|
||||
<author>MartiÑ Thómpson</author>
|
||||
<author>MartiÑ & Martin2 Thómpson</author>
|
||||
<copyright>2010 Martin Thompson</copyright>
|
||||
<hymn_number>1</hymn_number>
|
||||
<presentation>V1 C V2 C2 V3 B1 V1</presentation>
|
||||
<presentation>V1 C V2 C2 3a B1 V1 T U Rap1 Rap2 Rap3</presentation>
|
||||
<ccli>Blah</ccli>
|
||||
<capo print="false"></capo>
|
||||
<key></key>
|
||||
@ -17,7 +17,12 @@
|
||||
<alttheme>TestAltTheme</alttheme>
|
||||
<tempo></tempo>
|
||||
<time_sig></time_sig>
|
||||
<lyrics>;Comment
|
||||
<lyrics>[3a]
|
||||
. G A B
|
||||
V3 Line 1
|
||||
. G A B
|
||||
V3 Line 2
|
||||
|
||||
. A B C
|
||||
1 v1 Line 1___
|
||||
2 v2 Line 1___
|
||||
@ -25,10 +30,6 @@
|
||||
1 V1 Line 2
|
||||
2 V2 Line 2
|
||||
|
||||
[3]
|
||||
V3 Line 1
|
||||
V3 Line 2
|
||||
|
||||
[b1]
|
||||
Bridge 1
|
||||
---
|
||||
@ -36,12 +37,29 @@
|
||||
Bridge 1 line 2
|
||||
|
||||
[C]
|
||||
. A B
|
||||
. A B
|
||||
Chorus 1
|
||||
|
||||
[C2]
|
||||
. A B
|
||||
Chorus 2
|
||||
|
||||
[T]
|
||||
T Line 1
|
||||
|
||||
[Rap]
|
||||
1 Rap 1 Line 1
|
||||
2 Rap 2 Line 1
|
||||
1 Rap 1 Line 2
|
||||
2 Rap 2 Line 2
|
||||
|
||||
[rap3]
|
||||
Rap 3 Line 1
|
||||
Rap 3 Line 2
|
||||
|
||||
|
||||
[X]
|
||||
Unreferenced verse line 1
|
||||
</lyrics>
|
||||
<style index="default_style">
|
||||
<title enabled="true" valign="bottom" align="center" include_verse="false" margin-left="0" margin-right="0" margin-top="0" margin-bottom="0" font="Helvetica" size="26" bold="true" italic="true" underline="false" color="#FFFFFF" border="true" border_color="#000000" shadow="true" shadow_color="#000000" fill="false" fill_color="#000000"/>
|
||||
|
10
openlp/plugins/songs/lib/test/test3.opensong
Executable file
10
openlp/plugins/songs/lib/test/test3.opensong
Executable file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<song>
|
||||
<title>Test single verse</title>
|
||||
<author>Martin Thompson</author>
|
||||
<copyright>2010</copyright>
|
||||
<ccli>123456</ccli>
|
||||
<theme>Worship: Declaration</theme>
|
||||
<lyrics> Line 1
|
||||
Line 2
|
||||
</lyrics></song>
|
47
openlp/plugins/songs/lib/test/test_import_file.py
Normal file
47
openlp/plugins/songs/lib/test/test_import_file.py
Normal file
@ -0,0 +1,47 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2010 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
|
||||
# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
|
||||
# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
|
||||
# Carsten Tinggaard, Frode Woldsund #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
import sys
|
||||
|
||||
from openlp.plugins.songs.lib.opensongimport import OpenSongImport
|
||||
from openlp.core.lib.db import Manager
|
||||
from openlp.plugins.songs.lib.db import init_schema
|
||||
|
||||
import logging
|
||||
LOG_FILENAME = 'test_import_file.log'
|
||||
logging.basicConfig(filename=LOG_FILENAME,level=logging.INFO)
|
||||
|
||||
from test_opensongimport import wizard_stub, progbar_stub
|
||||
|
||||
def test(filenames):
|
||||
manager = Manager(u'songs', init_schema)
|
||||
o = OpenSongImport(manager, filenames=filenames)
|
||||
o.import_wizard = wizard_stub()
|
||||
o.commit = False
|
||||
o.do_import()
|
||||
o.print_song()
|
||||
|
||||
if __name__ == "__main__":
|
||||
test(sys.argv[1:])
|
@ -8,50 +8,29 @@ from traceback import print_exc
|
||||
import sys
|
||||
import codecs
|
||||
|
||||
import logging
|
||||
LOG_FILENAME = 'import.log'
|
||||
logging.basicConfig(filename=LOG_FILENAME,level=logging.INFO)
|
||||
|
||||
from test_opensongimport import wizard_stub, progbar_stub
|
||||
|
||||
# Useful test function for importing a variety of different files
|
||||
# Uncomment below depending on what problem trying to make occur!
|
||||
|
||||
def opensong_import_lots():
|
||||
ziploc = u'/home/mjt/openlp/OpenSong_Data/'
|
||||
files = []
|
||||
#files = [u'test.opensong.zip', ziploc+u'ADond.zip']
|
||||
files.extend(glob(ziploc+u'Songs.zip'))
|
||||
#files.extend(glob(ziploc+u'SOF.zip'))
|
||||
#files.extend(glob(ziploc+u'spanish_songs_for_opensong.zip'))
|
||||
# files.extend(glob(ziploc+u'opensong_*.zip'))
|
||||
files = [os.path.join(ziploc, u'RaoulSongs', u'Songs', u'Jesus Freak')]
|
||||
# files.extend(glob(ziploc+u'Songs.zip'))
|
||||
# files.extend(glob(ziploc+u'RaoulSongs.zip'))
|
||||
# files.extend(glob(ziploc+u'SOF.zip'))
|
||||
# files.extend(glob(ziploc+u'spanish_songs_for_opensong.zip'))
|
||||
# files.extend(glob(ziploc+u'opensong_*.zip'))
|
||||
errfile = codecs.open(u'import_lots_errors.txt', u'w', u'utf8')
|
||||
manager = Manager(u'songs', init_schema)
|
||||
for file in files:
|
||||
print u'Importing', file
|
||||
z = ZipFile(file, u'r')
|
||||
for song in z.infolist():
|
||||
# need to handle unicode filenames (CP437 - Winzip does this)
|
||||
filename = song.filename#.decode('cp852')
|
||||
parts = os.path.split(filename)
|
||||
if parts[-1] == u'':
|
||||
#No final part => directory
|
||||
continue
|
||||
print " ", file, ":",filename,
|
||||
songfile = z.open(song)
|
||||
#z.extract(song)
|
||||
#songfile=open(filename, u'r')
|
||||
o = OpenSongImport(manager)
|
||||
try:
|
||||
o.do_import_file(songfile)
|
||||
# o.song_import.print_song()
|
||||
except:
|
||||
print "Failure",
|
||||
|
||||
errfile.write(u'Failure: %s:%s\n' %(file, filename.decode('cp437')))
|
||||
songfile = z.open(song)
|
||||
for l in songfile.readlines():
|
||||
l = l.decode('utf8')
|
||||
print(u' |%s\n' % l.strip())
|
||||
errfile.write(u' |%s\n'%l.strip())
|
||||
print_exc(3, file = errfile)
|
||||
print_exc(3)
|
||||
sys.exit(1)
|
||||
# continue
|
||||
#o.finish()
|
||||
print "OK"
|
||||
#os.unlink(filename)
|
||||
# o.song_import.print_song()
|
||||
o = OpenSongImport(manager, filenames=files)
|
||||
o.import_wizard=wizard_stub()
|
||||
o.do_import()
|
||||
|
||||
if __name__ == "__main__":
|
||||
opensong_import_lots()
|
||||
|
@ -28,62 +28,101 @@ from openlp.plugins.songs.lib.opensongimport import OpenSongImport
|
||||
from openlp.core.lib.db import Manager
|
||||
from openlp.plugins.songs.lib.db import init_schema
|
||||
|
||||
import logging
|
||||
LOG_FILENAME = 'test.log'
|
||||
logging.basicConfig(filename=LOG_FILENAME,level=logging.INFO)
|
||||
|
||||
# Stubs to replace the UI functions for raw testing
|
||||
class wizard_stub:
|
||||
def __init__(self):
|
||||
self.importProgressBar=progbar_stub()
|
||||
def incrementProgressBar(self, str):
|
||||
pass
|
||||
class progbar_stub:
|
||||
def __init__(self):
|
||||
pass
|
||||
def setMaximum(self, arg):
|
||||
pass
|
||||
|
||||
def test():
|
||||
manager = Manager(u'songs', init_schema)
|
||||
o = OpenSongImport(manager)
|
||||
o.do_import(u'test.opensong', commit=False)
|
||||
o.song_import.print_song()
|
||||
assert o.song_import.copyright == u'2010 Martin Thompson'
|
||||
assert o.song_import.authors == [u'MartiÑ Thómpson']
|
||||
assert o.song_import.title == u'Martins Test'
|
||||
assert o.song_import.alternate_title == u''
|
||||
assert o.song_import.song_number == u'1'
|
||||
assert [u'C1', u'Chorus 1'] in o.song_import.verses
|
||||
assert [u'C2', u'Chorus 2'] in o.song_import.verses
|
||||
assert not [u'C3', u'Chorus 3'] in o.song_import.verses
|
||||
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.song_import.verses
|
||||
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.song_import.verses
|
||||
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.song_import.verses
|
||||
assert o.song_import.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3', u'B1', u'V1']
|
||||
assert o.song_import.ccli_number == u'Blah'
|
||||
assert o.song_import.topics == [u'TestTheme', u'TestAltTheme']
|
||||
o.do_import(u'test.opensong.zip', commit=False)
|
||||
o.song_import.print_song()
|
||||
o.finish()
|
||||
assert o.song_import.copyright == u'2010 Martin Thompson'
|
||||
assert o.song_import.authors == [u'MartiÑ Thómpson']
|
||||
assert o.song_import.title == u'Martins Test'
|
||||
assert o.song_import.alternate_title == u''
|
||||
assert o.song_import.song_number == u'1'
|
||||
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.song_import.verses
|
||||
assert [u'C1', u'Chorus 1'] in o.song_import.verses
|
||||
assert [u'C2', u'Chorus 2'] in o.song_import.verses
|
||||
assert not [u'C3', u'Chorus 3'] in o.song_import.verses
|
||||
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.song_import.verses
|
||||
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.song_import.verses
|
||||
assert o.song_import.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3', u'B1', u'V1']
|
||||
o = OpenSongImport(manager, filenames=[u'test.opensong'])
|
||||
o.import_wizard = wizard_stub()
|
||||
o.commit = False
|
||||
o.do_import()
|
||||
o.print_song()
|
||||
assert o.copyright == u'2010 Martin Thompson'
|
||||
assert o.authors == [u'MartiÑ Thómpson', u'Martin2 Thómpson']
|
||||
assert o.title == u'Martins Test'
|
||||
assert o.alternate_title == u''
|
||||
assert o.song_number == u'1'
|
||||
assert [u'C1', u'Chorus 1'] in o.verses
|
||||
assert [u'C2', u'Chorus 2'] in o.verses
|
||||
assert not [u'C3', u'Chorus 3'] in o.verses
|
||||
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.verses
|
||||
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.verses
|
||||
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.verses
|
||||
assert [u'V3A', u'V3 Line 1\nV3 Line 2'] in o.verses
|
||||
assert [u'RAP1', u'Rap 1 Line 1\nRap 1 Line 2'] in o.verses
|
||||
assert [u'RAP2', u'Rap 2 Line 1\nRap 2 Line 2'] in o.verses
|
||||
assert [u'RAP3', u'Rap 3 Line 1\nRap 3 Line 2'] in o.verses
|
||||
assert [u'X1', u'Unreferenced verse line 1'] in o.verses
|
||||
assert o.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3A', u'B1', u'V1', u'T1', u'RAP1', u'RAP2', u'RAP3']
|
||||
assert o.ccli_number == u'Blah'
|
||||
assert o.topics == [u'TestTheme', u'TestAltTheme']
|
||||
|
||||
o = OpenSongImport(manager)
|
||||
o.do_import(u'test2.opensong', commit=False)
|
||||
# o.finish()
|
||||
o.song_import.print_song()
|
||||
assert o.song_import.copyright == u'2010 Martin Thompson'
|
||||
assert o.song_import.authors == [u'Martin Thompson']
|
||||
assert o.song_import.title == u'Martins 2nd Test'
|
||||
assert o.song_import.alternate_title == u''
|
||||
assert o.song_import.song_number == u'2'
|
||||
print o.song_import.verses
|
||||
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.song_import.verses
|
||||
assert [u'C1', u'Chorus 1'] in o.song_import.verses
|
||||
assert [u'C2', u'Chorus 2'] in o.song_import.verses
|
||||
assert not [u'C3', u'Chorus 3'] in o.song_import.verses
|
||||
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.song_import.verses
|
||||
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.song_import.verses
|
||||
print o.song_import.verse_order_list
|
||||
assert o.song_import.verse_order_list == [u'V1', u'V2', u'B1', u'C1', u'C2']
|
||||
o.filenames = [u'test.opensong.zip']
|
||||
o.set_defaults()
|
||||
o.do_import()
|
||||
o.print_song()
|
||||
assert o.copyright == u'2010 Martin Thompson'
|
||||
assert o.authors == [u'MartiÑ Thómpson']
|
||||
assert o.title == u'Martins Test'
|
||||
assert o.alternate_title == u''
|
||||
assert o.song_number == u'1'
|
||||
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.verses
|
||||
assert [u'C1', u'Chorus 1'] in o.verses
|
||||
assert [u'C2', u'Chorus 2'] in o.verses
|
||||
assert not [u'C3', u'Chorus 3'] in o.verses
|
||||
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.verses
|
||||
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.verses
|
||||
print o.verse_order_list
|
||||
assert o.verse_order_list == [u'V1', u'C1', u'V2', u'C2', u'V3', u'B1', u'V1']
|
||||
|
||||
o.filenames = [u'test2.opensong']
|
||||
o.set_defaults()
|
||||
o.do_import()
|
||||
o.print_song()
|
||||
assert o.copyright == u'2010 Martin Thompson'
|
||||
assert o.authors == [u'Martin Thompson']
|
||||
assert o.title == u'Martins 2nd Test'
|
||||
assert o.alternate_title == u''
|
||||
assert o.song_number == u'2'
|
||||
print o.verses
|
||||
assert [u'B1', u'Bridge 1\nBridge 1 line 2'] in o.verses
|
||||
assert [u'C1', u'Chorus 1'] in o.verses
|
||||
assert [u'C2', u'Chorus 2'] in o.verses
|
||||
assert not [u'C3', u'Chorus 3'] in o.verses
|
||||
assert [u'V1', u'v1 Line 1\nV1 Line 2'] in o.verses
|
||||
assert [u'V2', u'v2 Line 1\nV2 Line 2'] in o.verses
|
||||
print o.verse_order_list
|
||||
assert o.verse_order_list == [u'V1', u'V2', u'B1', u'C1', u'C2']
|
||||
|
||||
o.filenames = [u'test3.opensong']
|
||||
o.set_defaults()
|
||||
o.do_import()
|
||||
o.print_song()
|
||||
assert o.copyright == u'2010'
|
||||
assert o.authors == [u'Martin Thompson']
|
||||
assert o.title == u'Test single verse'
|
||||
assert o.alternate_title == u''
|
||||
assert o.ccli_number == u'123456'
|
||||
assert o.verse_order_list == [u'V1']
|
||||
assert o.topics == [u'Worship: Declaration']
|
||||
print o.verses[0]
|
||||
assert [u'V1', u'Line 1\nLine 2'] in o.verses
|
||||
|
||||
print "Tests passed"
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
test()
|
||||
|
@ -119,7 +119,7 @@ class WowImport(SongImport):
|
||||
# TODO: check that it is a valid words of worship file (could
|
||||
# check header for WoW File Song Word)
|
||||
self.author = u''
|
||||
self.copyright= u''
|
||||
self.copyright = u''
|
||||
# Get the song title
|
||||
self.file_name = os.path.split(file)[1]
|
||||
self.import_wizard.incrementProgressBar(
|
||||
|
@ -50,7 +50,7 @@ class SongsPlugin(Plugin):
|
||||
"""
|
||||
Create and set up the Songs plugin.
|
||||
"""
|
||||
Plugin.__init__(self, u'Songs', u'1.9.2', plugin_helpers)
|
||||
Plugin.__init__(self, u'Songs', u'1.9.3', plugin_helpers)
|
||||
self.weight = -10
|
||||
self.manager = Manager(u'songs', init_schema)
|
||||
self.icon_path = u':/plugins/plugin_songs.png'
|
||||
|
@ -41,7 +41,7 @@ class SongUsagePlugin(Plugin):
|
||||
log.info(u'SongUsage Plugin loaded')
|
||||
|
||||
def __init__(self, plugin_helpers):
|
||||
Plugin.__init__(self, u'SongUsage', u'1.9.2', plugin_helpers)
|
||||
Plugin.__init__(self, u'SongUsage', u'1.9.3', plugin_helpers)
|
||||
self.weight = -4
|
||||
self.icon = build_icon(u':/plugins/plugin_songusage.png')
|
||||
self.songusagemanager = None
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
4155
resources/i18n/en_GB.ts
Normal file
4155
resources/i18n/en_GB.ts
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
0
resources/images/about-new.bmp
Executable file → Normal file
0
resources/images/about-new.bmp
Executable file → Normal file
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
1
resources/openlp.desktop
Normal file → Executable file
1
resources/openlp.desktop
Normal file → Executable file
@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env xdg-open
|
||||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Name=OpenLP
|
||||
|
@ -127,6 +127,8 @@ def import_bible():
|
||||
for row in rows:
|
||||
key = unicode(row[0], u'cp1252')
|
||||
value = unicode(row[1], u'cp1252')
|
||||
if key == u'Permission':
|
||||
key = u'Permissions'
|
||||
sql_insert = u'INSERT INTO metadata '\
|
||||
'("key", "value") '\
|
||||
'VALUES (?, ?)'
|
||||
|
@ -1,6 +1,6 @@
|
||||
--- openlp/core/resources.py.old Mon Jun 21 23:16:19 2010
|
||||
+++ openlp/core/resources.py Mon Jun 21 23:27:48 2010
|
||||
@@ -1,10 +1,31 @@
|
||||
@@ -1,10 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
@ -14,8 +14,9 @@
|
||||
+# --------------------------------------------------------------------------- #
|
||||
+# Copyright (c) 2008-2010 Raoul Snyman #
|
||||
+# Portions copyright (c) 2008-2010 Tim Bentley, Jonathan Corwin, Michael #
|
||||
+# Gorven, Scott Guerrieri, Christian Richter, Maikel Stuivenberg, Martin #
|
||||
+# Thompson, Jon Tibble, Carsten Tinggaard #
|
||||
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
|
||||
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
|
||||
+# Carsten Tinggaard, Frode Woldsund #
|
||||
+# --------------------------------------------------------------------------- #
|
||||
+# This program is free software; you can redistribute it and/or modify it #
|
||||
+# under the terms of the GNU General Public License as published by the Free #
|
||||
|
@ -24,214 +24,339 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
# Short description
|
||||
# Steps for creating languages:
|
||||
# 1. make sure that the openlp_en.ts file exist
|
||||
# 2. go to scripts folder and start:
|
||||
# python translation_utils.py -a
|
||||
###############################################################################
|
||||
|
||||
"""
|
||||
This script is used to maintain the translation files in OpenLP. It downloads
|
||||
the latest translation files from the Pootle translation server, updates the
|
||||
local translation files from both the source code and the files from Pootle,
|
||||
and can also generate the compiled translation files.
|
||||
|
||||
Create New Language
|
||||
-------------------
|
||||
|
||||
To create a new language, simply run this script with the ``-a`` command line
|
||||
option::
|
||||
|
||||
@:~$ ./translation_utils.py -a
|
||||
|
||||
Update Translation Files
|
||||
------------------------
|
||||
|
||||
The best way to update the translations is to download the files from Pootle,
|
||||
and then update the local files using both the downloaded files and the source.
|
||||
This is done easily via the ``-d``, ``-p`` and ``-u`` options::
|
||||
|
||||
@:~$ ./translation_utils.py -dpu
|
||||
|
||||
"""
|
||||
import os
|
||||
import urllib
|
||||
import re
|
||||
from shutil import copy
|
||||
|
||||
from optparse import OptionParser
|
||||
from PyQt4 import QtCore
|
||||
from BeautifulSoup import BeautifulSoup
|
||||
|
||||
class TranslationUtils(object):
|
||||
SERVER_URL = u'http://pootle.projecthq.biz/export/openlp/'
|
||||
IGNORED_PATHS = [u'scripts']
|
||||
IGNORED_FILES = [u'setup.py']
|
||||
|
||||
verbose_mode = False
|
||||
|
||||
class Command(object):
|
||||
"""
|
||||
Provide an enumeration of commands.
|
||||
"""
|
||||
Download = 1
|
||||
Create = 2
|
||||
Prepare = 3
|
||||
Update = 4
|
||||
Generate = 5
|
||||
|
||||
class CommandStack(object):
|
||||
"""
|
||||
This class provides an iterable stack.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.ignore_paths = [u'./scripts']
|
||||
self.ignore_files = [u'setup.py']
|
||||
self.server_url = u'http://pootle.projecthq.biz/export/openlp/'
|
||||
self.cmd_stack = []
|
||||
self.stack_count = 0
|
||||
self.verbose = False
|
||||
|
||||
self.current_index = 0
|
||||
self.data = []
|
||||
|
||||
def process_stack(self):
|
||||
if len(self.cmd_stack) > 0:
|
||||
if len(self.cmd_stack) == self.stack_count:
|
||||
print u'Process %d commands' % self.stack_count
|
||||
print u'%d. ' % (self.stack_count-len(self.cmd_stack)+1),
|
||||
command = self.cmd_stack.pop(0)
|
||||
if len(command) > 1:
|
||||
command[0](command[1])
|
||||
else:
|
||||
command[0]()
|
||||
def __len__(self):
|
||||
return len(self.data)
|
||||
|
||||
def __getitem__(self, index):
|
||||
if not index in self.data:
|
||||
return None
|
||||
elif self.data[index].get(u'arguments'):
|
||||
return self.data[index][u'command'], self.data[index][u'arguments']
|
||||
else:
|
||||
print "Finished all commands"
|
||||
return self.data[index][u'command']
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
def next(self):
|
||||
if self.current_index == len(self.data):
|
||||
raise StopIteration
|
||||
else:
|
||||
current_item = self.data[self.current_index][u'command']
|
||||
self.current_index += 1
|
||||
return current_item
|
||||
|
||||
def append(self, command, **kwargs):
|
||||
data = {u'command': command}
|
||||
if u'arguments' in kwargs:
|
||||
data[u'arguments'] = kwargs[u'arguments']
|
||||
self.data.append(data)
|
||||
|
||||
def reset(self):
|
||||
self.current_index = 0
|
||||
|
||||
def arguments(self):
|
||||
if self.data[self.current_index - 1].get(u'arguments'):
|
||||
return self.data[self.current_index - 1][u'arguments']
|
||||
else:
|
||||
return []
|
||||
|
||||
def __repr__(self):
|
||||
results = []
|
||||
for item in self.data:
|
||||
if item.get(u'arguments'):
|
||||
results.append(str((item[u'command'], item[u'arguments'])))
|
||||
else:
|
||||
results.append(str((item[u'command'], )))
|
||||
return u'[%s]' % u', '.join(results)
|
||||
|
||||
|
||||
def downloadTranslations(self):
|
||||
print 'Download Translation files from HQ-Server'
|
||||
page = urllib.urlopen(u'%s' % (self.server_url))
|
||||
soup = BeautifulSoup(page)
|
||||
languages = soup.findAll(text=re.compile(".*\.ts"))
|
||||
for language in languages:
|
||||
filename = os.path.join(u'..', u'resources', u'i18n',
|
||||
u'openlp_%s' % language)
|
||||
self.printVerbose(u'Get Translation File: %s' % filename)
|
||||
self.get_and_write_file(language, filename)
|
||||
print u' done'
|
||||
self.process_stack()
|
||||
def print_verbose(text):
|
||||
"""
|
||||
This method checks to see if we are in verbose mode, and if so prints
|
||||
``text`` out.
|
||||
|
||||
def get_and_write_file(self, language, filename):
|
||||
page = urllib.urlopen(u'%s%s' % (self.server_url, language))
|
||||
content = page.read().decode('utf8')
|
||||
page.close()
|
||||
file = open(filename, u'w')
|
||||
file.write(content.encode('utf8'))
|
||||
file.close()
|
||||
|
||||
def creation(self, language):
|
||||
print "Create new Translation File"
|
||||
"""
|
||||
Use this option to create a new translation file
|
||||
this function:
|
||||
* create the new *.ts file
|
||||
"""
|
||||
filename = os.path.join(u'..', u'resources', u'i18n',
|
||||
u'openlp_%s.ts' % language)
|
||||
self.get_and_write_file(u'en.ts', filename)
|
||||
self.printVerbose("""
|
||||
Please remind: For permanent providing this language:
|
||||
this language name have to append to the global list
|
||||
variable "translations" in this file
|
||||
and this file have to be uploaded to the Pootle Server
|
||||
Please contact the developers!
|
||||
""")
|
||||
print u' done'
|
||||
self.process_stack()
|
||||
|
||||
|
||||
def preparation(self):
|
||||
print u'Generating the openlp.pro file'
|
||||
stringlist = []
|
||||
start_dir = os.path.join(u'..')
|
||||
for root, dirs, files in os.walk(start_dir):
|
||||
for file in files:
|
||||
path = u'%s' % root
|
||||
path = path.replace('\\','/')
|
||||
path = path.replace('..','.')
|
||||
|
||||
if file.startswith(u'hook-') or file.startswith(u'test_'):
|
||||
continue
|
||||
``text``
|
||||
The text to print.
|
||||
"""
|
||||
global verbose_mode
|
||||
if verbose_mode:
|
||||
print u' %s' % text
|
||||
|
||||
cond = False
|
||||
for search in self.ignore_paths:
|
||||
if path.startswith(search):
|
||||
cond = True
|
||||
if cond:
|
||||
continue
|
||||
cond = False
|
||||
for search in self.ignore_files:
|
||||
if search == file:
|
||||
cond = True
|
||||
if cond:
|
||||
continue
|
||||
|
||||
if file.endswith(u'.py'):
|
||||
def run(command):
|
||||
"""
|
||||
This method runs an external application.
|
||||
|
||||
``command``
|
||||
The command to run.
|
||||
"""
|
||||
print_verbose(command)
|
||||
process = QtCore.QProcess()
|
||||
process.start(command)
|
||||
while (process.waitForReadyRead()):
|
||||
print_verbose(u'ReadyRead: %s' % QtCore.QString(process.readAll()))
|
||||
print_verbose(u'Error(s):\n%s' % process.readAllStandardError())
|
||||
print_verbose(u'Output:\n%s' % process.readAllStandardOutput())
|
||||
print u' Done.'
|
||||
|
||||
def download_file(source_filename, dest_filename):
|
||||
"""
|
||||
Download a file and save it to disk.
|
||||
|
||||
``source_filename``
|
||||
The file to download.
|
||||
|
||||
``dest_filename``
|
||||
The new local file name.
|
||||
"""
|
||||
print_verbose(u'Downloading from: %s' % (SERVER_URL + source_filename))
|
||||
page = urllib.urlopen(SERVER_URL + source_filename)
|
||||
content = page.read().decode('utf8')
|
||||
page.close()
|
||||
file = open(dest_filename, u'w')
|
||||
file.write(content.encode('utf8'))
|
||||
file.close()
|
||||
|
||||
def download_translations():
|
||||
"""
|
||||
This method downloads the translation files from the Pootle server.
|
||||
"""
|
||||
print 'Download translation files from Pootle'
|
||||
page = urllib.urlopen(SERVER_URL)
|
||||
soup = BeautifulSoup(page)
|
||||
languages = soup.findAll(text=re.compile(r'.*\.ts'))
|
||||
for language in languages:
|
||||
filename = os.path.join(os.path.abspath(u'..'), u'resources', u'i18n',
|
||||
language)
|
||||
print_verbose(u'Get Translation File: %s' % filename)
|
||||
download_file(language, filename)
|
||||
print u' Done.'
|
||||
|
||||
def prepare_project():
|
||||
"""
|
||||
This method creates the project file needed to update the translation files
|
||||
and compile them into .qm files.
|
||||
"""
|
||||
print u'Generating the openlp.pro file'
|
||||
lines = []
|
||||
start_dir = os.path.abspath(u'..')
|
||||
start_dir = start_dir + os.sep
|
||||
print_verbose(u'Starting directory: %s' % start_dir)
|
||||
for root, dirs, files in os.walk(start_dir):
|
||||
for file in files:
|
||||
path = root.replace(start_dir, u'').replace(u'\\', u'/') #.replace(u'..', u'.')
|
||||
if file.startswith(u'hook-') or file.startswith(u'test_'):
|
||||
continue
|
||||
ignore = False
|
||||
for ignored_path in IGNORED_PATHS:
|
||||
if path.startswith(ignored_path):
|
||||
ignore = True
|
||||
break
|
||||
if ignore:
|
||||
continue
|
||||
ignore = False
|
||||
for ignored_file in IGNORED_FILES:
|
||||
if file == ignored_file:
|
||||
ignore = True
|
||||
break
|
||||
if ignore:
|
||||
continue
|
||||
if file.endswith(u'.py') or file.endswith(u'.pyw'):
|
||||
if path:
|
||||
line = u'%s/%s' % (path, file)
|
||||
self.printVerbose(u'Parsing "%s"' % line)
|
||||
stringlist.append(u'SOURCES += %s' % line)
|
||||
elif file.endswith(u'.pyw'):
|
||||
line = u'%s/%s' % (path, file)
|
||||
self.printVerbose(u'Parsing "%s"' % line)
|
||||
stringlist.append(u'SOURCES += %s' % line)
|
||||
elif file.endswith(u'.ts'):
|
||||
line = u'%s/%s' % (path, file)
|
||||
self.printVerbose(u'Parsing "%s"' % line)
|
||||
stringlist.append(u'TRANSLATIONS += %s' % line)
|
||||
|
||||
stringlist.sort()
|
||||
self.write_file(os.path.join(start_dir, u'openlp.pro'), stringlist)
|
||||
print u' done'
|
||||
self.process_stack()
|
||||
else:
|
||||
line = file
|
||||
print_verbose(u'Parsing "%s"' % line)
|
||||
lines.append(u'SOURCES += %s' % line)
|
||||
elif file.endswith(u'.ts'):
|
||||
line = u'%s/%s' % (path, file)
|
||||
print_verbose(u'Parsing "%s"' % line)
|
||||
lines.append(u'TRANSLATIONS += %s' % line)
|
||||
lines.sort()
|
||||
file = open(os.path.join(start_dir, u'openlp.pro'), u'w')
|
||||
file.write(u'\n'.join(lines).encode('utf8'))
|
||||
file.close()
|
||||
print u' Done.'
|
||||
|
||||
def update(self):
|
||||
print u'Update the translation files'
|
||||
cmd = u'pylupdate4 -verbose -noobsolete ../openlp.pro'
|
||||
self.start_cmd(cmd)
|
||||
def update_translations():
|
||||
print u'Update the translation files'
|
||||
if not os.path.exists(os.path.join(os.path.abspath(u'..'), u'openlp.pro')):
|
||||
print u'You have no generated a project file yet, please run this ' + \
|
||||
u'script with the -p option.'
|
||||
return
|
||||
else:
|
||||
os.chdir(os.path.abspath(u'..'))
|
||||
run(u'pylupdate4 -verbose -noobsolete openlp.pro')
|
||||
os.chdir(os.path.abspath(u'scripts'))
|
||||
|
||||
def generate(self):
|
||||
print u'Generate the related *.qm files'
|
||||
cmd = u'lrelease ../openlp.pro'
|
||||
self.start_cmd(cmd)
|
||||
|
||||
def write_file(self, filename, stringlist):
|
||||
content = u''
|
||||
for line in stringlist:
|
||||
content = u'%s%s\n' % (content, line)
|
||||
file = open(filename, u'w')
|
||||
file.write(content.encode('utf8'))
|
||||
file.close()
|
||||
def generate_binaries():
|
||||
print u'Generate the related *.qm files'
|
||||
if not os.path.exists(os.path.join(os.path.abspath(u'..'), u'openlp.pro')):
|
||||
print u'You have not generated a project file yet, please run this ' + \
|
||||
u'script with the -p option. It is also recommended that you ' + \
|
||||
u'this script with the -u option to update the translation ' + \
|
||||
u'files as well.'
|
||||
return
|
||||
else:
|
||||
os.chdir(os.path.abspath(u'..'))
|
||||
run(u'lrelease openlp.pro')
|
||||
os.chdir(os.path.abspath(u'scripts'))
|
||||
src_path = os.path.join(os.path.abspath(u'..'), u'resources', u'i18n')
|
||||
dest_path = os.path.join(os.path.abspath(u'..'), u'openlp', u'i18n')
|
||||
if not os.path.exists(dest_path):
|
||||
os.makedirs(dest_path)
|
||||
src_list = os.listdir(src_path)
|
||||
for file in src_list:
|
||||
if re.search('.qm$', file):
|
||||
copy(os.path.join(src_path, u'%s' % file),
|
||||
os.path.join(dest_path, u'%s' % file))
|
||||
|
||||
def printVerbose(self, data):
|
||||
if self.verbose:
|
||||
print u' %s' % data
|
||||
|
||||
def start_cmd(self, command):
|
||||
self.printVerbose(command)
|
||||
self.process = QtCore.QProcess()
|
||||
self.process.start(command)
|
||||
while (self.process.waitForReadyRead()):
|
||||
self.printVerbose(u'ReadyRead: %s' % QtCore.QString(self.process.readAll()))
|
||||
self.printVerbose(self.process.readAllStandardError())
|
||||
self.printVerbose(self.process.readAllStandardOutput())
|
||||
print u' done'
|
||||
self.process_stack()
|
||||
|
||||
def create_translation(language):
|
||||
"""
|
||||
This method creates a new translation file.
|
||||
|
||||
``language``
|
||||
The language file to create.
|
||||
"""
|
||||
print "Create new Translation File"
|
||||
if not language.endswith(u'.ts'):
|
||||
language += u'.ts'
|
||||
filename = os.path.join(os.path.abspath(u'..'), u'resources', u'i18n', language)
|
||||
download_file(u'en.ts', filename)
|
||||
print u' ** Please Note **'
|
||||
print u' In order to get this file into OpenLP and onto the Pootle ' + \
|
||||
u'translation server you will need to subscribe to the OpenLP' + \
|
||||
u'Translators mailing list, and request that your language file ' + \
|
||||
u'be added to the project.'
|
||||
print u' Done'
|
||||
|
||||
def process_stack(command_stack):
|
||||
"""
|
||||
This method looks at the commands in the command stack, and processes them
|
||||
in the order they are in the stack.
|
||||
|
||||
``command_stack``
|
||||
The command stack to process.
|
||||
"""
|
||||
if command_stack:
|
||||
print u'Processing %d commands...' % len(command_stack)
|
||||
for command in command_stack:
|
||||
print u'%d.' % (command_stack.current_index),
|
||||
if command == Command.Download:
|
||||
download_translations()
|
||||
elif command == Command.Prepare:
|
||||
prepare_project()
|
||||
elif command == Command.Update:
|
||||
update_translations()
|
||||
elif command == Command.Generate:
|
||||
generate_binaries()
|
||||
elif command == Command.Create:
|
||||
arguments = command_stack.arguments()
|
||||
create_translation(*arguments)
|
||||
print u'Finished processing commands.'
|
||||
else:
|
||||
print u'No commands to process.'
|
||||
|
||||
def main():
|
||||
# start Main Class
|
||||
Util = TranslationUtils()
|
||||
|
||||
global verbose_mode
|
||||
# Set up command line options.
|
||||
usage = u'''
|
||||
This script handle the translation files for OpenLP.
|
||||
Usage: %prog [options]
|
||||
If no option will be used, options "-d -p -u -g" will be set automatically
|
||||
'''
|
||||
usage = u'%prog [options]\nOptions are parsed in the order they are ' + \
|
||||
u'listed below. If no options are given, "-dpug" will be used.\n\n' + \
|
||||
u'This script is used to manage OpenLP\'s translation files.'
|
||||
parser = OptionParser(usage=usage)
|
||||
parser.add_option('-d', '--download-ts', action='store_true',
|
||||
dest='download', help='Load languages from Pootle Server')
|
||||
parser.add_option('-c', '--create', metavar='lang',
|
||||
help='creation of new translation file, Parameter: language (e.g. "en_GB"')
|
||||
parser.add_option('-p', '--prepare', action='store_true', dest='prepare',
|
||||
help='preparation (generate pro file)')
|
||||
parser.add_option('-d', '--download-ts', dest='download',
|
||||
action='store_true', help='download language files from Pootle')
|
||||
parser.add_option('-c', '--create', dest='create', metavar='LANG',
|
||||
help='create a new translation file for language LANG, e.g. "en_GB"')
|
||||
parser.add_option('-p', '--prepare', dest='prepare', action='store_true',
|
||||
help='generate a project file, used to update the translations')
|
||||
parser.add_option('-u', '--update', action='store_true', dest='update',
|
||||
help='update translation files')
|
||||
parser.add_option('-g', '--generate', action='store_true', dest='generate',
|
||||
help='generate qm files')
|
||||
parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
|
||||
help='Give more informations while processing')
|
||||
|
||||
help='update translation files (needs a project file)')
|
||||
parser.add_option('-g', '--generate', dest='generate', action='store_true',
|
||||
help='compile .ts files into .qm files')
|
||||
parser.add_option('-v', '--verbose', dest='verbose', action='store_true',
|
||||
help='show extra information while processing translations')
|
||||
(options, args) = parser.parse_args()
|
||||
# Create and populate the command stack
|
||||
command_stack = CommandStack()
|
||||
if options.download:
|
||||
Util.cmd_stack.append([Util.downloadTranslations])
|
||||
command_stack.append(Command.Download)
|
||||
if options.create:
|
||||
Util.cmd_stack.append([Util.creation, u'%s' % options.create])
|
||||
command_stack.append(Command.Create, arguments=[options.create])
|
||||
if options.prepare:
|
||||
Util.cmd_stack.append([Util.preparation])
|
||||
command_stack.append(Command.Prepare)
|
||||
if options.update:
|
||||
Util.cmd_stack.append([Util.update])
|
||||
command_stack.append(Command.Update)
|
||||
if options.generate:
|
||||
Util.cmd_stack.append([Util.generate])
|
||||
if options.verbose:
|
||||
Util.verbose = True
|
||||
command_stack.append(Command.Generate)
|
||||
verbose_mode = options.verbose
|
||||
if not command_stack:
|
||||
command_stack.append(Command.Download)
|
||||
command_stack.append(Command.Prepare)
|
||||
command_stack.append(Command.Update)
|
||||
command_stack.append(Command.Generate)
|
||||
# Process the commands
|
||||
process_stack(command_stack)
|
||||
|
||||
if len(Util.cmd_stack) == 0:
|
||||
Util.cmd_stack.append([Util.downloadTranslations])
|
||||
Util.cmd_stack.append([Util.preparation])
|
||||
Util.cmd_stack.append([Util.update])
|
||||
Util.cmd_stack.append([Util.generate])
|
||||
|
||||
Util.stack_count = len(Util.cmd_stack)
|
||||
Util.process_stack()
|
||||
|
||||
|
||||
|
||||
if __name__ == u'__main__':
|
||||
if os.path.split(os.path.abspath(u'.'))[1] != u'scripts':
|
||||
print u'You need to run this script from the scripts directory.'
|
||||
|
@ -31,6 +31,18 @@ Windows Build Script
|
||||
This script is used to build the Windows binary and the accompanying installer.
|
||||
For this script to work out of the box, it depends on a number of things:
|
||||
|
||||
Python 2.6
|
||||
This build script only works with Python 2.6.
|
||||
|
||||
PyQt4
|
||||
You should already have this installed, OpenLP doesn't work without it. The
|
||||
version the script expects is the packaged one available from River Bank
|
||||
Computing.
|
||||
|
||||
PyEnchant
|
||||
This script expects the precompiled, installable version of PyEnchant to be
|
||||
installed. You can find this on the PyEnchant site.
|
||||
|
||||
Inno Setup 5
|
||||
Inno Setup should be installed into "C:\%PROGRAMFILES%\Inno Setup 5"
|
||||
|
||||
@ -41,9 +53,10 @@ UPX
|
||||
add that directory to your PATH environment variable.
|
||||
|
||||
PyInstaller
|
||||
PyInstaller should be a checkout of trunk, and in a directory called,
|
||||
"pyinstaller" on the same level as OpenLP's Bazaar shared repository
|
||||
directory.
|
||||
PyInstaller should be a checkout of revision 844 of trunk, and in a
|
||||
directory called, "pyinstaller" on the same level as OpenLP's Bazaar shared
|
||||
repository directory. The revision is very important as there is currently
|
||||
a major regression in HEAD.
|
||||
|
||||
To install PyInstaller, first checkout trunk from Subversion. The easiest
|
||||
way is to install TortoiseSVN and then checkout the following URL to a
|
||||
@ -80,17 +93,32 @@ from subprocess import Popen, PIPE
|
||||
script_path = os.path.split(os.path.abspath(__file__))[0]
|
||||
branch_path = os.path.abspath(os.path.join(script_path, u'..'))
|
||||
source_path = os.path.join(branch_path, u'openlp')
|
||||
i18n_path = os.path.join(branch_path, u'resources', u'i18n')
|
||||
build_path = os.path.join(branch_path, u'build', u'pyi.win32', u'OpenLP')
|
||||
dist_path = os.path.join(branch_path, u'dist', u'OpenLP')
|
||||
pyinstaller_path = os.path.abspath(os.path.join(branch_path, u'..', u'..', u'pyinstaller'))
|
||||
innosetup_path = os.path.join(os.getenv(u'PROGRAMFILES'), 'Inno Setup 5')
|
||||
iss_path = os.path.join(branch_path, u'resources', u'innosetup')
|
||||
lrelease_path = u'C:\\Python26\\Lib\\site-packages\\PyQt4\\bin\\lrelease.exe'
|
||||
enchant_path = u'C:\\Python26\\Lib\\site-packages\\enchant'
|
||||
|
||||
def clean_build_directories():
|
||||
#if not os.path.exists(build_path)
|
||||
for root, dirs, files in os.walk(build_path, topdown=False):
|
||||
print root
|
||||
for file in files:
|
||||
os.remove(os.path.join(root, file))
|
||||
#os.removedirs(build_path)
|
||||
for root, dirs, files in os.walk(dist_path, topdown=False):
|
||||
for file in files:
|
||||
os.remove(os.path.join(root, file))
|
||||
#os.removedirs(dist_path)
|
||||
|
||||
def run_pyinstaller():
|
||||
print u'Running PyInstaller...'
|
||||
os.chdir(branch_path)
|
||||
pyinstaller = Popen((u'python', os.path.join(pyinstaller_path, u'Build.py'),
|
||||
u'OpenLP.spec'))
|
||||
u'-y', u'OpenLP.spec'))
|
||||
code = pyinstaller.wait()
|
||||
if code != 0:
|
||||
raise Exception(u'Error running PyInstaller Build.py')
|
||||
@ -120,6 +148,19 @@ def write_version_file():
|
||||
f.write(versionstring)
|
||||
f.close()
|
||||
|
||||
def copy_enchant():
|
||||
print u'Copying enchant/pyenchant...'
|
||||
source = enchant_path
|
||||
dest = os.path.join(dist_path, u'enchant')
|
||||
for root, dirs, files in os.walk(source):
|
||||
for filename in files:
|
||||
if not filename.endswith(u'.pyc') and not filename.endswith(u'.pyo'):
|
||||
dest_path = os.path.join(dest, root[len(source)+1:])
|
||||
if not os.path.exists(dest_path):
|
||||
os.makedirs(dest_path)
|
||||
copy(os.path.join(root, filename),
|
||||
os.path.join(dest_path, filename))
|
||||
|
||||
def copy_plugins():
|
||||
print u'Copying plugins...'
|
||||
source = os.path.join(source_path, u'plugins')
|
||||
@ -138,6 +179,30 @@ def copy_windows_files():
|
||||
copy(os.path.join(iss_path, u'OpenLP.ico'), os.path.join(dist_path, u'OpenLP.ico'))
|
||||
copy(os.path.join(iss_path, u'LICENSE.txt'), os.path.join(dist_path, u'LICENSE.txt'))
|
||||
|
||||
def update_translations():
|
||||
print u'Updating translations...'
|
||||
os.chdir(script_path)
|
||||
translation_utils = Popen(u'python translation_utils.py -dpu')
|
||||
code = translation_utils.wait()
|
||||
if code != 0:
|
||||
print u'Error running translation_utils.py'
|
||||
|
||||
def compile_translations():
|
||||
print u'Compiling translations...'
|
||||
files = os.listdir(i18n_path)
|
||||
if not os.path.exists(os.path.join(dist_path, u'i18n')):
|
||||
os.makedirs(os.path.join(dist_path, u'i18n'))
|
||||
for file in files:
|
||||
if file.endswith(u'.ts'):
|
||||
source_path = os.path.join(i18n_path, file)
|
||||
dest_path = os.path.join(dist_path, u'i18n',
|
||||
file.replace(u'.ts', u'.qm'))
|
||||
lconvert = Popen(u'"%s" "%s" -qm "%s"' % (lrelease_path, \
|
||||
source_path, dest_path))
|
||||
code = lconvert.wait()
|
||||
if code != 0:
|
||||
print 'Error running lconvert on %s' % source_path
|
||||
|
||||
def run_innosetup():
|
||||
print u'Running Inno Setup...'
|
||||
os.chdir(iss_path)
|
||||
@ -150,17 +215,23 @@ def run_innosetup():
|
||||
raise Exception(u'Error running Inno Setup')
|
||||
|
||||
def main():
|
||||
print "Script path:", script_path
|
||||
print "Branch path:", branch_path
|
||||
print "Source path:", source_path
|
||||
print "\"dist\" path:", dist_path
|
||||
print "PyInstaller path:", pyinstaller_path
|
||||
print "Inno Setup path:", innosetup_path
|
||||
print "ISS file path:", iss_path
|
||||
import sys
|
||||
if len(sys.argv) > 1 and (sys.argv[1] == u'-v' or sys.argv[1] == u'--verbose'):
|
||||
print "Script path:", script_path
|
||||
print "Branch path:", branch_path
|
||||
print "Source path:", source_path
|
||||
print "\"dist\" path:", dist_path
|
||||
print "PyInstaller path:", pyinstaller_path
|
||||
print "Inno Setup path:", innosetup_path
|
||||
print "ISS file path:", iss_path
|
||||
#clean_build_directories()
|
||||
run_pyinstaller()
|
||||
write_version_file()
|
||||
copy_enchant()
|
||||
copy_plugins()
|
||||
copy_windows_files()
|
||||
update_translations()
|
||||
compile_translations()
|
||||
run_innosetup()
|
||||
print "Done."
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user