diff --git a/openlp.pyw b/openlp.pyw
index 805181c11..80b49321e 100755
--- a/openlp.pyw
+++ b/openlp.pyw
@@ -147,6 +147,9 @@ class OpenLP(QtGui.QApplication):
return self.exec_()
def hookException(self, exctype, value, traceback):
+ if not hasattr(self, u'mainWindow'):
+ log.exception(''.join(format_exception(exctype, value, traceback)))
+ return
if not hasattr(self, u'exceptionForm'):
self.exceptionForm = ExceptionForm(self.mainWindow)
self.exceptionForm.exceptionTextEdit.setPlainText(
@@ -159,18 +162,18 @@ def main():
the PyQt4 Application.
"""
# Set up command line options.
- usage = u'Usage: %prog [options] [qt-options]'
+ usage = 'Usage: %prog [options] [qt-options]'
parser = OptionParser(usage=usage)
- parser.add_option("-l", "--log-level", dest="loglevel",
- default="warning", metavar="LEVEL",
- help="Set logging to LEVEL level. Valid values are "
- "\"debug\", \"info\", \"warning\".")
- parser.add_option("-p", "--portable", dest="portable",
- action="store_true",
- help="Specify if this should be run as a portable app, "
- "off a USB flash drive.")
- parser.add_option("-s", "--style", dest="style",
- help="Set the Qt4 style (passed directly to Qt4).")
+ parser.add_option('-e', '--no-error-form', dest='no_error_form',
+ action='store_true', help='Disable the error notification form.')
+ parser.add_option('-l', '--log-level', dest='loglevel',
+ default='warning', metavar='LEVEL', help='Set logging to LEVEL '
+ 'level. Valid values are "debug", "info", "warning".')
+ parser.add_option('-p', '--portable', dest='portable',
+ action='store_true', help='Specify if this should be run as a '
+ 'portable app, off a USB flash drive (not implemented).')
+ parser.add_option('-s', '--style', dest='style',
+ help='Set the Qt4 style (passed directly to Qt4).')
# Set up logging
log_path = AppLocation.get_directory(AppLocation.CacheDir)
if not os.path.exists(log_path):
@@ -203,7 +206,8 @@ def main():
language = LanguageManager.get_language()
appTranslator = LanguageManager.get_translator(language)
app.installTranslator(appTranslator)
- sys.excepthook = app.hookException
+ if not options.no_error_form:
+ sys.excepthook = app.hookException
sys.exit(app.run())
if __name__ == u'__main__':
diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py
index 1f911491f..ade4bc019 100644
--- a/openlp/core/lib/__init__.py
+++ b/openlp/core/lib/__init__.py
@@ -38,63 +38,48 @@ log = logging.getLogger(__name__)
# TODO make external and configurable in alpha 4 via a settings dialog
html_expands = []
-html_expands.append({u'desc':u'Red', u'start tag':u'{r}', \
- u'start html':u'', \
- u'end tag':u'{/r}', u'end html':u'', \
- u'protected':False})
-html_expands.append({u'desc':u'Black', u'start tag':u'{b}', \
- u'start html':u'', \
- u'end tag':u'{/b}', u'end html':u'', \
- u'protected':False})
-html_expands.append({u'desc':u'Blue', u'start tag':u'{bl}', \
- u'start html':u'', \
- u'end tag':u'{/bl}', u'end html':u'', \
- u'protected':False})
-html_expands.append({u'desc':u'Yellow', u'start tag':u'{y}', \
- u'start html':u'', \
- u'end tag':u'{/y}', u'end html':u'', \
- u'protected':False})
-html_expands.append({u'desc':u'Green', u'start tag':u'{g}', \
- u'start html':u'', \
- u'end tag':u'{/g}', u'end html':u'', \
- u'protected':False})
-html_expands.append({u'desc':u'Pink', u'start tag':u'{pk}', \
- u'start html':u'', \
- u'end tag':u'{/pk}', u'end html':u'', \
- u'protected':False})
-html_expands.append({u'desc':u'Orange', u'start tag':u'{o}', \
- u'start html':u'', \
- u'end tag':u'{/o}', u'end html':u'', \
- u'protected':False})
-html_expands.append({u'desc':u'Purple', u'start tag':u'{pp}', \
- u'start html':u'', \
- u'end tag':u'{/pp}', u'end html':u'', \
- u'protected':False})
-html_expands.append({u'desc':u'White', u'start tag':u'{w}', \
- u'start html':u'', \
- u'end tag':u'{/w}', u'end html':u'', \
- u'protected':False})
-html_expands.append({u'desc':u'Superscript', u'start tag':u'{su}', \
- u'start html':u'', \
- u'end tag':u'{/su}', u'end html':u'', \
- u'protected':True})
-html_expands.append({u'desc':u'Subscript', u'start tag':u'{sb}', \
- u'start html':u'', \
- u'end tag':u'{/sb}', u'end html':u'', \
- u'protected':True})
-html_expands.append({u'desc':u'Paragraph', u'start tag':u'{p}', \
- u'start html':u'
', \
- u'end tag':u'{/p}', u'end html':u'
', \
- u'protected':True})
-html_expands.append({u'desc':u'Bold', u'start tag':u'{st}', \
- u'start html':u'', \
- u'end tag':u'{/st}', \
- u'end html':u'', \
- u'protected':True})
-html_expands.append({u'desc':u'Italics', u'start tag':u'{it}', \
- u'start html':u'', \
- u'end tag':u'{/it}', u'end html':u'', \
- u'protected':True})
+html_expands.append({u'desc':u'Red', u'start tag':u'{r}',
+ u'start html':u'',
+ u'end tag':u'{/r}', u'end html':u'', u'protected':False})
+html_expands.append({u'desc':u'Black', u'start tag':u'{b}',
+ u'start html':u'',
+ u'end tag':u'{/b}', u'end html':u'', u'protected':False})
+html_expands.append({u'desc':u'Blue', u'start tag':u'{bl}',
+ u'start html':u'',
+ u'end tag':u'{/bl}', u'end html':u'', u'protected':False})
+html_expands.append({u'desc':u'Yellow', u'start tag':u'{y}',
+ u'start html':u'',
+ u'end tag':u'{/y}', u'end html':u'', u'protected':False})
+html_expands.append({u'desc':u'Green', u'start tag':u'{g}',
+ u'start html':u'',
+ u'end tag':u'{/g}', u'end html':u'', u'protected':False})
+html_expands.append({u'desc':u'Pink', u'start tag':u'{pk}',
+ u'start html':u'',
+ u'end tag':u'{/pk}', u'end html':u'', u'protected':False})
+html_expands.append({u'desc':u'Orange', u'start tag':u'{o}',
+ u'start html':u'',
+ u'end tag':u'{/o}', u'end html':u'', u'protected':False})
+html_expands.append({u'desc':u'Purple', u'start tag':u'{pp}',
+ u'start html':u'',
+ u'end tag':u'{/pp}', u'end html':u'', u'protected':False})
+html_expands.append({u'desc':u'White', u'start tag':u'{w}',
+ u'start html':u'',
+ u'end tag':u'{/w}', u'end html':u'', u'protected':False})
+html_expands.append({u'desc':u'Superscript', u'start tag':u'{su}',
+ u'start html':u'', u'end tag':u'{/su}', u'end html':u'',
+ u'protected':True})
+html_expands.append({u'desc':u'Subscript', u'start tag':u'{sb}',
+ u'start html':u'', u'end tag':u'{/sb}', u'end html':u'',
+ u'protected':True})
+html_expands.append({u'desc':u'Paragraph', u'start tag':u'{p}',
+ u'start html':u'', u'end tag':u'{/p}', u'end html':u'
',
+ u'protected':True})
+html_expands.append({u'desc':u'Bold', u'start tag':u'{st}',
+ u'start html':u'', u'end tag':u'{/st}', u'end html':u'',
+ u'protected':True})
+html_expands.append({u'desc':u'Italics', u'start tag':u'{it}',
+ u'start html':u'', u'end tag':u'{/it}', u'end html':u'',
+ 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
diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py
index f0194da02..765d0f9fa 100644
--- a/openlp/core/lib/htmlbuilder.py
+++ b/openlp/core/lib/htmlbuilder.py
@@ -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 {
-
-
+
+
+
%s
@@ -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;'
diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py
index a6d62f618..625a74842 100644
--- a/openlp/core/lib/mediamanageritem.py
+++ b/openlp/core/lib/mediamanageritem.py
@@ -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')
diff --git a/openlp/core/lib/renderer.py b/openlp/core/lib/renderer.py
index 0cb92ad39..f97575c5e 100644
--- a/openlp/core/lib/renderer.py
+++ b/openlp/core/lib/renderer.py
@@ -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'' \
+ u'' % \
+ (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'' \
- u'
' % \
- (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'
'
- web.setHtml(html)
+ html = self.page_shell + styled_text + u'
'
+ 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
diff --git a/openlp/core/lib/rendermanager.py b/openlp/core/lib/rendermanager.py
index a6e494b01..e98ab4f01 100644
--- a/openlp/core/lib/rendermanager.py
+++ b/openlp/core/lib/rendermanager.py
@@ -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):
diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py
index b0d453af5..e7ec9c2af 100644
--- a/openlp/core/lib/serviceitem.py
+++ b/openlp/core/lib/serviceitem.py
@@ -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()
diff --git a/openlp/core/lib/spelltextedit.py b/openlp/core/lib/spelltextedit.py
index 7d227079b..07e8ad728 100644
--- a/openlp/core/lib/spelltextedit.py
+++ b/openlp/core/lib/spelltextedit.py
@@ -28,6 +28,7 @@ import re
import sys
try:
import enchant
+ from enchant import DictNotFoundError
enchant_available = True
except ImportError:
enchant_available = False
@@ -43,7 +44,10 @@ class SpellTextEdit(QtGui.QPlainTextEdit):
QtGui.QPlainTextEdit.__init__(self, *args)
# Default dictionary based on the current locale.
if enchant_available:
- self.dict = enchant.Dict()
+ try:
+ self.dict = enchant.Dict()
+ except DictNotFoundError:
+ self.dict = enchant.Dict(u'en_US')
self.highlighter = Highlighter(self.document())
self.highlighter.setDict(self.dict)
diff --git a/openlp/core/ui/aboutdialog.py b/openlp/core/ui/aboutdialog.py
index 3965b4ffc..3b9190156 100644
--- a/openlp/core/ui/aboutdialog.py
+++ b/openlp/core/ui/aboutdialog.py
@@ -199,18 +199,18 @@ class Ui_AboutDialog(object):
'Preamble\n'
'\n'
'The licenses for most software are designed to take away your '
- 'freedom to share and change it. By contrast, the GNU General '
+ 'freedom to share and change it. By contrast, the GNU General '
'Public License is intended to guarantee your freedom to share '
'and change free software--to make sure the software is free for '
- 'all its users. This General Public License applies to most of '
+ 'all its users. This General Public License applies to most of '
'the Free Software Foundation\'s software and to any other '
- 'program whose authors commit to using it. (Some other Free '
+ 'program whose authors commit to using it. (Some other Free '
'Software Foundation software is covered by the GNU Lesser '
- 'General Public License instead.) You can apply it to your '
+ 'General Public License instead.) You can apply it to your '
'programs, too.\n'
'\n'
'When we speak of free software, we are referring to freedom, not '
- 'price. Our General Public Licenses are designed to make sure '
+ 'price. Our General Public Licenses are designed to make sure '
'that you have the freedom to distribute copies of free software '
'(and charge for this service if you wish), that you receive '
'source code or can get it if you want it, that you can change '
@@ -225,8 +225,8 @@ class Ui_AboutDialog(object):
'\n'
'For example, if you distribute copies of such a program, whether '
'gratis or for a fee, you must give the recipients all the rights '
- 'that you have. You must make sure that they, too, receive or '
- 'can get the source code. And you must show them these terms so '
+ 'that you have. You must make sure that they, too, receive or '
+ 'can get the source code. And you must show them these terms so '
'they know their rights.\n'
'\n'
'We protect your rights with two steps: (1) copyright the '
@@ -235,15 +235,15 @@ class Ui_AboutDialog(object):
'\n'
'Also, for each author\'s protection and ours, we want to make '
'certain that everyone understands that there is no warranty for '
- 'this free software. If the software is modified by someone else '
+ 'this free software. If the software is modified by someone else '
'and passed on, we want its recipients to know that what they '
'have is not the original, so that any problems introduced by '
'others will not reflect on the original authors\' reputations.\n'
'\n'
'Finally, any free program is threatened constantly by software '
- 'patents. We wish to avoid the danger that redistributors of a '
+ 'patents. We wish to avoid the danger that redistributors of a '
'free program will individually obtain patent licenses, in effect '
- 'making the program proprietary. To prevent this, we have made '
+ 'making the program proprietary. To prevent this, we have made '
'it clear that any patent must be licensed for everyone\'s free '
'use or not licensed at all.\n'
'\n'
@@ -255,17 +255,17 @@ class Ui_AboutDialog(object):
'\n'
'0. This License applies to any program or other work which '
'contains a notice placed by the copyright holder saying it may '
- 'be distributed under the terms of this General Public License. '
+ 'be distributed under the terms of this General Public License. '
'The "Program", below, refers to any such program or work, and a '
'"work based on the Program" means either the Program or any '
'derivative work under copyright law: that is to say, a work '
'containing the Program or a portion of it, either verbatim or '
- 'with modifications and/or translated into another language. '
+ 'with modifications and/or translated into another language. '
'(Hereinafter, translation is included without limitation in the '
- 'term "modification".) Each licensee is addressed as "you".\n'
+ 'term "modification".) Each licensee is addressed as "you".\n'
'\n'
'Activities other than copying, distribution and modification are '
- 'not covered by this License; they are outside its scope. The '
+ 'not covered by this License; they are outside its scope. The '
'act of running the Program is not restricted, and the output '
'from the Program is covered only if its contents constitute a '
'work based on the Program (independent of having been made by '
@@ -305,17 +305,17 @@ class Ui_AboutDialog(object):
'notice that there is no warranty (or else, saying that you '
'provide a warranty) and that users may redistribute the program '
'under these conditions, and telling the user how to view a copy '
- 'of this License. (Exception: if the Program itself is '
+ 'of this License. (Exception: if the Program itself is '
'interactive but does not normally print such an announcement, '
'your work based on the Program is not required to print an '
'announcement.)\n'
'\n'
- 'These requirements apply to the modified work as a whole. If '
+ 'These requirements apply to the modified work as a whole. If '
'identifiable sections of that work are not derived from the '
'Program, and can be reasonably considered independent and '
'separate works in themselves, then this License, and its terms, '
'do not apply to those sections when you distribute them as '
- 'separate works. But when you distribute the same sections as '
+ 'separate works. But when you distribute the same sections as '
'part of a whole which is a work based on the Program, the '
'distribution of the whole must be on the terms of this License, '
'whose permissions for other licensees extend to the entire '
@@ -350,17 +350,17 @@ class Ui_AboutDialog(object):
'medium customarily used for software interchange; or,\n'
'\n'
'c) Accompany it with the information you received as to the '
- 'offer to distribute corresponding source code. (This '
+ 'offer to distribute corresponding source code. (This '
'alternative is allowed only for noncommercial distribution and '
'only if you received the program in object code or executable '
'form with such an offer, in accord with Subsection b above.)\n'
'\n'
'The source code for a work means the preferred form of the work '
- 'for making modifications to it. For an executable work, '
+ 'for making modifications to it. For an executable work, '
'complete source code means all the source code for all modules '
'it contains, plus any associated interface definition files, '
'plus the scripts used to control compilation and installation of '
- 'the executable. However, as a special exception, the source '
+ 'the executable. However, as a special exception, the source '
'code distributed need not include anything that is normally '
'distributed (in either source or binary form) with the major '
'components (compiler, kernel, and so on) of the operating system '
@@ -374,7 +374,7 @@ class Ui_AboutDialog(object):
'not compelled to copy the source along with the object code.\n'
'\n'
'4. You may not copy, modify, sublicense, or distribute the '
- 'Program except as expressly provided under this License. Any '
+ 'Program except as expressly provided under this License. Any '
'attempt otherwise to copy, modify, sublicense or distribute the '
'Program is void, and will automatically terminate your rights '
'under this License. However, parties who have received copies, '
@@ -383,10 +383,10 @@ class Ui_AboutDialog(object):
'compliance.\n'
'\n'
'5. You are not required to accept this License, since you have '
- 'not signed it. However, nothing else grants you permission to '
- 'modify or distribute the Program or its derivative works. These '
+ 'not signed it. However, nothing else grants you permission to '
+ 'modify or distribute the Program or its derivative works. These '
'actions are prohibited by law if you do not accept this '
- 'License. Therefore, by modifying or distributing the Program '
+ 'License. Therefore, by modifying or distributing the Program '
'(or any work based on the Program), you indicate your acceptance '
'of this License to do so, and all its terms and conditions for '
'copying, distributing or modifying the Program or works based on '
@@ -395,7 +395,7 @@ class Ui_AboutDialog(object):
'6. Each time you redistribute the Program (or any work based on '
'the Program), the recipient automatically receives a license '
'from the original licensor to copy, distribute or modify the '
- 'Program subject to these terms and conditions. You may not '
+ 'Program subject to these terms and conditions. You may not '
'impose any further restrictions on the recipients\' exercise of '
'the rights granted herein. You are not responsible for enforcing '
'compliance by third parties to this License.\n'
@@ -405,10 +405,10 @@ class Ui_AboutDialog(object):
'patent issues), conditions are imposed on you (whether by court '
'order, agreement or otherwise) that contradict the conditions of '
'this License, they do not excuse you from the conditions of this '
- 'License. If you cannot distribute so as to satisfy '
+ 'License. If you cannot distribute so as to satisfy '
'simultaneously your obligations under this License and any other '
'pertinent obligations, then as a consequence you may not '
- 'distribute the Program at all. For example, if a patent license '
+ 'distribute the Program at all. For example, if a patent license '
'would not permit royalty-free redistribution of the Program by '
'all those who receive copies directly or indirectly through you, '
'then the only way you could satisfy both it and this License '
@@ -423,7 +423,7 @@ class Ui_AboutDialog(object):
'any patents or other property right claims or to contest '
'validity of any such claims; this section has the sole purpose '
'of protecting the integrity of the free software distribution '
- 'system, which is implemented by public license practices. Many '
+ 'system, which is implemented by public license practices. Many '
'people have made generous contributions to the wide range of '
'software distributed through that system in reliance on '
'consistent application of that system; it is up to the '
@@ -439,29 +439,29 @@ class Ui_AboutDialog(object):
'interfaces, the original copyright holder who places the Program '
'under this License may add an explicit geographical distribution '
'limitation excluding those countries, so that distribution is '
- 'permitted only in or among countries not thus excluded. In such '
+ 'permitted only in or among countries not thus excluded. In such '
'case, this License incorporates the limitation as if written in '
'the body of this License.\n'
'\n'
'9. The Free Software Foundation may publish revised and/or new '
- 'versions of the General Public License from time to time. Such '
+ 'versions of the General Public License from time to time. Such '
'new versions will be similar in spirit to the present version, '
'but may differ in detail to address new problems or concerns.\n'
'\n'
- 'Each version is given a distinguishing version number. If the '
+ 'Each version is given a distinguishing version number. If the '
'Program specifies a version number of this License which applies '
- 'to it and \"any later version\', you have the option of '
+ 'to it and "any later version", you have the option of '
'following the terms and conditions either of that version or of '
- 'any later version published by the Free Software Foundation. If '
+ 'any later version published by the Free Software Foundation. If '
'the Program does not specify a version number of this License, '
'you may choose any version ever published by the Free Software '
'Foundation.\n'
'\n'
'10. If you wish to incorporate parts of the Program into other '
'free programs whose distribution conditions are different, write '
- 'to the author to ask for permission. For software which is '
+ 'to the author to ask for permission. For software which is '
'copyrighted by the Free Software Foundation, write to the Free '
- 'Software Foundation; we sometimes make exceptions for this. Our '
+ 'Software Foundation; we sometimes make exceptions for this. Our '
'decision will be guided by the two goals of preserving the free '
'status of all derivatives of our free software and of promoting '
'the sharing and reuse of software generally.\n'
@@ -470,12 +470,12 @@ class Ui_AboutDialog(object):
'\n'
'11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO '
'WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE '
- 'LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT '
+ 'LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT '
'HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT '
'WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, '
'BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY '
- 'AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE '
- 'QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE '
+ 'AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE '
+ 'QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE '
'PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY '
'SERVICING, REPAIR OR CORRECTION.\n'
'\n'
@@ -499,7 +499,7 @@ class Ui_AboutDialog(object):
'this is to make it free software which everyone can redistribute '
'and change under these terms.\n'
'\n'
- 'To do so, attach the following notices to the program. It is '
+ 'To do so, attach the following notices to the program. It is '
'safest to attach them to the start of each source file to most '
'effectively convey the exclusion of warranty; and each file '
'should have at least the "copyright" line and a pointer to where '
@@ -507,7 +507,7 @@ class Ui_AboutDialog(object):
'\n'
'\n'
- 'Copyright (C) \n'
+ 'Copyright (C) \n'
'\n'
'This program is free software; you can redistribute it and/or '
'modify it under the terms of the GNU General Public License as '
@@ -516,7 +516,7 @@ class Ui_AboutDialog(object):
'\n'
'This program is distributed in the hope that it will be useful, '
'but WITHOUT ANY WARRANTY; without even the implied warranty of '
- 'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the '
+ 'MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the '
'GNU General Public License for more details.\n'
'\n'
'You should have received a copy of the GNU General Public '
@@ -537,14 +537,14 @@ class Ui_AboutDialog(object):
'under certain conditions; type "show c" for details.\n'
'\n'
'The hypothetical commands "show w" and "show c" should show '
- 'the appropriate parts of the General Public License. Of course, '
+ 'the appropriate parts of the General Public License. Of course, '
'the commands you use may be called something other than "show '
'w" and "show c"; they could even be mouse-clicks or menu items--'
'whatever suits your program.\n'
'\n'
'You should also get your employer (if you work as a programmer) '
'or your school, if any, to sign a "copyright disclaimer" for the '
- 'program, if necessary. Here is a sample; alter the names:\n'
+ 'program, if necessary. Here is a sample; alter the names:\n'
'\n'
'Yoyodyne, Inc., hereby disclaims all copyright interest in the '
'program "Gnomovision" (which makes passes at compilers) written '
@@ -554,9 +554,9 @@ class Ui_AboutDialog(object):
'Ty Coon, President of Vice\n'
'\n'
'This General Public License does not permit incorporating your '
- 'program into proprietary programs. If your program is a '
+ 'program into proprietary programs. If your program is a '
'subroutine library, you may consider it more useful to permit '
- 'linking proprietary applications with the library. If this is '
+ 'linking proprietary applications with the library. If this is '
'what you want to do, use the GNU Lesser General Public License '
'instead of this License.'))
self.aboutNotebook.setTabText(
@@ -565,3 +565,4 @@ class Ui_AboutDialog(object):
self.contributeButton.setText(translate('OpenLP.AboutForm',
'Contribute'))
self.closeButton.setText(translate('OpenLP.AboutForm', 'Close'))
+
diff --git a/openlp/core/ui/exceptiondialog.py b/openlp/core/ui/exceptiondialog.py
index 28097e938..49e2c8151 100644
--- a/openlp/core/ui/exceptiondialog.py
+++ b/openlp/core/ui/exceptiondialog.py
@@ -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 '
diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py
index 6fbd7995c..d5fd1e861 100644
--- a/openlp/core/ui/maindisplay.py
+++ b/openlp/core/ui/maindisplay.py
@@ -99,6 +99,7 @@ class MainDisplay(DisplayWidget):
self.alertTab = None
self.hide_mode = None
self.setWindowTitle(u'OpenLP Display')
+ self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;')
self.setWindowFlags(QtCore.Qt.FramelessWindowHint |
QtCore.Qt.WindowStaysOnTopHint)
if self.isLive:
@@ -116,12 +117,18 @@ class MainDisplay(DisplayWidget):
self.screen = self.screens.current
self.setVisible(False)
self.setGeometry(self.screen[u'size'])
- self.scene = QtGui.QGraphicsScene()
- self.setScene(self.scene)
- self.webView = QtWebKit.QGraphicsWebView()
- self.scene.addItem(self.webView)
- self.webView.resize(self.screen[u'size'].width(), \
- self.screen[u'size'].height())
+ try:
+ self.webView = QtWebKit.QGraphicsWebView()
+ self.scene = QtGui.QGraphicsScene(self)
+ self.setScene(self.scene)
+ self.scene.addItem(self.webView)
+ self.webView.setGeometry(QtCore.QRectF(0, 0,
+ self.screen[u'size'].width(), self.screen[u'size'].height()))
+ except AttributeError:
+ # QGraphicsWebView a recent addition, so fall back to QWebView
+ self.webView = QtWebKit.QWebView(self)
+ self.webView.setGeometry(0, 0,
+ self.screen[u'size'].width(), self.screen[u'size'].height())
self.page = self.webView.page()
self.frame = self.page.mainFrame()
QtCore.QObject.connect(self.webView,
@@ -157,7 +164,7 @@ class MainDisplay(DisplayWidget):
- splash_image.height()) / 2,
splash_image)
serviceItem = ServiceItem()
- serviceItem.bg_frame = initialFrame
+ serviceItem.bg_image_bytes = image_to_byte(initialFrame)
self.webView.setHtml(build_html(serviceItem, self.screen,
self.parent.alertTab, self.isLive))
self.initialFrame = True
@@ -286,7 +293,7 @@ class MainDisplay(DisplayWidget):
"""
log.debug(u'video')
self.loaded = True
- js = u'show_video("play", "%s", %s, true);' % \
+ js = u'show_video("init", "%s", %s, true); show_video("play");' % \
(videoPath.replace(u'\\', u'\\\\'), str(float(volume)/float(10)))
self.frame.evaluateJavaScript(js)
return self.preview()
@@ -303,6 +310,10 @@ class MainDisplay(DisplayWidget):
Generates a preview of the image displayed.
"""
log.debug(u'preview for %s', self.isLive)
+ # We must have a service item to preview
+ if not hasattr(self, u'serviceItem'):
+ return
+ Receiver.send_message(u'openlp_process_events')
if self.isLive:
# Wait for the fade to finish before geting the preview.
# Important otherwise preview will have incorrect text if at all !
@@ -315,6 +326,12 @@ class MainDisplay(DisplayWidget):
# Important otherwise first preview will miss the background !
while not self.loaded:
Receiver.send_message(u'openlp_process_events')
+ # if was hidden keep it hidden
+ if self.isLive:
+ self.setVisible(True)
+ # if was hidden keep it hidden
+ if self.hide_mode and self.isLive:
+ self.hideDisplay(self.hide_mode)
preview = QtGui.QImage(self.screen[u'size'].width(),
self.screen[u'size'].height(),
QtGui.QImage.Format_ARGB32_Premultiplied)
@@ -322,9 +339,6 @@ class MainDisplay(DisplayWidget):
painter.setRenderHint(QtGui.QPainter.Antialiasing)
self.frame.render(painter)
painter.end()
- # Make display show up if in single screen mode
- if self.isLive:
- self.setVisible(True)
return preview
def buildHtml(self, serviceItem):
@@ -336,9 +350,11 @@ class MainDisplay(DisplayWidget):
self.loaded = False
self.initialFrame = False
self.serviceItem = serviceItem
- html = build_html(self.serviceItem, self.screen, self.parent.alertTab,\
+ html = build_html(self.serviceItem, self.screen, self.parent.alertTab,
self.isLive)
+ log.debug(u'buildHtml - pre setHtml')
self.webView.setHtml(html)
+ log.debug(u'buildHtml - post setHtml')
if serviceItem.foot_text and serviceItem.foot_text:
self.footer(serviceItem.foot_text)
# if was hidden keep it hidden
diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py
index abf035d65..0d158d042 100644
--- a/openlp/core/ui/mainwindow.py
+++ b/openlp/core/ui/mainwindow.py
@@ -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,
diff --git a/openlp/core/ui/plugindialog.py b/openlp/core/ui/plugindialog.py
index 9d12651c9..45305d75c 100644
--- a/openlp/core/ui/plugindialog.py
+++ b/openlp/core/ui/plugindialog.py
@@ -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'))
+
diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py
index c7d3c497f..5abb8d576 100644
--- a/openlp/core/ui/servicemanager.py
+++ b/openlp/core/ui/servicemanager.py
@@ -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):
"""
diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py
index 2ea985598..0a3e0c91b 100644
--- a/openlp/core/ui/slidecontroller.py
+++ b/openlp/core/ui/slidecontroller.py
@@ -209,7 +209,8 @@ class SlideController(QtGui.QWidget):
self.Toolbar.addToolbarSeparator(u'Close Separator')
self.Toolbar.addToolbarButton(
u'Edit Song', u':/general/general_edit.png',
- translate('OpenLP.SlideController', 'Edit and re-preview Song'),
+ translate('OpenLP.SlideController',
+ 'Edit and reload song preview'),
self.onEditSong)
if isLive:
self.Toolbar.addToolbarSeparator(u'Loop Separator')
@@ -269,11 +270,11 @@ class SlideController(QtGui.QWidget):
if isLive:
self.SongMenu = QtGui.QToolButton(self.Toolbar)
self.SongMenu.setText(translate('OpenLP.SlideController',
- 'Go to'))
+ 'Go To'))
self.SongMenu.setPopupMode(QtGui.QToolButton.InstantPopup)
self.Toolbar.addToolbarWidget(u'Song Menu', self.SongMenu)
self.SongMenu.setMenu(QtGui.QMenu(
- translate('OpenLP.SlideController', 'Go to'),
+ translate('OpenLP.SlideController', 'Go To'),
self.Toolbar))
self.Toolbar.makeWidgetsInvisible([u'Song Menu'])
# Screen preview area
@@ -636,9 +637,9 @@ class SlideController(QtGui.QWidget):
"""
if not self.serviceItem:
return
- Receiver.send_message(u'%s_first' % self.serviceItem.name.lower(),
- [self.serviceItem, self.isLive])
if self.serviceItem.is_command():
+ Receiver.send_message(u'%s_first' % self.serviceItem.name.lower(),
+ [self.serviceItem, self.isLive])
self.updatePreview()
else:
self.PreviewListWidget.selectRow(0)
@@ -651,9 +652,9 @@ class SlideController(QtGui.QWidget):
index = int(message[0])
if not self.serviceItem:
return
- Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
- [self.serviceItem, self.isLive, index])
if self.serviceItem.is_command():
+ Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
+ [self.serviceItem, self.isLive, index])
self.updatePreview()
else:
self.PreviewListWidget.selectRow(index)
@@ -768,9 +769,9 @@ class SlideController(QtGui.QWidget):
row = self.PreviewListWidget.currentRow()
self.selectedRow = 0
if row > -1 and row < self.PreviewListWidget.rowCount():
- Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
- [self.serviceItem, self.isLive, row])
if self.serviceItem.is_command() and self.isLive:
+ Receiver.send_message(u'%s_slide' % self.serviceItem.name.lower(),
+ [self.serviceItem, self.isLive, row])
self.updatePreview()
else:
frame, raw_html = self.serviceItem.get_rendered_frame(row)
diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py
index 692bfe041..cd2cfefad 100644
--- a/openlp/core/ui/thememanager.py
+++ b/openlp/core/ui/thememanager.py
@@ -242,14 +242,14 @@ class ThemeManager(QtGui.QWidget):
QtGui.QMessageBox.critical(self,
translate('OpenLP.ThemeManager', 'Error'),
unicode(translate('OpenLP.ThemeManager',
- 'Theme %s is use in %s plugin.')) % \
+ 'Theme %s is used in the %s plugin.')) % \
(theme, plugin.name))
return
if unicode(self.serviceComboBox.currentText()) == theme:
QtGui.QMessageBox.critical(self,
translate('OpenLP.ThemeManager', 'Error'),
unicode(translate('OpenLP.ThemeManager',
- 'Theme %s is use by the service manager.')) % theme)
+ 'Theme %s is used by the service manager.')) % theme)
return
row = self.themeListWidget.row(item)
self.themeListWidget.takeItem(row)
@@ -573,7 +573,7 @@ class ThemeManager(QtGui.QWidget):
translate('OpenLP.ThemeManager', 'Theme Exists'),
translate('OpenLP.ThemeManager',
'A theme with this name already '
- 'exists. Would you like to overwrite it?'),
+ 'exists. Would you like to overwrite it?'),
(QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
QtGui.QMessageBox.No)
if self.saveThemeName != u'':
diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py
index e728fb544..119bf6b55 100644
--- a/openlp/core/utils/__init__.py
+++ b/openlp/core/utils/__init__.py
@@ -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():
diff --git a/openlp/core/utils/languagemanager.py b/openlp/core/utils/languagemanager.py
index 275d6985b..f118c64b0 100644
--- a/openlp/core/utils/languagemanager.py
+++ b/openlp/core/utils/languagemanager.py
@@ -35,14 +35,14 @@ from PyQt4 import QtCore, QtGui
from openlp.core.utils import AppLocation
from openlp.core.lib import translate
-log = logging.getLogger()
+log = logging.getLogger(__name__)
class LanguageManager(object):
"""
Helper for Language selection
"""
- __qmList__ = None
- AutoLanguage = False
+ __qm_list__ = {}
+ auto_language = False
@staticmethod
def get_translator(language):
@@ -52,12 +52,11 @@ class LanguageManager(object):
``language``
The language to load into the translator
"""
- if LanguageManager.AutoLanguage:
+ if LanguageManager.auto_language:
language = QtCore.QLocale.system().name()
- lang_path = AppLocation.get_directory(AppLocation.AppDir)
- lang_path = os.path.join(lang_path, u'resources', u'i18n')
+ lang_path = AppLocation.get_directory(AppLocation.LanguageDir)
app_translator = QtCore.QTranslator()
- if app_translator.load("openlp_" + language, lang_path):
+ if app_translator.load(language, lang_path):
return app_translator
@staticmethod
@@ -65,8 +64,8 @@ class LanguageManager(object):
"""
Find all available language files in this OpenLP install
"""
- trans_dir = AppLocation.get_directory(AppLocation.AppDir)
- trans_dir = QtCore.QDir(os.path.join(trans_dir, u'resources', u'i18n'))
+ trans_dir = QtCore.QDir(AppLocation.get_directory(
+ AppLocation.LanguageDir))
file_names = trans_dir.entryList(QtCore.QStringList("*.qm"),
QtCore.QDir.Files, QtCore.QDir.Name)
for name in file_names:
@@ -96,7 +95,7 @@ class LanguageManager(object):
log.info(u'Language file: \'%s\' Loaded from conf file' % language)
reg_ex = QtCore.QRegExp("^\[(.*)\]")
if reg_ex.exactMatch(language):
- LanguageManager.AutoLanguage = True
+ LanguageManager.auto_language = True
language = reg_ex.cap(1)
return language
@@ -108,9 +107,12 @@ class LanguageManager(object):
``action``
The language menu option
"""
- action_name = u'%s' % action.objectName()
+ if action is None:
+ action_name = u'en'
+ else:
+ action_name = u'%s' % action.objectName()
qm_list = LanguageManager.get_qm_list()
- if LanguageManager.AutoLanguage:
+ if LanguageManager.auto_language:
language = u'[%s]' % qm_list[action_name]
else:
language = u'%s' % qm_list[action_name]
@@ -127,20 +129,18 @@ class LanguageManager(object):
"""
Initialise the list of available translations
"""
- LanguageManager.__qmList__ = {}
+ LanguageManager.__qm_list__ = {}
qm_files = LanguageManager.find_qm_files()
- for i, qmf in enumerate(qm_files):
- reg_ex = QtCore.QRegExp("^.*openlp_(.*).qm")
- if reg_ex.exactMatch(qmf):
- lang_name = reg_ex.cap(1)
- LanguageManager.__qmList__[u'%#2i %s' % (i+1,
- LanguageManager.language_name(qmf))] = lang_name
+ for counter, qmf in enumerate(qm_files):
+ name = unicode(qmf).split(u'.')[0]
+ LanguageManager.__qm_list__[u'%#2i %s' % (counter + 1,
+ LanguageManager.language_name(qmf))] = name
@staticmethod
def get_qm_list():
"""
Return the list of available translations
"""
- if LanguageManager.__qmList__ is None:
+ if not LanguageManager.__qm_list__:
LanguageManager.init_qm_list()
- return LanguageManager.__qmList__
+ return LanguageManager.__qm_list__
diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py
index 9e1da2267..b8a829b37 100644
--- a/openlp/plugins/alerts/alertsplugin.py
+++ b/openlp/plugins/alerts/alertsplugin.py
@@ -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)
diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py
index da542b23b..7f69c6ff0 100644
--- a/openlp/plugins/bibles/bibleplugin.py
+++ b/openlp/plugins/bibles/bibleplugin.py
@@ -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)
diff --git a/openlp/plugins/bibles/forms/importwizardform.py b/openlp/plugins/bibles/forms/importwizardform.py
index 67f3756dc..3b2611e8f 100644
--- a/openlp/plugins/bibles/forms/importwizardform.py
+++ b/openlp/plugins/bibles/forms/importwizardform.py
@@ -126,29 +126,29 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
if self.field(u'osis_location').toString() == u'':
QtGui.QMessageBox.critical(self,
translate('BiblesPlugin.ImportWizardForm',
- 'Invalid Bible Location'),
+ 'Invalid Bible Location'),
translate('BiblesPlugin.ImportWizardForm',
- 'You need to specify a file to import your '
- 'Bible from.'))
+ 'You need to specify a file to import your '
+ 'Bible from.'))
self.OSISLocationEdit.setFocus()
return False
elif self.field(u'source_format').toInt()[0] == BibleFormat.CSV:
if self.field(u'csv_booksfile').toString() == u'':
QtGui.QMessageBox.critical(self,
translate('BiblesPlugin.ImportWizardForm',
- 'Invalid Books File'),
+ 'Invalid Books File'),
translate('BiblesPlugin.ImportWizardForm',
- 'You need to specify a file with books of '
- 'the Bible to use in the import.'))
+ 'You need to specify a file with books of '
+ 'the Bible to use in the import.'))
self.BooksLocationEdit.setFocus()
return False
elif self.field(u'csv_versefile').toString() == u'':
QtGui.QMessageBox.critical(self,
translate('BiblesPlugin.ImportWizardForm',
- 'Invalid Verse File'),
+ 'Invalid Verse File'),
translate('BiblesPlugin.ImportWizardForm',
- 'You need to specify a file of Bible '
- 'verses to import.'))
+ 'You need to specify a file of Bible '
+ 'verses to import.'))
self.CsvVerseLocationEdit.setFocus()
return False
elif self.field(u'source_format').toInt()[0] == \
@@ -156,10 +156,10 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
if self.field(u'opensong_file').toString() == u'':
QtGui.QMessageBox.critical(self,
translate('BiblesPlugin.ImportWizardForm',
- 'Invalid OpenSong Bible'),
+ 'Invalid OpenSong Bible'),
translate('BiblesPlugin.ImportWizardForm',
- 'You need to specify an OpenSong Bible '
- 'file to import.'))
+ 'You need to specify an OpenSong Bible '
+ 'file to import.'))
self.OpenSongFileEdit.setFocus()
return False
return True
@@ -171,29 +171,26 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
if license_version == u'':
QtGui.QMessageBox.critical(self,
translate('BiblesPlugin.ImportWizardForm',
- 'Empty Version Name'),
+ 'Empty Version Name'),
translate('BiblesPlugin.ImportWizardForm',
- 'You need to specify a version name for your '
- 'Bible.'))
+ 'You need to specify a version name for your Bible.'))
self.VersionNameEdit.setFocus()
return False
elif license_copyright == u'':
QtGui.QMessageBox.critical(self,
translate('BiblesPlugin.ImportWizardForm',
- 'Empty Copyright'),
+ 'Empty Copyright'),
translate('BiblesPlugin.ImportWizardForm',
- 'You need to set a copyright for your Bible! '
- 'Bibles in the Public Domain need to be marked as '
- 'such.'))
+ 'You need to set a copyright for your Bible. '
+ 'Bibles in the Public Domain need to be marked as such.'))
self.CopyrightEdit.setFocus()
return False
elif self.manager.exists(license_version):
QtGui.QMessageBox.critical(self,
+ translate('BiblesPlugin.ImportWizardForm', 'Bible Exists'),
translate('BiblesPlugin.ImportWizardForm',
- 'Bible Exists'),
- translate('BiblesPlugin.ImportWizardForm',
- 'This Bible already exists! Please import '
- 'a different Bible or first delete the existing one.'))
+ 'This Bible already exists. Please import '
+ 'a different Bible or first delete the existing one.'))
self.VersionNameEdit.setFocus()
return False
return True
@@ -290,6 +287,9 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
def setDefaults(self):
settings = QtCore.QSettings()
settings.beginGroup(self.bibleplugin.settingsSection)
+ self.restart()
+ self.finishButton.setVisible(False)
+ self.cancelButton.setVisible(True)
self.setField(u'source_format', QtCore.QVariant(0))
self.setField(u'osis_location', QtCore.QVariant(''))
self.setField(u'csv_booksfile', QtCore.QVariant(''))
@@ -434,18 +434,16 @@ class ImportWizardForm(QtGui.QWizard, Ui_BibleImportWizard):
unicode(self.field(u'proxy_username').toString()),
proxy_password=unicode(self.field(u'proxy_password').toString())
)
- success = importer.do_import()
- if success:
+ if importer.do_import():
self.manager.save_meta_data(license_version, license_version,
license_copyright, license_permission)
self.manager.reload_bibles()
self.ImportProgressLabel.setText(
- translate('BiblesPlugin.ImportWizardForm',
- 'Finished import.'))
+ translate('BiblesPlugin.ImportWizardForm', 'Finished import.'))
else:
self.ImportProgressLabel.setText(
translate('BiblesPlugin.ImportWizardForm',
- 'Your Bible import failed.'))
+ 'Your Bible import failed.'))
delete_database(self.bibleplugin.settingsSection, importer.file)
def postImport(self):
diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py
index 51b6bb5fa..49bc82102 100644
--- a/openlp/plugins/bibles/lib/db.py
+++ b/openlp/plugins/bibles/lib/db.py
@@ -353,7 +353,7 @@ class BibleDB(QtCore.QObject, Manager):
QtGui.QMessageBox.information(self.bible_plugin.mediaItem,
translate('BiblesPlugin.BibleDB', 'Book not found'),
translate('BiblesPlugin.BibleDB', 'The book you requested '
- 'could not be found in this bible. Please check your '
+ 'could not be found in this bible. Please check your '
'spelling and that this is a complete bible not just '
'one testament.'))
return verse_list
diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py
index a0734aa98..fd2c2adff 100644
--- a/openlp/plugins/bibles/lib/manager.py
+++ b/openlp/plugins/bibles/lib/manager.py
@@ -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'
diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py
index 57e70617a..037951954 100644
--- a/openlp/plugins/bibles/lib/mediaitem.py
+++ b/openlp/plugins/bibles/lib/mediaitem.py
@@ -65,12 +65,6 @@ class BibleMediaItem(MediaManagerItem):
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'bibles_load_list'), self.reloadBibles)
- def _decodeQtObject(self, listobj, key):
- obj = listobj[QtCore.QString(key)]
- if isinstance(obj, QtCore.QVariant):
- obj = obj.toPyObject()
- return unicode(obj)
-
def requiredIcons(self):
MediaManagerItem.requiredIcons(self)
self.hasImportIcon = True
@@ -376,9 +370,6 @@ class BibleMediaItem(MediaManagerItem):
def onSearchProgressShow(self):
self.SearchProgress.setVisible(True)
Receiver.send_message(u'openlp_process_events')
- #self.SearchProgress.setMinimum(0)
- #self.SearchProgress.setMaximum(2)
- #self.SearchProgress.setValue(1)
def onSearchProgressHide(self):
self.SearchProgress.setVisible(False)
@@ -408,18 +399,28 @@ class BibleMediaItem(MediaManagerItem):
self.reloadBibles()
def onAdvancedFromVerse(self):
- frm = self.AdvancedFromVerse.currentText()
- self.adjustComboBox(frm, self.verses, self.AdvancedToVerse)
-
- def onAdvancedToChapter(self):
- frm = unicode(self.AdvancedFromChapter.currentText())
- to = unicode(self.AdvancedToChapter.currentText())
- if frm != to:
+ frm = int(self.AdvancedFromVerse.currentText())
+ chapter_frm = int(self.AdvancedFromChapter.currentText())
+ chapter_to = int(self.AdvancedToChapter.currentText())
+ if chapter_frm == chapter_to:
bible = unicode(self.AdvancedVersionComboBox.currentText())
book = unicode(self.AdvancedBookComboBox.currentText())
- # get the verse count for new chapter
- verses = self.parent.manager.get_verse_count(bible, book, int(to))
+ verses = self.parent.manager.get_verse_count(bible, book, chapter_to)
+ self.adjustComboBox(frm, verses, self.AdvancedToVerse)
+
+ def onAdvancedToChapter(self):
+ chapter_frm = int(self.AdvancedFromChapter.currentText())
+ chapter_to = int(self.AdvancedToChapter.currentText())
+ bible = unicode(self.AdvancedVersionComboBox.currentText())
+ book = unicode(self.AdvancedBookComboBox.currentText())
+ verses = self.parent.manager.get_verse_count(bible, book, chapter_to)
+ if chapter_frm != chapter_to:
self.adjustComboBox(1, verses, self.AdvancedToVerse)
+ else:
+ frm = int(self.AdvancedFromVerse.currentText())
+ to = int(self.AdvancedToVerse.currentText())
+ if to < frm:
+ self.adjustComboBox(frm, verses, self.AdvancedToVerse)
def onAdvancedSearchButton(self):
log.debug(u'Advanced Search Button pressed')
@@ -438,32 +439,72 @@ class BibleMediaItem(MediaManagerItem):
dual_bible, versetext)
if self.ClearAdvancedSearchComboBox.currentIndex() == 0:
self.listView.clear()
- self.displayResults(bible, dual_bible)
+ if self.listView.count() != 0:
+ bitem = self.listView.item(0)
+ item_dual_bible = self._decodeQtObject(bitem, 'dual_bible')
+ if item_dual_bible and dual_bible or not item_dual_bible and \
+ not dual_bible:
+ self.displayResults(bible, dual_bible)
+ elif QtGui.QMessageBox.critical(self,
+ translate('BiblePlugin.MediaItem', 'Error'),
+ translate('BiblePlugin.MediaItem', 'You cannot combine single '
+ 'and dual bible verses. Do you want to delete your search '
+ 'results and start a new search?'),
+ QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
+ QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.Yes:
+ self.listView.clear()
+ self.displayResults(bible, dual_bible)
+ else:
+ self.displayResults(bible, dual_bible)
def onAdvancedFromChapter(self):
bible = unicode(self.AdvancedVersionComboBox.currentText())
book = unicode(self.AdvancedBookComboBox.currentText())
- cf = int(self.AdvancedFromChapter.currentText())
- self.adjustComboBox(cf, self.chapters_from, self.AdvancedToChapter)
- # get the verse count for new chapter
- vse = self.parent.manager.get_verse_count(bible, book, cf)
- self.adjustComboBox(1, vse, self.AdvancedFromVerse)
- self.adjustComboBox(1, vse, self.AdvancedToVerse)
+ chapter_frm = int(self.AdvancedFromChapter.currentText())
+ self.adjustComboBox(chapter_frm, self.chapters_from,
+ self.AdvancedToChapter)
+ verse = self.parent.manager.get_verse_count(bible, book, chapter_frm)
+ self.adjustComboBox(1, verse, self.AdvancedToVerse)
+ self.adjustComboBox(1, verse, self.AdvancedFromVerse)
def onQuickSearchButton(self):
log.debug(u'Quick Search Button pressed')
bible = unicode(self.QuickVersionComboBox.currentText())
dual_bible = unicode(self.QuickSecondBibleComboBox.currentText())
text = unicode(self.QuickSearchEdit.text())
- if self.ClearQuickSearchComboBox.currentIndex() == 0:
- self.listView.clear()
self.search_results = self.parent.manager.get_verses(bible, text)
if dual_bible:
self.dual_search_results = self.parent.manager.get_verses(
dual_bible, text)
- if self.search_results:
+ if self.ClearQuickSearchComboBox.currentIndex() == 0:
+ self.listView.clear()
+ if self.listView.count() != 0 and self.search_results:
+ bitem = self.listView.item(0)
+ item_dual_bible = self._decodeQtObject(bitem, 'dual_bible')
+ if item_dual_bible and dual_bible or not item_dual_bible and \
+ not dual_bible:
+ self.displayResults(bible, dual_bible)
+ elif QtGui.QMessageBox.critical(self,
+ translate('BiblePlugin.MediaItem', 'Error'),
+ translate('BiblePlugin.MediaItem', 'You cannot combine single '
+ 'and dual bible verses. Do you want to delete your search '
+ 'results and start a new search?'),
+ QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
+ QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.Yes:
+ self.listView.clear()
+ self.displayResults(bible, dual_bible)
+ elif self.search_results:
self.displayResults(bible, dual_bible)
+ def _decodeQtObject(self, bitem, key):
+ reference = bitem.data(QtCore.Qt.UserRole)
+ if isinstance(reference, QtCore.QVariant):
+ reference = reference.toPyObject()
+ obj = reference[QtCore.QString(key)]
+ if isinstance(obj, QtCore.QVariant):
+ obj = obj.toPyObject()
+ return unicode(obj)
+
def generateSlideData(self, service_item, item=None):
"""
Generates and formats the slides for the service item as well as the
@@ -473,67 +514,40 @@ class BibleMediaItem(MediaManagerItem):
items = self.listView.selectedIndexes()
if len(items) == 0:
return False
- has_dual_bible = False
bible_text = u''
old_chapter = u''
raw_footer = []
raw_slides = []
+ raw_title = []
+ first_item = True
for item in items:
bitem = self.listView.item(item.row())
- reference = bitem.data(QtCore.Qt.UserRole)
- if isinstance(reference, QtCore.QVariant):
- reference = reference.toPyObject()
- dual_bible = self._decodeQtObject(reference, 'dual_bible')
- if dual_bible:
- has_dual_bible = True
- break
- # Let's loop through the main lot, and assemble our verses.
- for item in items:
- bitem = self.listView.item(item.row())
- reference = bitem.data(QtCore.Qt.UserRole)
- if isinstance(reference, QtCore.QVariant):
- reference = reference.toPyObject()
- book = self._decodeQtObject(reference, 'book')
- chapter = self._decodeQtObject(reference, 'chapter')
- verse = self._decodeQtObject(reference, 'verse')
- bible = self._decodeQtObject(reference, 'bible')
- version = self._decodeQtObject(reference, 'version')
- copyright = self._decodeQtObject(reference, 'copyright')
- permission = self._decodeQtObject(reference, 'permission')
- text = self._decodeQtObject(reference, 'text')
- dual_bible = self._decodeQtObject(reference, 'dual_bible')
- if dual_bible:
- dual_version = self._decodeQtObject(reference,
- 'dual_version')
- dual_copyright = self._decodeQtObject(reference,
- 'dual_copyright')
- dual_permission = self._decodeQtObject(reference,
- 'dual_permission')
- dual_text = self._decodeQtObject(reference, 'dual_text')
+ book = self._decodeQtObject(bitem, 'book')
+ chapter = int(self._decodeQtObject(bitem, 'chapter'))
+ verse = int(self._decodeQtObject(bitem, 'verse'))
+ bible = self._decodeQtObject(bitem, 'bible')
+ version = self._decodeQtObject(bitem, 'version')
+ copyright = self._decodeQtObject(bitem, 'copyright')
+ permission = self._decodeQtObject(bitem, 'permission')
+ text = self._decodeQtObject(bitem, 'text')
+ dual_bible = self._decodeQtObject(bitem, 'dual_bible')
+ dual_version = self._decodeQtObject(bitem, 'dual_version')
+ dual_copyright = self._decodeQtObject(bitem, 'dual_copyright')
+ dual_permission = self._decodeQtObject(bitem, 'dual_permission')
+ dual_text = self._decodeQtObject(bitem, 'dual_text')
verse_text = self.formatVerse(old_chapter, chapter, verse)
footer = u'%s (%s %s %s)' % (book, version, copyright, permission)
if footer not in raw_footer:
raw_footer.append(footer)
- if has_dual_bible:
- if dual_bible:
- footer = u'%s (%s %s %s)' % (book, dual_version,
- dual_copyright, dual_permission)
- if footer not in raw_footer:
- raw_footer.append(footer)
- # If there is an old bible_text we have to add it.
- if bible_text:
- raw_slides.append(bible_text)
- bible_text = u''
- bible_text = u'%s %s\n\n%s %s' % (verse_text, text,
- verse_text, dual_text)
- raw_slides.append(bible_text)
- bible_text = u''
- elif self.parent.settings_tab.layout_style == 0:
- bible_text = u'%s %s' % (verse_text, text)
- raw_slides.append(bible_text)
- bible_text = u''
- else:
- bible_text = u'%s %s %s\n' % (bible_text, verse_text, text)
+ if dual_bible:
+ footer = u'%s (%s %s %s)' % (book, dual_version, dual_copyright,
+ dual_permission)
+ if footer not in raw_footer:
+ raw_footer.append(footer)
+ bible_text = u'%s %s\n\n%s %s' % (verse_text, text, verse_text,
+ dual_text)
+ raw_slides.append(bible_text)
+ bible_text = u''
# If we are 'Verse Per Slide' then create a new slide.
elif self.parent.settings_tab.layout_style == 0:
bible_text = u'%s %s' % (verse_text, text)
@@ -545,29 +559,31 @@ class BibleMediaItem(MediaManagerItem):
# We have to be 'Continuous'.
else:
bible_text = u'%s %s %s\n' % (bible_text, verse_text, text)
+ if first_item:
+ start_item = item
+ first_item = False
+ elif self.checkTitle(item, old_item):
+ raw_title.append(self.formatTitle(start_item, old_item))
+ start_item = item
+ old_item = item
old_chapter = chapter
+ raw_title.append(self.formatTitle(start_item, item))
# If there are no more items we check whether we have to add bible_text.
if bible_text:
raw_slides.append(bible_text)
bible_text = u''
# Service Item: Capabilities
- if self.parent.settings_tab.layout_style == 2 and not has_dual_bible:
- # split the line but do not replace line breaks in renderer
+ if self.parent.settings_tab.layout_style == 2 and not dual_bible:
+ # Split the line but do not replace line breaks in renderer.
service_item.add_capability(ItemCapabilities.NoLineBreaks)
service_item.add_capability(ItemCapabilities.AllowsPreview)
service_item.add_capability(ItemCapabilities.AllowsLoop)
- service_item.add_capability(ItemCapabilities.AllowsAdditions)
# Service Item: Title
- if not service_item.title:
- if dual_bible:
- service_item.title = u'%s (%s, %s) %s' % (book, version,
- dual_version, verse_text)
+ for title in raw_title:
+ if not service_item.title:
+ service_item.title = title
else:
- service_item.title = u'%s (%s) %s' % (book, version, verse_text)
- elif service_item.title.find(
- translate('BiblesPlugin.MediaItem', 'etc')) == -1:
- service_item.title = u'%s, %s' % (service_item.title,
- translate('BiblesPlugin.MediaItem', 'etc'))
+ service_item.title += u', ' + title
# Service Item: Theme
if len(self.parent.settings_tab.bible_theme) == 0:
service_item.theme = None
@@ -582,12 +598,80 @@ class BibleMediaItem(MediaManagerItem):
service_item.raw_footer = raw_footer
return True
+ def formatTitle(self, start_item, old_item):
+ """
+ This methode is called, when we have to change the title, because
+ we are at the end of a verse range. E. g. if we want to add
+ Genesis 1:1-6 as well as Daniel 2:14.
+ """
+ old_bitem = self.listView.item(old_item.row())
+ old_chapter = int(self._decodeQtObject(old_bitem, 'chapter'))
+ old_verse = int(self._decodeQtObject(old_bitem, 'verse'))
+ start_bitem = self.listView.item(start_item.row())
+ start_book = self._decodeQtObject(start_bitem, 'book')
+ start_chapter = int(self._decodeQtObject(start_bitem, 'chapter'))
+ start_verse = int(self._decodeQtObject(start_bitem, 'verse'))
+ start_bible = self._decodeQtObject(start_bitem, 'bible')
+ start_dual_bible = self._decodeQtObject(start_bitem, 'dual_bible')
+ if start_dual_bible:
+ if start_verse == old_verse and start_chapter == old_chapter:
+ title = u'%s %s:%s (%s, %s)' % (start_book, start_chapter,
+ start_verse, start_bible, start_dual_bible)
+ elif start_chapter == old_chapter:
+ title = u'%s %s:%s-%s (%s, %s)' % (start_book, start_chapter,
+ start_verse, old_verse, start_bible, start_dual_bible)
+ else:
+ title = u'%s %s:%s-%s:%s (%s, %s)' % (start_book, start_chapter,
+ start_verse, old_chapter, old_verse, start_bible,
+ start_dual_bible)
+ else:
+ if start_verse == old_verse and start_chapter == old_chapter:
+ title = u'%s %s:%s (%s)' % (start_book, start_chapter,
+ start_verse, start_bible)
+ elif start_chapter == old_chapter:
+ title = u'%s %s:%s-%s (%s)' % (start_book, start_chapter,
+ start_verse, old_verse, start_bible)
+ else:
+ title = u'%s %s:%s-%s:%s (%s)' % (start_book, start_chapter,
+ start_verse, old_chapter, old_verse, start_bible)
+ return title
+
+ def checkTitle(self, item, old_item):
+ """
+ This methode checks if we are at the end of an verse range. If that is
+ the case, we return True, else False. E. g. if we added Genesis 1:1-6,
+ but the next verse is Daniel 2:14.
+ """
+ bitem = self.listView.item(item.row())
+ book = self._decodeQtObject(bitem, 'book')
+ chapter = int(self._decodeQtObject(bitem, 'chapter'))
+ verse = int(self._decodeQtObject(bitem, 'verse'))
+ bible = self._decodeQtObject(bitem, 'bible')
+ dual_bible = self._decodeQtObject(bitem, 'dual_bible')
+ old_bitem = self.listView.item(old_item.row())
+ old_book = self._decodeQtObject(old_bitem, 'book')
+ old_chapter = int(self._decodeQtObject(old_bitem, 'chapter'))
+ old_verse = int(self._decodeQtObject(old_bitem, 'verse'))
+ old_bible = self._decodeQtObject(old_bitem, 'bible')
+ old_dual_bible = self._decodeQtObject(old_bitem, 'dual_bible')
+ if old_bible != bible or old_dual_bible != dual_bible or \
+ old_book != book:
+ return True
+ elif old_verse + 1 != verse and old_chapter == chapter:
+ return True
+ elif old_chapter + 1 == chapter and (verse != 1 or
+ old_verse != self.parent.manager.get_verse_count(
+ old_bible, old_book, old_chapter)):
+ return True
+ else:
+ return False
+
def formatVerse(self, old_chapter, chapter, verse):
if not self.parent.settings_tab.show_new_chapters or \
old_chapter != chapter:
- verse_text = chapter + u':' + verse
+ verse_text = u'%s:%s' % (chapter, verse)
else:
- verse_text = verse
+ verse_text = u'%s' % verse
if self.parent.settings_tab.display_style == 1:
verse_text = u'{su}(' + verse_text + u'){/su}'
elif self.parent.settings_tab.display_style == 2:
@@ -678,7 +762,8 @@ class BibleMediaItem(MediaManagerItem):
self.dual_search_results[count].text)
}
bible_text = u' %s %d:%d (%s, %s)' % (verse.book.name,
- verse.chapter, verse.verse, version.value, dual_version.value)
+ verse.chapter, verse.verse, version.value,
+ dual_version.value)
else:
vdict = {
'book': QtCore.QVariant(verse.book.name),
@@ -689,14 +774,15 @@ class BibleMediaItem(MediaManagerItem):
'copyright': QtCore.QVariant(copyright.value),
'permission': QtCore.QVariant(permission.value),
'text': QtCore.QVariant(verse.text),
- 'dual_bible': QtCore.QVariant(dual_bible)
+ 'dual_bible': QtCore.QVariant(u''),
+ 'dual_version': QtCore.QVariant(u''),
+ 'dual_copyright': QtCore.QVariant(u''),
+ 'dual_permission': QtCore.QVariant(u''),
+ 'dual_text': QtCore.QVariant(u'')
}
bible_text = u' %s %d:%d (%s)' % (verse.book.name,
verse.chapter, verse.verse, version.value)
- # set the row title
bible_verse = QtGui.QListWidgetItem(bible_text)
- #bible_verse.setData(QtCore.Qt.UserRole,
- # QtCore.QVariant(bible_text))
bible_verse.setData(QtCore.Qt.UserRole, QtCore.QVariant(vdict))
self.listView.addItem(bible_verse)
row = self.listView.setCurrentRow(count + start_count)
diff --git a/openlp/plugins/bibles/lib/opensong.py b/openlp/plugins/bibles/lib/opensong.py
index 7acb7e2f2..f1d3efd74 100644
--- a/openlp/plugins/bibles/lib/opensong.py
+++ b/openlp/plugins/bibles/lib/opensong.py
@@ -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:
diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py
index 4e3819961..523ccb061 100644
--- a/openlp/plugins/custom/customplugin.py
+++ b/openlp/plugins/custom/customplugin.py
@@ -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)
diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py
index d34cd6a3c..7f910f395 100644
--- a/openlp/plugins/images/imageplugin.py
+++ b/openlp/plugins/images/imageplugin.py
@@ -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)
diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py
index db326f843..e50333eb2 100644
--- a/openlp/plugins/media/mediaplugin.py
+++ b/openlp/plugins/media/mediaplugin.py
@@ -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)
diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py
index 9ada43a5a..d054c3e9c 100644
--- a/openlp/plugins/presentations/lib/impresscontroller.py
+++ b/openlp/plugins/presentations/lib/impresscontroller.py
@@ -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()
diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py
index 001aeac4d..60b455368 100644
--- a/openlp/plugins/presentations/lib/mediaitem.py
+++ b/openlp/plugins/presentations/lib/mediaitem.py
@@ -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))
diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py
index e63063ffd..ada695625 100644
--- a/openlp/plugins/presentations/presentationplugin.py
+++ b/openlp/plugins/presentations/presentationplugin.py
@@ -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)
diff --git a/openlp/plugins/remotes/html/jquery.js b/openlp/plugins/remotes/html/jquery.js
old mode 100755
new mode 100644
diff --git a/openlp/plugins/remotes/remoteplugin.py b/openlp/plugins/remotes/remoteplugin.py
index 59ad9a99c..927a706a3 100644
--- a/openlp/plugins/remotes/remoteplugin.py
+++ b/openlp/plugins/remotes/remoteplugin.py
@@ -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
diff --git a/openlp/plugins/songs/forms/authorsform.py b/openlp/plugins/songs/forms/authorsform.py
index c7d1b0396..1dacd82cc 100644
--- a/openlp/plugins/songs/forms/authorsform.py
+++ b/openlp/plugins/songs/forms/authorsform.py
@@ -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:
diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py
index 870a68bca..0d2e65f95 100644
--- a/openlp/plugins/songs/forms/editsongdialog.py
+++ b/openlp/plugins/songs/forms/editsongdialog.py
@@ -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',
diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py
index 458e7200c..ae9938ab4 100644
--- a/openlp/plugins/songs/forms/editsongform.py
+++ b/openlp/plugins/songs/forms/editsongform.py
@@ -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
diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py
index 0c58b8650..c62fa058e 100644
--- a/openlp/plugins/songs/forms/songimportform.py
+++ b/openlp/plugins/songs/forms/songimportform.py
@@ -57,6 +57,15 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
self.registerFields()
self.finishButton = self.button(QtGui.QWizard.FinishButton)
self.cancelButton = self.button(QtGui.QWizard.CancelButton)
+ if not SongFormat.get_availability(SongFormat.OpenLP1):
+ self.openLP1DisabledWidget.setVisible(True)
+ self.openLP1ImportWidget.setVisible(False)
+ if not SongFormat.get_availability(SongFormat.SongsOfFellowship):
+ self.songsOfFellowshipDisabledWidget.setVisible(True)
+ self.songsOfFellowshipImportWidget.setVisible(False)
+ if not SongFormat.get_availability(SongFormat.Generic):
+ self.genericDisabledWidget.setVisible(True)
+ self.genericImportWidget.setVisible(False)
self.plugin = plugin
QtCore.QObject.connect(self.openLP2BrowseButton,
QtCore.SIGNAL(u'clicked()'),
@@ -64,12 +73,12 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
QtCore.QObject.connect(self.openLP1BrowseButton,
QtCore.SIGNAL(u'clicked()'),
self.onOpenLP1BrowseButtonClicked)
- QtCore.QObject.connect(self.openLyricsAddButton,
- QtCore.SIGNAL(u'clicked()'),
- self.onOpenLyricsAddButtonClicked)
- QtCore.QObject.connect(self.openLyricsRemoveButton,
- QtCore.SIGNAL(u'clicked()'),
- self.onOpenLyricsRemoveButtonClicked)
+ #QtCore.QObject.connect(self.openLyricsAddButton,
+ # QtCore.SIGNAL(u'clicked()'),
+ # self.onOpenLyricsAddButtonClicked)
+ #QtCore.QObject.connect(self.openLyricsRemoveButton,
+ # QtCore.SIGNAL(u'clicked()'),
+ # self.onOpenLyricsRemoveButtonClicked)
QtCore.QObject.connect(self.openSongAddButton,
QtCore.SIGNAL(u'clicked()'),
self.onOpenSongAddButtonClicked)
@@ -100,6 +109,9 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
QtCore.QObject.connect(self.genericRemoveButton,
QtCore.SIGNAL(u'clicked()'),
self.onGenericRemoveButtonClicked)
+ QtCore.QObject.connect(self.ewBrowseButton,
+ QtCore.SIGNAL(u'clicked()'),
+ self.onEWBrowseButtonClicked)
QtCore.QObject.connect(self.cancelButton,
QtCore.SIGNAL(u'clicked(bool)'),
self.onCancelButtonClicked)
@@ -145,15 +157,16 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
self.openLP1BrowseButton.setFocus()
return False
elif source_format == SongFormat.OpenLyrics:
- if self.openLyricsFileListWidget.count() == 0:
- QtGui.QMessageBox.critical(self,
- translate('SongsPlugin.ImportWizardForm',
- 'No OpenLyrics Files Selected'),
- translate('SongsPlugin.ImportWizardForm',
- 'You need to add at least one OpenLyrics '
- 'song file to import from.'))
- self.openLyricsAddButton.setFocus()
- return False
+ #if self.openLyricsFileListWidget.count() == 0:
+ # QtGui.QMessageBox.critical(self,
+ # translate('SongsPlugin.ImportWizardForm',
+ # 'No OpenLyrics Files Selected'),
+ # translate('SongsPlugin.ImportWizardForm',
+ # 'You need to add at least one OpenLyrics '
+ # 'song file to import from.'))
+ # self.openLyricsAddButton.setFocus()
+ # return False
+ return False
elif source_format == SongFormat.OpenSong:
if self.openSongFileListWidget.count() == 0:
QtGui.QMessageBox.critical(self,
@@ -204,6 +217,16 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
'presentation file to import from.'))
self.genericAddButton.setFocus()
return False
+ elif source_format == SongFormat.EasyWorship:
+ if self.ewFilenameEdit.text().isEmpty():
+ QtGui.QMessageBox.critical(self,
+ translate('SongsPlugin.ImportWizardForm',
+ 'No EasyWorship Song Database Selected'),
+ translate('SongsPlugin.ImportWizardForm',
+ 'You need to select an EasyWorship song database '
+ 'file to import from.'))
+ self.ewBrowseButton.setFocus()
+ return False
return True
elif self.currentId() == 2:
# Progress page
@@ -252,15 +275,15 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
self.openLP1FilenameEdit
)
- def onOpenLyricsAddButtonClicked(self):
- self.getFiles(
- translate('SongsPlugin.ImportWizardForm',
- 'Select OpenLyrics Files'),
- self.openLyricsFileListWidget
- )
+ #def onOpenLyricsAddButtonClicked(self):
+ # self.getFiles(
+ # translate('SongsPlugin.ImportWizardForm',
+ # 'Select OpenLyrics Files'),
+ # self.openLyricsFileListWidget
+ # )
- def onOpenLyricsRemoveButtonClicked(self):
- self.removeSelectedItems(self.openLyricsFileListWidget)
+ #def onOpenLyricsRemoveButtonClicked(self):
+ # self.removeSelectedItems(self.openLyricsFileListWidget)
def onOpenSongAddButtonClicked(self):
self.getFiles(
@@ -312,6 +335,13 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
def onGenericRemoveButtonClicked(self):
self.removeSelectedItems(self.genericFileListWidget)
+ def onEWBrowseButtonClicked(self):
+ self.getFileName(
+ translate('SongsPlugin.ImportWizardForm',
+ 'Select EasyWorship Database File'),
+ self.ewFilenameEdit
+ )
+
def onCancelButtonClicked(self, checked):
"""
Stop the import on pressing the cancel button.
@@ -331,15 +361,18 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
def setDefaults(self):
self.restart()
+ self.finishButton.setVisible(False)
+ self.cancelButton.setVisible(True)
self.formatComboBox.setCurrentIndex(0)
self.openLP2FilenameEdit.setText(u'')
self.openLP1FilenameEdit.setText(u'')
- self.openLyricsFileListWidget.clear()
+ #self.openLyricsFileListWidget.clear()
self.openSongFileListWidget.clear()
self.wordsOfWorshipFileListWidget.clear()
self.ccliFileListWidget.clear()
self.songsOfFellowshipFileListWidget.clear()
self.genericFileListWidget.clear()
+ self.ewFilenameEdit.setText(u'')
#self.csvFilenameEdit.setText(u'')
def incrementProgressBar(self, status_text, increment=1):
@@ -410,6 +443,11 @@ class ImportWizardForm(QtGui.QWizard, Ui_SongImportWizard):
importer = self.plugin.importSongs(SongFormat.Generic,
filenames=self.getListOfFiles(self.genericFileListWidget)
)
+ elif source_format == SongFormat.EasyWorship:
+ # Import an OpenLP 2.0 database
+ importer = self.plugin.importSongs(SongFormat.EasyWorship,
+ filename=unicode(self.ewFilenameEdit.text())
+ )
success = importer.do_import()
if success:
# reload songs
diff --git a/openlp/plugins/songs/forms/songimportwizard.py b/openlp/plugins/songs/forms/songimportwizard.py
index f174d5ce3..0fb36cfe7 100644
--- a/openlp/plugins/songs/forms/songimportwizard.py
+++ b/openlp/plugins/songs/forms/songimportwizard.py
@@ -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(
diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py
index a2c12a6d6..48f9a5a55 100644
--- a/openlp/plugins/songs/forms/songmaintenanceform.py
+++ b/openlp/plugins/songs/forms/songmaintenanceform.py
@@ -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)
diff --git a/openlp/plugins/songs/forms/topicsform.py b/openlp/plugins/songs/forms/topicsform.py
index a618dd9db..c45228527 100644
--- a/openlp/plugins/songs/forms/topicsform.py
+++ b/openlp/plugins/songs/forms/topicsform.py
@@ -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:
diff --git a/openlp/plugins/songs/lib/cclifileimport.py b/openlp/plugins/songs/lib/cclifileimport.py
index 08bccef79..9d0da5cfa 100755
--- a/openlp/plugins/songs/lib/cclifileimport.py
+++ b/openlp/plugins/songs/lib/cclifileimport.py
@@ -57,7 +57,7 @@ class CCLIFileImport(SongImport):
self.filenames = kwargs[u'filenames']
log.debug(self.filenames)
else:
- raise KeyError(u'Keyword argument "filenames" not supplied.')
+ raise KeyError(u'Keyword argument "filenames" not supplied.')
def do_import(self):
"""
@@ -66,11 +66,11 @@ class CCLIFileImport(SongImport):
log.debug(u'Starting CCLI File Import')
song_total = len(self.filenames)
self.import_wizard.importProgressBar.setMaximum(song_total)
- song_count = 1
+ song_count = 1
for filename in self.filenames:
self.import_wizard.incrementProgressBar(
- u'Importing song %s of %s' % (song_count, song_total))
- filename = unicode(filename)
+ u'Importing song %s of %s' % (song_count, song_total))
+ filename = unicode(filename)
log.debug(u'Importing CCLI File: %s', filename)
lines = []
if os.path.isfile(filename):
@@ -81,33 +81,34 @@ class CCLIFileImport(SongImport):
lines = infile.readlines()
ext = os.path.splitext(filename)[1]
if ext.lower() == ".usr":
- log.info(u'SongSelect .usr format file found %s: ' , filename)
+ log.info(u'SongSelect .usr format file found %s: ',
+ filename)
self.do_import_usr_file(lines)
elif ext.lower() == ".txt":
- log.info(u'SongSelect .txt format file found %s: ', filename)
+ log.info(u'SongSelect .txt format file found %s: ',
+ filename)
self.do_import_txt_file(lines)
else:
log.info(u'Extension %s is not valid', filename)
- pass
song_count += 1
if self.stop_import_flag:
- return False
+ return False
return True
def do_import_usr_file(self, textList):
"""
The :method:`do_import_usr_file` method provides OpenLP
with the ability to import CCLI SongSelect songs in
- *USR* file format
-
+ *USR* file format
+
``textList``
An array of strings containing the usr file content.
-
+
**SongSelect .usr file format**
``[File]``
USR file format first line
``Type=``
- Indicates the file type
+ Indicates the file type
e.g. *Type=SongSelect Import File*
``Version=3.0``
File format version
@@ -116,7 +117,7 @@ class CCLIFileImport(SongImport):
``Title=``
Contains the song title (e.g. *Title=Above All*)
``Author=``
- Contains a | delimited list of the song authors
+ Contains a | delimited list of the song authors
e.g. *Author=LeBlanc, Lenny | Baloche, Paul*
``Copyright=``
Contains a | delimited list of the song copyrights
@@ -136,7 +137,7 @@ class CCLIFileImport(SongImport):
Contains a list of the songs fields in order /t delimited
e.g. *Fields=Vers 1/tVers 2/tChorus 1/tAndere 1*
``Words=``
- Contains the songs various lyrics in order as shown by the
+ Contains the songs various lyrics in order as shown by the
*Fields* description
e.g. *Words=Above all powers....* [/n = CR, /n/t = CRLF]
"""
@@ -174,8 +175,8 @@ class CCLIFileImport(SongImport):
verse_type = u'O'
verse_text = unicode(words_list[counter])
verse_text = verse_text.replace("/n", "\n")
- if len(verse_text) > 0:
- self.add_verse(verse_text, verse_type);
+ if len(verse_text) > 0:
+ self.add_verse(verse_text, verse_type)
#Handle multiple authors
author_list = song_author.split(u'/')
if len(author_list) < 2:
@@ -192,10 +193,10 @@ class CCLIFileImport(SongImport):
"""
The :method:`do_import_txt_file` method provides OpenLP
with the ability to import CCLI SongSelect songs in
- *TXT* file format
-
+ *TXT* file format
+
``textList``
- An array of strings containing the txt file content.
+ An array of strings containing the txt file content.
**SongSelect .txt file format**
@@ -225,38 +226,38 @@ class CCLIFileImport(SongImport):
e.g. CCLI Number (e.g.CCLI-Liednummer: 2672885)
``Song Copyright``
e.g. © 1999 Integrity's Hosanna! Music | LenSongs Publishing
- ``Song Authors``
+ ``Song Authors``
e.g. Lenny LeBlanc | Paul Baloche
``Licencing info``
- e.g. For use solely with the SongSelect Terms of Use.
+ e.g. For use solely with the SongSelect Terms of Use.
All rights Reserved. www.ccli.com
- ``CCLI Licence number of user``
- e.g. CCL-Liedlizenznummer: 14 / CCLI License No. 14
+ ``CCLI Licence number of user``
+ e.g. CCL-Liedlizenznummer: 14 / CCLI License No. 14
"""
log.debug(u'TXT file text: %s', textList)
self.set_defaults()
line_number = 0
verse_text = u''
song_comments = u''
- song_copyright = u'';
+ song_copyright = u''
verse_start = False
for line in textList:
clean_line = line.strip()
if not clean_line:
- if line_number==0:
+ if line_number == 0:
continue
elif verse_start:
- if verse_text:
+ if verse_text:
self.add_verse(verse_text, verse_type)
verse_text = ''
verse_start = False
else:
#line_number=0, song title
- if line_number==0:
+ if line_number == 0:
song_name = clean_line
line_number += 1
- #line_number=1, verses
- elif line_number==1:
+ #line_number=1, verses
+ elif line_number == 1:
#line_number=1, ccli number, first line after verses
if clean_line.startswith(u'CCLI'):
line_number += 1
@@ -285,15 +286,16 @@ class CCLIFileImport(SongImport):
verse_text = verse_text + line
else:
#line_number=2, copyright
- if line_number==2:
+ if line_number == 2:
line_number += 1
song_copyright = clean_line
- #n=3, authors
- elif line_number==3:
+ #n=3, authors
+ elif line_number == 3:
line_number += 1
song_author = clean_line
- #line_number=4, comments lines before last line
- elif (line_number==4) and (not clean_line.startswith(u'CCL')):
+ #line_number=4, comments lines before last line
+ elif (line_number == 4) and \
+ (not clean_line.startswith(u'CCL')):
song_comments = song_comments + clean_line
# split on known separators
author_list = song_author.split(u'/')
@@ -307,4 +309,3 @@ class CCLIFileImport(SongImport):
self.ccli_number = song_ccli
self.comments = song_comments
self.finish()
-
diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py
new file mode 100644
index 000000000..16db36b20
--- /dev/null
+++ b/openlp/plugins/songs/lib/ewimport.py
@@ -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(' 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('']
+ 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(' 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
diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py
index da9618ef2..d8028db24 100644
--- a/openlp/plugins/songs/lib/importer.py
+++ b/openlp/plugins/songs/lib/importer.py
@@ -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']
diff --git a/openlp/plugins/songs/lib/olp1import.py b/openlp/plugins/songs/lib/olp1import.py
index 51e3e1fbf..de750cb24 100644
--- a/openlp/plugins/songs/lib/olp1import.py
+++ b/openlp/plugins/songs/lib/olp1import.py
@@ -28,10 +28,8 @@ The :mod:`olp1import` module provides the functionality for importing
openlp.org 1.x song databases into the current installation database.
"""
import logging
-try:
- import sqlite
-except:
- pass
+import chardet
+import sqlite
from openlp.core.lib import translate
from songimport import SongImport
@@ -43,6 +41,8 @@ class OpenLP1SongImport(SongImport):
The :class:`OpenLP1SongImport` class provides OpenLP with the ability to
import song databases from installations of openlp.org 1.x.
"""
+ last_encoding = u'windows-1252'
+
def __init__(self, manager, **kwargs):
"""
Initialise the import.
@@ -56,6 +56,34 @@ class OpenLP1SongImport(SongImport):
SongImport.__init__(self, manager)
self.import_source = kwargs[u'filename']
+ def decode_string(self, raw, guess):
+ """
+ Use chardet to detect the encoding of the raw string, and convert it
+ to unicode.
+
+ ``raw``
+ The raw bytestring to decode.
+ ``guess``
+ What chardet guessed the encoding to be.
+ """
+ if guess[u'confidence'] < 0.8:
+ codec = u'windows-1252'
+ else:
+ codec = guess[u'encoding']
+ try:
+ decoded = unicode(raw, codec)
+ self.last_encoding = codec
+ except UnicodeDecodeError:
+ log.exception(u'Error in detecting openlp.org 1.x database encoding.')
+ try:
+ decoded = unicode(raw, self.last_encoding)
+ except UnicodeDecodeError:
+ # possibly show an error form
+ #self.import_wizard.showError(u'There was a problem '
+ # u'detecting the encoding of a string')
+ decoded = raw
+ return decoded
+
def do_import(self):
"""
Run the import for an openlp.org 1.x song database.
@@ -63,6 +91,11 @@ class OpenLP1SongImport(SongImport):
# Connect to the database
connection = sqlite.connect(self.import_source)
cursor = connection.cursor()
+ # Determine if we're using a new or an old DB
+ cursor.execute(u'SELECT name FROM sqlite_master '
+ u'WHERE type = \'table\' AND name = \'tracks\'')
+ table_list = cursor.fetchall()
+ new_db = len(table_list) > 0
# Count the number of records we need to import, for the progress bar
cursor.execute(u'SELECT COUNT(songid) FROM songs')
count = int(cursor.fetchone()[0])
@@ -71,9 +104,10 @@ class OpenLP1SongImport(SongImport):
# "cache" our list of authors
cursor.execute(u'SELECT authorid, authorname FROM authors')
authors = cursor.fetchall()
- # "cache" our list of tracks
- cursor.execute(u'SELECT trackid, fulltrackname FROM tracks')
- tracks = cursor.fetchall()
+ if new_db:
+ # "cache" our list of tracks
+ cursor.execute(u'SELECT trackid, fulltrackname FROM tracks')
+ tracks = cursor.fetchall()
# Import the songs
cursor.execute(u'SELECT songid, songtitle, lyrics || \'\' AS lyrics, '
u'copyrightinfo FROM songs')
@@ -84,9 +118,10 @@ class OpenLP1SongImport(SongImport):
success = False
break
song_id = song[0]
- title = unicode(song[1], u'cp1252')
- lyrics = unicode(song[2], u'cp1252').replace(u'\r', u'')
- copyright = unicode(song[3], u'cp1252')
+ guess = chardet.detect(song[2])
+ title = self.decode_string(song[1], guess)
+ lyrics = self.decode_string(song[2], guess).replace(u'\r', u'')
+ copyright = self.decode_string(song[3], guess)
self.import_wizard.incrementProgressBar(
unicode(translate('SongsPlugin.ImportWizardForm',
'Importing "%s"...')) % title)
@@ -102,15 +137,12 @@ class OpenLP1SongImport(SongImport):
break
for author in authors:
if author[0] == author_id[0]:
- self.parse_author(unicode(author[1], u'cp1252'))
+ self.parse_author(self.decode_string(author[1], guess))
break
if self.stop_import_flag:
success = False
break
- cursor.execute(u'SELECT name FROM sqlite_master '
- u'WHERE type = \'table\' AND name = \'tracks\'')
- table_list = cursor.fetchall()
- if len(table_list) > 0:
+ if new_db:
cursor.execute(u'SELECT trackid FROM songtracks '
u'WHERE songid = %s ORDER BY listindex' % song_id)
track_ids = cursor.fetchall()
@@ -120,10 +152,11 @@ class OpenLP1SongImport(SongImport):
break
for track in tracks:
if track[0] == track_id[0]:
- self.add_media_file(unicode(track[1], u'cp1252'))
+ self.add_media_file(self.decode_string(track[1], guess))
break
if self.stop_import_flag:
success = False
break
self.finish()
return success
+
diff --git a/openlp/plugins/songs/lib/oooimport.py b/openlp/plugins/songs/lib/oooimport.py
index e8c723c0e..26a0abfcc 100644
--- a/openlp/plugins/songs/lib/oooimport.py
+++ b/openlp/plugins/songs/lib/oooimport.py
@@ -59,6 +59,7 @@ class OooImport(SongImport):
self.document = None
self.process_started = False
self.filenames = kwargs[u'filenames']
+ self.uno_connection_type = u'pipe' #u'socket'
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'song_stop_import'), self.stop_import)
@@ -106,8 +107,14 @@ class OooImport(SongImport):
loop = 0
while ctx is None and loop < 5:
try:
- ctx = resolver.resolve(u'uno:socket,host=localhost,' \
- + 'port=2002;urp;StarOffice.ComponentContext')
+ if self.uno_connection_type == u'pipe':
+ ctx = resolver.resolve(u'uno:' \
+ + u'pipe,name=openlp_pipe;' \
+ + u'urp;StarOffice.ComponentContext')
+ else:
+ ctx = resolver.resolve(u'uno:' \
+ + u'socket,host=localhost,port=2002;' \
+ + u'urp;StarOffice.ComponentContext')
except:
pass
self.start_ooo_process()
@@ -123,9 +130,14 @@ class OooImport(SongImport):
self.manager._FlagAsMethod(u'Bridge_GetStruct')
self.manager._FlagAsMethod(u'Bridge_GetValueObject')
else:
- cmd = u'openoffice.org -nologo -norestore -minimized ' \
- + u'-invisible -nofirststartwizard ' \
- + '-accept="socket,host=localhost,port=2002;urp;"'
+ if self.uno_connection_type == u'pipe':
+ cmd = u'openoffice.org -nologo -norestore -minimized ' \
+ + u'-invisible -nofirststartwizard ' \
+ + u'-accept=pipe,name=openlp_pipe;urp;'
+ else:
+ cmd = u'openoffice.org -nologo -norestore -minimized ' \
+ + u'-invisible -nofirststartwizard ' \
+ + u'-accept=socket,host=localhost,port=2002;urp;'
process = QtCore.QProcess()
process.startDetached(cmd)
process.waitForStarted()
diff --git a/openlp/plugins/songs/lib/opensongimport.py b/openlp/plugins/songs/lib/opensongimport.py
index f6048d566..f4be0dc87 100644
--- a/openlp/plugins/songs/lib/opensongimport.py
+++ b/openlp/plugins/songs/lib/opensongimport.py
@@ -29,7 +29,9 @@ import os
from zipfile import ZipFile
from lxml import objectify
from lxml.etree import Error, LxmlError
+import re
+from openlp.core.lib import translate
from openlp.plugins.songs.lib.songimport import SongImport
log = logging.getLogger(__name__)
@@ -112,12 +114,22 @@ class OpenSongImport(SongImport):
def do_import(self):
"""
- Import either a single opensong file, or a zipfile containing multiple
- opensong files. If `self.commit` is set False, the import will not be
- committed to the database (useful for test scripts).
+ Import either each of the files in self.filenames - each element of
+ which can be either a single opensong file, or a zipfile containing
+ multiple opensong files. If `self.commit` is set False, the
+ import will not be committed to the database (useful for test scripts).
"""
success = True
- self.import_wizard.importProgressBar.setMaximum(len(self.filenames))
+ numfiles = 0
+ for filename in self.filenames:
+ ext = os.path.splitext(filename)[1]
+ if ext.lower() == u'.zip':
+ z = ZipFile(filename, u'r')
+ numfiles += len(z.infolist())
+ else:
+ numfiles += 1
+ log.debug("Total number of files: %d", numfiles)
+ self.import_wizard.importProgressBar.setMaximum(numfiles)
for filename in self.filenames:
if self.stop_import_flag:
success = False
@@ -126,9 +138,6 @@ class OpenSongImport(SongImport):
if ext.lower() == u'.zip':
log.debug(u'Zipfile found %s', filename)
z = ZipFile(filename, u'r')
- self.import_wizard.importProgressBar.setMaximum(
- self.import_wizard.importProgressBar.maximum() +
- len(z.infolist()))
for song in z.infolist():
if self.stop_import_flag:
success = False
@@ -137,6 +146,7 @@ class OpenSongImport(SongImport):
if parts[-1] == u'':
#No final part => directory
continue
+ log.info(u'Zip importing %s', parts[-1])
self.import_wizard.incrementProgressBar(
unicode(translate('SongsPlugin.ImportWizardForm',
'Importing %s...')) % parts[-1])
@@ -144,11 +154,11 @@ class OpenSongImport(SongImport):
self.do_import_file(songfile)
if self.commit:
self.finish()
- self.set_defaults()
if self.stop_import_flag:
success = False
break
else:
+ # not a zipfile
log.info('Direct import %s', filename)
self.import_wizard.incrementProgressBar(
unicode(translate('SongsPlugin.ImportWizardForm',
@@ -157,9 +167,7 @@ class OpenSongImport(SongImport):
self.do_import_file(file)
if self.commit:
self.finish()
- self.set_defaults()
- if not self.commit:
- self.finish()
+
return success
def do_import_file(self, file):
@@ -167,10 +175,10 @@ class OpenSongImport(SongImport):
Process the OpenSong file - pass in a file-like object,
not a filename
"""
- self.authors = []
+ self.set_defaults()
try:
tree = objectify.parse(file)
- except Error, LxmlError:
+ except (Error, LxmlError):
log.exception(u'Error parsing XML')
return
root = tree.getroot()
@@ -196,7 +204,6 @@ class OpenSongImport(SongImport):
self.topics.append(unicode(root.alttheme))
# data storage while importing
verses = {}
- lyrics = unicode(root.lyrics)
# keep track of a "default" verse order, in case none is specified
our_verse_order = []
verses_seen = {}
@@ -204,6 +211,7 @@ class OpenSongImport(SongImport):
# erm, versetype!
versetype = u'V'
versenum = None
+ lyrics = unicode(root.lyrics)
for thisline in lyrics.split(u'\n'):
# remove comments
semicolon = thisline.find(u';')
@@ -218,16 +226,18 @@ class OpenSongImport(SongImport):
continue
# verse/chorus/etc. marker
if thisline[0] == u'[':
- versetype = thisline[1].upper()
- if versetype.isdigit():
- versenum = versetype
- versetype = u'V'
- elif thisline[2] != u']':
- # there's a number to go with it - extract that as well
- right_bracket = thisline.find(u']')
- versenum = thisline[2:right_bracket]
+ # drop the square brackets
+ right_bracket = thisline.find(u']')
+ content = thisline[1:right_bracket].upper()
+ # have we got any digits? If so, versenumber is everything from the digits
+ # to the end (even if there are some alpha chars on the end)
+ match = re.match(u'(.*)(\d+.*)', content)
+ if match is not None:
+ versetype = match.group(1)
+ versenum = match.group(2)
else:
- # if there's no number, assume it's no.1
+ # otherwise we assume number 1 and take the whole prefix as versetype
+ versetype = content
versenum = u'1'
continue
words = None
@@ -235,10 +245,10 @@ class OpenSongImport(SongImport):
if thisline[0].isdigit():
versenum = thisline[0]
words = thisline[1:].strip()
- if words is None and \
- versenum is not None and \
- versetype is not None:
+ if words is None:
words = thisline
+ if not versenum:
+ versenum = u'1'
if versenum is not None:
versetag = u'%s%s' % (versetype, versenum)
if not verses.has_key(versetype):
@@ -259,10 +269,13 @@ class OpenSongImport(SongImport):
versetypes.sort()
versetags = {}
for versetype in versetypes:
+ our_verse_type = versetype
+ if our_verse_type == u'':
+ our_verse_type = u'V'
versenums = verses[versetype].keys()
versenums.sort()
for num in versenums:
- versetag = u'%s%s' % (versetype, num)
+ versetag = u'%s%s' % (our_verse_type, num)
lines = u'\n'.join(verses[versetype][num])
self.verses.append([versetag, lines])
# Keep track of what we have for error checking later
@@ -271,16 +284,23 @@ class OpenSongImport(SongImport):
order = []
if u'presentation' in fields and root.presentation != u'':
order = unicode(root.presentation)
- order = order.split()
+ # We make all the tags in the lyrics upper case, so match that here
+ # and then split into a list on the whitespace
+ order = order.upper().split()
else:
if len(our_verse_order) > 0:
order = our_verse_order
else:
- log.warn(u'No verse order available for %s, skipping.', self.title)
+ log.warn(u'No verse order available for %s, skipping.',
+ self.title)
for tag in order:
- if len(tag) == 1:
- tag = tag + u'1' # Assume it's no.1 if it's not there
+ if tag[0].isdigit():
+ # Assume it's a verse if it has no prefix
+ tag = u'V' + tag
+ elif not re.search('\d+', tag):
+ # Assume it's no.1 if there's no digits
+ tag = tag + u'1'
if not versetags.has_key(tag):
- log.warn(u'Got order %s but not in versetags, skipping', tag)
+ log.info(u'Got order %s but not in versetags, dropping this item from presentation order', tag)
else:
self.verse_order_list.append(tag)
diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py
index 8d29a73a2..dcf4ed8d8 100644
--- a/openlp/plugins/songs/lib/songimport.py
+++ b/openlp/plugins/songs/lib/songimport.py
@@ -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
diff --git a/openlp/plugins/songs/lib/test/test.opensong b/openlp/plugins/songs/lib/test/test.opensong
index 1ce60cf3b..c75951492 100644
--- a/openlp/plugins/songs/lib/test/test.opensong
+++ b/openlp/plugins/songs/lib/test/test.opensong
@@ -1,10 +1,10 @@
Martins Test
- MartiÑ Thómpson
+ MartiÑ & Martin2 Thómpson
2010 Martin Thompson
1
- V1 C V2 C2 V3 B1 V1
+ V1 C V2 C2 3a B1 V1 T U Rap1 Rap2 Rap3
Blah
@@ -17,7 +17,12 @@
TestAltTheme
- ;Comment
+ [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