This commit is contained in:
Tim Bentley 2010-09-24 12:30:10 +01:00
commit f62ac45a29
74 changed files with 15146 additions and 13260 deletions

View File

@ -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,6 +206,7 @@ def main():
language = LanguageManager.get_language()
appTranslator = LanguageManager.get_translator(language)
app.installTranslator(appTranslator)
if not options.no_error_form:
sys.excepthook = app.hookException
sys.exit(app.run())

View File

@ -38,62 +38,47 @@ 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>', \
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>', \
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>', \
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>', \
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>', \
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

View File

@ -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;'

View File

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

View File

@ -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

View File

@ -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):

View File

@ -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()

View File

@ -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:
try:
self.dict = enchant.Dict()
except DictNotFoundError:
self.dict = enchant.Dict(u'en_US')
self.highlighter = Highlighter(self.document())
self.highlighter.setDict(self.dict)

View File

@ -450,7 +450,7 @@ class Ui_AboutDialog(object):
'\n'
'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 '
'the Program does not specify a version number of this License, '
@ -565,3 +565,4 @@ class Ui_AboutDialog(object):
self.contributeButton.setText(translate('OpenLP.AboutForm',
'Contribute'))
self.closeButton.setText(translate('OpenLP.AboutForm', 'Close'))

View File

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

View File

@ -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)
try:
self.webView = QtWebKit.QGraphicsWebView()
self.scene = QtGui.QGraphicsScene(self)
self.setScene(self.scene)
self.scene.addItem(self.webView)
self.webView.resize(self.screen[u'size'].width(), \
self.screen[u'size'].height())
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

View File

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

View File

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

View File

@ -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):
"""

View File

@ -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
if self.serviceItem.is_command():
Receiver.send_message(u'%s_first' % self.serviceItem.name.lower(),
[self.serviceItem, self.isLive])
if self.serviceItem.is_command():
self.updatePreview()
else:
self.PreviewListWidget.selectRow(0)
@ -651,9 +652,9 @@ class SlideController(QtGui.QWidget):
index = int(message[0])
if not self.serviceItem:
return
if self.serviceItem.is_command():
Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
[self.serviceItem, self.isLive, index])
if self.serviceItem.is_command():
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():
if self.serviceItem.is_command() and self.isLive:
Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
[self.serviceItem, self.isLive, row])
if self.serviceItem.is_command() and self.isLive:
self.updatePreview()
else:
frame, raw_html = self.serviceItem.get_rendered_frame(row)

View File

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

View File

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

View File

@ -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
"""
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__

View File

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

View File

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

View File

@ -173,8 +173,7 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
translate('BiblesPlugin.ImportWizardForm',
'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'':
@ -182,17 +181,15 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
translate('BiblesPlugin.ImportWizardForm',
'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 '
'This Bible already exists. Please import '
'a different Bible or first delete the existing one.'))
self.VersionNameEdit.setFocus()
return False
@ -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,14 +434,12 @@ 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',

View File

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

View File

@ -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,31 +439,71 @@ class BibleMediaItem(MediaManagerItem):
dual_bible, versetext)
if self.ClearAdvancedSearchComboBox.currentIndex() == 0:
self.listView.clear()
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):
"""
@ -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)
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:
bible_text = u'%s %s\n\n%s %s' % (verse_text, text, verse_text,
dual_text)
raw_slides.append(bible_text)
bible_text = u''
bible_text = u'%s %s\n\n%s %s' % (verse_text, text,
verse_text, dual_text)
raw_slides.append(bible_text)
bible_text = u''
elif self.parent.settings_tab.layout_style == 0:
bible_text = u'%s %s' % (verse_text, text)
raw_slides.append(bible_text)
bible_text = u''
else:
bible_text = u'%s %s %s\n' % (bible_text, verse_text, text)
# If we are 'Verse Per Slide' then create a new slide.
elif self.parent.settings_tab.layout_style == 0:
bible_text = u'%s %s' % (verse_text, text)
@ -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
for title in raw_title:
if not service_item.title:
if dual_bible:
service_item.title = u'%s (%s, %s) %s' % (book, version,
dual_version, verse_text)
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)

View File

@ -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:

View File

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

View File

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

View File

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

View File

@ -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()

View File

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

View 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
View File

View 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

View File

@ -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:

View File

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

View File

@ -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

View File

@ -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,14 +157,15 @@ 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()
#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:
@ -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

View File

@ -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(

View File

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

View File

@ -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:

View File

@ -81,14 +81,15 @@ 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
@ -175,7 +176,7 @@ class CCLIFileImport(SongImport):
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);
self.add_verse(verse_text, verse_type)
#Handle multiple authors
author_list = song_author.split(u'/')
if len(author_list) < 2:
@ -238,12 +239,12 @@ class CCLIFileImport(SongImport):
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:
@ -252,11 +253,11 @@ class CCLIFileImport(SongImport):
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:
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:
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')):
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()

View 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

View File

@ -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']

View File

@ -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,6 +104,7 @@ class OpenLP1SongImport(SongImport):
# "cache" our list of authors
cursor.execute(u'SELECT authorid, authorname FROM authors')
authors = cursor.fetchall()
if new_db:
# "cache" our list of tracks
cursor.execute(u'SELECT trackid, fulltrackname FROM tracks')
tracks = cursor.fetchall()
@ -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

View File

@ -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:
if self.uno_connection_type == u'pipe':
cmd = u'openoffice.org -nologo -norestore -minimized ' \
+ u'-invisible -nofirststartwizard ' \
+ '-accept="socket,host=localhost,port=2002;urp;"'
+ 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()

View File

@ -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
# drop the square brackets
right_bracket = thisline.find(u']')
versenum = thisline[2:right_bracket]
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)

View File

@ -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

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<song>
<title>Martins Test</title>
<author>MartiÑ Thómpson</author>
<author>MartiÑ &amp; 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"/>

View 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>

View 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:])

View File

@ -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",
o = OpenSongImport(manager, filenames=files)
o.import_wizard=wizard_stub()
o.do_import()
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()
if __name__ == "__main__":
opensong_import_lots()

View File

@ -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()

View File

@ -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(

View File

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

View File

@ -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

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
View File

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

1
resources/openlp.desktop Normal file → Executable file
View File

@ -1,3 +1,4 @@
#!/usr/bin/env xdg-open
[Desktop Entry]
Encoding=UTF-8
Name=OpenLP

View File

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

View File

@ -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 #

View File

@ -24,213 +24,338 @@
# 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 __len__(self):
return len(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])
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:
command[0]()
return self.data[index][u'command']
def __iter__(self):
return self
def next(self):
if self.current_index == len(self.data):
raise StopIteration
else:
print "Finished all commands"
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))
``text``
The text to print.
"""
global verbose_mode
if verbose_mode:
print u' %s' % text
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(filename, u'w')
file = open(dest_filename, u'w')
file.write(content.encode('utf8'))
file.close()
def creation(self, language):
print "Create new Translation File"
def download_translations():
"""
Use this option to create a new translation file
this function:
* create the new *.ts file
This method downloads the translation files from the Pootle server.
"""
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()
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 preparation(self):
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'
stringlist = []
start_dir = os.path.join(u'..')
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 = u'%s' % root
path = path.replace('\\','/')
path = path.replace('..','.')
path = root.replace(start_dir, u'').replace(u'\\', u'/') #.replace(u'..', u'.')
if file.startswith(u'hook-') or file.startswith(u'test_'):
continue
cond = False
for search in self.ignore_paths:
if path.startswith(search):
cond = True
if cond:
ignore = False
for ignored_path in IGNORED_PATHS:
if path.startswith(ignored_path):
ignore = True
break
if ignore:
continue
cond = False
for search in self.ignore_files:
if search == file:
cond = True
if cond:
ignore = False
for ignored_file in IGNORED_FILES:
if file == ignored_file:
ignore = True
break
if ignore:
continue
if file.endswith(u'.py'):
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)
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)
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()
def update(self):
print u'Update the translation files'
cmd = u'pylupdate4 -verbose -noobsolete ../openlp.pro'
self.start_cmd(cmd)
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'))
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 printVerbose(self, data):
if self.verbose:
print u' %s' % data
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 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 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 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
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()
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 __name__ == u'__main__':
if os.path.split(os.path.abspath(u'.'))[1] != u'scripts':

View File

@ -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,6 +215,8 @@ def run_innosetup():
raise Exception(u'Error running Inno Setup')
def main():
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
@ -157,10 +224,14 @@ def main():
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."