forked from openlp/openlp
r1715
This commit is contained in:
commit
418802b735
@ -20,3 +20,4 @@ _eric4project
|
||||
openlp/core/resources.py.old
|
||||
*.qm
|
||||
resources/windows/warnOpenLP.txt
|
||||
openlp.cfg
|
||||
|
18
openlp.pyw
18
openlp.pyw
@ -79,6 +79,8 @@ class OpenLP(QtGui.QApplication):
|
||||
class in order to provide the core of the application.
|
||||
"""
|
||||
|
||||
args = []
|
||||
|
||||
def exec_(self):
|
||||
"""
|
||||
Override exec method to allow the shared memory to be released on exit
|
||||
@ -92,7 +94,7 @@ class OpenLP(QtGui.QApplication):
|
||||
"""
|
||||
# On Windows, the args passed into the constructor are
|
||||
# ignored. Not very handy, so set the ones we want to use.
|
||||
self.args = args
|
||||
self.args.extend(args)
|
||||
# provide a listener for widgets to reqest a screen update.
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'openlp_process_events'), self.processEvents)
|
||||
@ -125,6 +127,8 @@ class OpenLP(QtGui.QApplication):
|
||||
# now kill the splashscreen
|
||||
self.splash.finish(self.mainWindow)
|
||||
log.debug(u'Splashscreen closed')
|
||||
# make sure Qt really display the splash screen
|
||||
self.processEvents()
|
||||
self.mainWindow.repaint()
|
||||
self.processEvents()
|
||||
if not has_run_wizard:
|
||||
@ -180,6 +184,18 @@ class OpenLP(QtGui.QApplication):
|
||||
"""
|
||||
self.restoreOverrideCursor()
|
||||
|
||||
def event(self, event):
|
||||
"""
|
||||
Enables direct file opening on OS X
|
||||
"""
|
||||
if event.type() == QtCore.QEvent.FileOpen:
|
||||
file_name = event.file()
|
||||
log.debug(u'Got open file event for %s!', file_name)
|
||||
self.args.insert(0, unicode(file_name))
|
||||
return True
|
||||
else:
|
||||
return QtGui.QApplication.event(self, event)
|
||||
|
||||
def main():
|
||||
"""
|
||||
The main function which parses command line options and then runs
|
||||
|
@ -205,7 +205,7 @@ def clean_tags(text):
|
||||
text = text.replace(u'<br>', u'\n')
|
||||
text = text.replace(u'{br}', u'\n')
|
||||
text = text.replace(u' ', u' ')
|
||||
for tag in DisplayTags.get_html_tags():
|
||||
for tag in FormattingTags.get_html_tags():
|
||||
text = text.replace(tag[u'start tag'], u'')
|
||||
text = text.replace(tag[u'end tag'], u'')
|
||||
return text
|
||||
@ -214,7 +214,7 @@ def expand_tags(text):
|
||||
"""
|
||||
Expand tags HTML for display
|
||||
"""
|
||||
for tag in DisplayTags.get_html_tags():
|
||||
for tag in FormattingTags.get_html_tags():
|
||||
text = text.replace(tag[u'start tag'], tag[u'start html'])
|
||||
text = text.replace(tag[u'end tag'], tag[u'end html'])
|
||||
return text
|
||||
@ -233,9 +233,9 @@ def check_directory_exists(dir):
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
from listwidgetwithdnd import ListWidgetWithDnD
|
||||
from displaytags import DisplayTags
|
||||
from eventreceiver import Receiver
|
||||
from listwidgetwithdnd import ListWidgetWithDnD
|
||||
from formattingtags import FormattingTags
|
||||
from spelltextedit import SpellTextEdit
|
||||
from settingsmanager import SettingsManager
|
||||
from plugin import PluginStatus, StringContent, Plugin
|
||||
|
@ -25,12 +25,12 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
Provide Html Tag management and Display Tag access class
|
||||
Provide HTML Tag management and Formatting Tag access class
|
||||
"""
|
||||
|
||||
from openlp.core.lib import translate
|
||||
|
||||
class DisplayTags(object):
|
||||
class FormattingTags(object):
|
||||
"""
|
||||
Static Class to HTML Tags to be access around the code the list is managed
|
||||
by the Options Tab.
|
||||
@ -42,89 +42,93 @@ class DisplayTags(object):
|
||||
"""
|
||||
Provide access to the html_expands list.
|
||||
"""
|
||||
return DisplayTags.html_expands
|
||||
return FormattingTags.html_expands
|
||||
|
||||
@staticmethod
|
||||
def reset_html_tags():
|
||||
"""
|
||||
Resets the html_expands list.
|
||||
"""
|
||||
DisplayTags.html_expands = []
|
||||
FormattingTags.html_expands = []
|
||||
base_tags = []
|
||||
# Append the base tags.
|
||||
# Hex Color tags from http://www.w3schools.com/html/html_colornames.asp
|
||||
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Red'),
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Red'),
|
||||
u'start tag': u'{r}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:red">',
|
||||
u'end tag': u'{/r}', u'end html': u'</span>', u'protected': True})
|
||||
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Black'),
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Black'),
|
||||
u'start tag': u'{b}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:black">',
|
||||
u'end tag': u'{/b}', u'end html': u'</span>', u'protected': True})
|
||||
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Blue'),
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Blue'),
|
||||
u'start tag': u'{bl}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:blue">',
|
||||
u'end tag': u'{/bl}', u'end html': u'</span>', u'protected': True})
|
||||
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Yellow'),
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Yellow'),
|
||||
u'start tag': u'{y}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:yellow">',
|
||||
u'end tag': u'{/y}', u'end html': u'</span>', u'protected': True})
|
||||
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Green'),
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Green'),
|
||||
u'start tag': u'{g}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:green">',
|
||||
u'end tag': u'{/g}', u'end html': u'</span>', u'protected': True})
|
||||
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Pink'),
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Pink'),
|
||||
u'start tag': u'{pk}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:#FFC0CB">',
|
||||
u'end tag': u'{/pk}', u'end html': u'</span>', u'protected': True})
|
||||
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Orange'),
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Orange'),
|
||||
u'start tag': u'{o}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:#FFA500">',
|
||||
u'end tag': u'{/o}', u'end html': u'</span>', u'protected': True})
|
||||
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Purple'),
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Purple'),
|
||||
u'start tag': u'{pp}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:#800080">',
|
||||
u'end tag': u'{/pp}', u'end html': u'</span>', u'protected': True})
|
||||
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'White'),
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'White'),
|
||||
u'start tag': u'{w}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:white">',
|
||||
u'end tag': u'{/w}', u'end html': u'</span>', u'protected': True})
|
||||
base_tags.append({
|
||||
u'desc': translate('OpenLP.DisplayTags', 'Superscript'),
|
||||
u'desc': translate('OpenLP.FormattingTags', 'Superscript'),
|
||||
u'start tag': u'{su}', u'start html': u'<sup>',
|
||||
u'end tag': u'{/su}', u'end html': u'</sup>', u'protected': True})
|
||||
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Subscript'),
|
||||
base_tags.append({
|
||||
u'desc': translate('OpenLP.FormattingTags', 'Subscript'),
|
||||
u'start tag': u'{sb}', u'start html': u'<sub>',
|
||||
u'end tag': u'{/sb}', u'end html': u'</sub>', u'protected': True})
|
||||
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Paragraph'),
|
||||
base_tags.append({
|
||||
u'desc': translate('OpenLP.FormattingTags', 'Paragraph'),
|
||||
u'start tag': u'{p}', u'start html': u'<p>', u'end tag': u'{/p}',
|
||||
u'end html': u'</p>', u'protected': True})
|
||||
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Bold'),
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Bold'),
|
||||
u'start tag': u'{st}', u'start html': u'<strong>',
|
||||
u'end tag': u'{/st}', u'end html': u'</strong>',
|
||||
u'protected': True})
|
||||
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Italics'),
|
||||
base_tags.append({
|
||||
u'desc': translate('OpenLP.FormattingTags', 'Italics'),
|
||||
u'start tag': u'{it}', u'start html': u'<em>', u'end tag': u'{/it}',
|
||||
u'end html': u'</em>', u'protected': True})
|
||||
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Underline'),
|
||||
base_tags.append({
|
||||
u'desc': translate('OpenLP.FormattingTags', 'Underline'),
|
||||
u'start tag': u'{u}',
|
||||
u'start html': u'<span style="text-decoration: underline;">',
|
||||
u'end tag': u'{/u}', u'end html': u'</span>', u'protected': True})
|
||||
base_tags.append({u'desc': translate('OpenLP.DisplayTags', 'Break'),
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Break'),
|
||||
u'start tag': u'{br}', u'start html': u'<br>', u'end tag': u'',
|
||||
u'end html': u'', u'protected': True})
|
||||
DisplayTags.add_html_tags(base_tags)
|
||||
FormattingTags.add_html_tags(base_tags)
|
||||
|
||||
@staticmethod
|
||||
def add_html_tags(tags):
|
||||
"""
|
||||
Add a list of tags to the list
|
||||
"""
|
||||
DisplayTags.html_expands.extend(tags)
|
||||
FormattingTags.html_expands.extend(tags)
|
||||
|
||||
@staticmethod
|
||||
def remove_html_tag(tag_id):
|
||||
"""
|
||||
Removes an individual html_expands tag.
|
||||
"""
|
||||
DisplayTags.html_expands.pop(tag_id)
|
||||
FormattingTags.html_expands.pop(tag_id)
|
@ -34,6 +34,7 @@ from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, \
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# FIXME: Add html5 doctype. However, do not break theme gradients.
|
||||
HTMLSRC = u"""
|
||||
<html>
|
||||
<head>
|
||||
@ -56,44 +57,44 @@ body {
|
||||
height: %spx;
|
||||
}
|
||||
#black {
|
||||
z-index:8;
|
||||
z-index: 8;
|
||||
background-color: black;
|
||||
display: none;
|
||||
}
|
||||
#bgimage {
|
||||
z-index:1;
|
||||
z-index: 1;
|
||||
}
|
||||
#image {
|
||||
z-index:2;
|
||||
z-index: 2;
|
||||
}
|
||||
#video1 {
|
||||
z-index:3;
|
||||
z-index: 3;
|
||||
}
|
||||
#video2 {
|
||||
z-index:3;
|
||||
z-index: 3;
|
||||
}
|
||||
#alert {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
z-index:10;
|
||||
z-index: 10;
|
||||
%s
|
||||
}
|
||||
#footer {
|
||||
position: absolute;
|
||||
z-index:6;
|
||||
z-index: 6;
|
||||
%s
|
||||
}
|
||||
/* lyric css */
|
||||
%s
|
||||
sup {
|
||||
font-size:0.6em;
|
||||
vertical-align:top;
|
||||
position:relative;
|
||||
top:-0.3em;
|
||||
font-size: 0.6em;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
top: -0.3em;
|
||||
}
|
||||
</style>
|
||||
<script language="javascript">
|
||||
<script>
|
||||
var timer = null;
|
||||
var video_timer = null;
|
||||
var current_video = '1';
|
||||
@ -317,10 +318,10 @@ sup {
|
||||
%s
|
||||
<div id="footer" class="footer"></div>
|
||||
<div id="black" class="size"></div>
|
||||
<div id="alert" style="visibility:hidden;"></div>
|
||||
<div id="alert" style="visibility:hidden"></div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
"""
|
||||
|
||||
def build_html(item, screen, alert, islive, background, image=None):
|
||||
"""
|
||||
@ -446,15 +447,15 @@ def build_lyrics_css(item, webkitvers):
|
||||
The version of qtwebkit we're using
|
||||
|
||||
"""
|
||||
style = """
|
||||
style = u"""
|
||||
.lyricstable {
|
||||
z-index:5;
|
||||
z-index: 5;
|
||||
position: absolute;
|
||||
display: table;
|
||||
%s
|
||||
}
|
||||
.lyricscell {
|
||||
display:table-cell;
|
||||
display: table-cell;
|
||||
word-wrap: break-word;
|
||||
%s
|
||||
}
|
||||
@ -558,8 +559,8 @@ def build_lyrics_format_css(theme, width, height):
|
||||
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; padding-left:%spx; 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_size,
|
||||
theme.font_main_color, 100 + int(theme.font_main_line_adjustment),
|
||||
left_margin, width, height)
|
||||
@ -608,7 +609,7 @@ def build_footer_css(item, height):
|
||||
``item``
|
||||
Service Item to be processed.
|
||||
"""
|
||||
style = """
|
||||
style = u"""
|
||||
left: %spx;
|
||||
bottom: %spx;
|
||||
width: %spx;
|
||||
@ -616,7 +617,7 @@ def build_footer_css(item, height):
|
||||
font-size: %spt;
|
||||
color: %s;
|
||||
text-align: left;
|
||||
white-space:nowrap;
|
||||
white-space: nowrap;
|
||||
"""
|
||||
theme = item.themedata
|
||||
if not theme or not item.footer:
|
||||
@ -634,7 +635,7 @@ def build_alert_css(alertTab, width):
|
||||
``alertTab``
|
||||
Details from the Alert tab for fonts etc
|
||||
"""
|
||||
style = """
|
||||
style = u"""
|
||||
width: %spx;
|
||||
vertical-align: %s;
|
||||
font-family: %s;
|
||||
|
@ -32,6 +32,7 @@ to wait for the conversion to happen.
|
||||
"""
|
||||
import logging
|
||||
import time
|
||||
import Queue
|
||||
|
||||
from PyQt4 import QtCore
|
||||
|
||||
@ -42,8 +43,8 @@ log = logging.getLogger(__name__)
|
||||
|
||||
class ImageThread(QtCore.QThread):
|
||||
"""
|
||||
A special Qt thread class to speed up the display of text based frames.
|
||||
This is threaded so it loads the frames in background
|
||||
A special Qt thread class to speed up the display of images. This is
|
||||
threaded so it loads the frames and generates byte stream in background.
|
||||
"""
|
||||
def __init__(self, manager):
|
||||
QtCore.QThread.__init__(self, None)
|
||||
@ -53,15 +54,87 @@ class ImageThread(QtCore.QThread):
|
||||
"""
|
||||
Run the thread.
|
||||
"""
|
||||
self.imageManager.process()
|
||||
self.imageManager._process()
|
||||
|
||||
|
||||
class Priority(object):
|
||||
"""
|
||||
Enumeration class for different priorities.
|
||||
|
||||
``Lowest``
|
||||
Only the image's byte stream has to be generated. But neither the
|
||||
``QImage`` nor the byte stream has been requested yet.
|
||||
|
||||
``Low``
|
||||
Only the image's byte stream has to be generated. Because the image's
|
||||
``QImage`` has been requested previously it is reasonable to assume that
|
||||
the byte stream will be needed before the byte stream of other images
|
||||
whose ``QImage`` were not generated due to a request.
|
||||
|
||||
``Normal``
|
||||
The image's byte stream as well as the image has to be generated.
|
||||
Neither the ``QImage`` nor the byte stream has been requested yet.
|
||||
|
||||
``High``
|
||||
The image's byte stream as well as the image has to be generated. The
|
||||
``QImage`` for this image has been requested.
|
||||
**Note**, this priority is only set when the ``QImage`` has not been
|
||||
generated yet.
|
||||
|
||||
``Urgent``
|
||||
The image's byte stream as well as the image has to be generated. The
|
||||
byte stream for this image has been requested.
|
||||
**Note**, this priority is only set when the byte stream has not been
|
||||
generated yet.
|
||||
"""
|
||||
Lowest = 4
|
||||
Low = 3
|
||||
Normal = 2
|
||||
High = 1
|
||||
Urgent = 0
|
||||
|
||||
|
||||
class Image(object):
|
||||
name = ''
|
||||
path = ''
|
||||
dirty = True
|
||||
image = None
|
||||
image_bytes = None
|
||||
"""
|
||||
This class represents an image. To mark an image as *dirty* set the instance
|
||||
variables ``image`` and ``image_bytes`` to ``None`` and add the image object
|
||||
to the queue of images to process.
|
||||
"""
|
||||
def __init__(self, name='', path=''):
|
||||
self.name = name
|
||||
self.path = path
|
||||
self.image = None
|
||||
self.image_bytes = None
|
||||
self.priority = Priority.Normal
|
||||
|
||||
|
||||
class PriorityQueue(Queue.PriorityQueue):
|
||||
"""
|
||||
Customised ``Queue.PriorityQueue``.
|
||||
"""
|
||||
def modify_priority(self, image, new_priority):
|
||||
"""
|
||||
Modifies the priority of the given ``image``.
|
||||
|
||||
``image``
|
||||
The image to remove. This should be an ``Image`` instance.
|
||||
|
||||
``new_priority``
|
||||
The image's new priority.
|
||||
"""
|
||||
self.remove(image)
|
||||
image.priority = new_priority
|
||||
self.put((image.priority, image))
|
||||
|
||||
def remove(self, image):
|
||||
"""
|
||||
Removes the given ``image`` from the queue.
|
||||
|
||||
``image``
|
||||
The image to remove. This should be an ``Image`` instance.
|
||||
"""
|
||||
if (image.priority, image) in self.queue:
|
||||
self.queue.remove((image.priority, image))
|
||||
|
||||
|
||||
class ImageManager(QtCore.QObject):
|
||||
@ -76,96 +149,117 @@ class ImageManager(QtCore.QObject):
|
||||
self.width = current_screen[u'size'].width()
|
||||
self.height = current_screen[u'size'].height()
|
||||
self._cache = {}
|
||||
self._thread_running = False
|
||||
self._cache_dirty = False
|
||||
self.image_thread = ImageThread(self)
|
||||
self._imageThread = ImageThread(self)
|
||||
self._conversion_queue = PriorityQueue()
|
||||
|
||||
def update_display(self):
|
||||
"""
|
||||
Screen has changed size so rebuild the cache to new size
|
||||
Screen has changed size so rebuild the cache to new size.
|
||||
"""
|
||||
log.debug(u'update_display')
|
||||
current_screen = ScreenList.get_instance().current
|
||||
self.width = current_screen[u'size'].width()
|
||||
self.height = current_screen[u'size'].height()
|
||||
# mark the images as dirty for a rebuild
|
||||
for key in self._cache.keys():
|
||||
image = self._cache[key]
|
||||
image.dirty = True
|
||||
image.image = resize_image(image.path, self.width, self.height)
|
||||
self._cache_dirty = True
|
||||
# only one thread please
|
||||
if not self._thread_running:
|
||||
self.image_thread.start()
|
||||
# Mark the images as dirty for a rebuild by setting the image and byte
|
||||
# stream to None.
|
||||
self._conversion_queue = PriorityQueue()
|
||||
for key, image in self._cache.iteritems():
|
||||
image.priority = Priority.Normal
|
||||
image.image = None
|
||||
image.image_bytes = None
|
||||
self._conversion_queue.put((image.priority, image))
|
||||
# We want only one thread.
|
||||
if not self._imageThread.isRunning():
|
||||
self._imageThread.start()
|
||||
|
||||
def get_image(self, name):
|
||||
"""
|
||||
Return the Qimage from the cache
|
||||
Return the ``QImage`` from the cache. If not present wait for the
|
||||
background thread to process it.
|
||||
"""
|
||||
log.debug(u'get_image %s' % name)
|
||||
return self._cache[name].image
|
||||
image = self._cache[name]
|
||||
if image.image is None:
|
||||
self._conversion_queue.modify_priority(image, Priority.High)
|
||||
while image.image is None:
|
||||
log.debug(u'get_image - waiting')
|
||||
time.sleep(0.1)
|
||||
elif image.image_bytes is None:
|
||||
# Set the priority to Low, because the image was requested but the
|
||||
# byte stream was not generated yet. However, we only need to do
|
||||
# this, when the image was generated before it was requested
|
||||
# (otherwise this is already taken care of).
|
||||
self._conversion_queue.modify_priority(image, Priority.Low)
|
||||
return image.image
|
||||
|
||||
def get_image_bytes(self, name):
|
||||
"""
|
||||
Returns the byte string for an image
|
||||
If not present wait for the background thread to process it.
|
||||
Returns the byte string for an image. If not present wait for the
|
||||
background thread to process it.
|
||||
"""
|
||||
log.debug(u'get_image_bytes %s' % name)
|
||||
if not self._cache[name].image_bytes:
|
||||
while self._cache[name].dirty:
|
||||
image = self._cache[name]
|
||||
if image.image_bytes is None:
|
||||
self._conversion_queue.modify_priority(image, Priority.Urgent)
|
||||
while image.image_bytes is None:
|
||||
log.debug(u'get_image_bytes - waiting')
|
||||
time.sleep(0.1)
|
||||
return self._cache[name].image_bytes
|
||||
return image.image_bytes
|
||||
|
||||
def del_image(self, name):
|
||||
"""
|
||||
Delete the Image from the Cache
|
||||
Delete the Image from the cache.
|
||||
"""
|
||||
log.debug(u'del_image %s' % name)
|
||||
if name in self._cache:
|
||||
self._conversion_queue.remove(self._cache[name])
|
||||
del self._cache[name]
|
||||
|
||||
def add_image(self, name, path):
|
||||
"""
|
||||
Add image to cache if it is not already there
|
||||
Add image to cache if it is not already there.
|
||||
"""
|
||||
log.debug(u'add_image %s:%s' % (name, path))
|
||||
if not name in self._cache:
|
||||
image = Image()
|
||||
image.name = name
|
||||
image.path = path
|
||||
image.image = resize_image(path, self.width, self.height)
|
||||
image = Image(name, path)
|
||||
self._cache[name] = image
|
||||
self._conversion_queue.put((image.priority, image))
|
||||
else:
|
||||
log.debug(u'Image in cache %s:%s' % (name, path))
|
||||
self._cache_dirty = True
|
||||
# only one thread please
|
||||
if not self._thread_running:
|
||||
self.image_thread.start()
|
||||
# We want only one thread.
|
||||
if not self._imageThread.isRunning():
|
||||
self._imageThread.start()
|
||||
|
||||
def process(self):
|
||||
def _process(self):
|
||||
"""
|
||||
Controls the processing called from a QThread
|
||||
Controls the processing called from a ``QtCore.QThread``.
|
||||
"""
|
||||
log.debug(u'process - started')
|
||||
self._thread_running = True
|
||||
self.clean_cache()
|
||||
# data loaded since we started ?
|
||||
while self._cache_dirty:
|
||||
log.debug(u'process - recycle')
|
||||
self.clean_cache()
|
||||
self._thread_running = False
|
||||
log.debug(u'process - ended')
|
||||
log.debug(u'_process - started')
|
||||
while not self._conversion_queue.empty():
|
||||
self._process_cache()
|
||||
log.debug(u'_process - ended')
|
||||
|
||||
def clean_cache(self):
|
||||
def _process_cache(self):
|
||||
"""
|
||||
Actually does the work.
|
||||
"""
|
||||
log.debug(u'clean_cache')
|
||||
# we will clean the cache now
|
||||
self._cache_dirty = False
|
||||
for key in self._cache.keys():
|
||||
image = self._cache[key]
|
||||
if image.dirty:
|
||||
image.image_bytes = image_to_byte(image.image)
|
||||
image.dirty = False
|
||||
log.debug(u'_process_cache')
|
||||
image = self._conversion_queue.get()[1]
|
||||
# Generate the QImage for the image.
|
||||
if image.image is None:
|
||||
image.image = resize_image(image.path, self.width, self.height)
|
||||
# Set the priority to Lowest and stop here as we need to process
|
||||
# more important images first.
|
||||
if image.priority == Priority.Normal:
|
||||
self._conversion_queue.modify_priority(image, Priority.Lowest)
|
||||
return
|
||||
# For image with high priority we set the priority to Low, as the
|
||||
# byte stream might be needed earlier the byte stream of image with
|
||||
# Normal priority. We stop here as we need to process more important
|
||||
# images first.
|
||||
elif image.priority == Priority.High:
|
||||
self._conversion_queue.modify_priority(image, Priority.Low)
|
||||
return
|
||||
# Generate the byte stream for the image.
|
||||
if image.image_bytes is None:
|
||||
image.image_bytes = image_to_byte(image.image)
|
||||
|
@ -27,8 +27,12 @@
|
||||
"""
|
||||
Extend QListWidget to handle drag and drop functionality
|
||||
"""
|
||||
import os.path
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import Receiver
|
||||
|
||||
class ListWidgetWithDnD(QtGui.QListWidget):
|
||||
"""
|
||||
Provide a list widget to store objects and handle drag and drop events
|
||||
@ -41,6 +45,16 @@ class ListWidgetWithDnD(QtGui.QListWidget):
|
||||
self.mimeDataText = name
|
||||
assert(self.mimeDataText)
|
||||
|
||||
def activateDnD(self):
|
||||
"""
|
||||
Activate DnD of widget
|
||||
"""
|
||||
self.setAcceptDrops(True)
|
||||
self.setDragDropMode(QtGui.QAbstractItemView.DragDrop)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'%s_dnd' % self.mimeDataText),
|
||||
self.parent().loadFile)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
"""
|
||||
Drag and drop event does not care what data is selected
|
||||
@ -58,3 +72,39 @@ class ListWidgetWithDnD(QtGui.QListWidget):
|
||||
drag.setMimeData(mimeData)
|
||||
mimeData.setText(self.mimeDataText)
|
||||
drag.start(QtCore.Qt.CopyAction)
|
||||
|
||||
def dragEnterEvent(self, event):
|
||||
if event.mimeData().hasUrls():
|
||||
event.accept()
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
def dragMoveEvent(self, event):
|
||||
if event.mimeData().hasUrls():
|
||||
event.setDropAction(QtCore.Qt.CopyAction)
|
||||
event.accept()
|
||||
else:
|
||||
event.ignore()
|
||||
|
||||
def dropEvent(self, event):
|
||||
"""
|
||||
Receive drop event check if it is a file and process it if it is.
|
||||
|
||||
``event``
|
||||
Handle of the event pint passed
|
||||
"""
|
||||
if event.mimeData().hasUrls():
|
||||
event.setDropAction(QtCore.Qt.CopyAction)
|
||||
event.accept()
|
||||
files = []
|
||||
for url in event.mimeData().urls():
|
||||
localFile = unicode(url.toLocalFile())
|
||||
if os.path.isfile(localFile):
|
||||
files.append(localFile)
|
||||
elif os.path.isdir(localFile):
|
||||
listing = os.listdir(localFile)
|
||||
for file in listing:
|
||||
files.append(os.path.join(localFile,file))
|
||||
Receiver.send_message(u'%s_dnd' % self.mimeDataText,files)
|
||||
else:
|
||||
event.ignore()
|
||||
|
@ -96,7 +96,7 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
self.plugin = plugin
|
||||
visible_title = self.plugin.getString(StringContent.VisibleName)
|
||||
self.title = unicode(visible_title[u'title'])
|
||||
self.settingsSection = self.plugin.name.lower()
|
||||
self.settingsSection = self.plugin.name
|
||||
self.icon = None
|
||||
if icon:
|
||||
self.icon = build_icon(icon)
|
||||
@ -113,7 +113,7 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
self.retranslateUi()
|
||||
self.auto_select_id = -1
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'%s_service_load' % self.plugin.name.lower()),
|
||||
QtCore.SIGNAL(u'%s_service_load' % self.plugin.name),
|
||||
self.serviceLoad)
|
||||
|
||||
def requiredIcons(self):
|
||||
@ -252,7 +252,6 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
self.listView.setSelectionMode(
|
||||
QtGui.QAbstractItemView.ExtendedSelection)
|
||||
self.listView.setAlternatingRowColors(True)
|
||||
self.listView.setDragEnabled(True)
|
||||
self.listView.setObjectName(u'%sListView' % self.plugin.name)
|
||||
# Add to pageLayout
|
||||
self.pageLayout.addWidget(self.listView)
|
||||
@ -288,6 +287,7 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
self.listView, u':/general/general_add.png',
|
||||
translate('OpenLP.MediaManagerItem',
|
||||
'&Add to selected Service Item'), self.onAddEditClick)
|
||||
self.addCustomContextActions()
|
||||
# Create the context menu and add all actions from the listView.
|
||||
self.menu = QtGui.QMenu()
|
||||
self.menu.addActions(self.listView.actions())
|
||||
@ -301,6 +301,13 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
QtCore.SIGNAL('customContextMenuRequested(QPoint)'),
|
||||
self.contextMenu)
|
||||
|
||||
def addCustomContextActions(self):
|
||||
"""
|
||||
Implement this method in your descendent media manager item to
|
||||
add any context menu items. This method is called automatically.
|
||||
"""
|
||||
pass
|
||||
|
||||
def initialise(self):
|
||||
"""
|
||||
Implement this method in your descendent media manager item to
|
||||
@ -331,26 +338,65 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
log.info(u'New files(s) %s', unicode(files))
|
||||
if files:
|
||||
Receiver.send_message(u'cursor_busy')
|
||||
names = []
|
||||
for count in range(0, self.listView.count()):
|
||||
names.append(self.listView.item(count).text())
|
||||
newFiles = []
|
||||
for file in files:
|
||||
filename = os.path.split(unicode(file))[1]
|
||||
if filename in names:
|
||||
self.validateAndLoad(files)
|
||||
Receiver.send_message(u'cursor_normal')
|
||||
|
||||
def loadFile(self, files):
|
||||
"""
|
||||
Turn file from Drag and Drop into an array so the Validate code
|
||||
can run it.
|
||||
|
||||
``files``
|
||||
The list of files to be loaded
|
||||
"""
|
||||
newFiles = []
|
||||
errorShown = False
|
||||
for file in files:
|
||||
type = file.split(u'.')[-1]
|
||||
if type.lower() not in self.onNewFileMasks:
|
||||
if not errorShown:
|
||||
critical_error_message_box(
|
||||
UiStrings().Duplicate,
|
||||
translate('OpenLP.MediaManagerItem',
|
||||
'Invalid File Type'),
|
||||
unicode(translate('OpenLP.MediaManagerItem',
|
||||
'Duplicate filename %s.\nThis filename is already in '
|
||||
'the list')) % filename)
|
||||
else:
|
||||
newFiles.append(file)
|
||||
'Invalid File %s.\nSuffix not supported'))
|
||||
% file)
|
||||
errorShown = True
|
||||
else:
|
||||
newFiles.append(file)
|
||||
if file:
|
||||
self.validateAndLoad(newFiles)
|
||||
|
||||
def validateAndLoad(self, files):
|
||||
"""
|
||||
Process a list for files either from the File Dialog or from Drag and
|
||||
Drop
|
||||
|
||||
``files``
|
||||
The files to be loaded
|
||||
"""
|
||||
names = []
|
||||
for count in range(0, self.listView.count()):
|
||||
names.append(self.listView.item(count).text())
|
||||
newFiles = []
|
||||
duplicatesFound = False
|
||||
for file in files:
|
||||
filename = os.path.split(unicode(file))[1]
|
||||
if filename in names:
|
||||
duplicatesFound = True
|
||||
else:
|
||||
newFiles.append(file)
|
||||
if newFiles:
|
||||
self.loadList(newFiles)
|
||||
lastDir = os.path.split(unicode(files[0]))[0]
|
||||
SettingsManager.set_last_dir(self.settingsSection, lastDir)
|
||||
SettingsManager.set_list(self.settingsSection,
|
||||
self.settingsSection, self.getFileList())
|
||||
Receiver.send_message(u'cursor_normal')
|
||||
if duplicatesFound:
|
||||
critical_error_message_box(
|
||||
UiStrings().Duplicate,
|
||||
unicode(translate('OpenLP.MediaManagerItem',
|
||||
'Duplicate files found on import and ignored.')))
|
||||
|
||||
def contextMenu(self, point):
|
||||
item = self.listView.itemAt(point)
|
||||
@ -550,7 +596,7 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
QtGui.QMessageBox.information(self, UiStrings().NISs,
|
||||
translate('OpenLP.MediaManagerItem',
|
||||
'You must select an existing service item to add to.'))
|
||||
elif self.plugin.name.lower() == serviceItem.name.lower():
|
||||
elif self.plugin.name == serviceItem.name:
|
||||
self.generateSlideData(serviceItem)
|
||||
self.plugin.serviceManager.addServiceItem(serviceItem,
|
||||
replace=True)
|
||||
|
@ -152,7 +152,7 @@ class Plugin(QtCore.QObject):
|
||||
self.version = version
|
||||
else:
|
||||
self.version = get_application_version()[u'version']
|
||||
self.settingsSection = self.name.lower()
|
||||
self.settingsSection = self.name
|
||||
self.icon = None
|
||||
self.media_item_class = media_item_class
|
||||
self.settings_tab_class = settings_tab_class
|
||||
|
@ -31,7 +31,7 @@ import os
|
||||
import sys
|
||||
import logging
|
||||
|
||||
from openlp.core.lib import Plugin, StringContent, PluginStatus
|
||||
from openlp.core.lib import Plugin, PluginStatus
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -46,8 +46,6 @@ VERSE = u'The Lord said to {r}Noah{/r}: \n' \
|
||||
'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
|
||||
FOOTER = [u'Arky Arky (Unknown)', u'Public Domain', u'CCLI 123456']
|
||||
|
||||
HTML_END = u'</div></body></html>'
|
||||
|
||||
class Renderer(object):
|
||||
"""
|
||||
Class to pull all Renderer interactions into one place. The plugins will
|
||||
@ -56,20 +54,20 @@ class Renderer(object):
|
||||
"""
|
||||
log.info(u'Renderer Loaded')
|
||||
|
||||
def __init__(self, image_manager, theme_manager):
|
||||
def __init__(self, imageManager, themeManager):
|
||||
"""
|
||||
Initialise the render manager.
|
||||
|
||||
``image_manager``
|
||||
``imageManager``
|
||||
A ImageManager instance which takes care of e. g. caching and resizing
|
||||
images.
|
||||
|
||||
``theme_manager``
|
||||
``themeManager``
|
||||
The ThemeManager instance, used to get the current theme details.
|
||||
"""
|
||||
log.debug(u'Initialisation started')
|
||||
self.theme_manager = theme_manager
|
||||
self.image_manager = image_manager
|
||||
self.themeManager = themeManager
|
||||
self.imageManager = imageManager
|
||||
self.screens = ScreenList.get_instance()
|
||||
self.service_theme = u''
|
||||
self.theme_level = u''
|
||||
@ -77,7 +75,7 @@ class Renderer(object):
|
||||
self.theme_data = None
|
||||
self.bg_frame = None
|
||||
self.force_page = False
|
||||
self.display = MainDisplay(None, self.image_manager, False)
|
||||
self.display = MainDisplay(None, self.imageManager, False)
|
||||
self.display.setup()
|
||||
|
||||
def update_display(self):
|
||||
@ -85,10 +83,10 @@ class Renderer(object):
|
||||
Updates the render manager's information about the current screen.
|
||||
"""
|
||||
log.debug(u'Update Display')
|
||||
self._calculate_default(self.screens.current[u'size'])
|
||||
self._calculate_default()
|
||||
if self.display:
|
||||
self.display.close()
|
||||
self.display = MainDisplay(None, self.image_manager, False)
|
||||
self.display = MainDisplay(None, self.imageManager, False)
|
||||
self.display.setup()
|
||||
self.bg_frame = None
|
||||
self.theme_data = None
|
||||
@ -101,14 +99,14 @@ class Renderer(object):
|
||||
The global-level theme to be set.
|
||||
|
||||
``theme_level``
|
||||
Defaults to *``ThemeLevel.Global``*. The theme level, can be
|
||||
Defaults to ``ThemeLevel.Global``. The theme level, can be
|
||||
``ThemeLevel.Global``, ``ThemeLevel.Service`` or
|
||||
``ThemeLevel.Song``.
|
||||
"""
|
||||
self.global_theme = global_theme
|
||||
self.theme_level = theme_level
|
||||
self.global_theme_data = \
|
||||
self.theme_manager.getThemeData(self.global_theme)
|
||||
self.themeManager.getThemeData(self.global_theme)
|
||||
self.theme_data = None
|
||||
|
||||
def set_service_theme(self, service_theme):
|
||||
@ -162,12 +160,12 @@ class Renderer(object):
|
||||
if override_levels:
|
||||
self.theme_data = override_theme
|
||||
else:
|
||||
self.theme_data = self.theme_manager.getThemeData(theme)
|
||||
self._calculate_default(self.screens.current[u'size'])
|
||||
self.theme_data = self.themeManager.getThemeData(theme)
|
||||
self._calculate_default()
|
||||
self._build_text_rectangle(self.theme_data)
|
||||
# if No file do not update cache
|
||||
if self.theme_data.background_filename:
|
||||
self.image_manager.add_image(self.theme_data.theme_name,
|
||||
self.imageManager.add_image(self.theme_data.theme_name,
|
||||
self.theme_data.background_filename)
|
||||
return self._rect, self._rect_footer
|
||||
|
||||
@ -185,7 +183,7 @@ class Renderer(object):
|
||||
# save value for use in format_slide
|
||||
self.force_page = force_page
|
||||
# set the default image size for previews
|
||||
self._calculate_default(self.screens.preview[u'size'])
|
||||
self._calculate_default()
|
||||
# build a service item to generate preview
|
||||
serviceItem = ServiceItem()
|
||||
serviceItem.theme = theme_data
|
||||
@ -193,7 +191,7 @@ class Renderer(object):
|
||||
# make big page for theme edit dialog to get line count
|
||||
serviceItem.add_from_text(u'', VERSE + VERSE + VERSE)
|
||||
else:
|
||||
self.image_manager.del_image(theme_data.theme_name)
|
||||
self.imageManager.del_image(theme_data.theme_name)
|
||||
serviceItem.add_from_text(u'', VERSE)
|
||||
serviceItem.renderer = self
|
||||
serviceItem.raw_footer = FOOTER
|
||||
@ -203,52 +201,58 @@ class Renderer(object):
|
||||
raw_html = serviceItem.get_rendered_frame(0)
|
||||
preview = self.display.text(raw_html)
|
||||
# Reset the real screen size for subsequent render requests
|
||||
self._calculate_default(self.screens.current[u'size'])
|
||||
self._calculate_default()
|
||||
return preview
|
||||
self.force_page = False
|
||||
|
||||
def format_slide(self, text, line_break, item):
|
||||
def format_slide(self, text, item):
|
||||
"""
|
||||
Calculate how much text can fit on a slide.
|
||||
|
||||
``text``
|
||||
The words to go on the slides.
|
||||
|
||||
``line_break``
|
||||
Add line endings after each line of text used for bibles.
|
||||
``item``
|
||||
The :class:`~openlp.core.lib.serviceitem.ServiceItem` item object.
|
||||
"""
|
||||
log.debug(u'format slide')
|
||||
# clean up line endings
|
||||
lines = self._lines_split(text)
|
||||
pages = self._paginate_slide(lines, line_break, self.force_page)
|
||||
if len(pages) > 1:
|
||||
# Songs and Custom
|
||||
if item.is_capable(ItemCapabilities.AllowsVirtualSplit):
|
||||
# Do not forget the line breaks !
|
||||
slides = text.split(u'[---]')
|
||||
pages = []
|
||||
for slide in slides:
|
||||
lines = slide.strip(u'\n').split(u'\n')
|
||||
new_pages = self._paginate_slide(lines, line_break,
|
||||
self.force_page)
|
||||
pages.extend(new_pages)
|
||||
# Bibles
|
||||
elif item.is_capable(ItemCapabilities.AllowsWordSplit):
|
||||
pages = self._paginate_slide_words(text, line_break)
|
||||
return pages
|
||||
# Add line endings after each line of text used for bibles.
|
||||
line_end = u'<br>'
|
||||
if item.is_capable(ItemCapabilities.NoLineBreaks):
|
||||
line_end = u' '
|
||||
# Bibles
|
||||
if item.is_capable(ItemCapabilities.AllowsWordSplit):
|
||||
pages = self._paginate_slide_words(text.split(u'\n'), line_end)
|
||||
else:
|
||||
# Clean up line endings.
|
||||
lines = self._lines_split(text)
|
||||
pages = self._paginate_slide(lines, line_end)
|
||||
if len(pages) > 1:
|
||||
# Songs and Custom
|
||||
if item.is_capable(ItemCapabilities.AllowsVirtualSplit):
|
||||
# Do not forget the line breaks!
|
||||
slides = text.split(u'[---]')
|
||||
pages = []
|
||||
for slide in slides:
|
||||
lines = slide.strip(u'\n').split(u'\n')
|
||||
pages.extend(self._paginate_slide(lines, line_end))
|
||||
new_pages = []
|
||||
for page in pages:
|
||||
while page.endswith(u'<br>'):
|
||||
page = page[:-4]
|
||||
new_pages.append(page)
|
||||
return new_pages
|
||||
|
||||
def _calculate_default(self, screen):
|
||||
def _calculate_default(self):
|
||||
"""
|
||||
Calculate the default dimentions of the screen.
|
||||
|
||||
``screen``
|
||||
The QSize of the screen.
|
||||
"""
|
||||
log.debug(u'_calculate default %s', screen)
|
||||
self.width = screen.width()
|
||||
self.height = screen.height()
|
||||
screen_size = self.screens.current[u'size']
|
||||
self.width = screen_size.width()
|
||||
self.height = screen_size.height()
|
||||
self.screen_ratio = float(self.height) / float(self.width)
|
||||
log.debug(u'calculate default %d, %d, %f',
|
||||
self.width, self.height, self.screen_ratio)
|
||||
log.debug(u'_calculate default %s, %f' % (screen_size,
|
||||
self.screen_ratio))
|
||||
# 90% is start of footer
|
||||
self.footer_start = int(self.height * 0.90)
|
||||
|
||||
@ -301,177 +305,202 @@ class Renderer(object):
|
||||
self.web.resize(self.page_width, self.page_height)
|
||||
self.web_frame = self.web.page().mainFrame()
|
||||
# Adjust width and height to account for shadow. outline done in css
|
||||
self.page_shell = u'<html><head><style>' \
|
||||
u'*{margin: 0; padding: 0; border: 0;} '\
|
||||
u'#main {position:absolute; top:0px; %s %s}</style></head><body>' \
|
||||
u'<div id="main">' % \
|
||||
html = u"""<!DOCTYPE html><html><head><script>
|
||||
function show_text(newtext) {
|
||||
var main = document.getElementById('main');
|
||||
main.innerHTML = newtext;
|
||||
// We need to be sure that the page is loaded, that is why we
|
||||
// return the element's height (even though we do not use the
|
||||
// returned value).
|
||||
return main.offsetHeight;
|
||||
}
|
||||
</script><style>*{margin: 0; padding: 0; border: 0;}
|
||||
#main {position: absolute; top: 0px; %s %s}</style></head><body>
|
||||
<div id="main"></div></body></html>""" % \
|
||||
(build_lyrics_format_css(self.theme_data, self.page_width,
|
||||
self.page_height), build_lyrics_outline_css(self.theme_data))
|
||||
self.web.setHtml(html)
|
||||
|
||||
def _paginate_slide(self, lines, line_break, force_page=False):
|
||||
def _paginate_slide(self, lines, line_end):
|
||||
"""
|
||||
Figure out how much text can appear on a slide, using the current
|
||||
theme settings.
|
||||
**Note:** The smallest possible "unit" of text for a slide is one line.
|
||||
If the line is too long it will be cut off when displayed.
|
||||
|
||||
``lines``
|
||||
The words to be fitted on the slide split into lines.
|
||||
|
||||
``line_break``
|
||||
Add line endings after each line of text (used for bibles).
|
||||
|
||||
``force_page``
|
||||
Flag to tell message lines in page.
|
||||
The text to be fitted on the slide split into lines.
|
||||
|
||||
``line_end``
|
||||
The text added after each line. Either ``u' '`` or ``u'<br>``.
|
||||
"""
|
||||
log.debug(u'_paginate_slide - Start')
|
||||
line_end = u''
|
||||
if line_break:
|
||||
line_end = u'<br>'
|
||||
formatted = []
|
||||
html_text = u''
|
||||
styled_text = u''
|
||||
line_count = 0
|
||||
for line in lines:
|
||||
if line_count != -1:
|
||||
line_count += 1
|
||||
styled_line = expand_tags(line) + line_end
|
||||
styled_text += styled_line
|
||||
html = self.page_shell + styled_text + HTML_END
|
||||
self.web.setHtml(html)
|
||||
# Text too long so go to next page.
|
||||
if self.web_frame.contentsSize().height() > self.page_height:
|
||||
if force_page and line_count > 0:
|
||||
Receiver.send_message(u'theme_line_count', line_count - 1)
|
||||
line_count = -1
|
||||
while html_text.endswith(u'<br>'):
|
||||
html_text = html_text[:-4]
|
||||
formatted.append(html_text)
|
||||
html_text = u''
|
||||
styled_text = styled_line
|
||||
html_text += line + line_end
|
||||
while html_text.endswith(u'<br>'):
|
||||
html_text = html_text[:-4]
|
||||
formatted.append(html_text)
|
||||
log.debug(u'_paginate_slide - End')
|
||||
return formatted
|
||||
|
||||
def _paginate_slide_words(self, text, line_break):
|
||||
"""
|
||||
Figure out how much text can appear on a slide, using the current
|
||||
theme settings. This version is to handle text which needs to be split
|
||||
into words to get it to fit.
|
||||
|
||||
``text``
|
||||
The words to be fitted on the slide split into lines.
|
||||
|
||||
``line_break``
|
||||
Add line endings after each line of text used for bibles.
|
||||
|
||||
"""
|
||||
log.debug(u'_paginate_slide_words - Start')
|
||||
line_end = u' '
|
||||
if line_break:
|
||||
line_end = u'<br>'
|
||||
formatted = []
|
||||
previous_html = u''
|
||||
previous_raw = u''
|
||||
lines = text.split(u'\n')
|
||||
separator = u'<br>'
|
||||
html_lines = map(expand_tags, lines)
|
||||
# Text too long so go to next page.
|
||||
if self._text_fits_on_slide(separator.join(html_lines)):
|
||||
html_text, previous_raw = self._binary_chop(formatted,
|
||||
previous_html, previous_raw, html_lines, lines, separator, u'')
|
||||
else:
|
||||
previous_raw = separator.join(lines)
|
||||
if previous_raw:
|
||||
formatted.append(previous_raw)
|
||||
log.debug(u'_paginate_slide - End')
|
||||
return formatted
|
||||
|
||||
def _paginate_slide_words(self, lines, line_end):
|
||||
"""
|
||||
Figure out how much text can appear on a slide, using the current
|
||||
theme settings.
|
||||
**Note:** The smallest possible "unit" of text for a slide is one word.
|
||||
If one line is too long it will be processed word by word. This is
|
||||
sometimes need for **bible** verses.
|
||||
|
||||
``lines``
|
||||
The text to be fitted on the slide split into lines.
|
||||
|
||||
``line_end``
|
||||
The text added after each line. Either ``u' '`` or ``u'<br>``.
|
||||
This is needed for **bibles**.
|
||||
"""
|
||||
log.debug(u'_paginate_slide_words - Start')
|
||||
formatted = []
|
||||
previous_html = u''
|
||||
previous_raw = u''
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
styled_line = expand_tags(line)
|
||||
html = self.page_shell + previous_html + styled_line + HTML_END
|
||||
self.web.setHtml(html)
|
||||
html_line = expand_tags(line)
|
||||
# Text too long so go to next page.
|
||||
if self.web_frame.contentsSize().height() > self.page_height:
|
||||
if self._text_fits_on_slide(previous_html + html_line):
|
||||
# Check if there was a verse before the current one and append
|
||||
# it, when it fits on the page.
|
||||
if previous_html:
|
||||
html = self.page_shell + previous_html + HTML_END
|
||||
self.web.setHtml(html)
|
||||
if self.web_frame.contentsSize().height() <= \
|
||||
self.page_height:
|
||||
while previous_raw.endswith(u'<br>'):
|
||||
previous_raw = previous_raw[:-4]
|
||||
if not self._text_fits_on_slide(previous_html):
|
||||
formatted.append(previous_raw)
|
||||
previous_html = u''
|
||||
previous_raw = u''
|
||||
html = self.page_shell + styled_line + HTML_END
|
||||
self.web.setHtml(html)
|
||||
# Now check if the current verse will fit, if it does
|
||||
# not we have to start to process the verse word by
|
||||
# word.
|
||||
if self.web_frame.contentsSize().height() <= \
|
||||
self.page_height:
|
||||
previous_html = styled_line + line_end
|
||||
if not self._text_fits_on_slide(html_line):
|
||||
previous_html = html_line + line_end
|
||||
previous_raw = line + line_end
|
||||
continue
|
||||
# Figure out how many words of the line will fit on screen by
|
||||
# using the algorithm known as "binary chop".
|
||||
# Figure out how many words of the line will fit on screen as
|
||||
# the line will not fit as a whole.
|
||||
raw_words = self._words_split(line)
|
||||
html_words = [expand_tags(word) for word in raw_words]
|
||||
smallest_index = 0
|
||||
highest_index = len(html_words) - 1
|
||||
index = int(highest_index / 2)
|
||||
while True:
|
||||
html = self.page_shell + previous_html + \
|
||||
u''.join(html_words[:index + 1]).strip() + HTML_END
|
||||
self.web.setHtml(html)
|
||||
if self.web_frame.contentsSize().height() > \
|
||||
self.page_height:
|
||||
# We know that it does not fit, so change/calculate the
|
||||
# new index and highest_index accordingly.
|
||||
highest_index = index
|
||||
index = int(index - (index - smallest_index) / 2)
|
||||
else:
|
||||
smallest_index = index
|
||||
index = int(index + (highest_index - index) / 2)
|
||||
# We found the number of words which will fit.
|
||||
if smallest_index == index or highest_index == index:
|
||||
index = smallest_index
|
||||
formatted.append(previous_raw.rstrip(u'<br>') +
|
||||
u''.join(raw_words[:index + 1]))
|
||||
previous_html = u''
|
||||
previous_raw = u''
|
||||
else:
|
||||
continue
|
||||
# Check if the rest of the line fits on the slide. If it
|
||||
# does we do not have to do the much more intensive "word by
|
||||
# word" checking.
|
||||
html = self.page_shell + \
|
||||
u''.join(html_words[index + 1:]).strip() + HTML_END
|
||||
self.web.setHtml(html)
|
||||
if self.web_frame.contentsSize().height() <= \
|
||||
self.page_height:
|
||||
previous_html = \
|
||||
u''.join(html_words[index + 1:]).strip() + line_end
|
||||
previous_raw = \
|
||||
u''.join(raw_words[index + 1:]).strip() + line_end
|
||||
break
|
||||
else:
|
||||
# The other words do not fit, thus reset the indexes,
|
||||
# create a new list and continue with "word by word".
|
||||
raw_words = raw_words[index + 1:]
|
||||
html_words = html_words[index + 1:]
|
||||
smallest_index = 0
|
||||
highest_index = len(html_words) - 1
|
||||
index = int(highest_index / 2)
|
||||
html_words = map(expand_tags, raw_words)
|
||||
previous_html, previous_raw = self._binary_chop(
|
||||
formatted, previous_html, previous_raw, html_words,
|
||||
raw_words, u' ', line_end)
|
||||
else:
|
||||
previous_html += styled_line + line_end
|
||||
previous_html += html_line + line_end
|
||||
previous_raw += line + line_end
|
||||
while previous_raw.endswith(u'<br>'):
|
||||
previous_raw = previous_raw[:-4]
|
||||
formatted.append(previous_raw)
|
||||
log.debug(u'_paginate_slide_words - End')
|
||||
return formatted
|
||||
|
||||
def _binary_chop(self, formatted, previous_html, previous_raw, html_list,
|
||||
raw_list, separator, line_end):
|
||||
"""
|
||||
This implements the binary chop algorithm for faster rendering. This
|
||||
algorithm works line based (line by line) and word based (word by word).
|
||||
It is assumed that this method is **only** called, when the lines/words
|
||||
to be rendered do **not** fit as a whole.
|
||||
|
||||
``formatted``
|
||||
The list to append any slides.
|
||||
|
||||
``previous_html``
|
||||
The html text which is know to fit on a slide, but is not yet added
|
||||
to the list of slides. (unicode string)
|
||||
|
||||
``previous_raw``
|
||||
The raw text (with formatting tags) which is know to fit on a slide,
|
||||
but is not yet added to the list of slides. (unicode string)
|
||||
|
||||
``html_list``
|
||||
The elements which do not fit on a slide and needs to be processed
|
||||
using the binary chop. The text contains html.
|
||||
|
||||
``raw_list``
|
||||
The elements which do not fit on a slide and needs to be processed
|
||||
using the binary chop. The elements can contain formatting tags.
|
||||
|
||||
``separator``
|
||||
The separator for the elements. For lines this is ``u'<br>'`` and
|
||||
for words this is ``u' '``.
|
||||
|
||||
``line_end``
|
||||
The text added after each "element line". Either ``u' '`` or
|
||||
``u'<br>``. This is needed for bibles.
|
||||
"""
|
||||
smallest_index = 0
|
||||
highest_index = len(html_list) - 1
|
||||
index = int(highest_index / 2)
|
||||
while True:
|
||||
if self._text_fits_on_slide(
|
||||
previous_html + separator.join(html_list[:index + 1]).strip()):
|
||||
# We know that it does not fit, so change/calculate the
|
||||
# new index and highest_index accordingly.
|
||||
highest_index = index
|
||||
index = int(index - (index - smallest_index) / 2)
|
||||
else:
|
||||
smallest_index = index
|
||||
index = int(index + (highest_index - index) / 2)
|
||||
# We found the number of words which will fit.
|
||||
if smallest_index == index or highest_index == index:
|
||||
index = smallest_index
|
||||
formatted.append(previous_raw.rstrip(u'<br>') +
|
||||
separator.join(raw_list[:index + 1]))
|
||||
previous_html = u''
|
||||
previous_raw = u''
|
||||
# Stop here as the theme line count was requested.
|
||||
if self.force_page:
|
||||
Receiver.send_message(u'theme_line_count', index + 1)
|
||||
break
|
||||
else:
|
||||
continue
|
||||
# Check if the remaining elements fit on the slide.
|
||||
if not self._text_fits_on_slide(
|
||||
separator.join(html_list[index + 1:]).strip()):
|
||||
previous_html = separator.join(
|
||||
html_list[index + 1:]).strip() + line_end
|
||||
previous_raw = separator.join(
|
||||
raw_list[index + 1:]).strip() + line_end
|
||||
break
|
||||
else:
|
||||
# The remaining elements do not fit, thus reset the indexes,
|
||||
# create a new list and continue.
|
||||
raw_list = raw_list[index + 1:]
|
||||
html_list = html_list[index + 1:]
|
||||
smallest_index = 0
|
||||
highest_index = len(html_list) - 1
|
||||
index = int(highest_index / 2)
|
||||
return previous_html, previous_raw
|
||||
|
||||
def _text_fits_on_slide(self, text):
|
||||
"""
|
||||
Checks if the given ``text`` fits on a slide. If it does ``True`` is
|
||||
returned, otherwise ``False``.
|
||||
|
||||
``text``
|
||||
The text to check. It can contain HTML tags.
|
||||
"""
|
||||
self.web_frame.evaluateJavaScript(u'show_text("%s")' %
|
||||
text.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
|
||||
return self.web_frame.contentsSize().height() > self.page_height
|
||||
|
||||
def _words_split(self, line):
|
||||
"""
|
||||
Split the slide up by word so can wrap better
|
||||
"""
|
||||
# this parse we are to be wordy
|
||||
line = line.replace(u'\n', u' ')
|
||||
words = line.split(u' ')
|
||||
return [word + u' ' for word in words]
|
||||
return line.split(u' ')
|
||||
|
||||
def _lines_split(self, text):
|
||||
"""
|
||||
@ -479,5 +508,5 @@ class Renderer(object):
|
||||
"""
|
||||
# this parse we do not want to use this so remove it
|
||||
text = text.replace(u'\n[---]', u'')
|
||||
lines = text.split(u'\n')
|
||||
return [line.replace(u'[---]', u'') for line in lines]
|
||||
text = text.replace(u'[---]', u'')
|
||||
return text.split(u'\n')
|
||||
|
@ -35,8 +35,7 @@ import logging
|
||||
import os
|
||||
import uuid
|
||||
|
||||
from openlp.core.lib import build_icon, clean_tags, expand_tags
|
||||
from openlp.core.lib.ui import UiStrings
|
||||
from openlp.core.lib import build_icon, clean_tags, expand_tags, translate
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -165,7 +164,6 @@ class ServiceItem(object):
|
||||
log.debug(u'Render called')
|
||||
self._display_frames = []
|
||||
self.bg_image_bytes = None
|
||||
line_break = not self.is_capable(ItemCapabilities.NoLineBreaks)
|
||||
theme = self.theme if self.theme else None
|
||||
self.main, self.footer = \
|
||||
self.renderer.set_override_theme(theme, use_override)
|
||||
@ -173,9 +171,8 @@ class ServiceItem(object):
|
||||
if self.service_item_type == ServiceItemType.Text:
|
||||
log.debug(u'Formatting slides')
|
||||
for slide in self._raw_frames:
|
||||
formatted = self.renderer \
|
||||
.format_slide(slide[u'raw_slide'], line_break, self)
|
||||
for page in formatted:
|
||||
pages = self.renderer.format_slide(slide[u'raw_slide'], self)
|
||||
for page in pages:
|
||||
page = page.replace(u'<br>', u'{br}')
|
||||
html = expand_tags(cgi.escape(page.rstrip()))
|
||||
self._display_frames.append({
|
||||
@ -210,7 +207,7 @@ class ServiceItem(object):
|
||||
"""
|
||||
self.service_item_type = ServiceItemType.Image
|
||||
self._raw_frames.append({u'title': title, u'path': path})
|
||||
self.renderer.image_manager.add_image(title, path)
|
||||
self.renderer.imageManager.add_image(title, path)
|
||||
self._new_item()
|
||||
|
||||
def add_from_text(self, title, raw_slide, verse_tag=None):
|
||||
@ -352,6 +349,9 @@ class ServiceItem(object):
|
||||
Updates the _uuid with the value from the original one
|
||||
The _uuid is unique for a given service item but this allows one to
|
||||
replace an original version.
|
||||
|
||||
``other``
|
||||
The service item to be merged with
|
||||
"""
|
||||
self._uuid = other._uuid
|
||||
self.notes = other.notes
|
||||
@ -447,10 +447,12 @@ class ServiceItem(object):
|
||||
start = None
|
||||
end = None
|
||||
if self.start_time != 0:
|
||||
start = UiStrings().StartTimeCode % \
|
||||
start = unicode(translate('OpenLP.ServiceItem',
|
||||
'<strong>Start</strong>: %s')) % \
|
||||
unicode(datetime.timedelta(seconds=self.start_time))
|
||||
if self.media_length != 0:
|
||||
end = UiStrings().LengthTime % \
|
||||
end = unicode(translate('OpenLP.ServiceItem',
|
||||
'<strong>Length</strong>: %s')) % \
|
||||
unicode(datetime.timedelta(seconds=self.media_length))
|
||||
if not start and not end:
|
||||
return None
|
||||
@ -459,5 +461,16 @@ class ServiceItem(object):
|
||||
elif not start and end:
|
||||
return end
|
||||
else:
|
||||
return u'%s : %s' % (start, end)
|
||||
return u'%s <br>%s' % (start, end)
|
||||
|
||||
def update_theme(self, theme):
|
||||
"""
|
||||
updates the theme in the service item
|
||||
|
||||
``theme``
|
||||
The new theme to be replaced in the service item
|
||||
"""
|
||||
self.theme = theme
|
||||
self._new_item()
|
||||
self.render()
|
||||
|
||||
|
@ -39,7 +39,7 @@ except ImportError:
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import translate, DisplayTags
|
||||
from openlp.core.lib import translate, FormattingTags
|
||||
from openlp.core.lib.ui import checkable_action
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -48,16 +48,17 @@ class SpellTextEdit(QtGui.QPlainTextEdit):
|
||||
"""
|
||||
Spell checking widget based on QPlanTextEdit.
|
||||
"""
|
||||
def __init__(self, *args):
|
||||
def __init__(self, parent=None, formattingTagsAllowed=True):
|
||||
global ENCHANT_AVAILABLE
|
||||
QtGui.QPlainTextEdit.__init__(self, *args)
|
||||
QtGui.QPlainTextEdit.__init__(self, parent)
|
||||
self.formattingTagsAllowed = formattingTagsAllowed
|
||||
# Default dictionary based on the current locale.
|
||||
if ENCHANT_AVAILABLE:
|
||||
try:
|
||||
self.dictionary = enchant.Dict()
|
||||
self.highlighter = Highlighter(self.document())
|
||||
self.highlighter.spellingDictionary = self.dictionary
|
||||
except Error, DictNotFoundError:
|
||||
except (Error, DictNotFoundError):
|
||||
ENCHANT_AVAILABLE = False
|
||||
log.debug(u'Could not load default dictionary')
|
||||
|
||||
@ -110,16 +111,17 @@ class SpellTextEdit(QtGui.QPlainTextEdit):
|
||||
spell_menu.addAction(action)
|
||||
# Only add the spelling suggests to the menu if there are
|
||||
# suggestions.
|
||||
if len(spell_menu.actions()):
|
||||
if spell_menu.actions():
|
||||
popupMenu.insertMenu(popupMenu.actions()[0], spell_menu)
|
||||
tagMenu = QtGui.QMenu(translate('OpenLP.SpellTextEdit',
|
||||
'Formatting Tags'))
|
||||
for html in DisplayTags.get_html_tags():
|
||||
action = SpellAction(html[u'desc'], tagMenu)
|
||||
action.correct.connect(self.htmlTag)
|
||||
tagMenu.addAction(action)
|
||||
popupMenu.insertSeparator(popupMenu.actions()[0])
|
||||
popupMenu.insertMenu(popupMenu.actions()[0], tagMenu)
|
||||
if self.formattingTagsAllowed:
|
||||
for html in FormattingTags.get_html_tags():
|
||||
action = SpellAction(html[u'desc'], tagMenu)
|
||||
action.correct.connect(self.htmlTag)
|
||||
tagMenu.addAction(action)
|
||||
popupMenu.insertSeparator(popupMenu.actions()[0])
|
||||
popupMenu.insertMenu(popupMenu.actions()[0], tagMenu)
|
||||
popupMenu.exec_(event.globalPos())
|
||||
|
||||
def setLanguage(self, action):
|
||||
@ -148,7 +150,7 @@ class SpellTextEdit(QtGui.QPlainTextEdit):
|
||||
"""
|
||||
Replaces the selected text with word.
|
||||
"""
|
||||
for html in DisplayTags.get_html_tags():
|
||||
for html in FormattingTags.get_html_tags():
|
||||
if tag == html[u'desc']:
|
||||
cursor = self.textCursor()
|
||||
if self.textCursor().hasSelection():
|
||||
|
@ -34,8 +34,7 @@ import logging
|
||||
from xml.dom.minidom import Document
|
||||
from lxml import etree, objectify
|
||||
|
||||
from openlp.core.lib import str_to_bool, translate
|
||||
from openlp.core.lib.ui import UiStrings
|
||||
from openlp.core.lib import str_to_bool
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -64,6 +64,7 @@ class UiStrings(object):
|
||||
self.Cancel = translate('OpenLP.Ui', 'Cancel')
|
||||
self.CCLINumberLabel = translate('OpenLP.Ui', 'CCLI number:')
|
||||
self.CreateService = translate('OpenLP.Ui', 'Create a new service.')
|
||||
self.ConfirmDelete = translate('OpenLP.Ui', 'Confirm Delete')
|
||||
self.Continuous = translate('OpenLP.Ui', 'Continuous')
|
||||
self.Default = unicode(translate('OpenLP.Ui', 'Default'))
|
||||
self.Delete = translate('OpenLP.Ui', '&Delete')
|
||||
@ -82,7 +83,6 @@ class UiStrings(object):
|
||||
self.Image = translate('OpenLP.Ui', 'Image')
|
||||
self.Import = translate('OpenLP.Ui', 'Import')
|
||||
self.LayoutStyle = translate('OpenLP.Ui', 'Layout style:')
|
||||
self.LengthTime = unicode(translate('OpenLP.Ui', 'Length %s'))
|
||||
self.Live = translate('OpenLP.Ui', 'Live')
|
||||
self.LiveBGError = translate('OpenLP.Ui', 'Live Background Error')
|
||||
self.LiveToolbar = translate('OpenLP.Ui', 'Live Toolbar')
|
||||
@ -102,6 +102,8 @@ class UiStrings(object):
|
||||
self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. '
|
||||
'Do you wish to continue?')
|
||||
self.OpenService = translate('OpenLP.Ui', 'Open service.')
|
||||
self.PlaySlidesInLoop = translate('OpenLP.Ui','Play Slides in Loop')
|
||||
self.PlaySlidesToEnd = translate('OpenLP.Ui','Play Slides to End')
|
||||
self.Preview = translate('OpenLP.Ui', 'Preview')
|
||||
self.PrintService = translate('OpenLP.Ui', 'Print Service')
|
||||
self.ReplaceBG = translate('OpenLP.Ui', 'Replace Background')
|
||||
@ -123,6 +125,10 @@ class UiStrings(object):
|
||||
self.SplitToolTip = translate('OpenLP.Ui', 'Split a slide into two '
|
||||
'only if it does not fit on the screen as one slide.')
|
||||
self.StartTimeCode = unicode(translate('OpenLP.Ui', 'Start %s'))
|
||||
self.StopPlaySlidesInLoop = translate('OpenLP.Ui',
|
||||
'Stop Play Slides in Loop')
|
||||
self.StopPlaySlidesToEnd = translate('OpenLP.Ui',
|
||||
'Stop Play Slides to End')
|
||||
self.Theme = translate('OpenLP.Ui', 'Theme', 'Singular')
|
||||
self.Themes = translate('OpenLP.Ui', 'Themes', 'Plural')
|
||||
self.Tools = translate('OpenLP.Ui', 'Tools')
|
||||
@ -323,8 +329,9 @@ def shortcut_action(parent, name, shortcuts, function, icon=None, checked=None,
|
||||
if checked is not None:
|
||||
action.setCheckable(True)
|
||||
action.setChecked(checked)
|
||||
action.setShortcuts(shortcuts)
|
||||
action.setShortcutContext(context)
|
||||
if shortcuts:
|
||||
action.setShortcuts(shortcuts)
|
||||
action.setShortcutContext(context)
|
||||
action_list = ActionList.get_instance()
|
||||
action_list.add_action(action, category)
|
||||
QtCore.QObject.connect(action, QtCore.SIGNAL(u'triggered(bool)'), function)
|
||||
|
@ -69,7 +69,7 @@ from advancedtab import AdvancedTab
|
||||
from aboutform import AboutForm
|
||||
from pluginform import PluginForm
|
||||
from settingsform import SettingsForm
|
||||
from displaytagform import DisplayTagForm
|
||||
from formattingtagform import FormattingTagForm
|
||||
from shortcutlistform import ShortcutListForm
|
||||
from mediadockmanager import MediaDockManager
|
||||
from servicemanager import ServiceManager
|
||||
|
@ -61,6 +61,5 @@ class AboutForm(QtGui.QDialog, Ui_AboutDialog):
|
||||
Launch a web browser and go to the contribute page on the site.
|
||||
"""
|
||||
import webbrowser
|
||||
url = u'http://www.openlp.org/en/documentation/introduction/' \
|
||||
+ u'contributing.html'
|
||||
url = u'http://openlp.org/en/documentation/introduction/contributing'
|
||||
webbrowser.open_new(url)
|
||||
|
@ -129,6 +129,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
||||
os.path.join(gettempdir(), u'openlp', screenshot)))
|
||||
item.setCheckState(QtCore.Qt.Unchecked)
|
||||
item.setFlags(item.flags() | QtCore.Qt.ItemIsUserCheckable)
|
||||
Receiver.send_message(u'cursor_normal')
|
||||
|
||||
def nextId(self):
|
||||
"""
|
||||
@ -156,10 +157,27 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
||||
item = self.themesListWidget.item(iter)
|
||||
if item.checkState() == QtCore.Qt.Checked:
|
||||
self.themeComboBox.addItem(item.text())
|
||||
# Check if this is a re-run of the wizard.
|
||||
self.has_run_wizard = QtCore.QSettings().value(
|
||||
u'general/has run wizard', QtCore.QVariant(False)).toBool()
|
||||
if self.has_run_wizard:
|
||||
# Add any existing themes to list.
|
||||
for theme in self.parent().themeManagerContents.getThemes():
|
||||
index = self.themeComboBox.findText(theme)
|
||||
if index == -1:
|
||||
self.themeComboBox.addItem(theme)
|
||||
default_theme = unicode(QtCore.QSettings().value(
|
||||
u'themes/global theme',
|
||||
QtCore.QVariant(u'')).toString())
|
||||
# Pre-select the current default theme.
|
||||
index = self.themeComboBox.findText(default_theme)
|
||||
self.themeComboBox.setCurrentIndex(index)
|
||||
elif pageId == FirstTimePage.Progress:
|
||||
Receiver.send_message(u'cursor_busy')
|
||||
self._preWizard()
|
||||
self._performWizard()
|
||||
self._postWizard()
|
||||
Receiver.send_message(u'cursor_normal')
|
||||
|
||||
def updateScreenListCombo(self):
|
||||
"""
|
||||
@ -248,11 +266,21 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
|
||||
"""
|
||||
if self.max_progress:
|
||||
self.progressBar.setValue(self.progressBar.maximum())
|
||||
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'Download complete. Click the finish button to start OpenLP.'))
|
||||
if self.has_run_wizard:
|
||||
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'Download complete.'
|
||||
' Click the finish button to return to OpenLP.'))
|
||||
else:
|
||||
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'Download complete.'
|
||||
' Click the finish button to start OpenLP.'))
|
||||
else:
|
||||
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'Click the finish button to start OpenLP.'))
|
||||
if self.has_run_wizard:
|
||||
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'Click the finish button to return to OpenLP.'))
|
||||
else:
|
||||
self.progressLabel.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'Click the finish button to start OpenLP.'))
|
||||
self.finishButton.setVisible(True)
|
||||
self.finishButton.setEnabled(True)
|
||||
self.cancelButton.setVisible(False)
|
||||
|
@ -49,7 +49,7 @@ class Ui_FirstTimeWizard(object):
|
||||
FirstTimeWizard.resize(550, 386)
|
||||
FirstTimeWizard.setModal(True)
|
||||
FirstTimeWizard.setWizardStyle(QtGui.QWizard.ModernStyle)
|
||||
FirstTimeWizard.setOptions(QtGui.QWizard.IndependentPages|
|
||||
FirstTimeWizard.setOptions(QtGui.QWizard.IndependentPages |
|
||||
QtGui.QWizard.NoBackButtonOnStartPage |
|
||||
QtGui.QWizard.NoBackButtonOnLastPage)
|
||||
self.finishButton = self.button(QtGui.QWizard.FinishButton)
|
||||
@ -81,9 +81,9 @@ class Ui_FirstTimeWizard(object):
|
||||
self.pluginLayout.addWidget(self.imageCheckBox)
|
||||
self.presentationCheckBox = QtGui.QCheckBox(self.pluginPage)
|
||||
if sys.platform == "darwin":
|
||||
self.presentationCheckBox.setChecked(False)
|
||||
self.presentationCheckBox.setChecked(False)
|
||||
else:
|
||||
self.presentationCheckBox.setChecked(True)
|
||||
self.presentationCheckBox.setChecked(True)
|
||||
self.presentationCheckBox.setObjectName(u'presentationCheckBox')
|
||||
self.pluginLayout.addWidget(self.presentationCheckBox)
|
||||
self.mediaCheckBox = QtGui.QCheckBox(self.pluginPage)
|
||||
@ -209,7 +209,7 @@ class Ui_FirstTimeWizard(object):
|
||||
'Select the Plugins you wish to use. '))
|
||||
self.songsCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Songs'))
|
||||
self.customCheckBox.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'Custom Text'))
|
||||
'Custom Slides'))
|
||||
self.bibleCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Bible'))
|
||||
self.imageCheckBox.setText(translate('OpenLP.FirstTimeWizard',
|
||||
'Images'))
|
||||
|
@ -28,17 +28,17 @@
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import translate
|
||||
from openlp.core.lib.ui import UiStrings, create_accept_reject_button_box
|
||||
from openlp.core.lib.ui import UiStrings
|
||||
|
||||
class Ui_DisplayTagDialog(object):
|
||||
class Ui_FormattingTagDialog(object):
|
||||
|
||||
def setupUi(self, displayTagDialog):
|
||||
displayTagDialog.setObjectName(u'displayTagDialog')
|
||||
displayTagDialog.resize(725, 548)
|
||||
self.listdataGridLayout = QtGui.QGridLayout(displayTagDialog)
|
||||
def setupUi(self, formattingTagDialog):
|
||||
formattingTagDialog.setObjectName(u'formattingTagDialog')
|
||||
formattingTagDialog.resize(725, 548)
|
||||
self.listdataGridLayout = QtGui.QGridLayout(formattingTagDialog)
|
||||
self.listdataGridLayout.setMargin(8)
|
||||
self.listdataGridLayout.setObjectName(u'listdataGridLayout')
|
||||
self.tagTableWidget = QtGui.QTableWidget(displayTagDialog)
|
||||
self.tagTableWidget = QtGui.QTableWidget(formattingTagDialog)
|
||||
self.tagTableWidget.setHorizontalScrollBarPolicy(
|
||||
QtCore.Qt.ScrollBarAlwaysOff)
|
||||
self.tagTableWidget.setEditTriggers(
|
||||
@ -67,11 +67,11 @@ class Ui_DisplayTagDialog(object):
|
||||
spacerItem = QtGui.QSpacerItem(40, 20,
|
||||
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
|
||||
self.horizontalLayout.addItem(spacerItem)
|
||||
self.deletePushButton = QtGui.QPushButton(displayTagDialog)
|
||||
self.deletePushButton = QtGui.QPushButton(formattingTagDialog)
|
||||
self.deletePushButton.setObjectName(u'deletePushButton')
|
||||
self.horizontalLayout.addWidget(self.deletePushButton)
|
||||
self.listdataGridLayout.addLayout(self.horizontalLayout, 1, 0, 1, 1)
|
||||
self.editGroupBox = QtGui.QGroupBox(displayTagDialog)
|
||||
self.editGroupBox = QtGui.QGroupBox(formattingTagDialog)
|
||||
self.editGroupBox.setObjectName(u'editGroupBox')
|
||||
self.dataGridLayout = QtGui.QGridLayout(self.editGroupBox)
|
||||
self.dataGridLayout.setObjectName(u'dataGridLayout')
|
||||
@ -112,38 +112,38 @@ class Ui_DisplayTagDialog(object):
|
||||
self.savePushButton.setObjectName(u'savePushButton')
|
||||
self.dataGridLayout.addWidget(self.savePushButton, 4, 2, 1, 1)
|
||||
self.listdataGridLayout.addWidget(self.editGroupBox, 2, 0, 1, 1)
|
||||
self.buttonBox = QtGui.QDialogButtonBox(displayTagDialog)
|
||||
self.buttonBox.setObjectName('displayTagDialogButtonBox')
|
||||
self.buttonBox = QtGui.QDialogButtonBox(formattingTagDialog)
|
||||
self.buttonBox.setObjectName('formattingTagDialogButtonBox')
|
||||
self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Close)
|
||||
self.listdataGridLayout.addWidget(self.buttonBox, 3, 0, 1, 1)
|
||||
|
||||
self.retranslateUi(displayTagDialog)
|
||||
QtCore.QMetaObject.connectSlotsByName(displayTagDialog)
|
||||
self.retranslateUi(formattingTagDialog)
|
||||
QtCore.QMetaObject.connectSlotsByName(formattingTagDialog)
|
||||
|
||||
def retranslateUi(self, displayTagDialog):
|
||||
displayTagDialog.setWindowTitle(translate('OpenLP.displayTagDialog',
|
||||
'Configure Display Tags'))
|
||||
def retranslateUi(self, formattingTagDialog):
|
||||
formattingTagDialog.setWindowTitle(translate(
|
||||
'OpenLP.FormattingTagDialog', 'Configure Formatting Tags'))
|
||||
self.editGroupBox.setTitle(
|
||||
translate('OpenLP.DisplayTagDialog', 'Edit Selection'))
|
||||
translate('OpenLP.FormattingTagDialog', 'Edit Selection'))
|
||||
self.savePushButton.setText(
|
||||
translate('OpenLP.DisplayTagDialog', 'Save'))
|
||||
translate('OpenLP.FormattingTagDialog', 'Save'))
|
||||
self.descriptionLabel.setText(
|
||||
translate('OpenLP.DisplayTagDialog', 'Description'))
|
||||
self.tagLabel.setText(translate('OpenLP.DisplayTagDialog', 'Tag'))
|
||||
translate('OpenLP.FormattingTagDialog', 'Description'))
|
||||
self.tagLabel.setText(translate('OpenLP.FormattingTagDialog', 'Tag'))
|
||||
self.startTagLabel.setText(
|
||||
translate('OpenLP.DisplayTagDialog', 'Start tag'))
|
||||
translate('OpenLP.FormattingTagDialog', 'Start tag'))
|
||||
self.endTagLabel.setText(
|
||||
translate('OpenLP.DisplayTagDialog', 'End tag'))
|
||||
translate('OpenLP.FormattingTagDialog', 'End tag'))
|
||||
self.deletePushButton.setText(UiStrings().Delete)
|
||||
self.newPushButton.setText(UiStrings().New)
|
||||
self.tagTableWidget.horizontalHeaderItem(0).setText(
|
||||
translate('OpenLP.DisplayTagDialog', 'Description'))
|
||||
translate('OpenLP.FormattingTagDialog', 'Description'))
|
||||
self.tagTableWidget.horizontalHeaderItem(1).setText(
|
||||
translate('OpenLP.DisplayTagDialog', 'Tag Id'))
|
||||
translate('OpenLP.FormattingTagDialog', 'Tag Id'))
|
||||
self.tagTableWidget.horizontalHeaderItem(2).setText(
|
||||
translate('OpenLP.DisplayTagDialog', 'Start HTML'))
|
||||
translate('OpenLP.FormattingTagDialog', 'Start HTML'))
|
||||
self.tagTableWidget.horizontalHeaderItem(3).setText(
|
||||
translate('OpenLP.DisplayTagDialog', 'End HTML'))
|
||||
translate('OpenLP.FormattingTagDialog', 'End HTML'))
|
||||
self.tagTableWidget.setColumnWidth(0, 120)
|
||||
self.tagTableWidget.setColumnWidth(1, 80)
|
||||
self.tagTableWidget.setColumnWidth(2, 330)
|
@ -25,22 +25,22 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`DisplayTagTab` provides an Tag Edit facility. The Base set are
|
||||
protected and included each time loaded. Custom tags can be defined and saved.
|
||||
The Custom Tag arrays are saved in a pickle so QSettings works on them. Base
|
||||
The :mod:`formattingtagform` provides an Tag Edit facility. The Base set are
|
||||
protected and included each time loaded. Custom tags can be defined and saved.
|
||||
The Custom Tag arrays are saved in a pickle so QSettings works on them. Base
|
||||
Tags cannot be changed.
|
||||
"""
|
||||
import cPickle
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import translate, DisplayTags
|
||||
from openlp.core.lib import translate, FormattingTags
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui.displaytagdialog import Ui_DisplayTagDialog
|
||||
from openlp.core.ui.formattingtagdialog import Ui_FormattingTagDialog
|
||||
|
||||
class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
|
||||
class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
"""
|
||||
The :class:`DisplayTagTab` manages the settings tab .
|
||||
The :class:`FormattingTagForm` manages the settings tab .
|
||||
"""
|
||||
def __init__(self, parent):
|
||||
"""
|
||||
@ -48,7 +48,7 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
|
||||
"""
|
||||
QtGui.QDialog.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
self._loadDisplayTags()
|
||||
self._loadFormattingTags()
|
||||
QtCore.QObject.connect(self.tagTableWidget,
|
||||
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onRowSelected)
|
||||
QtCore.QObject.connect(self.newPushButton,
|
||||
@ -65,19 +65,20 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
|
||||
Load Display and set field state.
|
||||
"""
|
||||
# Create initial copy from master
|
||||
self._loadDisplayTags()
|
||||
self._loadFormattingTags()
|
||||
self._resetTable()
|
||||
self.selected = -1
|
||||
return QtGui.QDialog.exec_(self)
|
||||
|
||||
def _loadDisplayTags(self):
|
||||
def _loadFormattingTags(self):
|
||||
"""
|
||||
Load the Tags from store so can be used in the system or used to
|
||||
update the display. If Cancel was selected this is needed to reset the
|
||||
dsiplay to the correct version.
|
||||
"""
|
||||
# Initial Load of the Tags
|
||||
DisplayTags.reset_html_tags()
|
||||
FormattingTags.reset_html_tags()
|
||||
# Formatting Tags were also known as display tags.
|
||||
user_expands = QtCore.QSettings().value(u'displayTags/html_tags',
|
||||
QtCore.QVariant(u'')).toString()
|
||||
# cPickle only accepts str not unicode strings
|
||||
@ -85,14 +86,14 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
|
||||
if user_expands_string:
|
||||
user_tags = cPickle.loads(user_expands_string)
|
||||
# If we have some user ones added them as well
|
||||
DisplayTags.add_html_tags(user_tags)
|
||||
FormattingTags.add_html_tags(user_tags)
|
||||
|
||||
def onRowSelected(self):
|
||||
"""
|
||||
Table Row selected so display items and set field state.
|
||||
"""
|
||||
row = self.tagTableWidget.currentRow()
|
||||
html = DisplayTags.get_html_tags()[row]
|
||||
html = FormattingTags.get_html_tags()[row]
|
||||
self.selected = row
|
||||
self.descriptionLineEdit.setText(html[u'desc'])
|
||||
self.tagLineEdit.setText(self._strip(html[u'start tag']))
|
||||
@ -117,23 +118,23 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
|
||||
"""
|
||||
Add a new tag to list only if it is not a duplicate.
|
||||
"""
|
||||
for html in DisplayTags.get_html_tags():
|
||||
for html in FormattingTags.get_html_tags():
|
||||
if self._strip(html[u'start tag']) == u'n':
|
||||
critical_error_message_box(
|
||||
translate('OpenLP.DisplayTagTab', 'Update Error'),
|
||||
translate('OpenLP.DisplayTagTab',
|
||||
translate('OpenLP.FormattingTagForm', 'Update Error'),
|
||||
translate('OpenLP.FormattingTagForm',
|
||||
'Tag "n" already defined.'))
|
||||
return
|
||||
# Add new tag to list
|
||||
tag = {
|
||||
u'desc': translate('OpenLP.DisplayTagTab', 'New Tag'),
|
||||
u'desc': translate('OpenLP.FormattingTagForm', 'New Tag'),
|
||||
u'start tag': u'{n}',
|
||||
u'start html': translate('OpenLP.DisplayTagTab', '<HTML here>'),
|
||||
u'start html': translate('OpenLP.FormattingTagForm', '<HTML here>'),
|
||||
u'end tag': u'{/n}',
|
||||
u'end html': translate('OpenLP.DisplayTagTab', '</and here>'),
|
||||
u'end html': translate('OpenLP.FormattingTagForm', '</and here>'),
|
||||
u'protected': False
|
||||
}
|
||||
DisplayTags.add_html_tags([tag])
|
||||
FormattingTags.add_html_tags([tag])
|
||||
self._resetTable()
|
||||
# Highlight new row
|
||||
self.tagTableWidget.selectRow(self.tagTableWidget.rowCount() - 1)
|
||||
@ -145,7 +146,7 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
|
||||
Delete selected custom tag.
|
||||
"""
|
||||
if self.selected != -1:
|
||||
DisplayTags.remove_html_tag(self.selected)
|
||||
FormattingTags.remove_html_tag(self.selected)
|
||||
self.selected = -1
|
||||
self._resetTable()
|
||||
self._saveTable()
|
||||
@ -154,7 +155,7 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
|
||||
"""
|
||||
Update Custom Tag details if not duplicate and save the data.
|
||||
"""
|
||||
html_expands = DisplayTags.get_html_tags()
|
||||
html_expands = FormattingTags.get_html_tags()
|
||||
if self.selected != -1:
|
||||
html = html_expands[self.selected]
|
||||
tag = unicode(self.tagLineEdit.text())
|
||||
@ -162,8 +163,8 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
|
||||
if self._strip(html1[u'start tag']) == tag and \
|
||||
linenumber != self.selected:
|
||||
critical_error_message_box(
|
||||
translate('OpenLP.DisplayTagTab', 'Update Error'),
|
||||
unicode(translate('OpenLP.DisplayTagTab',
|
||||
translate('OpenLP.FormattingTagForm', 'Update Error'),
|
||||
unicode(translate('OpenLP.FormattingTagForm',
|
||||
'Tag %s already defined.')) % tag)
|
||||
return
|
||||
html[u'desc'] = unicode(self.descriptionLineEdit.text())
|
||||
@ -177,18 +178,15 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
|
||||
|
||||
def _saveTable(self):
|
||||
"""
|
||||
Saves all display tags except protected ones.
|
||||
Saves all formatting tags except protected ones.
|
||||
"""
|
||||
tags = []
|
||||
for tag in DisplayTags.get_html_tags():
|
||||
for tag in FormattingTags.get_html_tags():
|
||||
if not tag[u'protected']:
|
||||
tags.append(tag)
|
||||
if tags:
|
||||
QtCore.QSettings().setValue(u'displayTags/html_tags',
|
||||
QtCore.QVariant(cPickle.dumps(tags)))
|
||||
else:
|
||||
QtCore.QSettings().setValue(u'displayTags/html_tags',
|
||||
QtCore.QVariant(u''))
|
||||
# Formatting Tags were also known as display tags.
|
||||
QtCore.QSettings().setValue(u'displayTags/html_tags',
|
||||
QtCore.QVariant(cPickle.dumps(tags) if tags else u''))
|
||||
|
||||
def _resetTable(self):
|
||||
"""
|
||||
@ -199,7 +197,7 @@ class DisplayTagForm(QtGui.QDialog, Ui_DisplayTagDialog):
|
||||
self.newPushButton.setEnabled(True)
|
||||
self.savePushButton.setEnabled(False)
|
||||
self.deletePushButton.setEnabled(False)
|
||||
for linenumber, html in enumerate(DisplayTags.get_html_tags()):
|
||||
for linenumber, html in enumerate(FormattingTags.get_html_tags()):
|
||||
self.tagTableWidget.setRowCount(
|
||||
self.tagTableWidget.rowCount() + 1)
|
||||
self.tagTableWidget.setItem(linenumber, 0,
|
@ -44,7 +44,7 @@ class GeneralTab(SettingsTab):
|
||||
"""
|
||||
self.screens = ScreenList.get_instance()
|
||||
self.icon_path = u':/icon/openlp-logo-16x16.png'
|
||||
generalTranslated = translate('GeneralTab', 'General')
|
||||
generalTranslated = translate('OpenLP.GeneralTab', 'General')
|
||||
SettingsTab.__init__(self, parent, u'General', generalTranslated)
|
||||
|
||||
def setupUi(self):
|
||||
|
@ -48,13 +48,13 @@ class MainDisplay(QtGui.QGraphicsView):
|
||||
"""
|
||||
This is the display screen.
|
||||
"""
|
||||
def __init__(self, parent, image_manager, live):
|
||||
def __init__(self, parent, imageManager, live):
|
||||
if live:
|
||||
QtGui.QGraphicsView.__init__(self)
|
||||
else:
|
||||
QtGui.QGraphicsView.__init__(self, parent)
|
||||
self.isLive = live
|
||||
self.image_manager = image_manager
|
||||
self.imageManager = imageManager
|
||||
self.screens = ScreenList.get_instance()
|
||||
self.alertTab = None
|
||||
self.hideMode = None
|
||||
@ -188,7 +188,7 @@ class MainDisplay(QtGui.QGraphicsView):
|
||||
while not self.webLoaded:
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
self.setGeometry(self.screen[u'size'])
|
||||
self.frame.evaluateJavaScript(u'show_text("%s")' % \
|
||||
self.frame.evaluateJavaScript(u'show_text("%s")' %
|
||||
slide.replace(u'\\', u'\\\\').replace(u'\"', u'\\\"'))
|
||||
return self.preview()
|
||||
|
||||
@ -232,11 +232,13 @@ class MainDisplay(QtGui.QGraphicsView):
|
||||
"""
|
||||
API for replacement backgrounds so Images are added directly to cache
|
||||
"""
|
||||
self.image_manager.add_image(name, path)
|
||||
self.image(name)
|
||||
self.imageManager.add_image(name, path)
|
||||
if hasattr(self, u'serviceItem'):
|
||||
self.override[u'image'] = name
|
||||
self.override[u'theme'] = self.serviceItem.themedata.theme_name
|
||||
self.image(name)
|
||||
return True
|
||||
return False
|
||||
|
||||
def image(self, name):
|
||||
"""
|
||||
@ -247,7 +249,7 @@ class MainDisplay(QtGui.QGraphicsView):
|
||||
The name of the image to be displayed
|
||||
"""
|
||||
log.debug(u'image to display')
|
||||
image = self.image_manager.get_image_bytes(name)
|
||||
image = self.imageManager.get_image_bytes(name)
|
||||
self.resetVideo()
|
||||
self.displayImage(image)
|
||||
return self.preview()
|
||||
@ -349,6 +351,9 @@ class MainDisplay(QtGui.QGraphicsView):
|
||||
"""
|
||||
Loads and starts a video to run with the option of sound
|
||||
"""
|
||||
# We request a background video but have no service Item
|
||||
if isBackground and not hasattr(self, u'serviceItem'):
|
||||
return None
|
||||
if not self.mediaObject:
|
||||
self.createMediaObject()
|
||||
log.debug(u'video')
|
||||
@ -477,13 +482,13 @@ class MainDisplay(QtGui.QGraphicsView):
|
||||
self.override = {}
|
||||
else:
|
||||
# replace the background
|
||||
background = self.image_manager. \
|
||||
background = self.imageManager. \
|
||||
get_image_bytes(self.override[u'image'])
|
||||
if self.serviceItem.themedata.background_filename:
|
||||
self.serviceItem.bg_image_bytes = self.image_manager. \
|
||||
self.serviceItem.bg_image_bytes = self.imageManager. \
|
||||
get_image_bytes(self.serviceItem.themedata.theme_name)
|
||||
if image:
|
||||
image_bytes = self.image_manager.get_image_bytes(image)
|
||||
image_bytes = self.imageManager.get_image_bytes(image)
|
||||
else:
|
||||
image_bytes = None
|
||||
html = build_html(self.serviceItem, self.screen, self.alertTab,
|
||||
|
@ -28,20 +28,23 @@
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
from tempfile import gettempdir
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import Renderer, build_icon, OpenLPDockWidget, \
|
||||
PluginManager, Receiver, translate, ImageManager
|
||||
PluginManager, Receiver, translate, ImageManager, PluginStatus
|
||||
from openlp.core.lib.ui import UiStrings, base_action, checkable_action, \
|
||||
icon_action, shortcut_action
|
||||
from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, \
|
||||
ThemeManager, SlideController, PluginForm, MediaDockManager, \
|
||||
ShortcutListForm, DisplayTagForm
|
||||
ShortcutListForm, FormattingTagForm
|
||||
from openlp.core.utils import AppLocation, add_actions, LanguageManager, \
|
||||
get_application_version, delete_file
|
||||
from openlp.core.utils.actions import ActionList, CategoryOrder
|
||||
from openlp.core.ui.firsttimeform import FirstTimeForm
|
||||
from openlp.core.ui import ScreenList
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -65,6 +68,12 @@ MEDIA_MANAGER_STYLE = """
|
||||
}
|
||||
"""
|
||||
|
||||
PROGRESSBAR_STYLE = """
|
||||
QProgressBar{
|
||||
height: 10px;
|
||||
}
|
||||
"""
|
||||
|
||||
class Ui_MainWindow(object):
|
||||
def setupUi(self, mainWindow):
|
||||
"""
|
||||
@ -93,12 +102,16 @@ class Ui_MainWindow(object):
|
||||
self.previewController.panel.setVisible(previewVisible)
|
||||
liveVisible = QtCore.QSettings().value(u'user interface/live panel',
|
||||
QtCore.QVariant(True)).toBool()
|
||||
panelLocked = QtCore.QSettings().value(u'user interface/lock panel',
|
||||
QtCore.QVariant(False)).toBool()
|
||||
self.liveController.panel.setVisible(liveVisible)
|
||||
# Create menu
|
||||
self.menuBar = QtGui.QMenuBar(mainWindow)
|
||||
self.menuBar.setObjectName(u'menuBar')
|
||||
self.fileMenu = QtGui.QMenu(self.menuBar)
|
||||
self.fileMenu.setObjectName(u'fileMenu')
|
||||
self.recentFilesMenu = QtGui.QMenu(self.fileMenu)
|
||||
self.recentFilesMenu.setObjectName(u'recentFilesMenu')
|
||||
self.fileImportMenu = QtGui.QMenu(self.fileMenu)
|
||||
self.fileImportMenu.setObjectName(u'fileImportMenu')
|
||||
self.fileExportMenu = QtGui.QMenu(self.fileMenu)
|
||||
@ -128,6 +141,7 @@ class Ui_MainWindow(object):
|
||||
self.statusBar.addPermanentWidget(self.loadProgressBar)
|
||||
self.loadProgressBar.hide()
|
||||
self.loadProgressBar.setValue(0)
|
||||
self.loadProgressBar.setStyleSheet(PROGRESSBAR_STYLE)
|
||||
self.defaultThemeLabel = QtGui.QLabel(self.statusBar)
|
||||
self.defaultThemeLabel.setObjectName(u'defaultThemeLabel')
|
||||
self.statusBar.addPermanentWidget(self.defaultThemeLabel)
|
||||
@ -213,7 +227,11 @@ class Ui_MainWindow(object):
|
||||
self.viewLivePanel = shortcut_action(mainWindow, u'viewLivePanel',
|
||||
[QtGui.QKeySequence(u'F12')], self.setLivePanelVisibility,
|
||||
checked=liveVisible, category=UiStrings().View)
|
||||
action_list.add_category(UiStrings().ViewMode, CategoryOrder.standardMenu)
|
||||
self.lockPanel = shortcut_action(mainWindow, u'lockPanel',
|
||||
None, self.setLockPanel,
|
||||
checked=panelLocked, category=None)
|
||||
action_list.add_category(UiStrings().ViewMode,
|
||||
CategoryOrder.standardMenu)
|
||||
self.modeDefaultItem = checkable_action(
|
||||
mainWindow, u'modeDefaultItem', category=UiStrings().ViewMode)
|
||||
self.modeSetupItem = checkable_action(
|
||||
@ -231,9 +249,13 @@ class Ui_MainWindow(object):
|
||||
self.toolsOpenDataFolder = icon_action(mainWindow,
|
||||
u'toolsOpenDataFolder', u':/general/general_open.png',
|
||||
category=UiStrings().Tools)
|
||||
self.toolsFirstTimeWizard = icon_action(mainWindow,
|
||||
u'toolsFirstTimeWizard', u':/general/general_revert.png',
|
||||
category=UiStrings().Tools)
|
||||
self.updateThemeImages = base_action(mainWindow,
|
||||
u'updateThemeImages', category=UiStrings().Tools)
|
||||
action_list.add_category(UiStrings().Settings, CategoryOrder.standardMenu)
|
||||
action_list.add_category(UiStrings().Settings,
|
||||
CategoryOrder.standardMenu)
|
||||
self.settingsPluginListItem = shortcut_action(mainWindow,
|
||||
u'settingsPluginListItem', [QtGui.QKeySequence(u'Alt+F7')],
|
||||
self.onPluginItemClicked, u':/system/settings_plugin_list.png',
|
||||
@ -255,40 +277,45 @@ class Ui_MainWindow(object):
|
||||
u'settingsShortcutsItem',
|
||||
u':/system/system_configure_shortcuts.png',
|
||||
category=UiStrings().Settings)
|
||||
self.displayTagItem = icon_action(mainWindow,
|
||||
# Formatting Tags were also known as display tags.
|
||||
self.formattingTagItem = icon_action(mainWindow,
|
||||
u'displayTagItem', u':/system/tag_editor.png',
|
||||
category=UiStrings().Settings)
|
||||
self.settingsConfigureItem = icon_action(mainWindow,
|
||||
u'settingsConfigureItem', u':/system/system_settings.png',
|
||||
category=UiStrings().Settings)
|
||||
action_list.add_category(UiStrings().Help, CategoryOrder.standardMenu)
|
||||
self.helpDocumentationItem = icon_action(mainWindow,
|
||||
u'helpDocumentationItem', u':/system/system_help_contents.png',
|
||||
category=None)#UiStrings().Help)
|
||||
self.helpDocumentationItem.setEnabled(False)
|
||||
self.helpAboutItem = shortcut_action(mainWindow, u'helpAboutItem',
|
||||
[QtGui.QKeySequence(u'Ctrl+F1')], self.onHelpAboutItemClicked,
|
||||
self.aboutItem = shortcut_action(mainWindow, u'aboutItem',
|
||||
[QtGui.QKeySequence(u'Ctrl+F1')], self.onAboutItemClicked,
|
||||
u':/system/system_about.png', category=UiStrings().Help)
|
||||
self.helpOnlineHelpItem = shortcut_action(
|
||||
mainWindow, u'helpOnlineHelpItem', [QtGui.QKeySequence(u'F1')],
|
||||
self.onHelpOnlineHelpClicked, u':/system/system_online_help.png',
|
||||
category=UiStrings().Help)
|
||||
self.helpWebSiteItem = base_action(
|
||||
mainWindow, u'helpWebSiteItem', category=UiStrings().Help)
|
||||
if os.name == u'nt':
|
||||
self.localHelpFile = os.path.join(
|
||||
AppLocation.get_directory(AppLocation.AppDir), 'OpenLP.chm')
|
||||
self.offlineHelpItem = shortcut_action(
|
||||
mainWindow, u'offlineHelpItem', [QtGui.QKeySequence(u'F1')],
|
||||
self.onOfflineHelpClicked,
|
||||
u':/system/system_help_contents.png', category=UiStrings().Help)
|
||||
self.onlineHelpItem = shortcut_action(
|
||||
mainWindow, u'onlineHelpItem',
|
||||
[QtGui.QKeySequence(u'Alt+F1')], self.onOnlineHelpClicked,
|
||||
u':/system/system_online_help.png', category=UiStrings().Help)
|
||||
self.webSiteItem = base_action(
|
||||
mainWindow, u'webSiteItem', category=UiStrings().Help)
|
||||
add_actions(self.fileImportMenu,
|
||||
(self.importThemeItem, self.importLanguageItem))
|
||||
add_actions(self.fileExportMenu,
|
||||
(self.exportThemeItem, self.exportLanguageItem))
|
||||
self.fileMenuActions = (self.fileNewItem, self.fileOpenItem,
|
||||
self.fileSaveItem, self.fileSaveAsItem, None,
|
||||
self.printServiceOrderItem, None, self.fileImportMenu.menuAction(),
|
||||
self.fileExportMenu.menuAction(), self.fileExitItem)
|
||||
add_actions(self.fileMenu, (self.fileNewItem, self.fileOpenItem,
|
||||
self.fileSaveItem, self.fileSaveAsItem,
|
||||
self.recentFilesMenu.menuAction(), None,
|
||||
self.fileImportMenu.menuAction(), self.fileExportMenu.menuAction(),
|
||||
None, self.printServiceOrderItem, self.fileExitItem))
|
||||
add_actions(self.viewModeMenu, (self.modeDefaultItem,
|
||||
self.modeSetupItem, self.modeLiveItem))
|
||||
add_actions(self.viewMenu, (self.viewModeMenu.menuAction(),
|
||||
None, self.viewMediaManagerItem, self.viewServiceManagerItem,
|
||||
self.viewThemeManagerItem, None, self.viewPreviewPanel,
|
||||
self.viewLivePanel))
|
||||
self.viewLivePanel, None, self.lockPanel))
|
||||
# i18n add Language Actions
|
||||
add_actions(self.settingsLanguageMenu, (self.autoLanguageItem, None))
|
||||
add_actions(self.settingsLanguageMenu, self.languageGroup.actions())
|
||||
@ -298,18 +325,23 @@ class Ui_MainWindow(object):
|
||||
add_actions(self.settingsMenu, (self.settingsPluginListItem,
|
||||
self.settingsLanguageMenu.menuAction(), None,
|
||||
self.settingsConfigureItem, self.settingsShortcutsItem,
|
||||
self.displayTagItem))
|
||||
self.formattingTagItem))
|
||||
else:
|
||||
add_actions(self.settingsMenu, (self.settingsPluginListItem,
|
||||
self.settingsLanguageMenu.menuAction(), None,
|
||||
self.displayTagItem, self.settingsShortcutsItem,
|
||||
self.formattingTagItem, self.settingsShortcutsItem,
|
||||
self.settingsConfigureItem))
|
||||
add_actions(self.toolsMenu, (self.toolsAddToolItem, None))
|
||||
add_actions(self.toolsMenu, (self.toolsOpenDataFolder, None))
|
||||
add_actions(self.toolsMenu, (self.toolsFirstTimeWizard, None))
|
||||
add_actions(self.toolsMenu, [self.updateThemeImages])
|
||||
add_actions(self.helpMenu, (self.helpDocumentationItem,
|
||||
self.helpOnlineHelpItem, None, self.helpWebSiteItem,
|
||||
self.helpAboutItem))
|
||||
if os.name == u'nt':
|
||||
add_actions(self.helpMenu, (self.offlineHelpItem,
|
||||
self.onlineHelpItem, None, self.webSiteItem,
|
||||
self.aboutItem))
|
||||
else:
|
||||
add_actions(self.helpMenu, (self.onlineHelpItem, None,
|
||||
self.webSiteItem, self.aboutItem))
|
||||
add_actions(self.menuBar, (self.fileMenu.menuAction(),
|
||||
self.viewMenu.menuAction(), self.toolsMenu.menuAction(),
|
||||
self.settingsMenu.menuAction(), self.helpMenu.menuAction()))
|
||||
@ -318,13 +350,13 @@ class Ui_MainWindow(object):
|
||||
self.mediaToolBox.setCurrentIndex(0)
|
||||
# Connect up some signals and slots
|
||||
QtCore.QObject.connect(self.fileMenu,
|
||||
QtCore.SIGNAL(u'aboutToShow()'), self.updateFileMenu)
|
||||
QtCore.SIGNAL(u'aboutToShow()'), self.updateRecentFilesMenu)
|
||||
QtCore.QMetaObject.connectSlotsByName(mainWindow)
|
||||
# Hide the entry, as it does not have any functionality yet.
|
||||
self.toolsAddToolItem.setVisible(False)
|
||||
self.importLanguageItem.setVisible(False)
|
||||
self.exportLanguageItem.setVisible(False)
|
||||
self.helpDocumentationItem.setVisible(False)
|
||||
self.setLockPanel(panelLocked)
|
||||
|
||||
def retranslateUi(self, mainWindow):
|
||||
"""
|
||||
@ -335,6 +367,8 @@ class Ui_MainWindow(object):
|
||||
self.fileMenu.setTitle(translate('OpenLP.MainWindow', '&File'))
|
||||
self.fileImportMenu.setTitle(translate('OpenLP.MainWindow', '&Import'))
|
||||
self.fileExportMenu.setTitle(translate('OpenLP.MainWindow', '&Export'))
|
||||
self.recentFilesMenu.setTitle(
|
||||
translate('OpenLP.MainWindow', '&Recent Files'))
|
||||
self.viewMenu.setTitle(translate('OpenLP.MainWindow', '&View'))
|
||||
self.viewModeMenu.setTitle(translate('OpenLP.MainWindow', 'M&ode'))
|
||||
self.toolsMenu.setTitle(translate('OpenLP.MainWindow', '&Tools'))
|
||||
@ -382,8 +416,8 @@ class Ui_MainWindow(object):
|
||||
translate('OpenLP.MainWindow', '&Language'))
|
||||
self.settingsShortcutsItem.setText(
|
||||
translate('OpenLP.MainWindow', 'Configure &Shortcuts...'))
|
||||
self.displayTagItem.setText(
|
||||
translate('OpenLP.MainWindow', '&Configure Display Tags'))
|
||||
self.formattingTagItem.setText(
|
||||
translate('OpenLP.MainWindow', '&Configure Formatting Tags...'))
|
||||
self.settingsConfigureItem.setText(
|
||||
translate('OpenLP.MainWindow', '&Configure OpenLP...'))
|
||||
self.viewMediaManagerItem.setText(
|
||||
@ -414,20 +448,25 @@ class Ui_MainWindow(object):
|
||||
translate('OpenLP.MainWindow', '&Live Panel'))
|
||||
self.viewLivePanel.setToolTip(
|
||||
translate('OpenLP.MainWindow', 'Toggle Live Panel'))
|
||||
self.lockPanel.setText(
|
||||
translate('OpenLP.MainWindow', 'L&ock Panels'))
|
||||
self.lockPanel.setStatusTip(
|
||||
translate('OpenLP.MainWindow', 'Prevent the panels being moved.'))
|
||||
self.viewLivePanel.setStatusTip(translate('OpenLP.MainWindow',
|
||||
'Toggle the visibility of the live panel.'))
|
||||
self.settingsPluginListItem.setText(translate('OpenLP.MainWindow',
|
||||
'&Plugin List'))
|
||||
self.settingsPluginListItem.setStatusTip(
|
||||
translate('OpenLP.MainWindow', 'List the Plugins'))
|
||||
self.helpDocumentationItem.setText(
|
||||
translate('OpenLP.MainWindow', '&User Guide'))
|
||||
self.helpAboutItem.setText(translate('OpenLP.MainWindow', '&About'))
|
||||
self.helpAboutItem.setStatusTip(
|
||||
self.aboutItem.setText(translate('OpenLP.MainWindow', '&About'))
|
||||
self.aboutItem.setStatusTip(
|
||||
translate('OpenLP.MainWindow', 'More information about OpenLP'))
|
||||
self.helpOnlineHelpItem.setText(
|
||||
if os.name == u'nt':
|
||||
self.offlineHelpItem.setText(
|
||||
translate('OpenLP.MainWindow', '&User Guide'))
|
||||
self.onlineHelpItem.setText(
|
||||
translate('OpenLP.MainWindow', '&Online Help'))
|
||||
self.helpWebSiteItem.setText(
|
||||
self.webSiteItem.setText(
|
||||
translate('OpenLP.MainWindow', '&Web Site'))
|
||||
for item in self.languageGroup.actions():
|
||||
item.setText(item.objectName())
|
||||
@ -445,6 +484,10 @@ class Ui_MainWindow(object):
|
||||
translate('OpenLP.MainWindow', 'Open &Data Folder...'))
|
||||
self.toolsOpenDataFolder.setStatusTip(translate('OpenLP.MainWindow',
|
||||
'Open the folder where songs, bibles and other data resides.'))
|
||||
self.toolsFirstTimeWizard.setText(
|
||||
translate('OpenLP.MainWindow', 'Re-run First Time Wizard'))
|
||||
self.toolsFirstTimeWizard.setStatusTip(translate('OpenLP.MainWindow',
|
||||
'Re-run the First Time Wizard, importing songs, Bibles and themes.'))
|
||||
self.updateThemeImages.setText(
|
||||
translate('OpenLP.MainWindow', 'Update Theme Images'))
|
||||
self.updateThemeImages.setStatusTip(
|
||||
@ -485,7 +528,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
self.serviceNotSaved = False
|
||||
self.aboutForm = AboutForm(self)
|
||||
self.settingsForm = SettingsForm(self, self)
|
||||
self.displayTagForm = DisplayTagForm(self)
|
||||
self.formattingTagForm = FormattingTagForm(self)
|
||||
self.shortcutForm = ShortcutListForm(self)
|
||||
self.recentFiles = QtCore.QStringList()
|
||||
# Set up the path with plugins
|
||||
@ -497,8 +540,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
self.setupUi(self)
|
||||
# Load settings after setupUi so default UI sizes are overwritten
|
||||
self.loadSettings()
|
||||
# Once settings are loaded update FileMenu with recentFiles
|
||||
self.updateFileMenu()
|
||||
# Once settings are loaded update the menu with the recent files.
|
||||
self.updateRecentFilesMenu()
|
||||
self.pluginForm = PluginForm(self)
|
||||
# Set up signals and slots
|
||||
QtCore.QObject.connect(self.importThemeItem,
|
||||
@ -516,14 +559,16 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
QtCore.QObject.connect(self.themeManagerDock,
|
||||
QtCore.SIGNAL(u'visibilityChanged(bool)'),
|
||||
self.viewThemeManagerItem.setChecked)
|
||||
QtCore.QObject.connect(self.helpWebSiteItem,
|
||||
QtCore.QObject.connect(self.webSiteItem,
|
||||
QtCore.SIGNAL(u'triggered()'), self.onHelpWebSiteClicked)
|
||||
QtCore.QObject.connect(self.toolsOpenDataFolder,
|
||||
QtCore.SIGNAL(u'triggered()'), self.onToolsOpenDataFolderClicked)
|
||||
QtCore.QObject.connect(self.toolsFirstTimeWizard,
|
||||
QtCore.SIGNAL(u'triggered()'), self.onFirstTimeWizardClicked)
|
||||
QtCore.QObject.connect(self.updateThemeImages,
|
||||
QtCore.SIGNAL(u'triggered()'), self.onUpdateThemeImages)
|
||||
QtCore.QObject.connect(self.displayTagItem,
|
||||
QtCore.SIGNAL(u'triggered()'), self.onDisplayTagItemClicked)
|
||||
QtCore.QObject.connect(self.formattingTagItem,
|
||||
QtCore.SIGNAL(u'triggered()'), self.onFormattingTagItemClicked)
|
||||
QtCore.QObject.connect(self.settingsConfigureItem,
|
||||
QtCore.SIGNAL(u'triggered()'), self.onSettingsConfigureItemClicked)
|
||||
QtCore.QObject.connect(self.settingsShortcutsItem,
|
||||
@ -653,7 +698,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
QtCore.QVariant(False)).toBool():
|
||||
self.serviceManagerContents.loadLastFile()
|
||||
view_mode = QtCore.QSettings().value(u'%s/view mode' % \
|
||||
self.generalSettingsSection, u'default')
|
||||
self.generalSettingsSection, u'default').toString()
|
||||
if view_mode == u'default':
|
||||
self.modeDefaultItem.setChecked(True)
|
||||
elif view_mode == u'setup':
|
||||
@ -682,11 +727,46 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
plugin.firstTime()
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
temp_dir = os.path.join(unicode(gettempdir()), u'openlp')
|
||||
if not os.path.exists(temp_dir):
|
||||
shutil.rmtree(temp_dir, True)
|
||||
|
||||
def onFirstTimeWizardClicked(self):
|
||||
"""
|
||||
Re-run the first time wizard. Prompts the user for run confirmation
|
||||
If wizard is run, songs, bibles and themes are imported. The default
|
||||
theme is changed (if necessary). The plugins in pluginmanager are
|
||||
set active/in-active to match the selection in the wizard.
|
||||
"""
|
||||
answer = QtGui.QMessageBox.warning(self,
|
||||
translate('OpenLP.MainWindow', 'Re-run First Time Wizard?'),
|
||||
translate('OpenLP.MainWindow',
|
||||
'Are you sure you want to re-run the First Time Wizard?\n\n'
|
||||
'Re-running this wizard may make changes to your current '
|
||||
'OpenLP configuration and possibly add songs to your '
|
||||
'existing songs list and change your default theme.'),
|
||||
QtGui.QMessageBox.StandardButtons(
|
||||
QtGui.QMessageBox.Yes |
|
||||
QtGui.QMessageBox.No),
|
||||
QtGui.QMessageBox.No)
|
||||
if answer == QtGui.QMessageBox.No:
|
||||
return
|
||||
for filename in os.listdir(temp_dir):
|
||||
delete_file(os.path.join(temp_dir, filename))
|
||||
os.removedirs(temp_dir)
|
||||
Receiver.send_message(u'cursor_busy')
|
||||
screens = ScreenList.get_instance()
|
||||
if FirstTimeForm(screens, self).exec_() == QtGui.QDialog.Accepted:
|
||||
self.firstTime()
|
||||
for plugin in self.pluginManager.plugins:
|
||||
self.activePlugin = plugin
|
||||
oldStatus = self.activePlugin.status
|
||||
self.activePlugin.setStatus()
|
||||
if oldStatus != self.activePlugin.status:
|
||||
if self.activePlugin.status == PluginStatus.Active:
|
||||
self.activePlugin.toggleStatus(PluginStatus.Active)
|
||||
self.activePlugin.appStartup()
|
||||
else:
|
||||
self.activePlugin.toggleStatus(PluginStatus.Inactive)
|
||||
self.themeManagerContents.configUpdated()
|
||||
self.themeManagerContents.loadThemes(True)
|
||||
Receiver.send_message(u'theme_update_global',
|
||||
self.themeManagerContents.global_theme)
|
||||
|
||||
def blankCheck(self):
|
||||
"""
|
||||
@ -723,14 +803,20 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
import webbrowser
|
||||
webbrowser.open_new(u'http://openlp.org/')
|
||||
|
||||
def onHelpOnlineHelpClicked(self):
|
||||
def onOfflineHelpClicked(self):
|
||||
"""
|
||||
Load the local OpenLP help file
|
||||
"""
|
||||
os.startfile(self.localHelpFile)
|
||||
|
||||
def onOnlineHelpClicked(self):
|
||||
"""
|
||||
Load the online OpenLP manual
|
||||
"""
|
||||
import webbrowser
|
||||
webbrowser.open_new(u'http://manual.openlp.org/')
|
||||
|
||||
def onHelpAboutItemClicked(self):
|
||||
def onAboutItemClicked(self):
|
||||
"""
|
||||
Show the About form
|
||||
"""
|
||||
@ -756,11 +842,11 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
"""
|
||||
self.themeManagerContents.updatePreviewImages()
|
||||
|
||||
def onDisplayTagItemClicked(self):
|
||||
def onFormattingTagItemClicked(self):
|
||||
"""
|
||||
Show the Settings dialog
|
||||
"""
|
||||
self.displayTagForm.exec_()
|
||||
self.formattingTagForm.exec_()
|
||||
|
||||
def onSettingsConfigureItemClicked(self):
|
||||
"""
|
||||
@ -936,7 +1022,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
self.mediaManagerDock.setVisible(not self.mediaManagerDock.isVisible())
|
||||
|
||||
def toggleServiceManager(self):
|
||||
self.serviceManagerDock.setVisible(not self.serviceManagerDock.isVisible())
|
||||
self.serviceManagerDock.setVisible(
|
||||
not self.serviceManagerDock.isVisible())
|
||||
|
||||
def toggleThemeManager(self):
|
||||
self.themeManagerDock.setVisible(not self.themeManagerDock.isVisible())
|
||||
@ -956,6 +1043,37 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
QtCore.QVariant(visible))
|
||||
self.viewPreviewPanel.setChecked(visible)
|
||||
|
||||
def setLockPanel(self, lock):
|
||||
"""
|
||||
Sets the ability to stop the toolbars being changed.
|
||||
"""
|
||||
if lock:
|
||||
self.themeManagerDock.setFeatures(
|
||||
QtGui.QDockWidget.NoDockWidgetFeatures)
|
||||
self.serviceManagerDock.setFeatures(
|
||||
QtGui.QDockWidget.NoDockWidgetFeatures)
|
||||
self.mediaManagerDock.setFeatures(
|
||||
QtGui.QDockWidget.NoDockWidgetFeatures)
|
||||
self.viewMediaManagerItem.setEnabled(False)
|
||||
self.viewServiceManagerItem.setEnabled(False)
|
||||
self.viewThemeManagerItem.setEnabled(False)
|
||||
self.viewPreviewPanel.setEnabled(False)
|
||||
self.viewLivePanel.setEnabled(False)
|
||||
else:
|
||||
self.themeManagerDock.setFeatures(
|
||||
QtGui.QDockWidget.AllDockWidgetFeatures)
|
||||
self.serviceManagerDock.setFeatures(
|
||||
QtGui.QDockWidget.AllDockWidgetFeatures)
|
||||
self.mediaManagerDock.setFeatures(
|
||||
QtGui.QDockWidget.AllDockWidgetFeatures)
|
||||
self.viewMediaManagerItem.setEnabled(True)
|
||||
self.viewServiceManagerItem.setEnabled(True)
|
||||
self.viewThemeManagerItem.setEnabled(True)
|
||||
self.viewPreviewPanel.setEnabled(True)
|
||||
self.viewLivePanel.setEnabled(True)
|
||||
QtCore.QSettings().setValue(u'user interface/lock panel',
|
||||
QtCore.QVariant(lock))
|
||||
|
||||
def setLivePanelVisibility(self, visible):
|
||||
"""
|
||||
Sets the visibility of the live panel including saving the setting and
|
||||
@ -986,6 +1104,13 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
self.restoreGeometry(
|
||||
settings.value(u'main window geometry').toByteArray())
|
||||
self.restoreState(settings.value(u'main window state').toByteArray())
|
||||
self.liveController.splitter.restoreState(
|
||||
settings.value(u'live splitter geometry').toByteArray())
|
||||
self.previewController.splitter.restoreState(
|
||||
settings.value(u'preview splitter geometry').toByteArray())
|
||||
self.controlSplitter.restoreState(
|
||||
settings.value(u'mainwindow splitter geometry').toByteArray())
|
||||
|
||||
settings.endGroup()
|
||||
|
||||
def saveSettings(self):
|
||||
@ -1006,32 +1131,44 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
QtCore.QVariant(self.saveState()))
|
||||
settings.setValue(u'main window geometry',
|
||||
QtCore.QVariant(self.saveGeometry()))
|
||||
settings.setValue(u'live splitter geometry',
|
||||
QtCore.QVariant(self.liveController.splitter.saveState()))
|
||||
settings.setValue(u'preview splitter geometry',
|
||||
QtCore.QVariant(self.previewController.splitter.saveState()))
|
||||
settings.setValue(u'mainwindow splitter geometry',
|
||||
QtCore.QVariant(self.controlSplitter.saveState()))
|
||||
settings.endGroup()
|
||||
|
||||
def updateFileMenu(self):
|
||||
def updateRecentFilesMenu(self):
|
||||
"""
|
||||
Updates the file menu with the latest list of service files accessed.
|
||||
Updates the recent file menu with the latest list of service files
|
||||
accessed.
|
||||
"""
|
||||
recentFileCount = QtCore.QSettings().value(
|
||||
u'advanced/recent file count', QtCore.QVariant(4)).toInt()[0]
|
||||
self.fileMenu.clear()
|
||||
add_actions(self.fileMenu, self.fileMenuActions[:-1])
|
||||
existingRecentFiles = [recentFile for recentFile in self.recentFiles
|
||||
if QtCore.QFile.exists(recentFile)]
|
||||
recentFilesToDisplay = existingRecentFiles[0:recentFileCount]
|
||||
if recentFilesToDisplay:
|
||||
self.fileMenu.addSeparator()
|
||||
for fileId, filename in enumerate(recentFilesToDisplay):
|
||||
log.debug('Recent file name: %s', filename)
|
||||
action = base_action(self, u'')
|
||||
action.setText(u'&%d %s' %
|
||||
(fileId + 1, QtCore.QFileInfo(filename).fileName()))
|
||||
action.setData(QtCore.QVariant(filename))
|
||||
self.connect(action, QtCore.SIGNAL(u'triggered()'),
|
||||
self.serviceManagerContents.onRecentServiceClicked)
|
||||
self.fileMenu.addAction(action)
|
||||
self.fileMenu.addSeparator()
|
||||
self.fileMenu.addAction(self.fileMenuActions[-1])
|
||||
self.recentFilesMenu.clear()
|
||||
for fileId, filename in enumerate(recentFilesToDisplay):
|
||||
log.debug('Recent file name: %s', filename)
|
||||
action = base_action(self, u'')
|
||||
action.setText(u'&%d %s' %
|
||||
(fileId + 1, QtCore.QFileInfo(filename).fileName()))
|
||||
action.setData(QtCore.QVariant(filename))
|
||||
self.connect(action, QtCore.SIGNAL(u'triggered()'),
|
||||
self.serviceManagerContents.onRecentServiceClicked)
|
||||
self.recentFilesMenu.addAction(action)
|
||||
clearRecentFilesAction = base_action(self, u'')
|
||||
clearRecentFilesAction.setText(
|
||||
translate('OpenLP.MainWindow', 'Clear List',
|
||||
'Clear List of recent files'))
|
||||
clearRecentFilesAction.setStatusTip(
|
||||
translate('OpenLP.MainWindow', 'Clear the list of recent files.'))
|
||||
add_actions(self.recentFilesMenu, (None, clearRecentFilesAction))
|
||||
self.connect(clearRecentFilesAction, QtCore.SIGNAL(u'triggered()'),
|
||||
self.recentFiles.clear)
|
||||
clearRecentFilesAction.setEnabled(not self.recentFiles.isEmpty())
|
||||
|
||||
def addRecentFile(self, filename):
|
||||
"""
|
||||
|
@ -66,7 +66,7 @@ class MediaDockManager(object):
|
||||
match = False
|
||||
for dock_index in range(0, self.media_dock.count()):
|
||||
if self.media_dock.widget(dock_index).settingsSection == \
|
||||
media_item.plugin.name.lower():
|
||||
media_item.plugin.name:
|
||||
match = True
|
||||
break
|
||||
if not match:
|
||||
@ -84,6 +84,6 @@ class MediaDockManager(object):
|
||||
for dock_index in range(0, self.media_dock.count()):
|
||||
if self.media_dock.widget(dock_index):
|
||||
if self.media_dock.widget(dock_index).settingsSection == \
|
||||
media_item.plugin.name.lower():
|
||||
media_item.plugin.name:
|
||||
self.media_dock.widget(dock_index).setVisible(False)
|
||||
self.media_dock.removeItem(dock_index)
|
||||
|
@ -108,7 +108,7 @@ class Ui_PrintServiceDialog(object):
|
||||
self.footerLabel = QtGui.QLabel(self.optionsWidget)
|
||||
self.footerLabel.setObjectName(u'footerLabel')
|
||||
self.optionsLayout.addWidget(self.footerLabel)
|
||||
self.footerTextEdit = SpellTextEdit(self.optionsWidget)
|
||||
self.footerTextEdit = SpellTextEdit(self.optionsWidget, False)
|
||||
self.footerTextEdit.setObjectName(u'footerTextEdit')
|
||||
self.optionsLayout.addWidget(self.footerTextEdit)
|
||||
self.optionsGroupBox = QtGui.QGroupBox()
|
||||
|
@ -24,6 +24,7 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
import cgi
|
||||
import datetime
|
||||
import os
|
||||
|
||||
@ -183,7 +184,7 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
|
||||
self._addElement(u'style', custom_css, html_data.head,
|
||||
attribute=(u'type', u'text/css'))
|
||||
self._addElement(u'body', parent=html_data)
|
||||
self._addElement(u'h1', unicode(self.titleLineEdit.text()),
|
||||
self._addElement(u'h1', cgi.escape(unicode(self.titleLineEdit.text())),
|
||||
html_data.body, classId=u'serviceTitle')
|
||||
for index, item in enumerate(self.serviceManager.serviceItems):
|
||||
self._addPreviewItem(html_data.body, item[u'service_item'], index)
|
||||
@ -193,8 +194,9 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
|
||||
classId=u'customNotes')
|
||||
self._addElement(u'span', translate('OpenLP.ServiceManager',
|
||||
'Custom Service Notes: '), div, classId=u'customNotesTitle')
|
||||
self._addElement(u'span', self.footerTextEdit.toPlainText(), div,
|
||||
classId=u'customNotesText')
|
||||
self._addElement(u'span',
|
||||
cgi.escape(self.footerTextEdit.toPlainText()),
|
||||
div, classId=u'customNotesText')
|
||||
self.document.setHtml(html.tostring(html_data))
|
||||
self.previewWidget.updatePreview()
|
||||
|
||||
@ -204,8 +206,8 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
|
||||
item_title = self._addElement(u'h2', parent=div, classId=u'itemTitle')
|
||||
self._addElement(u'img', parent=item_title,
|
||||
attribute=(u'src', item.icon))
|
||||
self._addElement(u'span', u' ' + item.get_display_title(),
|
||||
item_title)
|
||||
self._addElement(u'span',
|
||||
u' ' + cgi.escape(item.get_display_title()), item_title)
|
||||
if self.slideTextCheckBox.isChecked():
|
||||
# Add the text of the service item.
|
||||
if item.is_text():
|
||||
@ -230,8 +232,9 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
|
||||
foot_text = item.foot_text
|
||||
foot_text = foot_text.partition(u'<br>')[2]
|
||||
if foot_text:
|
||||
foot = self._addElement(u'div', foot_text, parent=div,
|
||||
classId=u'itemFooter')
|
||||
foot_text = cgi.escape(foot_text.replace(u'<br>', u'\n'))
|
||||
self._addElement(u'div', foot_text.replace(u'\n', u'<br>'),
|
||||
parent=div, classId=u'itemFooter')
|
||||
# Add service items' notes.
|
||||
if self.notesCheckBox.isChecked():
|
||||
if item.notes:
|
||||
@ -239,8 +242,8 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
|
||||
self._addElement(u'span',
|
||||
translate('OpenLP.ServiceManager', 'Notes: '), p,
|
||||
classId=u'itemNotesTitle')
|
||||
notes = self._addElement(u'span',
|
||||
item.notes.replace(u'\n', u'<br />'), p,
|
||||
self._addElement(u'span',
|
||||
cgi.escape(unicode(item.notes)).replace(u'\n', u'<br>'), p,
|
||||
classId=u'itemNotesText')
|
||||
# Add play length of media files.
|
||||
if item.is_media() and self.metaDataCheckBox.isChecked():
|
||||
|
@ -35,6 +35,8 @@ class Ui_ServiceItemEditDialog(object):
|
||||
def setupUi(self, serviceItemEditDialog):
|
||||
serviceItemEditDialog.setObjectName(u'serviceItemEditDialog')
|
||||
self.dialogLayout = QtGui.QGridLayout(serviceItemEditDialog)
|
||||
self.dialogLayout.setContentsMargins(8, 8, 8, 8)
|
||||
self.dialogLayout.setSpacing(8)
|
||||
self.dialogLayout.setObjectName(u'dialogLayout')
|
||||
self.listWidget = QtGui.QListWidget(serviceItemEditDialog)
|
||||
self.listWidget.setAlternatingRowColors(True)
|
||||
|
@ -79,7 +79,7 @@ class ServiceItemEditForm(QtGui.QDialog, Ui_ServiceItemEditDialog):
|
||||
if not item:
|
||||
return
|
||||
row = self.listWidget.row(item)
|
||||
self.itemList.remove(self.itemList[row])
|
||||
self.itemList.pop(row)
|
||||
self.loadData()
|
||||
if row == self.listWidget.count():
|
||||
self.listWidget.setCurrentRow(row - 1)
|
||||
@ -109,7 +109,7 @@ class ServiceItemEditForm(QtGui.QDialog, Ui_ServiceItemEditDialog):
|
||||
return
|
||||
row = self.listWidget.row(item)
|
||||
temp = self.itemList[row]
|
||||
self.itemList.remove(self.itemList[row])
|
||||
self.itemList.pop(row)
|
||||
if direction == u'up':
|
||||
row -= 1
|
||||
else:
|
||||
|
@ -24,6 +24,7 @@
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
import cgi
|
||||
import cPickle
|
||||
import logging
|
||||
import os
|
||||
@ -48,18 +49,18 @@ class ServiceManagerList(QtGui.QTreeWidget):
|
||||
"""
|
||||
Set up key bindings and mouse behaviour for the service list
|
||||
"""
|
||||
def __init__(self, mainwindow, parent=None, name=None):
|
||||
def __init__(self, serviceManager, parent=None, name=None):
|
||||
QtGui.QTreeWidget.__init__(self, parent)
|
||||
self.mainwindow = mainwindow
|
||||
self.serviceManager = serviceManager
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
if isinstance(event, QtGui.QKeyEvent):
|
||||
# here accept the event and do something
|
||||
if event.key() == QtCore.Qt.Key_Up:
|
||||
self.mainwindow.onMoveSelectionUp()
|
||||
self.serviceManager.onMoveSelectionUp()
|
||||
event.accept()
|
||||
elif event.key() == QtCore.Qt.Key_Down:
|
||||
self.mainwindow.onMoveSelectionDown()
|
||||
self.serviceManager.onMoveSelectionDown()
|
||||
event.accept()
|
||||
event.ignore()
|
||||
else:
|
||||
@ -408,20 +409,33 @@ class ServiceManager(QtGui.QWidget):
|
||||
return False
|
||||
self.newFile()
|
||||
|
||||
def onLoadServiceClicked(self):
|
||||
def onLoadServiceClicked(self, loadFile=None):
|
||||
"""
|
||||
Loads the service file and saves the existing one it there is one
|
||||
unchanged
|
||||
|
||||
``loadFile``
|
||||
The service file to the loaded. Will be None is from menu so
|
||||
selection will be required.
|
||||
"""
|
||||
if self.isModified():
|
||||
result = self.saveModifiedService()
|
||||
if result == QtGui.QMessageBox.Cancel:
|
||||
return False
|
||||
elif result == QtGui.QMessageBox.Save:
|
||||
self.saveFile()
|
||||
fileName = unicode(QtGui.QFileDialog.getOpenFileName(self.mainwindow,
|
||||
translate('OpenLP.ServiceManager', 'Open File'),
|
||||
SettingsManager.get_last_dir(
|
||||
self.mainwindow.serviceSettingsSection),
|
||||
translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz)')))
|
||||
if not fileName:
|
||||
return False
|
||||
if not loadFile:
|
||||
fileName = unicode(QtGui.QFileDialog.getOpenFileName(
|
||||
self.mainwindow,
|
||||
translate('OpenLP.ServiceManager', 'Open File'),
|
||||
SettingsManager.get_last_dir(
|
||||
self.mainwindow.serviceSettingsSection),
|
||||
translate('OpenLP.ServiceManager',
|
||||
'OpenLP Service Files (*.osz)')))
|
||||
if not fileName:
|
||||
return False
|
||||
else:
|
||||
fileName = loadFile
|
||||
SettingsManager.set_last_dir(self.mainwindow.serviceSettingsSection,
|
||||
split_filename(fileName)[0])
|
||||
self.loadFile(fileName)
|
||||
@ -474,6 +488,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
item[u'service_item'].get_service_repr()})
|
||||
if not item[u'service_item'].uses_file():
|
||||
continue
|
||||
skipMissing = False
|
||||
for frame in item[u'service_item'].get_frames():
|
||||
if item[u'service_item'].is_image():
|
||||
path_from = frame[u'path']
|
||||
@ -482,25 +497,29 @@ class ServiceManager(QtGui.QWidget):
|
||||
# Only write a file once
|
||||
if path_from in write_list:
|
||||
continue
|
||||
file_size = os.path.getsize(path_from)
|
||||
size_limit = 52428800 # 50MiB
|
||||
#if file_size > size_limit:
|
||||
# # File exeeds size_limit bytes, ask user
|
||||
# message = unicode(translate('OpenLP.ServiceManager',
|
||||
# 'Do you want to include \n%.1f MB file "%s"\n'
|
||||
# 'into the service file?\nThis may take some time.\n\n'
|
||||
# 'Please note that you need to\ntake care of that file'
|
||||
# ' yourself,\nif you leave it out.')) % \
|
||||
# (file_size/1048576, os.path.split(path_from)[1])
|
||||
# ans = QtGui.QMessageBox.question(self.mainwindow,
|
||||
# translate('OpenLP.ServiceManager', 'Including Large '
|
||||
# 'File'), message, QtGui.QMessageBox.StandardButtons(
|
||||
# QtGui.QMessageBox.Ok|QtGui.QMessageBox.Cancel),
|
||||
# QtGui.QMessageBox.Ok)
|
||||
# if ans == QtGui.QMessageBox.Cancel:
|
||||
# continue
|
||||
write_list.append(path_from)
|
||||
total_size += file_size
|
||||
if not os.path.exists(path_from):
|
||||
if not skipMissing:
|
||||
Receiver.send_message(u'cursor_normal')
|
||||
title = unicode(translate('OpenLP.ServiceManager',
|
||||
'Service File Missing'))
|
||||
message = unicode(translate('OpenLP.ServiceManager',
|
||||
'File missing from service\n\n %s \n\n'
|
||||
'Continue saving?' % path_from ))
|
||||
answer = QtGui.QMessageBox.critical(self, title,
|
||||
message,
|
||||
QtGui.QMessageBox.StandardButtons(
|
||||
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No |
|
||||
QtGui.QMessageBox.YesToAll))
|
||||
if answer == QtGui.QMessageBox.No:
|
||||
self.mainwindow.finishedProgressBar()
|
||||
return False
|
||||
if answer == QtGui.QMessageBox.YesToAll:
|
||||
skipMissing = True
|
||||
Receiver.send_message(u'cursor_busy')
|
||||
else:
|
||||
file_size = os.path.getsize(path_from)
|
||||
write_list.append(path_from)
|
||||
total_size += file_size
|
||||
log.debug(u'ServiceManager.saveFile - ZIP contents size is %i bytes' %
|
||||
total_size)
|
||||
service_content = cPickle.dumps(service)
|
||||
@ -688,7 +707,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
QtGui.QAction, serviceItem[u'service_item'].theme)
|
||||
if themeAction is not None:
|
||||
themeAction.setChecked(True)
|
||||
action = self.menu.exec_(self.serviceManagerList.mapToGlobal(point))
|
||||
self.menu.exec_(self.serviceManagerList.mapToGlobal(point))
|
||||
|
||||
def onServiceItemNoteForm(self):
|
||||
item = self.findServiceItem()[0]
|
||||
@ -701,6 +720,9 @@ class ServiceManager(QtGui.QWidget):
|
||||
self.setModified()
|
||||
|
||||
def onStartTimeForm(self):
|
||||
"""
|
||||
Opens a dialog to type in service item notes.
|
||||
"""
|
||||
item = self.findServiceItem()[0]
|
||||
self.startTimeForm.item = self.serviceItems[item]
|
||||
if self.startTimeForm.exec_():
|
||||
@ -781,50 +803,25 @@ class ServiceManager(QtGui.QWidget):
|
||||
|
||||
def onMoveSelectionUp(self):
|
||||
"""
|
||||
Moves the selection up the window. Called by the up arrow.
|
||||
Moves the cursor selection up the window.
|
||||
Called by the up arrow.
|
||||
"""
|
||||
serviceIterator = QtGui.QTreeWidgetItemIterator(self.serviceManagerList)
|
||||
tempItem = None
|
||||
setLastItem = False
|
||||
while serviceIterator.value():
|
||||
if serviceIterator.value().isSelected() and tempItem is None:
|
||||
setLastItem = True
|
||||
serviceIterator.value().setSelected(False)
|
||||
if serviceIterator.value().isSelected():
|
||||
# We are on the first record
|
||||
if tempItem:
|
||||
tempItem.setSelected(True)
|
||||
serviceIterator.value().setSelected(False)
|
||||
else:
|
||||
tempItem = serviceIterator.value()
|
||||
lastItem = serviceIterator.value()
|
||||
serviceIterator += 1
|
||||
# Top Item was selected so set the last one
|
||||
if setLastItem:
|
||||
lastItem.setSelected(True)
|
||||
self.setModified()
|
||||
item = self.serviceManagerList.currentItem()
|
||||
itemBefore = self.serviceManagerList.itemAbove(item)
|
||||
if itemBefore is None:
|
||||
return
|
||||
self.serviceManagerList.setCurrentItem(itemBefore)
|
||||
|
||||
def onMoveSelectionDown(self):
|
||||
"""
|
||||
Moves the selection down the window. Called by the down arrow.
|
||||
Moves the cursor selection down the window.
|
||||
Called by the down arrow.
|
||||
"""
|
||||
serviceIterator = QtGui.QTreeWidgetItemIterator(self.serviceManagerList)
|
||||
firstItem = None
|
||||
setSelected = False
|
||||
while serviceIterator.value():
|
||||
if not firstItem:
|
||||
firstItem = serviceIterator.value()
|
||||
if setSelected:
|
||||
setSelected = False
|
||||
serviceIterator.value().setSelected(True)
|
||||
elif serviceIterator.value() and \
|
||||
serviceIterator.value().isSelected():
|
||||
serviceIterator.value().setSelected(False)
|
||||
setSelected = True
|
||||
serviceIterator += 1
|
||||
if setSelected:
|
||||
firstItem.setSelected(True)
|
||||
self.setModified()
|
||||
item = self.serviceManagerList.currentItem()
|
||||
itemAfter = self.serviceManagerList.itemBelow(item)
|
||||
if itemAfter is None:
|
||||
return
|
||||
self.serviceManagerList.setCurrentItem(itemAfter)
|
||||
|
||||
def onCollapseAll(self):
|
||||
"""
|
||||
@ -832,7 +829,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
"""
|
||||
for item in self.serviceItems:
|
||||
item[u'expanded'] = False
|
||||
self.regenerateServiceItems()
|
||||
self.serviceManagerList.collapseAll()
|
||||
|
||||
def collapsed(self, item):
|
||||
"""
|
||||
@ -848,7 +845,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
"""
|
||||
for item in self.serviceItems:
|
||||
item[u'expanded'] = True
|
||||
self.regenerateServiceItems()
|
||||
self.serviceManagerList.expandAll()
|
||||
|
||||
def expanded(self, item):
|
||||
"""
|
||||
@ -856,19 +853,19 @@ class ServiceManager(QtGui.QWidget):
|
||||
correct state.
|
||||
"""
|
||||
pos = item.data(0, QtCore.Qt.UserRole).toInt()[0]
|
||||
self.serviceItems[pos -1 ][u'expanded'] = True
|
||||
self.serviceItems[pos - 1][u'expanded'] = True
|
||||
|
||||
def onServiceTop(self):
|
||||
"""
|
||||
Move the current ServiceItem to the top of the list.
|
||||
"""
|
||||
item, child = self.findServiceItem()
|
||||
if item < len(self.serviceItems) and item is not -1:
|
||||
if item < len(self.serviceItems) and item != -1:
|
||||
temp = self.serviceItems[item]
|
||||
self.serviceItems.remove(self.serviceItems[item])
|
||||
self.serviceItems.insert(0, temp)
|
||||
self.repaintServiceList(0, child)
|
||||
self.setModified()
|
||||
self.setModified()
|
||||
|
||||
def onServiceUp(self):
|
||||
"""
|
||||
@ -880,31 +877,31 @@ class ServiceManager(QtGui.QWidget):
|
||||
self.serviceItems.remove(self.serviceItems[item])
|
||||
self.serviceItems.insert(item - 1, temp)
|
||||
self.repaintServiceList(item - 1, child)
|
||||
self.setModified()
|
||||
self.setModified()
|
||||
|
||||
def onServiceDown(self):
|
||||
"""
|
||||
Move the current ServiceItem one position down in the list.
|
||||
"""
|
||||
item, child = self.findServiceItem()
|
||||
if item < len(self.serviceItems) and item is not -1:
|
||||
if item < len(self.serviceItems) and item != -1:
|
||||
temp = self.serviceItems[item]
|
||||
self.serviceItems.remove(self.serviceItems[item])
|
||||
self.serviceItems.insert(item + 1, temp)
|
||||
self.repaintServiceList(item + 1, child)
|
||||
self.setModified()
|
||||
self.setModified()
|
||||
|
||||
def onServiceEnd(self):
|
||||
"""
|
||||
Move the current ServiceItem to the bottom of the list.
|
||||
"""
|
||||
item, child = self.findServiceItem()
|
||||
if item < len(self.serviceItems) and item is not -1:
|
||||
if item < len(self.serviceItems) and item != -1:
|
||||
temp = self.serviceItems[item]
|
||||
self.serviceItems.remove(self.serviceItems[item])
|
||||
self.serviceItems.insert(len(self.serviceItems), temp)
|
||||
self.repaintServiceList(len(self.serviceItems) - 1, child)
|
||||
self.setModified()
|
||||
self.setModified()
|
||||
|
||||
def onDeleteFromService(self):
|
||||
"""
|
||||
@ -914,7 +911,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
if item != -1:
|
||||
self.serviceItems.remove(self.serviceItems[item])
|
||||
self.repaintServiceList(item - 1, -1)
|
||||
self.setModified()
|
||||
self.setModified()
|
||||
|
||||
def repaintServiceList(self, serviceItem, serviceItemChild):
|
||||
"""
|
||||
@ -956,7 +953,19 @@ class ServiceManager(QtGui.QWidget):
|
||||
treewidgetitem.setIcon(0,
|
||||
build_icon(u':/general/general_delete.png'))
|
||||
treewidgetitem.setText(0, serviceitem.get_display_title())
|
||||
treewidgetitem.setToolTip(0, serviceitem.notes)
|
||||
tips = []
|
||||
if serviceitem.theme and serviceitem.theme != -1:
|
||||
tips.append(u'<strong>%s:</strong> <em>%s</em>' %
|
||||
(unicode(translate('OpenLP.ServiceManager', 'Slide theme')),
|
||||
serviceitem.theme))
|
||||
if serviceitem.notes:
|
||||
tips.append(u'<strong>%s: </strong> %s' %
|
||||
(unicode(translate('OpenLP.ServiceManager', 'Notes')),
|
||||
cgi.escape(unicode(serviceitem.notes))))
|
||||
if item[u'service_item'] \
|
||||
.is_capable(ItemCapabilities.AllowsVariableStartTime):
|
||||
tips.append(item[u'service_item'].get_media_time())
|
||||
treewidgetitem.setToolTip(0, u'<br>'.join(tips))
|
||||
treewidgetitem.setData(0, QtCore.Qt.UserRole,
|
||||
QtCore.QVariant(item[u'order']))
|
||||
treewidgetitem.setSelected(item[u'selected'])
|
||||
@ -966,11 +975,6 @@ class ServiceManager(QtGui.QWidget):
|
||||
text = frame[u'title'].replace(u'\n', u' ')
|
||||
child.setText(0, text[:40])
|
||||
child.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(count))
|
||||
if item[u'service_item'] \
|
||||
.is_capable(ItemCapabilities.AllowsVariableStartTime):
|
||||
tip = item[u'service_item'].get_media_time()
|
||||
if tip:
|
||||
child.setToolTip(0, tip)
|
||||
if serviceItem == itemcount:
|
||||
if item[u'expanded'] and serviceItemChild == count:
|
||||
self.serviceManagerList.setCurrentItem(child)
|
||||
@ -1257,7 +1261,14 @@ class ServiceManager(QtGui.QWidget):
|
||||
Handle of the event pint passed
|
||||
"""
|
||||
link = event.mimeData()
|
||||
if link.hasText():
|
||||
if event.mimeData().hasUrls():
|
||||
event.setDropAction(QtCore.Qt.CopyAction)
|
||||
event.accept()
|
||||
for url in event.mimeData().urls():
|
||||
filename = unicode(url.toLocalFile())
|
||||
if filename.endswith(u'.osz'):
|
||||
self.onLoadServiceClicked(filename)
|
||||
elif event.mimeData().hasText():
|
||||
plugin = unicode(event.mimeData().text())
|
||||
item = self.serviceManagerList.itemAt(event.pos())
|
||||
# ServiceManager started the drag and drop
|
||||
@ -1338,7 +1349,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
if not theme:
|
||||
theme = None
|
||||
item = self.findServiceItem()[0]
|
||||
self.serviceItems[item][u'service_item'].theme = theme
|
||||
self.serviceItems[item][u'service_item'].update_theme(theme)
|
||||
self.regenerateServiceItems()
|
||||
|
||||
def _getParentItemData(self, item):
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import translate
|
||||
from openlp.core.lib import translate, SpellTextEdit
|
||||
from openlp.core.lib.ui import create_accept_reject_button_box
|
||||
|
||||
class ServiceNoteForm(QtGui.QDialog):
|
||||
@ -49,8 +49,10 @@ class ServiceNoteForm(QtGui.QDialog):
|
||||
def setupUi(self):
|
||||
self.setObjectName(u'serviceNoteEdit')
|
||||
self.dialogLayout = QtGui.QVBoxLayout(self)
|
||||
self.dialogLayout.setContentsMargins(8, 8, 8, 8)
|
||||
self.dialogLayout.setSpacing(8)
|
||||
self.dialogLayout.setObjectName(u'verticalLayout')
|
||||
self.textEdit = QtGui.QTextEdit(self)
|
||||
self.textEdit = SpellTextEdit(self, False)
|
||||
self.textEdit.setObjectName(u'textEdit')
|
||||
self.dialogLayout.addWidget(self.textEdit)
|
||||
self.dialogLayout.addWidget(create_accept_reject_button_box(self))
|
||||
|
@ -29,7 +29,7 @@ The :mod:`settingsform` provides a user interface for the OpenLP settings
|
||||
"""
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.lib import Receiver, build_icon, PluginStatus
|
||||
from openlp.core.ui import AdvancedTab, GeneralTab, ThemesTab
|
||||
|
@ -123,7 +123,7 @@ class Ui_ShortcutListDialog(object):
|
||||
|
||||
def retranslateUi(self, shortcutListDialog):
|
||||
shortcutListDialog.setWindowTitle(
|
||||
translate('OpenLP.ShortcutListDialog', 'Customize Shortcuts'))
|
||||
translate('OpenLP.ShortcutListDialog', 'Configure Shortcuts'))
|
||||
self.descriptionLabel.setText(translate('OpenLP.ShortcutListDialog',
|
||||
'Select an action and click one of the buttons below to start '
|
||||
'capturing a new primary or alternate shortcut, respectively.'))
|
||||
|
@ -247,7 +247,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog):
|
||||
alternate_label_text = action.defaultShortcuts[1].toString()
|
||||
shortcuts = self._actionShortcuts(action)
|
||||
# We do not want to loose pending changes, that is why we have to
|
||||
# keep the text when, this function has not been triggered by a signal.
|
||||
# keep the text when, this function has not been triggered by a
|
||||
# signal.
|
||||
if item is None:
|
||||
primary_text = self.primaryPushButton.text()
|
||||
alternate_text = self.alternatePushButton.text()
|
||||
@ -280,7 +281,8 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog):
|
||||
"""
|
||||
Restores all default shortcuts.
|
||||
"""
|
||||
if self.buttonBox.buttonRole(button) != QtGui.QDialogButtonBox.ResetRole:
|
||||
if self.buttonBox.buttonRole(button) != \
|
||||
QtGui.QDialogButtonBox.ResetRole:
|
||||
return
|
||||
if QtGui.QMessageBox.question(self,
|
||||
translate('OpenLP.ShortcutListDialog', 'Restore Default Shortcuts'),
|
||||
|
@ -27,12 +27,14 @@
|
||||
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
import copy
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from PyQt4.phonon import Phonon
|
||||
|
||||
from openlp.core.lib import OpenLPToolbar, Receiver, resize_image, \
|
||||
ItemCapabilities, translate
|
||||
from openlp.core.lib import OpenLPToolbar, Receiver, ItemCapabilities, \
|
||||
translate, build_icon
|
||||
from openlp.core.lib.ui import UiStrings, shortcut_action
|
||||
from openlp.core.ui import HideMode, MainDisplay, ScreenList
|
||||
from openlp.core.utils.actions import ActionList, CategoryOrder
|
||||
@ -193,13 +195,11 @@ class SlideController(QtGui.QWidget):
|
||||
self.playSlidesLoop = shortcut_action(self.playSlidesMenu,
|
||||
u'playSlidesLoop', [], self.onPlaySlidesLoop,
|
||||
u':/media/media_time.png', False, UiStrings().LiveToolbar)
|
||||
self.playSlidesLoop.setText(
|
||||
translate('OpenLP.SlideController', 'Play Slides in Loop'))
|
||||
self.playSlidesLoop.setText(UiStrings().PlaySlidesInLoop)
|
||||
self.playSlidesOnce = shortcut_action(self.playSlidesMenu,
|
||||
u'playSlidesOnce', [], self.onPlaySlidesOnce,
|
||||
u':/media/media_time.png', False, UiStrings().LiveToolbar)
|
||||
self.playSlidesOnce.setText(
|
||||
translate('OpenLP.SlideController', 'Play Slides to End'))
|
||||
self.playSlidesOnce.setText(UiStrings().PlaySlidesToEnd)
|
||||
if QtCore.QSettings().value(self.parent().generalSettingsSection +
|
||||
u'/enable slide loop', QtCore.QVariant(True)).toBool():
|
||||
self.playSlidesMenu.setDefaultAction(self.playSlidesLoop)
|
||||
@ -412,9 +412,11 @@ class SlideController(QtGui.QWidget):
|
||||
self.display.videoStop()
|
||||
|
||||
def servicePrevious(self):
|
||||
time.sleep(0.1)
|
||||
Receiver.send_message('servicemanager_previous_item')
|
||||
|
||||
def serviceNext(self):
|
||||
time.sleep(0.1)
|
||||
Receiver.send_message('servicemanager_next_item')
|
||||
|
||||
def screenSizeChanged(self):
|
||||
@ -506,6 +508,11 @@ class SlideController(QtGui.QWidget):
|
||||
self.mediabar.setVisible(False)
|
||||
self.toolbar.makeWidgetsInvisible([u'Song Menu'])
|
||||
self.toolbar.makeWidgetsInvisible(self.loopList)
|
||||
# Reset the button
|
||||
self.playSlidesOnce.setChecked(False)
|
||||
self.playSlidesOnce.setIcon(build_icon(u':/media/media_time.png'))
|
||||
self.playSlidesLoop.setChecked(False)
|
||||
self.playSlidesLoop.setIcon(build_icon(u':/media/media_time.png'))
|
||||
if item.is_text():
|
||||
if QtCore.QSettings().value(
|
||||
self.parent().songsSettingsSection + u'/display songbar',
|
||||
@ -597,7 +604,8 @@ class SlideController(QtGui.QWidget):
|
||||
log.debug(u'processManagerItem live = %s' % self.isLive)
|
||||
self.onStopLoop()
|
||||
old_item = self.serviceItem
|
||||
self.serviceItem = serviceItem
|
||||
# take a copy not a link to the servicemeanager copy.
|
||||
self.serviceItem = copy.copy(serviceItem)
|
||||
if old_item and self.isLive and old_item.is_capable(
|
||||
ItemCapabilities.ProvidesOwnDisplay):
|
||||
self._resetBlank()
|
||||
@ -1056,6 +1064,14 @@ class SlideController(QtGui.QWidget):
|
||||
else:
|
||||
self.playSlidesLoop.setChecked(checked)
|
||||
log.debug(u'onPlaySlidesLoop %s' % checked)
|
||||
if checked:
|
||||
self.playSlidesLoop.setIcon(build_icon(u':/media/media_stop.png'))
|
||||
self.playSlidesLoop.setText(UiStrings().StopPlaySlidesInLoop)
|
||||
self.playSlidesOnce.setIcon(build_icon(u':/media/media_time.png'))
|
||||
self.playSlidesOnce.setText(UiStrings().PlaySlidesToEnd)
|
||||
else:
|
||||
self.playSlidesLoop.setIcon(build_icon(u':/media/media_time.png'))
|
||||
self.playSlidesLoop.setText(UiStrings().PlaySlidesInLoop)
|
||||
self.playSlidesMenu.setDefaultAction(self.playSlidesLoop)
|
||||
self.playSlidesOnce.setChecked(False)
|
||||
self.onToggleLoop()
|
||||
@ -1069,6 +1085,14 @@ class SlideController(QtGui.QWidget):
|
||||
else:
|
||||
self.playSlidesOnce.setChecked(checked)
|
||||
log.debug(u'onPlaySlidesOnce %s' % checked)
|
||||
if checked:
|
||||
self.playSlidesOnce.setIcon(build_icon(u':/media/media_stop.png'))
|
||||
self.playSlidesOnce.setText(UiStrings().StopPlaySlidesToEnd)
|
||||
self.playSlidesLoop.setIcon(build_icon(u':/media/media_time.png'))
|
||||
self.playSlidesLoop.setText(UiStrings().PlaySlidesInLoop)
|
||||
else:
|
||||
self.playSlidesOnce.setIcon(build_icon(u':/media/media_time'))
|
||||
self.playSlidesOnce.setText(UiStrings().PlaySlidesToEnd)
|
||||
self.playSlidesMenu.setDefaultAction(self.playSlidesOnce)
|
||||
self.playSlidesLoop.setChecked(False)
|
||||
self.onToggleLoop()
|
||||
|
@ -39,7 +39,8 @@ from openlp.core.lib import OpenLPToolbar, get_text_file_string, build_icon, \
|
||||
check_directory_exists
|
||||
from openlp.core.lib.theme import ThemeXML, BackgroundType, VerticalType, \
|
||||
BackgroundGradientType
|
||||
from openlp.core.lib.ui import UiStrings, critical_error_message_box
|
||||
from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
|
||||
context_menu_action, context_menu_separator
|
||||
from openlp.core.theme import Theme
|
||||
from openlp.core.ui import FileRenameForm, ThemeForm
|
||||
from openlp.core.utils import AppLocation, delete_file, file_is_unicode, \
|
||||
@ -104,25 +105,29 @@ class ThemeManager(QtGui.QWidget):
|
||||
self.contextMenu)
|
||||
# build the context menu
|
||||
self.menu = QtGui.QMenu()
|
||||
self.editAction = self.menu.addAction(
|
||||
translate('OpenLP.ThemeManager', '&Edit Theme'))
|
||||
self.editAction.setIcon(build_icon(u':/themes/theme_edit.png'))
|
||||
self.copyAction = self.menu.addAction(
|
||||
translate('OpenLP.ThemeManager', '&Copy Theme'))
|
||||
self.copyAction.setIcon(build_icon(u':/themes/theme_edit.png'))
|
||||
self.renameAction = self.menu.addAction(
|
||||
translate('OpenLP.ThemeManager', '&Rename Theme'))
|
||||
self.renameAction.setIcon(build_icon(u':/themes/theme_edit.png'))
|
||||
self.deleteAction = self.menu.addAction(
|
||||
translate('OpenLP.ThemeManager', '&Delete Theme'))
|
||||
self.deleteAction.setIcon(build_icon(u':/general/general_delete.png'))
|
||||
self.separator = self.menu.addSeparator()
|
||||
self.globalAction = self.menu.addAction(
|
||||
translate('OpenLP.ThemeManager', 'Set As &Global Default'))
|
||||
self.globalAction.setIcon(build_icon(u':/general/general_export.png'))
|
||||
self.exportAction = self.menu.addAction(
|
||||
translate('OpenLP.ThemeManager', '&Export Theme'))
|
||||
self.exportAction.setIcon(build_icon(u':/general/general_export.png'))
|
||||
self.editAction = context_menu_action(
|
||||
self.menu, u':/themes/theme_edit.png',
|
||||
translate('OpenLP.ThemeManager', '&Edit Theme'), self.onEditTheme)
|
||||
self.copyAction = context_menu_action(
|
||||
self.menu, u':/themes/theme_edit.png',
|
||||
translate('OpenLP.ThemeManager', '&Copy Theme'), self.onCopyTheme)
|
||||
self.renameAction = context_menu_action(
|
||||
self.menu, u':/themes/theme_edit.png',
|
||||
translate('OpenLP.ThemeManager', '&Rename Theme'),
|
||||
self.onRenameTheme)
|
||||
self.deleteAction = context_menu_action(
|
||||
self.menu, u':/general/general_delete.png',
|
||||
translate('OpenLP.ThemeManager', '&Delete Theme'),
|
||||
self.onDeleteTheme)
|
||||
context_menu_separator(self.menu)
|
||||
self.globalAction = context_menu_action(
|
||||
self.menu, u':/general/general_export.png',
|
||||
translate('OpenLP.ThemeManager', 'Set As &Global Default'),
|
||||
self.changeGlobalFromScreen)
|
||||
self.exportAction = context_menu_action(
|
||||
self.menu, u':/general/general_export.png',
|
||||
translate('OpenLP.ThemeManager', '&Export Theme'),
|
||||
self.onExportTheme)
|
||||
# Signals
|
||||
QtCore.QObject.connect(self.themeListWidget,
|
||||
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
|
||||
@ -198,19 +203,7 @@ class ThemeManager(QtGui.QWidget):
|
||||
self.deleteAction.setVisible(True)
|
||||
self.renameAction.setVisible(True)
|
||||
self.globalAction.setVisible(True)
|
||||
action = self.menu.exec_(self.themeListWidget.mapToGlobal(point))
|
||||
if action == self.editAction:
|
||||
self.onEditTheme()
|
||||
if action == self.copyAction:
|
||||
self.onCopyTheme()
|
||||
if action == self.renameAction:
|
||||
self.onRenameTheme()
|
||||
if action == self.deleteAction:
|
||||
self.onDeleteTheme()
|
||||
if action == self.globalAction:
|
||||
self.changeGlobalFromScreen()
|
||||
if action == self.exportAction:
|
||||
self.onExportTheme()
|
||||
self.menu.exec_(self.themeListWidget.mapToGlobal(point))
|
||||
|
||||
def changeGlobalFromTab(self, themeName):
|
||||
"""
|
||||
@ -298,11 +291,10 @@ class ThemeManager(QtGui.QWidget):
|
||||
Copies an existing theme to a new name
|
||||
"""
|
||||
item = self.themeListWidget.currentItem()
|
||||
oldThemeName = unicode(
|
||||
translate('OpenLP.ThemeManager', 'Copy of %s',
|
||||
'Copy of <theme name>')) % unicode(
|
||||
item.data(QtCore.Qt.UserRole).toString())
|
||||
self.fileRenameForm.fileNameEdit.setText(oldThemeName)
|
||||
oldThemeName = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||
self.fileRenameForm.fileNameEdit.setText(
|
||||
unicode(translate('OpenLP.ThemeManager',
|
||||
'Copy of %s','Copy of <theme name>')) % oldThemeName)
|
||||
if self.fileRenameForm.exec_(True):
|
||||
newThemeName = unicode(self.fileRenameForm.fileNameEdit.text())
|
||||
if self.checkIfThemeExists(newThemeName):
|
||||
|
@ -37,7 +37,7 @@ class ThemesTab(SettingsTab):
|
||||
"""
|
||||
def __init__(self, parent, mainwindow):
|
||||
self.mainwindow = mainwindow
|
||||
generalTranslated = translate('ThemeTab', 'Themes')
|
||||
generalTranslated = translate('OpenLP.ThemesTab', 'Themes')
|
||||
SettingsTab.__init__(self, parent, u'Themes', generalTranslated)
|
||||
self.icon_path = u':/themes/theme_new.png'
|
||||
|
||||
|
@ -53,6 +53,7 @@ APPLICATION_VERSION = {}
|
||||
IMAGES_FILTER = None
|
||||
UNO_CONNECTION_TYPE = u'pipe'
|
||||
#UNO_CONNECTION_TYPE = u'socket'
|
||||
VERSION_SPLITTER = re.compile(r'([0-9]+).([0-9]+).([0-9]+)(?:-bzr([0-9]+))?')
|
||||
|
||||
class VersionThread(QtCore.QThread):
|
||||
"""
|
||||
@ -61,8 +62,6 @@ class VersionThread(QtCore.QThread):
|
||||
"""
|
||||
def __init__(self, parent):
|
||||
QtCore.QThread.__init__(self, parent)
|
||||
self.version_splitter = re.compile(
|
||||
r'([0-9]+).([0-9]+).([0-9]+)(?:-bzr([0-9]+))?')
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
@ -73,7 +72,7 @@ class VersionThread(QtCore.QThread):
|
||||
version = check_latest_version(app_version)
|
||||
remote_version = {}
|
||||
local_version = {}
|
||||
match = self.version_splitter.match(version)
|
||||
match = VERSION_SPLITTER.match(version)
|
||||
if match:
|
||||
remote_version[u'major'] = int(match.group(1))
|
||||
remote_version[u'minor'] = int(match.group(2))
|
||||
@ -82,7 +81,7 @@ class VersionThread(QtCore.QThread):
|
||||
remote_version[u'revision'] = int(match.group(4))
|
||||
else:
|
||||
return
|
||||
match = self.version_splitter.match(app_version[u'full'])
|
||||
match = VERSION_SPLITTER.match(app_version[u'full'])
|
||||
if match:
|
||||
local_version[u'major'] = int(match.group(1))
|
||||
local_version[u'minor'] = int(match.group(2))
|
||||
@ -387,6 +386,17 @@ def split_filename(path):
|
||||
else:
|
||||
return os.path.split(path)
|
||||
|
||||
def clean_filename(filename):
|
||||
"""
|
||||
Removes invalid characters from the given ``filename``.
|
||||
|
||||
``filename``
|
||||
The "dirty" file name to clean.
|
||||
"""
|
||||
if not isinstance(filename, unicode):
|
||||
filename = unicode(filename, u'utf-8')
|
||||
return re.sub(r'[/\\?*|<>\[\]":<>+%]+', u'_', filename).strip(u'_')
|
||||
|
||||
def delete_file(file_path_name):
|
||||
"""
|
||||
Deletes a file from the system.
|
||||
@ -460,25 +470,6 @@ def file_is_unicode(filename):
|
||||
return None
|
||||
return ucsfile
|
||||
|
||||
def string_is_unicode(test_string):
|
||||
"""
|
||||
Makes sure a string is unicode.
|
||||
|
||||
``test_string``
|
||||
The string to confirm is unicode.
|
||||
"""
|
||||
return_string = u''
|
||||
if not test_string:
|
||||
return return_string
|
||||
if isinstance(test_string, unicode):
|
||||
return_string = test_string
|
||||
if not isinstance(test_string, unicode):
|
||||
try:
|
||||
return_string = unicode(test_string, u'utf-8')
|
||||
except UnicodeError:
|
||||
log.exception("Error encoding string to unicode")
|
||||
return return_string
|
||||
|
||||
def get_uno_command():
|
||||
"""
|
||||
Returns the UNO command to launch an openoffice.org instance.
|
||||
@ -511,5 +502,5 @@ from actions import ActionList
|
||||
|
||||
__all__ = [u'AppLocation', u'get_application_version', u'check_latest_version',
|
||||
u'add_actions', u'get_filesystem_encoding', u'LanguageManager',
|
||||
u'ActionList', u'get_web_page', u'file_is_unicode', u'string_is_unicode',
|
||||
u'get_uno_command', u'get_uno_instance', u'delete_file']
|
||||
u'ActionList', u'get_web_page', u'file_is_unicode', u'get_uno_command',
|
||||
u'get_uno_instance', u'delete_file', u'clean_filename']
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.lib import Plugin, StringContent, build_icon, translate
|
||||
from openlp.core.lib.db import Manager
|
||||
@ -43,7 +43,7 @@ class AlertsPlugin(Plugin):
|
||||
log.info(u'Alerts Plugin loaded')
|
||||
|
||||
def __init__(self, plugin_helpers):
|
||||
Plugin.__init__(self, u'Alerts', plugin_helpers,
|
||||
Plugin.__init__(self, u'alerts', plugin_helpers,
|
||||
settings_tab_class=AlertsTab)
|
||||
self.weight = -3
|
||||
self.icon_path = u':/plugins/plugin_alerts.png'
|
||||
@ -104,7 +104,7 @@ class AlertsPlugin(Plugin):
|
||||
def about(self):
|
||||
about_text = translate('AlertsPlugin', '<strong>Alerts Plugin</strong>'
|
||||
'<br />The alert plugin controls the displaying of nursery alerts '
|
||||
'on the display screen')
|
||||
'on the display screen.')
|
||||
return about_text
|
||||
|
||||
def setPluginTextStrings(self):
|
||||
|
@ -176,8 +176,8 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
||||
# We found '<>' in the alert text, but the ParameterEdit field is empty.
|
||||
if text.find(u'<>') != -1 and not self.parameterEdit.text() and \
|
||||
QtGui.QMessageBox.question(self,
|
||||
translate('AlertPlugin.AlertForm', 'No Parameter Found'),
|
||||
translate('AlertPlugin.AlertForm', 'You have not entered a '
|
||||
translate('AlertsPlugin.AlertForm', 'No Parameter Found'),
|
||||
translate('AlertsPlugin.AlertForm', 'You have not entered a '
|
||||
'parameter to be replaced.\nDo you want to continue anyway?'),
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
|
||||
QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
|
||||
@ -187,8 +187,8 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
||||
# in the alert text.
|
||||
elif text.find(u'<>') == -1 and self.parameterEdit.text() and \
|
||||
QtGui.QMessageBox.question(self,
|
||||
translate('AlertPlugin.AlertForm', 'No Placeholder Found'),
|
||||
translate('AlertPlugin.AlertForm', 'The alert text does not'
|
||||
translate('AlertsPlugin.AlertForm', 'No Placeholder Found'),
|
||||
translate('AlertsPlugin.AlertForm', 'The alert text does not'
|
||||
' contain \'<>\'.\nDo you want to continue anyway?'),
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
|
||||
QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
|
||||
|
@ -44,85 +44,85 @@ class AlertsTab(SettingsTab):
|
||||
self.fontGroupBox.setObjectName(u'fontGroupBox')
|
||||
self.fontLayout = QtGui.QFormLayout(self.fontGroupBox)
|
||||
self.fontLayout.setObjectName(u'fontLayout')
|
||||
self.FontLabel = QtGui.QLabel(self.fontGroupBox)
|
||||
self.FontLabel.setObjectName(u'FontLabel')
|
||||
self.FontComboBox = QtGui.QFontComboBox(self.fontGroupBox)
|
||||
self.FontComboBox.setObjectName(u'FontComboBox')
|
||||
self.fontLayout.addRow(self.FontLabel, self.FontComboBox)
|
||||
self.FontColorLabel = QtGui.QLabel(self.fontGroupBox)
|
||||
self.FontColorLabel.setObjectName(u'FontColorLabel')
|
||||
self.ColorLayout = QtGui.QHBoxLayout()
|
||||
self.ColorLayout.setObjectName(u'ColorLayout')
|
||||
self.FontColorButton = QtGui.QPushButton(self.fontGroupBox)
|
||||
self.FontColorButton.setObjectName(u'FontColorButton')
|
||||
self.ColorLayout.addWidget(self.FontColorButton)
|
||||
self.ColorLayout.addSpacing(20)
|
||||
self.BackgroundColorLabel = QtGui.QLabel(self.fontGroupBox)
|
||||
self.BackgroundColorLabel.setObjectName(u'BackgroundColorLabel')
|
||||
self.ColorLayout.addWidget(self.BackgroundColorLabel)
|
||||
self.BackgroundColorButton = QtGui.QPushButton(self.fontGroupBox)
|
||||
self.BackgroundColorButton.setObjectName(u'BackgroundColorButton')
|
||||
self.ColorLayout.addWidget(self.BackgroundColorButton)
|
||||
self.fontLayout.addRow(self.FontColorLabel, self.ColorLayout)
|
||||
self.FontSizeLabel = QtGui.QLabel(self.fontGroupBox)
|
||||
self.FontSizeLabel.setObjectName(u'FontSizeLabel')
|
||||
self.FontSizeSpinBox = QtGui.QSpinBox(self.fontGroupBox)
|
||||
self.FontSizeSpinBox.setObjectName(u'FontSizeSpinBox')
|
||||
self.fontLayout.addRow(self.FontSizeLabel, self.FontSizeSpinBox)
|
||||
self.TimeoutLabel = QtGui.QLabel(self.fontGroupBox)
|
||||
self.TimeoutLabel.setObjectName(u'TimeoutLabel')
|
||||
self.TimeoutSpinBox = QtGui.QSpinBox(self.fontGroupBox)
|
||||
self.TimeoutSpinBox.setMaximum(180)
|
||||
self.TimeoutSpinBox.setObjectName(u'TimeoutSpinBox')
|
||||
self.fontLayout.addRow(self.TimeoutLabel, self.TimeoutSpinBox)
|
||||
self.fontLabel = QtGui.QLabel(self.fontGroupBox)
|
||||
self.fontLabel.setObjectName(u'fontLabel')
|
||||
self.fontComboBox = QtGui.QFontComboBox(self.fontGroupBox)
|
||||
self.fontComboBox.setObjectName(u'fontComboBox')
|
||||
self.fontLayout.addRow(self.fontLabel, self.fontComboBox)
|
||||
self.fontColorLabel = QtGui.QLabel(self.fontGroupBox)
|
||||
self.fontColorLabel.setObjectName(u'fontColorLabel')
|
||||
self.colorLayout = QtGui.QHBoxLayout()
|
||||
self.colorLayout.setObjectName(u'colorLayout')
|
||||
self.fontColorButton = QtGui.QPushButton(self.fontGroupBox)
|
||||
self.fontColorButton.setObjectName(u'fontColorButton')
|
||||
self.colorLayout.addWidget(self.fontColorButton)
|
||||
self.colorLayout.addSpacing(20)
|
||||
self.backgroundColorLabel = QtGui.QLabel(self.fontGroupBox)
|
||||
self.backgroundColorLabel.setObjectName(u'backgroundColorLabel')
|
||||
self.colorLayout.addWidget(self.backgroundColorLabel)
|
||||
self.backgroundColorButton = QtGui.QPushButton(self.fontGroupBox)
|
||||
self.backgroundColorButton.setObjectName(u'backgroundColorButton')
|
||||
self.colorLayout.addWidget(self.backgroundColorButton)
|
||||
self.fontLayout.addRow(self.fontColorLabel, self.colorLayout)
|
||||
self.fontSizeLabel = QtGui.QLabel(self.fontGroupBox)
|
||||
self.fontSizeLabel.setObjectName(u'fontSizeLabel')
|
||||
self.fontSizeSpinBox = QtGui.QSpinBox(self.fontGroupBox)
|
||||
self.fontSizeSpinBox.setObjectName(u'fontSizeSpinBox')
|
||||
self.fontLayout.addRow(self.fontSizeLabel, self.fontSizeSpinBox)
|
||||
self.timeoutLabel = QtGui.QLabel(self.fontGroupBox)
|
||||
self.timeoutLabel.setObjectName(u'timeoutLabel')
|
||||
self.timeoutSpinBox = QtGui.QSpinBox(self.fontGroupBox)
|
||||
self.timeoutSpinBox.setMaximum(180)
|
||||
self.timeoutSpinBox.setObjectName(u'timeoutSpinBox')
|
||||
self.fontLayout.addRow(self.timeoutLabel, self.timeoutSpinBox)
|
||||
create_valign_combo(self, self.fontGroupBox, self.fontLayout)
|
||||
self.leftLayout.addWidget(self.fontGroupBox)
|
||||
self.leftLayout.addStretch()
|
||||
self.PreviewGroupBox = QtGui.QGroupBox(self.rightColumn)
|
||||
self.PreviewGroupBox.setObjectName(u'PreviewGroupBox')
|
||||
self.PreviewLayout = QtGui.QVBoxLayout(self.PreviewGroupBox)
|
||||
self.PreviewLayout.setObjectName(u'PreviewLayout')
|
||||
self.FontPreview = QtGui.QLineEdit(self.PreviewGroupBox)
|
||||
self.FontPreview.setObjectName(u'FontPreview')
|
||||
self.PreviewLayout.addWidget(self.FontPreview)
|
||||
self.rightLayout.addWidget(self.PreviewGroupBox)
|
||||
self.previewGroupBox = QtGui.QGroupBox(self.rightColumn)
|
||||
self.previewGroupBox.setObjectName(u'previewGroupBox')
|
||||
self.previewLayout = QtGui.QVBoxLayout(self.previewGroupBox)
|
||||
self.previewLayout.setObjectName(u'previewLayout')
|
||||
self.fontPreview = QtGui.QLineEdit(self.previewGroupBox)
|
||||
self.fontPreview.setObjectName(u'fontPreview')
|
||||
self.previewLayout.addWidget(self.fontPreview)
|
||||
self.rightLayout.addWidget(self.previewGroupBox)
|
||||
self.rightLayout.addStretch()
|
||||
# Signals and slots
|
||||
QtCore.QObject.connect(self.BackgroundColorButton,
|
||||
QtCore.QObject.connect(self.backgroundColorButton,
|
||||
QtCore.SIGNAL(u'pressed()'), self.onBackgroundColorButtonClicked)
|
||||
QtCore.QObject.connect(self.FontColorButton,
|
||||
QtCore.QObject.connect(self.fontColorButton,
|
||||
QtCore.SIGNAL(u'pressed()'), self.onFontColorButtonClicked)
|
||||
QtCore.QObject.connect(self.FontComboBox,
|
||||
QtCore.QObject.connect(self.fontComboBox,
|
||||
QtCore.SIGNAL(u'activated(int)'), self.onFontComboBoxClicked)
|
||||
QtCore.QObject.connect(self.TimeoutSpinBox,
|
||||
QtCore.QObject.connect(self.timeoutSpinBox,
|
||||
QtCore.SIGNAL(u'valueChanged(int)'), self.onTimeoutSpinBoxChanged)
|
||||
QtCore.QObject.connect(self.FontSizeSpinBox,
|
||||
QtCore.QObject.connect(self.fontSizeSpinBox,
|
||||
QtCore.SIGNAL(u'valueChanged(int)'), self.onFontSizeSpinBoxChanged)
|
||||
|
||||
def retranslateUi(self):
|
||||
self.fontGroupBox.setTitle(
|
||||
translate('AlertsPlugin.AlertsTab', 'Font'))
|
||||
self.FontLabel.setText(
|
||||
self.fontLabel.setText(
|
||||
translate('AlertsPlugin.AlertsTab', 'Font name:'))
|
||||
self.FontColorLabel.setText(
|
||||
self.fontColorLabel.setText(
|
||||
translate('AlertsPlugin.AlertsTab', 'Font color:'))
|
||||
self.BackgroundColorLabel.setText(
|
||||
self.backgroundColorLabel.setText(
|
||||
translate('AlertsPlugin.AlertsTab', 'Background color:'))
|
||||
self.FontSizeLabel.setText(
|
||||
self.fontSizeLabel.setText(
|
||||
translate('AlertsPlugin.AlertsTab', 'Font size:'))
|
||||
self.FontSizeSpinBox.setSuffix(UiStrings().FontSizePtUnit)
|
||||
self.TimeoutLabel.setText(
|
||||
self.fontSizeSpinBox.setSuffix(UiStrings().FontSizePtUnit)
|
||||
self.timeoutLabel.setText(
|
||||
translate('AlertsPlugin.AlertsTab', 'Alert timeout:'))
|
||||
self.TimeoutSpinBox.setSuffix(UiStrings().Seconds)
|
||||
self.PreviewGroupBox.setTitle(UiStrings().Preview)
|
||||
self.FontPreview.setText(UiStrings().OLPV2)
|
||||
self.timeoutSpinBox.setSuffix(UiStrings().Seconds)
|
||||
self.previewGroupBox.setTitle(UiStrings().Preview)
|
||||
self.fontPreview.setText(UiStrings().OLPV2)
|
||||
|
||||
def onBackgroundColorButtonClicked(self):
|
||||
new_color = QtGui.QColorDialog.getColor(
|
||||
QtGui.QColor(self.bg_color), self)
|
||||
if new_color.isValid():
|
||||
self.bg_color = new_color.name()
|
||||
self.BackgroundColorButton.setStyleSheet(
|
||||
self.backgroundColorButton.setStyleSheet(
|
||||
u'background-color: %s' % self.bg_color)
|
||||
self.updateDisplay()
|
||||
|
||||
@ -134,15 +134,15 @@ class AlertsTab(SettingsTab):
|
||||
QtGui.QColor(self.font_color), self)
|
||||
if new_color.isValid():
|
||||
self.font_color = new_color.name()
|
||||
self.FontColorButton.setStyleSheet(
|
||||
self.fontColorButton.setStyleSheet(
|
||||
u'background-color: %s' % self.font_color)
|
||||
self.updateDisplay()
|
||||
|
||||
def onTimeoutSpinBoxChanged(self):
|
||||
self.timeout = self.TimeoutSpinBox.value()
|
||||
self.timeout = self.timeoutSpinBox.value()
|
||||
|
||||
def onFontSizeSpinBoxChanged(self):
|
||||
self.font_size = self.FontSizeSpinBox.value()
|
||||
self.font_size = self.fontSizeSpinBox.value()
|
||||
self.updateDisplay()
|
||||
|
||||
def load(self):
|
||||
@ -160,16 +160,16 @@ class AlertsTab(SettingsTab):
|
||||
self.location = settings.value(
|
||||
u'location', QtCore.QVariant(1)).toInt()[0]
|
||||
settings.endGroup()
|
||||
self.FontSizeSpinBox.setValue(self.font_size)
|
||||
self.TimeoutSpinBox.setValue(self.timeout)
|
||||
self.FontColorButton.setStyleSheet(
|
||||
self.fontSizeSpinBox.setValue(self.font_size)
|
||||
self.timeoutSpinBox.setValue(self.timeout)
|
||||
self.fontColorButton.setStyleSheet(
|
||||
u'background-color: %s' % self.font_color)
|
||||
self.BackgroundColorButton.setStyleSheet(
|
||||
self.backgroundColorButton.setStyleSheet(
|
||||
u'background-color: %s' % self.bg_color)
|
||||
self.verticalComboBox.setCurrentIndex(self.location)
|
||||
font = QtGui.QFont()
|
||||
font.setFamily(self.font_face)
|
||||
self.FontComboBox.setCurrentFont(font)
|
||||
self.fontComboBox.setCurrentFont(font)
|
||||
self.updateDisplay()
|
||||
|
||||
def save(self):
|
||||
@ -178,7 +178,7 @@ class AlertsTab(SettingsTab):
|
||||
settings.setValue(u'background color', QtCore.QVariant(self.bg_color))
|
||||
settings.setValue(u'font color', QtCore.QVariant(self.font_color))
|
||||
settings.setValue(u'font size', QtCore.QVariant(self.font_size))
|
||||
self.font_face = self.FontComboBox.currentFont().family()
|
||||
self.font_face = self.fontComboBox.currentFont().family()
|
||||
settings.setValue(u'font face', QtCore.QVariant(self.font_face))
|
||||
settings.setValue(u'timeout', QtCore.QVariant(self.timeout))
|
||||
self.location = self.verticalComboBox.currentIndex()
|
||||
@ -187,10 +187,10 @@ class AlertsTab(SettingsTab):
|
||||
|
||||
def updateDisplay(self):
|
||||
font = QtGui.QFont()
|
||||
font.setFamily(self.FontComboBox.currentFont().family())
|
||||
font.setFamily(self.fontComboBox.currentFont().family())
|
||||
font.setBold(True)
|
||||
font.setPointSize(self.font_size)
|
||||
self.FontPreview.setFont(font)
|
||||
self.FontPreview.setStyleSheet(u'background-color: %s; color: %s' %
|
||||
self.fontPreview.setFont(font)
|
||||
self.fontPreview.setStyleSheet(u'background-color: %s; color: %s' %
|
||||
(self.bg_color, self.font_color))
|
||||
|
||||
|
@ -41,7 +41,7 @@ class BiblePlugin(Plugin):
|
||||
log.info(u'Bible Plugin loaded')
|
||||
|
||||
def __init__(self, plugin_helpers):
|
||||
Plugin.__init__(self, u'Bibles', plugin_helpers,
|
||||
Plugin.__init__(self, u'bibles', plugin_helpers,
|
||||
BibleMediaItem, BiblesTab)
|
||||
self.weight = -9
|
||||
self.icon_path = u':/plugins/plugin_bibles.png'
|
||||
@ -117,9 +117,9 @@ class BiblePlugin(Plugin):
|
||||
self.toolsUpgradeItem = QtGui.QAction(tools_menu)
|
||||
self.toolsUpgradeItem.setObjectName(u'toolsUpgradeItem')
|
||||
self.toolsUpgradeItem.setText(
|
||||
translate('BiblePlugin', '&Upgrade older Bibles'))
|
||||
translate('BiblesPlugin', '&Upgrade older Bibles'))
|
||||
self.toolsUpgradeItem.setStatusTip(
|
||||
translate('BiblePlugin', 'Upgrade the Bible databases to the '
|
||||
translate('BiblesPlugin', 'Upgrade the Bible databases to the '
|
||||
'latest format.'))
|
||||
tools_menu.addAction(self.toolsUpgradeItem)
|
||||
QtCore.QObject.connect(self.toolsUpgradeItem,
|
||||
|
@ -27,7 +27,6 @@
|
||||
"""
|
||||
The bible import functions for OpenLP
|
||||
"""
|
||||
import csv
|
||||
import logging
|
||||
import os
|
||||
import os.path
|
||||
@ -39,7 +38,7 @@ from openlp.core.lib import Receiver, translate
|
||||
from openlp.core.lib.db import delete_database
|
||||
from openlp.core.lib.ui import UiStrings, critical_error_message_box
|
||||
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
|
||||
from openlp.core.utils import AppLocation, string_is_unicode
|
||||
from openlp.core.utils import AppLocation
|
||||
from openlp.plugins.bibles.lib.manager import BibleFormat
|
||||
from openlp.plugins.bibles.lib.db import BiblesResourcesDB, clean_filename
|
||||
|
||||
|
@ -27,20 +27,19 @@
|
||||
The bible import functions for OpenLP
|
||||
"""
|
||||
import logging
|
||||
import os.path
|
||||
import re
|
||||
import os
|
||||
import shutil
|
||||
from tempfile import gettempdir
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import Receiver, SettingsManager, translate, \
|
||||
check_directory_exists
|
||||
from openlp.core.lib.db import delete_database
|
||||
from openlp.core.lib.ui import UiStrings, critical_error_message_box
|
||||
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
|
||||
from openlp.core.utils import AppLocation, delete_file
|
||||
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta, OldBibleDB,\
|
||||
BiblesResourcesDB, clean_filename
|
||||
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta, OldBibleDB, \
|
||||
BiblesResourcesDB
|
||||
from openlp.plugins.bibles.lib.http import BSExtract, BGExtract, CWExtract
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -70,8 +69,8 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
self.mediaItem = bibleplugin.mediaItem
|
||||
self.suffix = u'.sqlite'
|
||||
self.settingsSection = u'bibles'
|
||||
self.path = AppLocation.get_section_data_path(
|
||||
self.settingsSection)
|
||||
self.path = AppLocation.get_section_data_path(self.settingsSection)
|
||||
self.temp_dir = os.path.join(gettempdir(), u'openlp')
|
||||
self.files = self.manager.old_bible_databases
|
||||
self.success = {}
|
||||
self.newbibles = {}
|
||||
@ -93,20 +92,6 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
log.debug(u'Stopping import')
|
||||
self.stop_import_flag = True
|
||||
|
||||
def onCheckBoxIndexChanged(self, index):
|
||||
"""
|
||||
Show/Hide warnings if CheckBox state has changed
|
||||
"""
|
||||
for number, filename in enumerate(self.files):
|
||||
if not self.checkBox[number].checkState() == QtCore.Qt.Checked:
|
||||
self.verticalWidget[number].hide()
|
||||
self.formWidget[number].hide()
|
||||
else:
|
||||
version_name = unicode(self.versionNameEdit[number].text())
|
||||
if self.manager.exists(version_name):
|
||||
self.verticalWidget[number].show()
|
||||
self.formWidget[number].show()
|
||||
|
||||
def reject(self):
|
||||
"""
|
||||
Stop the wizard on cancel button, close button or ESC key.
|
||||
@ -115,8 +100,6 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
self.stop_import_flag = True
|
||||
if not self.currentPage() == self.progressPage:
|
||||
self.done(QtGui.QDialog.Rejected)
|
||||
else:
|
||||
self.postWizard()
|
||||
|
||||
def onCurrentIdChanged(self, pageId):
|
||||
"""
|
||||
@ -126,7 +109,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
self.preWizard()
|
||||
self.performWizard()
|
||||
self.postWizard()
|
||||
elif self.page(pageId) == self.selectPage and self.maxBibles == 0:
|
||||
elif self.page(pageId) == self.selectPage and not self.files:
|
||||
self.next()
|
||||
|
||||
def onBackupBrowseButtonClicked(self):
|
||||
@ -245,78 +228,13 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
Add the content to the scrollArea.
|
||||
"""
|
||||
self.checkBox = {}
|
||||
self.versionNameEdit = {}
|
||||
self.versionNameLabel = {}
|
||||
self.versionInfoLabel = {}
|
||||
self.versionInfoPixmap = {}
|
||||
self.verticalWidget = {}
|
||||
self.horizontalLayout = {}
|
||||
self.formWidget = {}
|
||||
self.formLayoutAttention = {}
|
||||
for number, filename in enumerate(self.files):
|
||||
bible = OldBibleDB(self.mediaItem, path=self.path, file=filename[0])
|
||||
self.checkBox[number] = QtGui.QCheckBox(self.scrollAreaContents)
|
||||
checkBoxName = u'checkBox[%d]' % number
|
||||
self.checkBox[number].setObjectName(checkBoxName)
|
||||
self.checkBox[number].setObjectName(u'checkBox[%d]' % number)
|
||||
self.checkBox[number].setText(bible.get_name())
|
||||
self.checkBox[number].setCheckState(QtCore.Qt.Checked)
|
||||
self.formLayout.addWidget(self.checkBox[number])
|
||||
self.verticalWidget[number] = QtGui.QWidget(self.scrollAreaContents)
|
||||
verticalWidgetName = u'verticalWidget[%d]' % number
|
||||
self.verticalWidget[number].setObjectName(verticalWidgetName)
|
||||
self.horizontalLayout[number] = QtGui.QHBoxLayout(
|
||||
self.verticalWidget[number])
|
||||
self.horizontalLayout[number].setContentsMargins(25, 0, 0, 0)
|
||||
horizontalLayoutName = u'horizontalLayout[%d]' % number
|
||||
self.horizontalLayout[number].setObjectName(horizontalLayoutName)
|
||||
self.versionInfoPixmap[number] = QtGui.QLabel(
|
||||
self.verticalWidget[number])
|
||||
versionInfoPixmapName = u'versionInfoPixmap[%d]' % number
|
||||
self.versionInfoPixmap[number].setObjectName(versionInfoPixmapName)
|
||||
self.versionInfoPixmap[number].setPixmap(QtGui.QPixmap(
|
||||
u':/bibles/bibles_upgrade_alert.png'))
|
||||
self.versionInfoPixmap[number].setAlignment(QtCore.Qt.AlignRight)
|
||||
self.horizontalLayout[number].addWidget(
|
||||
self.versionInfoPixmap[number])
|
||||
self.versionInfoLabel[number] = QtGui.QLabel(
|
||||
self.verticalWidget[number])
|
||||
versionInfoLabelName = u'versionInfoLabel[%d]' % number
|
||||
self.versionInfoLabel[number].setObjectName(versionInfoLabelName)
|
||||
sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding,
|
||||
QtGui.QSizePolicy.Preferred)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.versionInfoLabel[number].sizePolicy().hasHeightForWidth())
|
||||
self.versionInfoLabel[number].setSizePolicy(sizePolicy)
|
||||
self.horizontalLayout[number].addWidget(
|
||||
self.versionInfoLabel[number])
|
||||
self.formLayout.addWidget(self.verticalWidget[number])
|
||||
self.formWidget[number] = QtGui.QWidget(self.scrollAreaContents)
|
||||
formWidgetName = u'formWidget[%d]' % number
|
||||
self.formWidget[number].setObjectName(formWidgetName)
|
||||
self.formLayoutAttention[number] = QtGui.QFormLayout(
|
||||
self.formWidget[number])
|
||||
self.formLayoutAttention[number].setContentsMargins(25, 0, 0, 5)
|
||||
formLayoutAttentionName = u'formLayoutAttention[%d]' % number
|
||||
self.formLayoutAttention[number].setObjectName(
|
||||
formLayoutAttentionName)
|
||||
self.versionNameLabel[number] = QtGui.QLabel(
|
||||
self.formWidget[number])
|
||||
self.versionNameLabel[number].setObjectName(u'VersionNameLabel')
|
||||
self.formLayoutAttention[number].setWidget(0,
|
||||
QtGui.QFormLayout.LabelRole, self.versionNameLabel[number])
|
||||
self.versionNameEdit[number] = QtGui.QLineEdit(
|
||||
self.formWidget[number])
|
||||
self.versionNameEdit[number].setObjectName(u'VersionNameEdit')
|
||||
self.formLayoutAttention[number].setWidget(0,
|
||||
QtGui.QFormLayout.FieldRole, self.versionNameEdit[number])
|
||||
self.versionNameEdit[number].setText(bible.get_name())
|
||||
self.formLayout.addWidget(self.formWidget[number])
|
||||
# Set up the Signal for the checkbox.
|
||||
QtCore.QObject.connect(self.checkBox[number],
|
||||
QtCore.SIGNAL(u'stateChanged(int)'),
|
||||
self.onCheckBoxIndexChanged)
|
||||
self.spacerItem = QtGui.QSpacerItem(20, 5, QtGui.QSizePolicy.Minimum,
|
||||
QtGui.QSizePolicy.Expanding)
|
||||
self.formLayout.addItem(self.spacerItem)
|
||||
@ -329,23 +247,6 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
for number, filename in enumerate(self.files):
|
||||
self.formLayout.removeWidget(self.checkBox[number])
|
||||
self.checkBox[number].setParent(None)
|
||||
self.horizontalLayout[number].removeWidget(
|
||||
self.versionInfoPixmap[number])
|
||||
self.versionInfoPixmap[number].setParent(None)
|
||||
self.horizontalLayout[number].removeWidget(
|
||||
self.versionInfoLabel[number])
|
||||
self.versionInfoLabel[number].setParent(None)
|
||||
self.formLayout.removeWidget(self.verticalWidget[number])
|
||||
self.verticalWidget[number].setParent(None)
|
||||
self.formLayoutAttention[number].removeWidget(
|
||||
self.versionNameLabel[number])
|
||||
self.versionNameLabel[number].setParent(None)
|
||||
self.formLayoutAttention[number].removeWidget(
|
||||
self.versionNameEdit[number])
|
||||
self.formLayoutAttention[number].deleteLater()
|
||||
self.versionNameEdit[number].setParent(None)
|
||||
self.formLayout.removeWidget(self.formWidget[number])
|
||||
self.formWidget[number].setParent(None)
|
||||
self.formLayout.removeItem(self.spacerItem)
|
||||
|
||||
def retranslateUi(self):
|
||||
@ -387,12 +288,6 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
self.selectPage.setSubTitle(
|
||||
translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'Please select the Bibles to upgrade'))
|
||||
for number, bible in enumerate(self.files):
|
||||
self.versionNameLabel[number].setText(
|
||||
translate('BiblesPlugin.UpgradeWizardForm', 'Version name:'))
|
||||
self.versionInfoLabel[number].setText(
|
||||
translate('BiblesPlugin.UpgradeWizardForm', 'This '
|
||||
'Bible still exists. Please change the name or uncheck it.'))
|
||||
self.progressPage.setTitle(translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading'))
|
||||
self.progressPage.setSubTitle(
|
||||
@ -413,7 +308,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
if not backup_path:
|
||||
critical_error_message_box(UiStrings().EmptyField,
|
||||
translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'You need to specify a Backup Directory for your '
|
||||
'You need to specify a backup directory for your '
|
||||
'Bibles.'))
|
||||
self.backupDirectoryEdit.setFocus()
|
||||
return False
|
||||
@ -427,58 +322,16 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
return False
|
||||
return True
|
||||
elif self.currentPage() == self.selectPage:
|
||||
check_directory_exists(self.temp_dir)
|
||||
for number, filename in enumerate(self.files):
|
||||
if not self.checkBox[number].checkState() == QtCore.Qt.Checked:
|
||||
continue
|
||||
version_name = unicode(self.versionNameEdit[number].text())
|
||||
if not version_name:
|
||||
critical_error_message_box(UiStrings().EmptyField,
|
||||
translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'You need to specify a version name for your Bible.'))
|
||||
self.versionNameEdit[number].setFocus()
|
||||
return False
|
||||
elif self.manager.exists(version_name):
|
||||
critical_error_message_box(
|
||||
translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'Bible Exists'),
|
||||
translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'This Bible already exists. Please upgrade '
|
||||
'a different Bible, delete the existing one or '
|
||||
'uncheck.'))
|
||||
self.versionNameEdit[number].setFocus()
|
||||
return False
|
||||
elif os.path.exists(os.path.join(self.path, clean_filename(
|
||||
version_name))) and version_name == filename[1]:
|
||||
newfilename = u'old_database_%s' % filename[0]
|
||||
if not os.path.exists(os.path.join(self.path,
|
||||
newfilename)):
|
||||
os.rename(os.path.join(self.path, filename[0]),
|
||||
os.path.join(self.path, newfilename))
|
||||
self.files[number] = [newfilename, filename[1]]
|
||||
continue
|
||||
else:
|
||||
critical_error_message_box(
|
||||
translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'Bible Exists'),
|
||||
translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'This Bible already exists. Please upgrade '
|
||||
'a different Bible, delete the existing one or '
|
||||
'uncheck.'))
|
||||
self.verticalWidget[number].show()
|
||||
self.formWidget[number].show()
|
||||
self.versionNameEdit[number].setFocus()
|
||||
return False
|
||||
elif os.path.exists(os.path.join(self.path,
|
||||
clean_filename(version_name))):
|
||||
critical_error_message_box(
|
||||
translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'Bible Exists'),
|
||||
translate('BiblesPlugin.UpgradeWizardForm',
|
||||
'This Bible already exists. Please upgrade '
|
||||
'a different Bible, delete the existing one or '
|
||||
'uncheck.'))
|
||||
self.versionNameEdit[number].setFocus()
|
||||
return False
|
||||
# Move bibles to temp dir.
|
||||
if not os.path.exists(os.path.join(self.temp_dir, filename[0])):
|
||||
shutil.move(
|
||||
os.path.join(self.path, filename[0]), self.temp_dir)
|
||||
else:
|
||||
delete_file(os.path.join(self.path, filename[0]))
|
||||
return True
|
||||
if self.currentPage() == self.progressPage:
|
||||
return True
|
||||
@ -497,16 +350,8 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
self.files = self.manager.old_bible_databases
|
||||
self.addScrollArea()
|
||||
self.retranslateUi()
|
||||
self.maxBibles = len(self.files)
|
||||
for number, filename in enumerate(self.files):
|
||||
self.checkBox[number].setCheckState(QtCore.Qt.Checked)
|
||||
oldname = filename[1]
|
||||
if self.manager.exists(oldname):
|
||||
self.verticalWidget[number].show()
|
||||
self.formWidget[number].show()
|
||||
else:
|
||||
self.verticalWidget[number].hide()
|
||||
self.formWidget[number].hide()
|
||||
self.progressBar.show()
|
||||
self.restart()
|
||||
self.finishButton.setVisible(False)
|
||||
@ -518,9 +363,8 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
Prepare the UI for the upgrade.
|
||||
"""
|
||||
OpenLPWizard.preWizard(self)
|
||||
self.progressLabel.setText(translate(
|
||||
'BiblesPlugin.UpgradeWizardForm',
|
||||
'Starting Bible upgrade...'))
|
||||
self.progressLabel.setText(
|
||||
translate('BiblesPlugin.UpgradeWizardForm', 'Starting upgrade...'))
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
|
||||
def performWizard(self):
|
||||
@ -529,48 +373,42 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
"""
|
||||
self.include_webbible = False
|
||||
proxy_server = None
|
||||
if self.maxBibles == 0:
|
||||
if not self.files:
|
||||
self.progressLabel.setText(
|
||||
translate('BiblesPlugin.UpgradeWizardForm', 'There are no '
|
||||
'Bibles available to upgrade.'))
|
||||
'Bibles that need to be upgraded.'))
|
||||
self.progressBar.hide()
|
||||
return
|
||||
self.maxBibles = 0
|
||||
max_bibles = 0
|
||||
for number, file in enumerate(self.files):
|
||||
if self.checkBox[number].checkState() == QtCore.Qt.Checked:
|
||||
self.maxBibles += 1
|
||||
number = 0
|
||||
for biblenumber, filename in enumerate(self.files):
|
||||
max_bibles += 1
|
||||
oldBible = None
|
||||
for number, filename in enumerate(self.files):
|
||||
# Close the previous bible's connection.
|
||||
if oldBible is not None:
|
||||
oldBible.close_connection()
|
||||
# Set to None to make obvious that we have already closed the
|
||||
# database.
|
||||
oldBible = None
|
||||
if self.stop_import_flag:
|
||||
bible_failed = True
|
||||
self.success[number] = False
|
||||
break
|
||||
bible_failed = False
|
||||
self.success[biblenumber] = False
|
||||
if not self.checkBox[biblenumber].checkState() == QtCore.Qt.Checked:
|
||||
if not self.checkBox[number].checkState() == QtCore.Qt.Checked:
|
||||
self.success[number] = False
|
||||
continue
|
||||
self.progressBar.reset()
|
||||
oldbible = OldBibleDB(self.mediaItem, path=self.path,
|
||||
oldBible = OldBibleDB(self.mediaItem, path=self.temp_dir,
|
||||
file=filename[0])
|
||||
name = filename[1]
|
||||
if name is None:
|
||||
delete_file(os.path.join(self.path, filename[0]))
|
||||
self.incrementProgressBar(unicode(translate(
|
||||
'BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\nFailed')) %
|
||||
(number + 1, self.maxBibles, name),
|
||||
self.progressBar.maximum() - self.progressBar.value())
|
||||
number += 1
|
||||
continue
|
||||
self.progressLabel.setText(unicode(translate(
|
||||
'BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\nUpgrading ...')) %
|
||||
(number + 1, self.maxBibles, name))
|
||||
if os.path.exists(os.path.join(self.path, filename[0])):
|
||||
name = unicode(self.versionNameEdit[biblenumber].text())
|
||||
(number + 1, max_bibles, name))
|
||||
self.newbibles[number] = BibleDB(self.mediaItem, path=self.path,
|
||||
name=name)
|
||||
name=name, file=filename[0])
|
||||
self.newbibles[number].register(self.plugin.upgrade_wizard)
|
||||
metadata = oldbible.get_metadata()
|
||||
metadata = oldBible.get_metadata()
|
||||
webbible = False
|
||||
meta_data = {}
|
||||
for meta in metadata:
|
||||
@ -597,7 +435,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
u'name: "%s" failed' % (
|
||||
meta_data[u'download source'],
|
||||
meta_data[u'download name']))
|
||||
delete_database(self.path, clean_filename(name))
|
||||
self.newbibles[number].session.close()
|
||||
del self.newbibles[number]
|
||||
critical_error_message_box(
|
||||
translate('BiblesPlugin.UpgradeWizardForm',
|
||||
@ -608,9 +446,9 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
self.incrementProgressBar(unicode(translate(
|
||||
'BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\nFailed')) %
|
||||
(number + 1, self.maxBibles, name),
|
||||
(number + 1, max_bibles, name),
|
||||
self.progressBar.maximum() - self.progressBar.value())
|
||||
number += 1
|
||||
self.success[number] = False
|
||||
continue
|
||||
bible = BiblesResourcesDB.get_webbible(
|
||||
meta_data[u'download name'],
|
||||
@ -623,25 +461,25 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
language_id = self.newbibles[number].get_language(name)
|
||||
if not language_id:
|
||||
log.warn(u'Upgrading from "%s" failed' % filename[0])
|
||||
delete_database(self.path, clean_filename(name))
|
||||
self.newbibles[number].session.close()
|
||||
del self.newbibles[number]
|
||||
self.incrementProgressBar(unicode(translate(
|
||||
'BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\nFailed')) %
|
||||
(number + 1, self.maxBibles, name),
|
||||
(number + 1, max_bibles, name),
|
||||
self.progressBar.maximum() - self.progressBar.value())
|
||||
number += 1
|
||||
self.success[number] = False
|
||||
continue
|
||||
self.progressBar.setMaximum(len(books))
|
||||
for book in books:
|
||||
if self.stop_import_flag:
|
||||
bible_failed = True
|
||||
self.success[number] = False
|
||||
break
|
||||
self.incrementProgressBar(unicode(translate(
|
||||
'BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\n'
|
||||
'Upgrading %s ...')) %
|
||||
(number + 1, self.maxBibles, name, book))
|
||||
(number + 1, max_bibles, name, book))
|
||||
book_ref_id = self.newbibles[number].\
|
||||
get_book_ref_id_by_name(book, len(books), language_id)
|
||||
if not book_ref_id:
|
||||
@ -649,24 +487,24 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
u'name: "%s" aborted by user' % (
|
||||
meta_data[u'download source'],
|
||||
meta_data[u'download name']))
|
||||
delete_database(self.path, clean_filename(name))
|
||||
self.newbibles[number].session.close()
|
||||
del self.newbibles[number]
|
||||
bible_failed = True
|
||||
self.success[number] = False
|
||||
break
|
||||
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
|
||||
db_book = self.newbibles[number].create_book(book,
|
||||
book_ref_id, book_details[u'testament_id'])
|
||||
# Try to import still downloaded verses
|
||||
oldbook = oldbible.get_book(book)
|
||||
# Try to import already downloaded verses.
|
||||
oldbook = oldBible.get_book(book)
|
||||
if oldbook:
|
||||
verses = oldbible.get_verses(oldbook[u'id'])
|
||||
verses = oldBible.get_verses(oldbook[u'id'])
|
||||
if not verses:
|
||||
log.warn(u'No verses found to import for book '
|
||||
u'"%s"', book)
|
||||
continue
|
||||
for verse in verses:
|
||||
if self.stop_import_flag:
|
||||
bible_failed = True
|
||||
self.success[number] = False
|
||||
break
|
||||
self.newbibles[number].create_verse(db_book.id,
|
||||
int(verse[u'chapter']),
|
||||
@ -680,40 +518,40 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
language_id = self.newbibles[number].get_language(name)
|
||||
if not language_id:
|
||||
log.warn(u'Upgrading books from "%s" failed' % name)
|
||||
delete_database(self.path, clean_filename(name))
|
||||
self.newbibles[number].session.close()
|
||||
del self.newbibles[number]
|
||||
self.incrementProgressBar(unicode(translate(
|
||||
'BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\nFailed')) %
|
||||
(number + 1, self.maxBibles, name),
|
||||
(number + 1, max_bibles, name),
|
||||
self.progressBar.maximum() - self.progressBar.value())
|
||||
number += 1
|
||||
self.success[number] = False
|
||||
continue
|
||||
books = oldbible.get_books()
|
||||
books = oldBible.get_books()
|
||||
self.progressBar.setMaximum(len(books))
|
||||
for book in books:
|
||||
if self.stop_import_flag:
|
||||
bible_failed = True
|
||||
self.success[number] = False
|
||||
break
|
||||
self.incrementProgressBar(unicode(translate(
|
||||
'BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\n'
|
||||
'Upgrading %s ...')) %
|
||||
(number + 1, self.maxBibles, name, book[u'name']))
|
||||
(number + 1, max_bibles, name, book[u'name']))
|
||||
book_ref_id = self.newbibles[number].\
|
||||
get_book_ref_id_by_name(book[u'name'], len(books),
|
||||
language_id)
|
||||
if not book_ref_id:
|
||||
log.warn(u'Upgrading books from %s " '\
|
||||
'failed - aborted by user' % name)
|
||||
delete_database(self.path, clean_filename(name))
|
||||
self.newbibles[number].session.close()
|
||||
del self.newbibles[number]
|
||||
bible_failed = True
|
||||
self.success[number] = False
|
||||
break
|
||||
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
|
||||
db_book = self.newbibles[number].create_book(book[u'name'],
|
||||
book_ref_id, book_details[u'testament_id'])
|
||||
verses = oldbible.get_verses(book[u'id'])
|
||||
verses = oldBible.get_verses(book[u'id'])
|
||||
if not verses:
|
||||
log.warn(u'No verses found to import for book '
|
||||
u'"%s"', book[u'name'])
|
||||
@ -721,31 +559,32 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
continue
|
||||
for verse in verses:
|
||||
if self.stop_import_flag:
|
||||
bible_failed = True
|
||||
self.success[number] = False
|
||||
break
|
||||
self.newbibles[number].create_verse(db_book.id,
|
||||
int(verse[u'chapter']),
|
||||
int(verse[u'verse']), unicode(verse[u'text']))
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
self.newbibles[number].session.commit()
|
||||
if not bible_failed:
|
||||
if self.success.has_key(number) and not self.success[number]:
|
||||
self.incrementProgressBar(unicode(translate(
|
||||
'BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\nFailed')) %
|
||||
(number + 1, max_bibles, name),
|
||||
self.progressBar.maximum() - self.progressBar.value())
|
||||
else:
|
||||
self.success[number] = True
|
||||
self.newbibles[number].create_meta(u'Version', name)
|
||||
oldbible.close_connection()
|
||||
delete_file(os.path.join(self.path, filename[0]))
|
||||
self.incrementProgressBar(unicode(translate(
|
||||
'BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\n'
|
||||
'Complete')) %
|
||||
(number + 1, self.maxBibles, name))
|
||||
self.success[biblenumber] = True
|
||||
else:
|
||||
self.incrementProgressBar(unicode(translate(
|
||||
'BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\nFailed')) %
|
||||
(number + 1, self.maxBibles, name),
|
||||
self.progressBar.maximum() - self.progressBar.value())
|
||||
delete_database(self.path, clean_filename(name))
|
||||
number += 1
|
||||
(number + 1, max_bibles, name))
|
||||
if self.newbibles.has_key(number):
|
||||
self.newbibles[number].session.close()
|
||||
# Close the last bible's connection if possible.
|
||||
if oldBible is not None:
|
||||
oldBible.close_connection()
|
||||
|
||||
def postWizard(self):
|
||||
"""
|
||||
@ -754,10 +593,14 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
successful_import = 0
|
||||
failed_import = 0
|
||||
for number, filename in enumerate(self.files):
|
||||
if number in self.success and self.success[number] == True:
|
||||
if self.success.has_key(number) and self.success[number]:
|
||||
successful_import += 1
|
||||
elif self.checkBox[number].checkState() == QtCore.Qt.Checked:
|
||||
failed_import += 1
|
||||
# Delete upgraded (but not complete, corrupted, ...) bible.
|
||||
delete_file(os.path.join(self.path, filename[0]))
|
||||
# Copy not upgraded bible back.
|
||||
shutil.move(os.path.join(self.temp_dir, filename[0]), self.path)
|
||||
if failed_import > 0:
|
||||
failed_import_text = unicode(translate(
|
||||
'BiblesPlugin.UpgradeWizardForm',
|
||||
@ -778,7 +621,8 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
'Bible(s): %s successful%s')) % (successful_import,
|
||||
failed_import_text))
|
||||
else:
|
||||
self.progressLabel.setText(
|
||||
translate('BiblesPlugin.UpgradeWizardForm', 'Upgrade '
|
||||
'failed.'))
|
||||
self.progressLabel.setText(translate(
|
||||
'BiblesPlugin.UpgradeWizardForm', 'Upgrade failed.'))
|
||||
# Remove temp directory.
|
||||
shutil.rmtree(self.temp_dir, True)
|
||||
OpenLPWizard.postWizard(self)
|
||||
|
@ -44,8 +44,8 @@ class LanguageForm(QDialog, Ui_LanguageDialog):
|
||||
Class to manage a dialog which ask the user for a language.
|
||||
"""
|
||||
log.info(u'LanguageForm loaded')
|
||||
|
||||
def __init__(self, parent = None):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
"""
|
||||
Constructor
|
||||
"""
|
||||
@ -57,12 +57,11 @@ class LanguageForm(QDialog, Ui_LanguageDialog):
|
||||
if bible_name:
|
||||
self.bibleLabel.setText(unicode(bible_name))
|
||||
items = BiblesResourcesDB.get_languages()
|
||||
for item in items:
|
||||
self.languageComboBox.addItem(item[u'name'])
|
||||
self.languageComboBox.addItems([item[u'name'] for item in items])
|
||||
return QDialog.exec_(self)
|
||||
|
||||
|
||||
def accept(self):
|
||||
if self.languageComboBox.currentText() == u'':
|
||||
if not self.languageComboBox.currentText():
|
||||
critical_error_message_box(
|
||||
message=translate('BiblesPlugin.LanguageForm',
|
||||
'You need to choose a language.'))
|
||||
|
@ -115,7 +115,8 @@ class CSVBible(BibleDB):
|
||||
if self.stop_import_flag:
|
||||
break
|
||||
self.wizard.incrementProgressBar(unicode(
|
||||
translate('BibleDB.Wizard', 'Importing books... %s')) %
|
||||
translate('BiblesPlugin.CSVBible',
|
||||
'Importing books... %s')) %
|
||||
unicode(line[2], details['encoding']))
|
||||
book_ref_id = self.get_book_ref_id_by_name(
|
||||
unicode(line[2], details['encoding']), 67, language_id)
|
||||
@ -155,7 +156,7 @@ class CSVBible(BibleDB):
|
||||
book = self.get_book(line_book)
|
||||
book_ptr = book.name
|
||||
self.wizard.incrementProgressBar(unicode(translate(
|
||||
'BibleDB.Wizard', 'Importing verses from %s...',
|
||||
'BiblesPlugin.CSVBible', 'Importing verses from %s...',
|
||||
'Importing verses from <book name>...')) % book.name)
|
||||
self.session.commit()
|
||||
try:
|
||||
@ -163,7 +164,7 @@ class CSVBible(BibleDB):
|
||||
except UnicodeError:
|
||||
verse_text = unicode(line[3], u'cp1252')
|
||||
self.create_verse(book.id, line[1], line[2], verse_text)
|
||||
self.wizard.incrementProgressBar(translate('BibleDB.Wizard',
|
||||
self.wizard.incrementProgressBar(translate('BiblesPlugin.CSVBible',
|
||||
'Importing verses... done.'))
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
self.session.commit()
|
||||
|
@ -36,10 +36,10 @@ from sqlalchemy import Column, ForeignKey, or_, Table, types
|
||||
from sqlalchemy.orm import class_mapper, mapper, relation
|
||||
from sqlalchemy.orm.exc import UnmappedClassError
|
||||
|
||||
from openlp.core.lib import Receiver, translate, check_directory_exists
|
||||
from openlp.core.lib import Receiver, translate
|
||||
from openlp.core.lib.db import BaseModel, init_db, Manager
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.utils import AppLocation
|
||||
from openlp.core.utils import AppLocation, clean_filename
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -63,19 +63,6 @@ class Verse(BaseModel):
|
||||
"""
|
||||
pass
|
||||
|
||||
def clean_filename(filename):
|
||||
"""
|
||||
Clean up the version name of the Bible and convert it into a valid
|
||||
file name.
|
||||
|
||||
``filename``
|
||||
The "dirty" file name or version name.
|
||||
"""
|
||||
if not isinstance(filename, unicode):
|
||||
filename = unicode(filename, u'utf-8')
|
||||
filename = re.sub(r'[^\w]+', u'_', filename).strip(u'_')
|
||||
return filename + u'.sqlite'
|
||||
|
||||
def init_schema(url):
|
||||
"""
|
||||
Setup a bible database connection and initialise the database schema.
|
||||
@ -158,7 +145,7 @@ class BibleDB(QtCore.QObject, Manager):
|
||||
self.name = kwargs[u'name']
|
||||
if not isinstance(self.name, unicode):
|
||||
self.name = unicode(self.name, u'utf-8')
|
||||
self.file = clean_filename(self.name)
|
||||
self.file = clean_filename(self.name) + u'.sqlite'
|
||||
if u'file' in kwargs:
|
||||
self.file = kwargs[u'file']
|
||||
Manager.__init__(self, u'bibles', init_schema, self.file)
|
||||
@ -210,7 +197,7 @@ class BibleDB(QtCore.QObject, Manager):
|
||||
The book_reference_id from bibles_resources.sqlite of the book.
|
||||
|
||||
``testament``
|
||||
*Defaults to 1.* The testament_reference_id from
|
||||
*Defaults to 1.* The testament_reference_id from
|
||||
bibles_resources.sqlite of the testament this book belongs to.
|
||||
"""
|
||||
log.debug(u'BibleDB.create_book("%s", "%s")', name, bk_ref_id)
|
||||
@ -329,7 +316,7 @@ class BibleDB(QtCore.QObject, Manager):
|
||||
return self.get_object_filtered(Book, Book.book_reference_id.like(id))
|
||||
|
||||
def get_book_ref_id_by_name(self, book, maxbooks, language_id=None):
|
||||
log.debug(u'BibleDB.get_book_ref_id_by_name:("%s", "%s")', book,
|
||||
log.debug(u'BibleDB.get_book_ref_id_by_name:("%s", "%s")', book,
|
||||
language_id)
|
||||
if BiblesResourcesDB.get_book(book, True):
|
||||
book_temp = BiblesResourcesDB.get_book(book, True)
|
||||
@ -471,7 +458,7 @@ class BibleDB(QtCore.QObject, Manager):
|
||||
|
||||
def get_language(self, bible_name=None):
|
||||
"""
|
||||
If no language is given it calls a dialog window where the user could
|
||||
If no language is given it calls a dialog window where the user could
|
||||
select the bible language.
|
||||
Return the language id of a bible.
|
||||
|
||||
@ -521,9 +508,9 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
some resources which are used in the Bibles plugin.
|
||||
A wrapper class around a small SQLite database which contains the download
|
||||
resources, a biblelist from the different download resources, the books,
|
||||
chapter counts and verse counts for the web download Bibles, a language
|
||||
reference, the testament reference and some alternative book names. This
|
||||
class contains a singleton "cursor" so that only one connection to the
|
||||
chapter counts and verse counts for the web download Bibles, a language
|
||||
reference, the testament reference and some alternative book names. This
|
||||
class contains a singleton "cursor" so that only one connection to the
|
||||
SQLite database is ever used.
|
||||
"""
|
||||
cursor = None
|
||||
@ -582,7 +569,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
|
||||
``name``
|
||||
The name or abbreviation of the book.
|
||||
|
||||
|
||||
``lower``
|
||||
True if the comparsion should be only lowercase
|
||||
"""
|
||||
@ -592,7 +579,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
if lower:
|
||||
books = BiblesResourcesDB.run_sql(u'SELECT id, testament_id, name, '
|
||||
u'abbreviation, chapters FROM book_reference WHERE '
|
||||
u'LOWER(name) = ? OR LOWER(abbreviation) = ?',
|
||||
u'LOWER(name) = ? OR LOWER(abbreviation) = ?',
|
||||
(name.lower(), name.lower()))
|
||||
else:
|
||||
books = BiblesResourcesDB.run_sql(u'SELECT id, testament_id, name, '
|
||||
@ -621,7 +608,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
if not isinstance(id, int):
|
||||
id = int(id)
|
||||
books = BiblesResourcesDB.run_sql(u'SELECT id, testament_id, name, '
|
||||
u'abbreviation, chapters FROM book_reference WHERE id = ?',
|
||||
u'abbreviation, chapters FROM book_reference WHERE id = ?',
|
||||
(id, ))
|
||||
if books:
|
||||
return {
|
||||
@ -645,12 +632,12 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
``chapter``
|
||||
The chapter number.
|
||||
"""
|
||||
log.debug(u'BiblesResourcesDB.get_chapter("%s", "%s")', book_id,
|
||||
log.debug(u'BiblesResourcesDB.get_chapter("%s", "%s")', book_id,
|
||||
chapter)
|
||||
if not isinstance(chapter, int):
|
||||
chapter = int(chapter)
|
||||
chapters = BiblesResourcesDB.run_sql(u'SELECT id, book_reference_id, '
|
||||
u'chapter, verse_count FROM chapters WHERE book_reference_id = ?',
|
||||
u'chapter, verse_count FROM chapters WHERE book_reference_id = ?',
|
||||
(book_id,))
|
||||
if chapters:
|
||||
return {
|
||||
@ -687,7 +674,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
``chapter``
|
||||
The number of the chapter.
|
||||
"""
|
||||
log.debug(u'BiblesResourcesDB.get_verse_count("%s", "%s")', book_id,
|
||||
log.debug(u'BiblesResourcesDB.get_verse_count("%s", "%s")', book_id,
|
||||
chapter)
|
||||
details = BiblesResourcesDB.get_chapter(book_id, chapter)
|
||||
if details:
|
||||
@ -715,7 +702,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
}
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_webbibles(source):
|
||||
"""
|
||||
@ -737,7 +724,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
u'id': bible[0],
|
||||
u'name': bible[1],
|
||||
u'abbreviation': bible[2],
|
||||
u'language_id': bible[3],
|
||||
u'language_id': bible[3],
|
||||
u'download_source_id': bible[4]
|
||||
}
|
||||
for bible in bibles
|
||||
@ -752,11 +739,11 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
|
||||
``abbreviation``
|
||||
The abbreviation of the webbible.
|
||||
|
||||
|
||||
``source``
|
||||
The source of the webbible.
|
||||
"""
|
||||
log.debug(u'BiblesResourcesDB.get_webbibles("%s", "%s")', abbreviation,
|
||||
log.debug(u'BiblesResourcesDB.get_webbibles("%s", "%s")', abbreviation,
|
||||
source)
|
||||
if not isinstance(abbreviation, unicode):
|
||||
abbreviation = unicode(abbreviation)
|
||||
@ -765,14 +752,14 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
source = BiblesResourcesDB.get_download_source(source)
|
||||
bible = BiblesResourcesDB.run_sql(u'SELECT id, name, abbreviation, '
|
||||
u'language_id, download_source_id FROM webbibles WHERE '
|
||||
u'download_source_id = ? AND abbreviation = ?', (source[u'id'],
|
||||
u'download_source_id = ? AND abbreviation = ?', (source[u'id'],
|
||||
abbreviation))
|
||||
if bible:
|
||||
return {
|
||||
u'id': bible[0][0],
|
||||
u'name': bible[0][1],
|
||||
u'abbreviation': bible[0][2],
|
||||
u'language_id': bible[0][3],
|
||||
u'language_id': bible[0][3],
|
||||
u'download_source_id': bible[0][4]
|
||||
}
|
||||
else:
|
||||
@ -785,11 +772,11 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
|
||||
``name``
|
||||
The name to search the id.
|
||||
|
||||
|
||||
``language_id``
|
||||
The language_id for which language should be searched
|
||||
"""
|
||||
log.debug(u'BiblesResourcesDB.get_alternative_book_name("%s", "%s")',
|
||||
log.debug(u'BiblesResourcesDB.get_alternative_book_name("%s", "%s")',
|
||||
name, language_id)
|
||||
if language_id:
|
||||
books = BiblesResourcesDB.run_sql(u'SELECT book_reference_id, name '
|
||||
@ -806,7 +793,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
@staticmethod
|
||||
def get_language(name):
|
||||
"""
|
||||
Return a dict containing the language id, name and code by name or
|
||||
Return a dict containing the language id, name and code by name or
|
||||
abbreviation.
|
||||
|
||||
``name``
|
||||
@ -865,7 +852,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
|
||||
class AlternativeBookNamesDB(QtCore.QObject, Manager):
|
||||
"""
|
||||
This class represents a database-bound alternative book names system.
|
||||
This class represents a database-bound alternative book names system.
|
||||
"""
|
||||
cursor = None
|
||||
conn = None
|
||||
@ -874,7 +861,7 @@ class AlternativeBookNamesDB(QtCore.QObject, Manager):
|
||||
def get_cursor():
|
||||
"""
|
||||
Return the cursor object. Instantiate one if it doesn't exist yet.
|
||||
If necessary loads up the database and creates the tables if the
|
||||
If necessary loads up the database and creates the tables if the
|
||||
database doesn't exist.
|
||||
"""
|
||||
if AlternativeBookNamesDB.cursor is None:
|
||||
@ -904,7 +891,7 @@ class AlternativeBookNamesDB(QtCore.QObject, Manager):
|
||||
|
||||
``parameters``
|
||||
Any variable parameters to add to the query
|
||||
|
||||
|
||||
``commit``
|
||||
If a commit statement is necessary this should be True.
|
||||
"""
|
||||
@ -921,11 +908,11 @@ class AlternativeBookNamesDB(QtCore.QObject, Manager):
|
||||
|
||||
``name``
|
||||
The name to search the id.
|
||||
|
||||
|
||||
``language_id``
|
||||
The language_id for which language should be searched
|
||||
"""
|
||||
log.debug(u'AlternativeBookNamesDB.get_book_reference_id("%s", "%s")',
|
||||
log.debug(u'AlternativeBookNamesDB.get_book_reference_id("%s", "%s")',
|
||||
name, language_id)
|
||||
if language_id:
|
||||
books = AlternativeBookNamesDB.run_sql(u'SELECT book_reference_id, '
|
||||
@ -962,11 +949,11 @@ class AlternativeBookNamesDB(QtCore.QObject, Manager):
|
||||
|
||||
class OldBibleDB(QtCore.QObject, Manager):
|
||||
"""
|
||||
This class conects to the old bible databases to reimport them to the new
|
||||
This class conects to the old bible databases to reimport them to the new
|
||||
database scheme.
|
||||
"""
|
||||
cursor = None
|
||||
|
||||
|
||||
def __init__(self, parent, **kwargs):
|
||||
"""
|
||||
The constructor loads up the database and creates and initialises the
|
||||
|
@ -29,9 +29,7 @@ The :mod:`http` module enables OpenLP to retrieve scripture from bible
|
||||
websites.
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import sqlite3
|
||||
import socket
|
||||
import urllib
|
||||
from HTMLParser import HTMLParseError
|
||||
@ -40,7 +38,7 @@ from BeautifulSoup import BeautifulSoup, NavigableString, Tag
|
||||
|
||||
from openlp.core.lib import Receiver, translate
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.utils import AppLocation, get_web_page
|
||||
from openlp.core.utils import get_web_page
|
||||
from openlp.plugins.bibles.lib import SearchResults
|
||||
from openlp.plugins.bibles.lib.db import BibleDB, BiblesResourcesDB, \
|
||||
Book
|
||||
@ -69,10 +67,10 @@ class BGExtract(object):
|
||||
``chapter``
|
||||
Chapter number.
|
||||
"""
|
||||
log.debug(u'BGExtract.get_bible_chapter("%s", "%s", "%s")', version,
|
||||
log.debug(u'BGExtract.get_bible_chapter("%s", "%s", "%s")', version,
|
||||
bookname, chapter)
|
||||
urlbookname = urllib.quote(bookname.encode("utf-8"))
|
||||
url_params = u'search=%s+%s&version=%s' % (urlbookname, chapter,
|
||||
url_params = u'search=%s+%s&version=%s' % (urlbookname, chapter,
|
||||
version)
|
||||
cleaner = [(re.compile(' |<br />|\'\+\''), lambda match: '')]
|
||||
soup = get_soup_for_bible_ref(
|
||||
@ -147,7 +145,10 @@ class BGExtract(object):
|
||||
send_error_message(u'download')
|
||||
return None
|
||||
page_source = page.read()
|
||||
page_source = unicode(page_source, 'utf8')
|
||||
try:
|
||||
page_source = unicode(page_source, u'utf8')
|
||||
except UnicodeDecodeError:
|
||||
page_source = unicode(page_source, u'cp1251')
|
||||
page_source_temp = re.search(u'<table .*?class="infotable".*?>.*?'\
|
||||
u'</table>', page_source, re.DOTALL)
|
||||
if page_source_temp:
|
||||
@ -200,7 +201,7 @@ class BSExtract(object):
|
||||
``chapter``
|
||||
Chapter number
|
||||
"""
|
||||
log.debug(u'BSExtract.get_bible_chapter("%s", "%s", "%s")', version,
|
||||
log.debug(u'BSExtract.get_bible_chapter("%s", "%s", "%s")', version,
|
||||
bookname, chapter)
|
||||
urlversion = urllib.quote(version.encode("utf-8"))
|
||||
urlbookname = urllib.quote(bookname.encode("utf-8"))
|
||||
@ -227,7 +228,7 @@ class BSExtract(object):
|
||||
|
||||
def get_books_from_http(self, version):
|
||||
"""
|
||||
Load a list of all books a Bible contains from Bibleserver mobile
|
||||
Load a list of all books a Bible contains from Bibleserver mobile
|
||||
website.
|
||||
|
||||
``version``
|
||||
@ -273,7 +274,7 @@ class CWExtract(object):
|
||||
``chapter``
|
||||
Chapter number
|
||||
"""
|
||||
log.debug(u'CWExtract.get_bible_chapter("%s", "%s", "%s")', version,
|
||||
log.debug(u'CWExtract.get_bible_chapter("%s", "%s", "%s")', version,
|
||||
bookname, chapter)
|
||||
urlbookname = bookname.replace(u' ', u'-')
|
||||
urlbookname = urlbookname.lower()
|
||||
@ -386,7 +387,7 @@ class HTTPBible(BibleDB):
|
||||
"""
|
||||
self.wizard.progressBar.setMaximum(68)
|
||||
self.wizard.incrementProgressBar(unicode(translate(
|
||||
'BiblesPlugin.HTTPBible',
|
||||
'BiblesPlugin.HTTPBible',
|
||||
'Registering Bible and loading books...')))
|
||||
self.create_meta(u'download source', self.download_source)
|
||||
self.create_meta(u'download name', self.download_name)
|
||||
@ -412,7 +413,7 @@ class HTTPBible(BibleDB):
|
||||
self.wizard.progressBar.setMaximum(len(books)+2)
|
||||
self.wizard.incrementProgressBar(unicode(translate(
|
||||
'BiblesPlugin.HTTPBible', 'Registering Language...')))
|
||||
bible = BiblesResourcesDB.get_webbible(self.download_name,
|
||||
bible = BiblesResourcesDB.get_webbible(self.download_name,
|
||||
self.download_source.lower())
|
||||
if bible[u'language_id']:
|
||||
language_id = bible[u'language_id']
|
||||
@ -429,14 +430,14 @@ class HTTPBible(BibleDB):
|
||||
self.wizard.incrementProgressBar(unicode(translate(
|
||||
'BiblesPlugin.HTTPBible', 'Importing %s...',
|
||||
'Importing <book name>...')) % book)
|
||||
book_ref_id = self.get_book_ref_id_by_name(book, len(books),
|
||||
book_ref_id = self.get_book_ref_id_by_name(book, len(books),
|
||||
language_id)
|
||||
if not book_ref_id:
|
||||
log.exception(u'Importing books from %s - download name: "%s" '\
|
||||
'failed' % (self.download_source, self.download_name))
|
||||
return False
|
||||
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
|
||||
log.debug(u'Book details: Name:%s; id:%s; testament_id:%s',
|
||||
log.debug(u'Book details: Name:%s; id:%s; testament_id:%s',
|
||||
book, book_ref_id, book_details[u'testament_id'])
|
||||
self.create_book(book, book_ref_id, book_details[u'testament_id'])
|
||||
if self.stop_import_flag:
|
||||
@ -521,7 +522,7 @@ class HTTPBible(BibleDB):
|
||||
def get_chapter_count(self, book):
|
||||
"""
|
||||
Return the number of chapters in a particular book.
|
||||
|
||||
|
||||
``book``
|
||||
The book object to get the chapter count for.
|
||||
"""
|
||||
@ -594,14 +595,14 @@ def send_error_message(error_type):
|
||||
"""
|
||||
if error_type == u'download':
|
||||
critical_error_message_box(
|
||||
translate('BiblePlugin.HTTPBible', 'Download Error'),
|
||||
translate('BiblePlugin.HTTPBible', 'There was a '
|
||||
translate('BiblesPlugin.HTTPBible', 'Download Error'),
|
||||
translate('BiblesPlugin.HTTPBible', 'There was a '
|
||||
'problem downloading your verse selection. Please check your '
|
||||
'Internet connection, and if this error continues to occur '
|
||||
'please consider reporting a bug.'))
|
||||
elif error_type == u'parse':
|
||||
critical_error_message_box(
|
||||
translate('BiblePlugin.HTTPBible', 'Parse Error'),
|
||||
translate('BiblePlugin.HTTPBible', 'There was a '
|
||||
translate('BiblesPlugin.HTTPBible', 'Parse Error'),
|
||||
translate('BiblesPlugin.HTTPBible', 'There was a '
|
||||
'problem extracting your verse selection. If this error continues '
|
||||
'to occur please consider reporting a bug.'))
|
||||
|
@ -34,7 +34,7 @@ from openlp.core.lib import Receiver, SettingsManager, translate
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.utils import AppLocation, delete_file
|
||||
from openlp.plugins.bibles.lib import parse_reference
|
||||
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta, OldBibleDB
|
||||
from openlp.plugins.bibles.lib.db import BibleDB, BibleMeta
|
||||
from csvbible import CSVBible
|
||||
from http import HTTPBible
|
||||
from opensong import OpenSongBible
|
||||
@ -151,9 +151,10 @@ class BibleManager(object):
|
||||
name = bible.get_name()
|
||||
# Remove corrupted files.
|
||||
if name is None:
|
||||
bible.session.close()
|
||||
delete_file(os.path.join(self.path, filename))
|
||||
continue
|
||||
# Find old database versions
|
||||
# Find old database versions.
|
||||
if bible.is_old_database():
|
||||
self.old_bible_databases.append([filename, name])
|
||||
bible.session.close()
|
||||
@ -220,7 +221,7 @@ class BibleManager(object):
|
||||
return [
|
||||
{
|
||||
u'name': book.name,
|
||||
u'book_reference_id': book.book_reference_id,
|
||||
u'book_reference_id': book.book_reference_id,
|
||||
u'chapters': self.db_cache[bible].get_chapter_count(book)
|
||||
}
|
||||
for book in self.db_cache[bible].get_books()
|
||||
@ -229,10 +230,10 @@ class BibleManager(object):
|
||||
def get_chapter_count(self, bible, book):
|
||||
"""
|
||||
Returns the number of Chapters for a given book.
|
||||
|
||||
|
||||
``bible``
|
||||
Unicode. The Bible to get the list of books from.
|
||||
|
||||
|
||||
``book``
|
||||
The book object to get the chapter count for.
|
||||
"""
|
||||
@ -295,7 +296,7 @@ class BibleManager(object):
|
||||
if db_book:
|
||||
book_id = db_book.book_reference_id
|
||||
log.debug(u'Book name corrected to "%s"', db_book.name)
|
||||
new_reflist.append((book_id, item[1], item[2],
|
||||
new_reflist.append((book_id, item[1], item[2],
|
||||
item[3]))
|
||||
else:
|
||||
log.debug(u'OpenLP failed to find book %s', item[0])
|
||||
|
@ -34,7 +34,8 @@ from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \
|
||||
translate
|
||||
from openlp.core.lib.searchedit import SearchEdit
|
||||
from openlp.core.lib.ui import UiStrings, add_widget_completer, \
|
||||
media_item_combo_box, critical_error_message_box, find_and_set_in_combo_box
|
||||
media_item_combo_box, critical_error_message_box, \
|
||||
find_and_set_in_combo_box, build_icon
|
||||
from openlp.plugins.bibles.forms import BibleImportForm
|
||||
from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, \
|
||||
VerseReferenceList, get_reference_match
|
||||
@ -57,8 +58,8 @@ class BibleMediaItem(MediaManagerItem):
|
||||
|
||||
def __init__(self, parent, plugin, icon):
|
||||
self.IconPath = u'songs/song'
|
||||
self.lockIcon = QtGui.QIcon(u':/bibles/bibles_search_lock.png')
|
||||
self.unlockIcon = QtGui.QIcon(u':/bibles/bibles_search_unlock.png')
|
||||
self.lockIcon = build_icon(u':/bibles/bibles_search_lock.png')
|
||||
self.unlockIcon = build_icon(u':/bibles/bibles_search_unlock.png')
|
||||
MediaManagerItem.__init__(self, parent, plugin, icon)
|
||||
# Place to store the search results for both bibles.
|
||||
self.settings = self.plugin.settings_tab
|
||||
@ -86,7 +87,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
not second_bible:
|
||||
self.displayResults(bible, second_bible)
|
||||
elif critical_error_message_box(
|
||||
message=translate('BiblePlugin.MediaItem',
|
||||
message=translate('BiblesPlugin.MediaItem',
|
||||
'You cannot combine single and dual Bible verse search results. '
|
||||
'Do you want to delete your search results and start a new '
|
||||
'search?'),
|
||||
@ -394,6 +395,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
log.debug(u'Reloading Bibles')
|
||||
self.plugin.manager.reload_bibles()
|
||||
self.loadBibles()
|
||||
self.updateAutoCompleter()
|
||||
|
||||
def initialiseAdvancedBible(self, bible):
|
||||
"""
|
||||
@ -436,7 +438,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
if verse_count == 0:
|
||||
self.advancedSearchButton.setEnabled(False)
|
||||
critical_error_message_box(
|
||||
message=translate('BiblePlugin.MediaItem',
|
||||
message=translate('BiblesPlugin.MediaItem',
|
||||
'Bible not fully loaded.'))
|
||||
else:
|
||||
self.advancedSearchButton.setEnabled(True)
|
||||
@ -611,7 +613,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
if restore:
|
||||
old_text = unicode(combo.currentText())
|
||||
combo.clear()
|
||||
combo.addItems([unicode(i) for i in range(range_from, range_to + 1)])
|
||||
combo.addItems(map(unicode, range(range_from, range_to + 1)))
|
||||
if restore and combo.findText(old_text) != -1:
|
||||
combo.setCurrentIndex(combo.findText(old_text))
|
||||
|
||||
@ -693,8 +695,8 @@ class BibleMediaItem(MediaManagerItem):
|
||||
verse.verse, verse.verse))
|
||||
if passage_not_found:
|
||||
QtGui.QMessageBox.information(self,
|
||||
translate('BiblePlugin.MediaItem', 'Information'),
|
||||
unicode(translate('BiblePlugin.MediaItem',
|
||||
translate('BiblesPlugin.MediaItem', 'Information'),
|
||||
unicode(translate('BiblesPlugin.MediaItem',
|
||||
'The second Bible does not contain all the verses '
|
||||
'that are in the main Bible. Only verses found in both '
|
||||
'Bibles will be shown. %d verses have not been '
|
||||
@ -983,7 +985,8 @@ class BibleMediaItem(MediaManagerItem):
|
||||
Search for some Bible verses (by reference).
|
||||
"""
|
||||
bible = unicode(self.quickVersionComboBox.currentText())
|
||||
search_results = self.plugin.manager.get_verses(bible, string, False, False)
|
||||
search_results = self.plugin.manager.get_verses(bible, string, False,
|
||||
False)
|
||||
if search_results:
|
||||
versetext = u' '.join([verse.text for verse in search_results])
|
||||
return [[string, versetext]]
|
||||
|
Binary file not shown.
@ -46,7 +46,7 @@ class CustomPlugin(Plugin):
|
||||
log.info(u'Custom Plugin loaded')
|
||||
|
||||
def __init__(self, plugin_helpers):
|
||||
Plugin.__init__(self, u'Custom', plugin_helpers,
|
||||
Plugin.__init__(self, u'custom', plugin_helpers,
|
||||
CustomMediaItem, CustomTab)
|
||||
self.weight = -5
|
||||
self.manager = Manager(u'custom', init_schema)
|
||||
|
@ -93,7 +93,6 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
self.titleEdit.setText(u'')
|
||||
self.creditEdit.setText(u'')
|
||||
self.themeComboBox.setCurrentIndex(0)
|
||||
self.titleEdit.setFocus(QtCore.Qt.OtherFocusReason)
|
||||
else:
|
||||
self.customSlide = self.manager.get_object(CustomSlide, id)
|
||||
self.titleEdit.setText(self.customSlide.title)
|
||||
@ -104,10 +103,9 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
self.slideListView.addItem(slide[1])
|
||||
theme = self.customSlide.theme_name
|
||||
find_and_set_in_combo_box(self.themeComboBox, theme)
|
||||
self.titleEdit.setFocus(QtCore.Qt.OtherFocusReason)
|
||||
# If not preview hide the preview button.
|
||||
self.previewButton.setVisible(False)
|
||||
if preview:
|
||||
self.previewButton.setVisible(True)
|
||||
self.previewButton.setVisible(preview)
|
||||
|
||||
def reject(self):
|
||||
Receiver.send_message(u'custom_edit_clear')
|
||||
|
@ -200,6 +200,17 @@ class CustomMediaItem(MediaManagerItem):
|
||||
Remove a custom item from the list and database
|
||||
"""
|
||||
if check_item_selected(self.listView, UiStrings().SelectDelete):
|
||||
items = self.listView.selectedIndexes()
|
||||
if QtGui.QMessageBox.question(self,
|
||||
UiStrings().ConfirmDelete,
|
||||
translate('CustomPlugin.MediaItem',
|
||||
'Are you sure you want to delete the %n selected custom'
|
||||
' slides(s)?', '',
|
||||
QtCore.QCoreApplication.CodecForTr, len(items)),
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
|
||||
QtGui.QMessageBox.No),
|
||||
QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No:
|
||||
return
|
||||
row_list = [item.row() for item in self.listView.selectedIndexes()]
|
||||
row_list.sort(reverse=True)
|
||||
id_list = [(item.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||
|
@ -36,7 +36,7 @@ class ImagePlugin(Plugin):
|
||||
log.info(u'Image Plugin loaded')
|
||||
|
||||
def __init__(self, plugin_helpers):
|
||||
Plugin.__init__(self, u'Images', plugin_helpers, ImageMediaItem)
|
||||
Plugin.__init__(self, u'images', plugin_helpers, ImageMediaItem)
|
||||
self.weight = -7
|
||||
self.icon_path = u':/plugins/plugin_images.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
|
@ -52,6 +52,8 @@ class ImageMediaItem(MediaManagerItem):
|
||||
self.hasSearch = True
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'live_theme_changed'), self.liveThemeChanged)
|
||||
# Allow DnD from the desktop
|
||||
self.listView.activateDnD()
|
||||
|
||||
def retranslateUi(self):
|
||||
self.onNewPrompt = translate('ImagePlugin.MediaItem',
|
||||
@ -131,6 +133,7 @@ class ImageMediaItem(MediaManagerItem):
|
||||
icon = self.iconFromFile(imageFile, thumb)
|
||||
item_name = QtGui.QListWidgetItem(filename)
|
||||
item_name.setIcon(icon)
|
||||
item_name.setToolTip(imageFile)
|
||||
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(imageFile))
|
||||
self.listView.addItem(item_name)
|
||||
if not initialLoad:
|
||||
@ -208,8 +211,13 @@ class ImageMediaItem(MediaManagerItem):
|
||||
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
|
||||
if os.path.exists(filename):
|
||||
(path, name) = os.path.split(filename)
|
||||
self.plugin.liveController.display.directImage(name, filename)
|
||||
self.resetAction.setVisible(True)
|
||||
if self.plugin.liveController.display.directImage(name,
|
||||
filename):
|
||||
self.resetAction.setVisible(True)
|
||||
else:
|
||||
critical_error_message_box(UiStrings().LiveBGError,
|
||||
translate('ImagePlugin.MediaItem',
|
||||
'There was no display item to amend.'))
|
||||
else:
|
||||
critical_error_message_box(UiStrings().LiveBGError,
|
||||
unicode(translate('ImagePlugin.MediaItem',
|
||||
|
@ -39,6 +39,8 @@ from PyQt4.phonon import Phonon
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
CLAPPERBOARD = QtGui.QPixmap(u':/media/media_video.png').toImage()
|
||||
|
||||
class MediaMediaItem(MediaManagerItem):
|
||||
"""
|
||||
This is the custom media manager item for Media Slides.
|
||||
@ -48,8 +50,7 @@ class MediaMediaItem(MediaManagerItem):
|
||||
def __init__(self, parent, plugin, icon):
|
||||
self.IconPath = u'images/image'
|
||||
self.background = False
|
||||
self.PreviewFunction = QtGui.QPixmap(
|
||||
u':/media/media_video.png').toImage()
|
||||
self.PreviewFunction = CLAPPERBOARD
|
||||
MediaManagerItem.__init__(self, parent, plugin, icon)
|
||||
self.singleServiceItem = False
|
||||
self.hasSearch = True
|
||||
@ -60,6 +61,8 @@ class MediaMediaItem(MediaManagerItem):
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'openlp_phonon_creation'),
|
||||
self.createPhonon)
|
||||
# Allow DnD from the desktop
|
||||
self.listView.activateDnD()
|
||||
|
||||
def retranslateUi(self):
|
||||
self.onNewPrompt = translate('MediaPlugin.MediaItem', 'Select Media')
|
||||
@ -114,8 +117,12 @@ class MediaMediaItem(MediaManagerItem):
|
||||
filename = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||
if os.path.exists(filename):
|
||||
(path, name) = os.path.split(filename)
|
||||
self.plugin.liveController.display.video(filename, 0, True)
|
||||
self.resetAction.setVisible(True)
|
||||
if self.plugin.liveController.display.video(filename, 0, True):
|
||||
self.resetAction.setVisible(True)
|
||||
else:
|
||||
critical_error_message_box(UiStrings().LiveBGError,
|
||||
translate('MediaPlugin.MediaItem',
|
||||
'There was no display item to amend.'))
|
||||
else:
|
||||
critical_error_message_box(UiStrings().LiveBGError,
|
||||
unicode(translate('MediaPlugin.MediaItem',
|
||||
@ -197,17 +204,17 @@ class MediaMediaItem(MediaManagerItem):
|
||||
SettingsManager.set_list(self.settingsSection,
|
||||
u'media', self.getFileList())
|
||||
|
||||
def loadList(self, files):
|
||||
def loadList(self, media):
|
||||
# Sort the themes by its filename considering language specific
|
||||
# characters. lower() is needed for windows!
|
||||
files.sort(cmp=locale.strcoll,
|
||||
media.sort(cmp=locale.strcoll,
|
||||
key=lambda filename: os.path.split(unicode(filename))[1].lower())
|
||||
for file in files:
|
||||
filename = os.path.split(unicode(file))[1]
|
||||
for track in media:
|
||||
filename = os.path.split(unicode(track))[1]
|
||||
item_name = QtGui.QListWidgetItem(filename)
|
||||
img = QtGui.QPixmap(u':/media/media_video.png').toImage()
|
||||
item_name.setIcon(build_icon(img))
|
||||
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
|
||||
item_name.setIcon(build_icon(CLAPPERBOARD))
|
||||
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track))
|
||||
item_name.setToolTip(track)
|
||||
self.listView.addItem(item_name)
|
||||
|
||||
def createPhonon(self):
|
||||
|
@ -39,7 +39,7 @@ class MediaPlugin(Plugin):
|
||||
log.info(u'%s MediaPlugin loaded', __name__)
|
||||
|
||||
def __init__(self, plugin_helpers):
|
||||
Plugin.__init__(self, u'Media', plugin_helpers,
|
||||
Plugin.__init__(self, u'media', plugin_helpers,
|
||||
MediaMediaItem, MediaTab)
|
||||
self.weight = -6
|
||||
self.icon_path = u':/plugins/plugin_media.png'
|
||||
|
@ -58,6 +58,8 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
self.hasSearch = True
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'mediaitem_presentation_rebuild'), self.rebuild)
|
||||
# Allow DnD from the desktop
|
||||
self.listView.activateDnD()
|
||||
|
||||
def retranslateUi(self):
|
||||
"""
|
||||
@ -205,6 +207,7 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
item_name = QtGui.QListWidgetItem(filename)
|
||||
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
|
||||
item_name.setIcon(icon)
|
||||
item_name.setToolTip(file)
|
||||
self.listView.addItem(item_name)
|
||||
Receiver.send_message(u'cursor_normal')
|
||||
if not initialLoad:
|
||||
|
@ -52,7 +52,7 @@ class PresentationPlugin(Plugin):
|
||||
"""
|
||||
log.debug(u'Initialised')
|
||||
self.controllers = {}
|
||||
Plugin.__init__(self, u'Presentations', plugin_helpers)
|
||||
Plugin.__init__(self, u'presentations', plugin_helpers)
|
||||
self.weight = -8
|
||||
self.icon_path = u':/plugins/plugin_presentations.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
|
@ -150,13 +150,11 @@ class HttpResponse(object):
|
||||
|
||||
class HttpServer(object):
|
||||
"""
|
||||
Ability to control OpenLP via a webbrowser
|
||||
e.g. http://localhost:4316/send/slidecontroller_live_next
|
||||
http://localhost:4316/send/alerts_text?q=your%20alert%20text
|
||||
Ability to control OpenLP via a web browser.
|
||||
"""
|
||||
def __init__(self, plugin):
|
||||
"""
|
||||
Initialise the httpserver, and start the server
|
||||
Initialise the httpserver, and start the server.
|
||||
"""
|
||||
log.debug(u'Initialise httpserver')
|
||||
self.plugin = plugin
|
||||
@ -170,9 +168,9 @@ class HttpServer(object):
|
||||
|
||||
def start_tcp(self):
|
||||
"""
|
||||
Start the http server, use the port in the settings default to 4316
|
||||
Start the http server, use the port in the settings default to 4316.
|
||||
Listen out for slide and song changes so they can be broadcast to
|
||||
clients. Listen out for socket connections
|
||||
clients. Listen out for socket connections.
|
||||
"""
|
||||
log.debug(u'Start TCP server')
|
||||
port = QtCore.QSettings().value(
|
||||
@ -195,20 +193,20 @@ class HttpServer(object):
|
||||
|
||||
def slide_change(self, row):
|
||||
"""
|
||||
Slide change listener. Store the item and tell the clients
|
||||
Slide change listener. Store the item and tell the clients.
|
||||
"""
|
||||
self.current_slide = row
|
||||
|
||||
def item_change(self, items):
|
||||
"""
|
||||
Item (song) change listener. Store the slide and tell the clients
|
||||
Item (song) change listener. Store the slide and tell the clients.
|
||||
"""
|
||||
self.current_item = items[0]
|
||||
|
||||
def new_connection(self):
|
||||
"""
|
||||
A new http connection has been made. Create a client object to handle
|
||||
communication
|
||||
communication.
|
||||
"""
|
||||
log.debug(u'new http connection')
|
||||
socket = self.server.nextPendingConnection()
|
||||
@ -225,15 +223,16 @@ class HttpServer(object):
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Close down the http server
|
||||
Close down the http server.
|
||||
"""
|
||||
log.debug(u'close http server')
|
||||
self.server.close()
|
||||
|
||||
|
||||
class HttpConnection(object):
|
||||
"""
|
||||
A single connection, this handles communication between the server
|
||||
and the client
|
||||
and the client.
|
||||
"""
|
||||
def __init__(self, parent, socket):
|
||||
"""
|
||||
@ -287,9 +286,12 @@ class HttpConnection(object):
|
||||
"""
|
||||
self.template_vars = {
|
||||
'app_title': translate('RemotePlugin.Mobile', 'OpenLP 2.0 Remote'),
|
||||
'stage_title': translate('RemotePlugin.Mobile', 'OpenLP 2.0 Stage View'),
|
||||
'service_manager': translate('RemotePlugin.Mobile', 'Service Manager'),
|
||||
'slide_controller': translate('RemotePlugin.Mobile', 'Slide Controller'),
|
||||
'stage_title': translate('RemotePlugin.Mobile',
|
||||
'OpenLP 2.0 Stage View'),
|
||||
'service_manager': translate('RemotePlugin.Mobile',
|
||||
'Service Manager'),
|
||||
'slide_controller': translate('RemotePlugin.Mobile',
|
||||
'Slide Controller'),
|
||||
'alerts': translate('RemotePlugin.Mobile', 'Alerts'),
|
||||
'search': translate('RemotePlugin.Mobile', 'Search'),
|
||||
'back': translate('RemotePlugin.Mobile', 'Back'),
|
||||
@ -301,7 +303,8 @@ class HttpConnection(object):
|
||||
'text': translate('RemotePlugin.Mobile', 'Text'),
|
||||
'show_alert': translate('RemotePlugin.Mobile', 'Show Alert'),
|
||||
'go_live': translate('RemotePlugin.Mobile', 'Go Live'),
|
||||
'add_to_service': translate('RemotePlugin.Mobile', 'Add To Service'),
|
||||
'add_to_service': translate('RemotePlugin.Mobile',
|
||||
'Add to Service'),
|
||||
'no_results': translate('RemotePlugin.Mobile', 'No Results'),
|
||||
'options': translate('RemotePlugin.Mobile', 'Options')
|
||||
}
|
||||
@ -357,7 +360,8 @@ class HttpConnection(object):
|
||||
if ext == u'.html':
|
||||
mimetype = u'text/html'
|
||||
variables = self.template_vars
|
||||
html = Template(filename=path, input_encoding=u'utf-8', output_encoding=u'utf-8').render(**variables)
|
||||
html = Template(filename=path, input_encoding=u'utf-8',
|
||||
output_encoding=u'utf-8').render(**variables)
|
||||
elif ext == u'.css':
|
||||
mimetype = u'text/css'
|
||||
elif ext == u'.js':
|
||||
@ -396,7 +400,7 @@ class HttpConnection(object):
|
||||
if self.parent.current_item else u''
|
||||
}
|
||||
return HttpResponse(json.dumps({u'results': result}),
|
||||
{u'Content-Type': u'application/json'})
|
||||
{u'Content-Type': u'application/json'})
|
||||
|
||||
def display(self, action):
|
||||
"""
|
||||
@ -482,10 +486,11 @@ class HttpConnection(object):
|
||||
|
||||
def pluginInfo(self, action):
|
||||
"""
|
||||
Return plugin related information, based on the action
|
||||
Return plugin related information, based on the action.
|
||||
|
||||
``action`` - The action to perform
|
||||
if 'search' return a list of plugin names which support search
|
||||
``action``
|
||||
The action to perform. If *search* return a list of plugin names
|
||||
which support search.
|
||||
"""
|
||||
if action == u'search':
|
||||
searches = []
|
||||
@ -500,10 +505,10 @@ class HttpConnection(object):
|
||||
|
||||
def search(self, type):
|
||||
"""
|
||||
Return a list of items that match the search text
|
||||
Return a list of items that match the search text.
|
||||
|
||||
``type``
|
||||
The plugin name to search in.
|
||||
The plugin name to search in.
|
||||
"""
|
||||
text = json.loads(self.url_params[u'data'][0])[u'request'][u'text']
|
||||
plugin = self.parent.plugin.pluginManager.get_plugin_by_name(type)
|
||||
@ -527,7 +532,7 @@ class HttpConnection(object):
|
||||
|
||||
def add_to_service(self, type):
|
||||
"""
|
||||
Add item of type ``type`` to the end of the service
|
||||
Add item of type ``type`` to the end of the service.
|
||||
"""
|
||||
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id']
|
||||
plugin = self.parent.plugin.pluginManager.get_plugin_by_name(type)
|
||||
|
@ -39,7 +39,7 @@ class RemotesPlugin(Plugin):
|
||||
"""
|
||||
remotes constructor
|
||||
"""
|
||||
Plugin.__init__(self, u'Remotes', plugin_helpers,
|
||||
Plugin.__init__(self, u'remotes', plugin_helpers,
|
||||
settings_tab_class=RemoteTab)
|
||||
self.icon_path = u':/plugins/plugin_remote.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
|
@ -90,7 +90,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
self.onVerseListViewPressed)
|
||||
QtCore.QObject.connect(self.themeAddButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.mediaitem.plugin.renderer.theme_manager.onAddTheme)
|
||||
self.mediaitem.plugin.renderer.themeManager.onAddTheme)
|
||||
QtCore.QObject.connect(self.maintenanceButton,
|
||||
QtCore.SIGNAL(u'clicked()'), self.onMaintenanceButtonClicked)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
@ -209,9 +209,11 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
self.alternativeEdit.setText(u'')
|
||||
if self.song.song_book_id != 0:
|
||||
book_name = self.manager.get_object(Book, self.song.song_book_id)
|
||||
find_and_set_in_combo_box(self.songBookComboBox, unicode(book_name.name))
|
||||
find_and_set_in_combo_box(
|
||||
self.songBookComboBox, unicode(book_name.name))
|
||||
if self.song.theme_name:
|
||||
find_and_set_in_combo_box(self.themeComboBox, unicode(self.song.theme_name))
|
||||
find_and_set_in_combo_box(
|
||||
self.themeComboBox, unicode(self.song.theme_name))
|
||||
if self.song.copyright:
|
||||
self.copyrightEdit.setText(self.song.copyright)
|
||||
else:
|
||||
|
@ -37,6 +37,8 @@ from editversedialog import Ui_EditVerseDialog
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
VERSE_REGEX = re.compile(r'---\[(.+):\D*(\d*)\D*.*\]---')
|
||||
|
||||
class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
|
||||
"""
|
||||
This is the form that is used to edit the verses of the song.
|
||||
@ -60,7 +62,6 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
|
||||
QtCore.QObject.connect(self.verseTypeComboBox,
|
||||
QtCore.SIGNAL(u'currentIndexChanged(int)'),
|
||||
self.onVerseTypeComboBoxChanged)
|
||||
self.verse_regex = re.compile(r'---\[(.+):\D*(\d*)\D*.*\]---')
|
||||
|
||||
def contextMenu(self, point):
|
||||
item = self.serviceManagerList.itemAt(point)
|
||||
@ -105,7 +106,7 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
|
||||
if position == -1:
|
||||
return
|
||||
text = text[:position + 4]
|
||||
match = self.verse_regex.match(text)
|
||||
match = VERSE_REGEX.match(text)
|
||||
if match:
|
||||
verse_tag = match.group(1)
|
||||
try:
|
||||
@ -136,7 +137,7 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
|
||||
if position == -1:
|
||||
return
|
||||
text = text[:position + 4]
|
||||
match = self.verse_regex.match(text)
|
||||
match = VERSE_REGEX.match(text)
|
||||
if match:
|
||||
verse_type = match.group(1)
|
||||
verse_type_index = VerseType.from_loose_input(verse_type)
|
||||
|
@ -28,6 +28,7 @@
|
||||
The :mod:`songexportform` module provides the wizard for exporting songs to the
|
||||
OpenLyrics format.
|
||||
"""
|
||||
import locale
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
@ -249,6 +250,7 @@ class SongExportForm(OpenLPWizard):
|
||||
# Load the list of songs.
|
||||
Receiver.send_message(u'cursor_busy')
|
||||
songs = self.plugin.manager.get_all_objects(Song)
|
||||
songs.sort(cmp=locale.strcoll, key=lambda song: song.title.lower())
|
||||
for song in songs:
|
||||
authors = u', '.join([author.display_name
|
||||
for author in song.authors])
|
||||
|
@ -699,7 +699,8 @@ class SongImportForm(OpenLPWizard):
|
||||
elif source_format == SongFormat.OpenLP1:
|
||||
# Import an openlp.org database
|
||||
importer = self.plugin.importSongs(SongFormat.OpenLP1,
|
||||
filename=unicode(self.openLP1FilenameEdit.text())
|
||||
filename=unicode(self.openLP1FilenameEdit.text()),
|
||||
plugin=self.plugin
|
||||
)
|
||||
elif source_format == SongFormat.OpenLyrics:
|
||||
# Import OpenLyrics songs
|
||||
|
@ -267,6 +267,12 @@ def clean_song(manager, song):
|
||||
``song``
|
||||
The song object.
|
||||
"""
|
||||
if isinstance(song.title, buffer):
|
||||
song.title = unicode(song.title)
|
||||
if isinstance(song.alternate_title, buffer):
|
||||
song.alternate_title = unicode(song.alternate_title)
|
||||
if isinstance(song.lyrics, buffer):
|
||||
song.lyrics = unicode(song.lyrics)
|
||||
song.title = song.title.rstrip() if song.title else u''
|
||||
if song.alternate_title is None:
|
||||
song.alternate_title = u''
|
||||
|
@ -29,7 +29,7 @@ The :mod:`db` module provides the database and schema that is the backend for
|
||||
the Songs plugin
|
||||
"""
|
||||
|
||||
from sqlalchemy import Column, ForeignKey, Index, Table, types
|
||||
from sqlalchemy import Column, ForeignKey, Table, types
|
||||
from sqlalchemy.orm import mapper, relation
|
||||
|
||||
from openlp.core.lib.db import BaseModel, init_db
|
||||
|
@ -26,13 +26,10 @@
|
||||
###############################################################################
|
||||
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
||||
from lxml import etree, objectify
|
||||
|
||||
from openlp.core.lib import translate
|
||||
from openlp.core.ui.wizard import WizardStrings
|
||||
from openlp.plugins.songs.lib import VerseType
|
||||
from openlp.plugins.songs.lib.songimport import SongImport
|
||||
|
||||
|
@ -31,18 +31,33 @@ EasyWorship song databases into the current installation database.
|
||||
|
||||
import os
|
||||
import struct
|
||||
import re
|
||||
|
||||
from openlp.core.lib import translate
|
||||
from openlp.core.ui.wizard import WizardStrings
|
||||
from openlp.plugins.songs.lib import VerseType
|
||||
from openlp.plugins.songs.lib import retrieve_windows_encoding
|
||||
from songimport import SongImport
|
||||
|
||||
RTF_STRIPPING_REGEX = re.compile(r'\{\\tx[^}]*\}')
|
||||
# regex: at least two newlines, can have spaces between them
|
||||
SLIDE_BREAK_REGEX = re.compile(r'\n *?\n[\n ]*')
|
||||
NUMBER_REGEX = re.compile(r'[0-9]+')
|
||||
NOTE_REGEX = re.compile(r'\(.*?\)')
|
||||
|
||||
def strip_rtf(blob, encoding):
|
||||
depth = 0
|
||||
control = False
|
||||
clear_text = []
|
||||
control_word = []
|
||||
|
||||
# workaround for \tx bug: remove one pair of curly braces
|
||||
# if \tx is encountered
|
||||
match = RTF_STRIPPING_REGEX.search(blob)
|
||||
if match:
|
||||
# start and end indices of match are curly braces - filter them out
|
||||
blob = ''.join([blob[i] for i in xrange(len(blob))
|
||||
if i != match.start() and i !=match.end()])
|
||||
|
||||
for c in blob:
|
||||
if control:
|
||||
# for delimiters, set control to False
|
||||
@ -259,9 +274,45 @@ class EasyWorshipSongImport(SongImport):
|
||||
if words:
|
||||
# Format the lyrics
|
||||
words = strip_rtf(words, self.encoding)
|
||||
for verse in words.split(u'\n\n'):
|
||||
verse_type = VerseType.Tags[VerseType.Verse]
|
||||
for verse in SLIDE_BREAK_REGEX.split(words):
|
||||
verse = verse.strip()
|
||||
if not verse:
|
||||
continue
|
||||
verse_split = verse.split(u'\n', 1)
|
||||
first_line_is_tag = False
|
||||
# EW tags: verse, chorus, pre-chorus, bridge, tag,
|
||||
# intro, ending, slide
|
||||
for type in VerseType.Names+[u'tag', u'slide']:
|
||||
type = type.lower()
|
||||
ew_tag = verse_split[0].strip().lower()
|
||||
if ew_tag.startswith(type):
|
||||
verse_type = type[0]
|
||||
if type == u'tag' or type == u'slide':
|
||||
verse_type = VerseType.Tags[VerseType.Other]
|
||||
first_line_is_tag = True
|
||||
number_found = False
|
||||
# check if tag is followed by number and/or note
|
||||
if len(ew_tag) > len(type):
|
||||
match = NUMBER_REGEX.search(ew_tag)
|
||||
if match:
|
||||
number = match.group()
|
||||
verse_type += number
|
||||
number_found = True
|
||||
match = NOTE_REGEX.search(ew_tag)
|
||||
if match:
|
||||
self.comments += ew_tag + u'\n'
|
||||
if not number_found:
|
||||
verse_type += u'1'
|
||||
break
|
||||
self.add_verse(
|
||||
verse.strip(), VerseType.Tags[VerseType.Verse])
|
||||
verse_split[-1].strip() if first_line_is_tag else verse,
|
||||
verse_type)
|
||||
if len(self.comments) > 5:
|
||||
self.comments += unicode(
|
||||
translate('SongsPlugin.EasyWorshipSongImport',
|
||||
'\n[above are Song Tags with notes imported from \
|
||||
EasyWorship]'))
|
||||
if self.stop_import_flag:
|
||||
break
|
||||
if not self.finish():
|
||||
|
@ -35,7 +35,8 @@ from sqlalchemy.sql import or_
|
||||
from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \
|
||||
translate, check_item_selected, PluginStatus
|
||||
from openlp.core.lib.searchedit import SearchEdit
|
||||
from openlp.core.lib.ui import UiStrings
|
||||
from openlp.core.lib.ui import UiStrings, context_menu_action, \
|
||||
context_menu_separator
|
||||
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
|
||||
SongImportForm, SongExportForm
|
||||
from openlp.plugins.songs.lib import OpenLyrics, SongXML, VerseType, \
|
||||
@ -128,6 +129,13 @@ class SongMediaItem(MediaManagerItem):
|
||||
QtCore.SIGNAL(u'searchTypeChanged(int)'),
|
||||
self.onSearchTextButtonClick)
|
||||
|
||||
def addCustomContextActions(self):
|
||||
context_menu_separator(self.listView)
|
||||
context_menu_action(
|
||||
self.listView, u':/general/general_clone.png',
|
||||
translate('OpenLP.MediaManagerItem',
|
||||
'&Clone'), self.onCloneClick)
|
||||
|
||||
def onFocus(self):
|
||||
self.searchTextEdit.setFocus()
|
||||
|
||||
@ -353,19 +361,37 @@ class SongMediaItem(MediaManagerItem):
|
||||
if check_item_selected(self.listView, UiStrings().SelectDelete):
|
||||
items = self.listView.selectedIndexes()
|
||||
if QtGui.QMessageBox.question(self,
|
||||
translate('SongsPlugin.MediaItem', 'Delete Song(s)?'),
|
||||
UiStrings().ConfirmDelete,
|
||||
translate('SongsPlugin.MediaItem',
|
||||
'Are you sure you want to delete the %n selected song(s)?', '',
|
||||
QtCore.QCoreApplication.CodecForTr, len(items)),
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok |
|
||||
QtGui.QMessageBox.Cancel),
|
||||
QtGui.QMessageBox.Ok) == QtGui.QMessageBox.Cancel:
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
|
||||
QtGui.QMessageBox.No),
|
||||
QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No:
|
||||
return
|
||||
for item in items:
|
||||
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||
self.plugin.manager.delete_object(Song, item_id)
|
||||
self.onSearchTextButtonClick()
|
||||
|
||||
def onCloneClick(self):
|
||||
"""
|
||||
Clone a Song
|
||||
"""
|
||||
log.debug(u'onCloneClick')
|
||||
if check_item_selected(self.listView, UiStrings().SelectEdit):
|
||||
self.editItem = self.listView.currentItem()
|
||||
item_id = (self.editItem.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||
old_song = self.plugin.manager.get_object(Song, item_id)
|
||||
song_xml = self.openLyrics.song_to_xml(old_song)
|
||||
new_song_id = self.openLyrics.xml_to_song(song_xml)
|
||||
new_song = self.plugin.manager.get_object(Song, new_song_id)
|
||||
new_song.title = u'%s <%s>' % (new_song.title,
|
||||
translate('SongsPlugin.MediaItem', 'copy',
|
||||
'For song cloning'))
|
||||
self.plugin.manager.save_object(new_song)
|
||||
self.onSongListLoad()
|
||||
|
||||
def generateSlideData(self, service_item, item=None, xmlVersion=False):
|
||||
log.debug(u'generateSlideData (%s:%s)' % (service_item, item))
|
||||
item_id = self._getIdOfItemToGenerate(item, self.remoteSong)
|
||||
|
@ -57,6 +57,8 @@ class OpenLP1SongImport(SongImport):
|
||||
The database providing the data to import.
|
||||
"""
|
||||
SongImport.__init__(self, manager, **kwargs)
|
||||
self.available_themes = \
|
||||
kwargs[u'plugin'].formparent.themeManagerContents.getThemes()
|
||||
|
||||
def do_import(self):
|
||||
"""
|
||||
@ -70,27 +72,34 @@ class OpenLP1SongImport(SongImport):
|
||||
encoding = self.get_encoding()
|
||||
if not encoding:
|
||||
return
|
||||
# Connect to the database
|
||||
# Connect to the database.
|
||||
connection = sqlite.connect(self.import_source, mode=0444,
|
||||
encoding=(encoding, 'replace'))
|
||||
cursor = connection.cursor()
|
||||
# Determine if we're using a new or an old DB
|
||||
# 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\'')
|
||||
new_db = len(cursor.fetchall()) > 0
|
||||
# "cache" our list of authors
|
||||
# "cache" our list of authors.
|
||||
cursor.execute(u'-- types int, unicode')
|
||||
cursor.execute(u'SELECT authorid, authorname FROM authors')
|
||||
authors = cursor.fetchall()
|
||||
if new_db:
|
||||
# "cache" our list of tracks
|
||||
# "cache" our list of tracks.
|
||||
cursor.execute(u'-- types int, unicode')
|
||||
cursor.execute(u'SELECT trackid, fulltrackname FROM tracks')
|
||||
tracks = cursor.fetchall()
|
||||
# Import the songs
|
||||
cursor.execute(u'-- types int, unicode, unicode, unicode')
|
||||
# "cache" our list of themes.
|
||||
cursor.execute(u'-- types int, unicode')
|
||||
cursor.execute(u'SELECT settingsid, settingsname FROM settings')
|
||||
themes = {}
|
||||
for theme_id, theme_name in cursor.fetchall():
|
||||
if theme_name in self.available_themes:
|
||||
themes[theme_id] = theme_name
|
||||
# Import the songs.
|
||||
cursor.execute(u'-- types int, unicode, unicode, unicode, int')
|
||||
cursor.execute(u'SELECT songid, songtitle, lyrics || \'\' AS lyrics, '
|
||||
u'copyrightinfo FROM songs')
|
||||
u'copyrightinfo, settingsid FROM songs')
|
||||
songs = cursor.fetchall()
|
||||
self.import_wizard.progressBar.setMaximum(len(songs))
|
||||
for song in songs:
|
||||
@ -101,8 +110,12 @@ class OpenLP1SongImport(SongImport):
|
||||
self.title = song[1]
|
||||
lyrics = song[2].replace(u'\r\n', u'\n')
|
||||
self.add_copyright(song[3])
|
||||
if themes.has_key(song[4]):
|
||||
self.theme_name = themes[song[4]]
|
||||
verses = lyrics.split(u'\n\n')
|
||||
[self.add_verse(verse.strip()) for verse in verses if verse.strip()]
|
||||
for verse in verses:
|
||||
if verse.strip():
|
||||
self.add_verse(verse.strip())
|
||||
cursor.execute(u'-- types int')
|
||||
cursor.execute(u'SELECT authorid FROM songauthors '
|
||||
u'WHERE songid = %s' % song_id)
|
||||
@ -137,12 +150,12 @@ class OpenLP1SongImport(SongImport):
|
||||
"""
|
||||
Detect character encoding of an openlp.org 1.x song database.
|
||||
"""
|
||||
# Connect to the database
|
||||
# Connect to the database.
|
||||
connection = sqlite.connect(self.import_source, mode=0444)
|
||||
cursor = connection.cursor()
|
||||
|
||||
detector = UniversalDetector()
|
||||
# detect charset by authors
|
||||
# Detect charset by authors.
|
||||
cursor.execute(u'SELECT authorname FROM authors')
|
||||
authors = cursor.fetchall()
|
||||
for author in authors:
|
||||
@ -150,7 +163,7 @@ class OpenLP1SongImport(SongImport):
|
||||
if detector.done:
|
||||
detector.close()
|
||||
return detector.result[u'encoding']
|
||||
# detect charset by songs
|
||||
# Detect charset by songs.
|
||||
cursor.execute(u'SELECT songtitle, copyrightinfo, '
|
||||
u'lyrics || \'\' AS lyrics FROM songs')
|
||||
songs = cursor.fetchall()
|
||||
@ -160,7 +173,7 @@ class OpenLP1SongImport(SongImport):
|
||||
if detector.done:
|
||||
detector.close()
|
||||
return detector.result[u'encoding']
|
||||
# detect charset by songs
|
||||
# Detect charset by songs.
|
||||
cursor.execute(u'SELECT name FROM sqlite_master '
|
||||
u'WHERE type = \'table\' AND name = \'tracks\'')
|
||||
if len(cursor.fetchall()) > 0:
|
||||
|
@ -35,6 +35,7 @@ import re
|
||||
from lxml import etree
|
||||
|
||||
from openlp.core.lib import check_directory_exists, Receiver, translate
|
||||
from openlp.core.utils import clean_filename
|
||||
from openlp.plugins.songs.lib import OpenLyrics
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -72,8 +73,7 @@ class OpenLyricsExport(object):
|
||||
tree = etree.ElementTree(etree.fromstring(xml))
|
||||
filename = u'%s (%s)' % (song.title,
|
||||
u', '.join([author.display_name for author in song.authors]))
|
||||
filename = re.sub(
|
||||
r'[/\\?*|<>\[\]":<>+%]+', u'_', filename).strip(u'_')
|
||||
filename = clean_filename(filename)
|
||||
# Ensure the filename isn't too long for some filesystems
|
||||
filename = u'%s.xml' % filename[0:250 - len(self.save_path)]
|
||||
# Pass a file object, because lxml does not cope with some special
|
||||
|
@ -26,9 +26,7 @@
|
||||
###############################################################################
|
||||
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from zipfile import ZipFile
|
||||
|
||||
from lxml import objectify
|
||||
from lxml.etree import Error, LxmlError
|
||||
@ -110,48 +108,13 @@ class OpenSongImport(SongImport):
|
||||
SongImport.__init__(self, manager, **kwargs)
|
||||
|
||||
def do_import(self):
|
||||
"""
|
||||
Import either each of the files in self.import_source - each element of
|
||||
which can be either a single opensong file, or a zipfile containing
|
||||
multiple opensong files.
|
||||
"""
|
||||
numfiles = 0
|
||||
for filename in self.import_source:
|
||||
ext = os.path.splitext(filename)[1]
|
||||
if ext.lower() == u'.zip':
|
||||
z = ZipFile(filename, u'r')
|
||||
numfiles += len(z.infolist())
|
||||
z.close()
|
||||
else:
|
||||
numfiles += 1
|
||||
log.debug(u'Total number of files: %d', numfiles)
|
||||
self.import_wizard.progressBar.setMaximum(numfiles)
|
||||
self.import_wizard.progressBar.setMaximum(len(self.import_source))
|
||||
for filename in self.import_source:
|
||||
if self.stop_import_flag:
|
||||
return
|
||||
ext = os.path.splitext(filename)[1]
|
||||
if ext.lower() == u'.zip':
|
||||
log.debug(u'Zipfile found %s', filename)
|
||||
z = ZipFile(filename, u'r')
|
||||
for song in z.infolist():
|
||||
if self.stop_import_flag:
|
||||
z.close()
|
||||
return
|
||||
parts = os.path.split(song.filename)
|
||||
if parts[-1] == u'':
|
||||
# No final part => directory
|
||||
continue
|
||||
log.info(u'Zip importing %s', parts[-1])
|
||||
song_file = z.open(song)
|
||||
self.do_import_file(song_file)
|
||||
song_file.close()
|
||||
z.close()
|
||||
else:
|
||||
# not a zipfile
|
||||
log.info(u'Direct import %s', filename)
|
||||
song_file = open(filename)
|
||||
self.do_import_file(song_file)
|
||||
song_file.close()
|
||||
song_file = open(filename)
|
||||
self.do_import_file(song_file)
|
||||
song_file.close()
|
||||
|
||||
def do_import_file(self, file):
|
||||
"""
|
||||
|
@ -36,7 +36,6 @@ import re
|
||||
|
||||
from openlp.plugins.songs.lib import VerseType
|
||||
from openlp.plugins.songs.lib.songimport import SongImport
|
||||
from openlp.plugins.songs.lib.ui import SongStrings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
@ -29,9 +29,8 @@ import re
|
||||
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.lib import Receiver, translate, check_directory_exists
|
||||
from openlp.core.lib import Receiver, translate
|
||||
from openlp.core.ui.wizard import WizardStrings
|
||||
from openlp.core.utils import AppLocation
|
||||
from openlp.plugins.songs.lib import clean_song, VerseType
|
||||
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile
|
||||
from openlp.plugins.songs.lib.ui import SongStrings
|
||||
|
@ -102,7 +102,6 @@ class SongShowPlusImport(SongImport):
|
||||
if not isinstance(self.import_source, list):
|
||||
return
|
||||
self.import_wizard.progressBar.setMaximum(len(self.import_source))
|
||||
|
||||
for file in self.import_source:
|
||||
self.sspVerseOrderList = []
|
||||
otherCount = 0
|
||||
@ -111,7 +110,6 @@ class SongShowPlusImport(SongImport):
|
||||
self.import_wizard.incrementProgressBar(
|
||||
WizardStrings.ImportingType % file_name, 0)
|
||||
songData = open(file, 'rb')
|
||||
|
||||
while True:
|
||||
blockKey, = struct.unpack("I", songData.read(4))
|
||||
# The file ends with 4 NUL's
|
||||
@ -126,8 +124,9 @@ class SongShowPlusImport(SongImport):
|
||||
songData.read(2))
|
||||
verseName = songData.read(verseNameLength)
|
||||
lengthDescriptorSize, = struct.unpack("B", songData.read(1))
|
||||
log.debug(lengthDescriptorSize)
|
||||
# Detect if/how long the length descriptor is
|
||||
if lengthDescriptorSize == 12:
|
||||
if lengthDescriptorSize == 12 or lengthDescriptorSize == 20:
|
||||
lengthDescriptor, = struct.unpack("I", songData.read(4))
|
||||
elif lengthDescriptorSize == 2:
|
||||
lengthDescriptor = 1
|
||||
@ -135,6 +134,7 @@ class SongShowPlusImport(SongImport):
|
||||
lengthDescriptor = 0
|
||||
else:
|
||||
lengthDescriptor, = struct.unpack("B", songData.read(1))
|
||||
log.debug(lengthDescriptorSize)
|
||||
data = songData.read(lengthDescriptor)
|
||||
if blockKey == TITLE:
|
||||
self.title = unicode(data, u'cp1252')
|
||||
|
@ -31,7 +31,6 @@ Worship songs into the OpenLP database.
|
||||
import os
|
||||
import logging
|
||||
|
||||
from openlp.core.ui.wizard import WizardStrings
|
||||
from openlp.plugins.songs.lib.songimport import SongImport
|
||||
|
||||
BLOCK_TYPES = (u'V', u'C', u'B')
|
||||
|
@ -73,6 +73,8 @@ from openlp.core.utils import get_application_version
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
CHORD_REGEX = re.compile(u'<chord name=".*?"/>')
|
||||
|
||||
class SongXML(object):
|
||||
"""
|
||||
This class builds and parses the XML used to describe songs.
|
||||
@ -234,7 +236,6 @@ class OpenLyrics(object):
|
||||
IMPLEMENTED_VERSION = u'0.7'
|
||||
def __init__(self, manager):
|
||||
self.manager = manager
|
||||
self.chord_regex = re.compile(u'<chord name=".*?"/>')
|
||||
|
||||
def song_to_xml(self, song):
|
||||
"""
|
||||
@ -245,8 +246,9 @@ class OpenLyrics(object):
|
||||
# Append the necessary meta data to the song.
|
||||
song_xml.set(u'xmlns', u'http://openlyrics.info/namespace/2009/song')
|
||||
song_xml.set(u'version', OpenLyrics.IMPLEMENTED_VERSION)
|
||||
song_xml.set(u'createdIn', get_application_version()[u'version'])
|
||||
song_xml.set(u'modifiedIn', get_application_version()[u'version'])
|
||||
application_name = u'OpenLP ' + get_application_version()[u'version']
|
||||
song_xml.set(u'createdIn', application_name)
|
||||
song_xml.set(u'modifiedIn', application_name)
|
||||
song_xml.set(u'modifiedDate',
|
||||
datetime.datetime.now().strftime(u'%Y-%m-%dT%H:%M:%S'))
|
||||
properties = etree.SubElement(song_xml, u'properties')
|
||||
@ -319,7 +321,7 @@ class OpenLyrics(object):
|
||||
if xml[:5] == u'<?xml':
|
||||
xml = xml[38:]
|
||||
# Remove chords from xml.
|
||||
xml = self.chord_regex.sub(u'', xml)
|
||||
xml = CHORD_REGEX.sub(u'', xml)
|
||||
song_xml = objectify.fromstring(xml)
|
||||
if hasattr(song_xml, u'properties'):
|
||||
properties = song_xml.properties
|
||||
|
@ -57,7 +57,7 @@ class SongsPlugin(Plugin):
|
||||
"""
|
||||
Create and set up the Songs plugin.
|
||||
"""
|
||||
Plugin.__init__(self, u'Songs', plugin_helpers, SongMediaItem, SongsTab)
|
||||
Plugin.__init__(self, u'songs', plugin_helpers, SongMediaItem, SongsTab)
|
||||
self.weight = -10
|
||||
self.manager = Manager(u'songs', init_schema)
|
||||
self.icon_path = u':/plugins/plugin_songs.png'
|
||||
|
@ -33,7 +33,7 @@ from PyQt4 import QtCore, QtGui
|
||||
from openlp.core.lib import Plugin, StringContent, Receiver, build_icon, \
|
||||
translate
|
||||
from openlp.core.lib.db import Manager
|
||||
from openlp.core.lib.ui import base_action, shortcut_action, UiStrings
|
||||
from openlp.core.lib.ui import base_action, shortcut_action
|
||||
from openlp.core.utils.actions import ActionList
|
||||
from openlp.plugins.songusage.forms import SongUsageDetailForm, \
|
||||
SongUsageDeleteForm
|
||||
@ -45,11 +45,13 @@ class SongUsagePlugin(Plugin):
|
||||
log.info(u'SongUsage Plugin loaded')
|
||||
|
||||
def __init__(self, plugin_helpers):
|
||||
Plugin.__init__(self, u'SongUsage', plugin_helpers)
|
||||
Plugin.__init__(self, u'songusage', plugin_helpers)
|
||||
self.weight = -4
|
||||
self.icon = build_icon(u':/plugins/plugin_songusage.png')
|
||||
self.activeIcon = build_icon(u':/songusage/song_usage_active.png')
|
||||
self.inactiveIcon = build_icon(u':/songusage/song_usage_inactive.png')
|
||||
self.manager = None
|
||||
self.songusageActive = False
|
||||
self.songUsageActive = False
|
||||
|
||||
def addToolsMenuItem(self, tools_menu):
|
||||
"""
|
||||
@ -84,17 +86,29 @@ class SongUsagePlugin(Plugin):
|
||||
self.songUsageStatus.setText(translate(
|
||||
'SongUsagePlugin', 'Toggle Tracking'))
|
||||
self.songUsageStatus.setStatusTip(translate('SongUsagePlugin',
|
||||
'Toggle the tracking of song usage.'))
|
||||
#Add Menus together
|
||||
'Toggle the tracking of song usage.'))
|
||||
# Add Menus together
|
||||
self.toolsMenu.addAction(self.songUsageMenu.menuAction())
|
||||
self.songUsageMenu.addAction(self.songUsageStatus)
|
||||
self.songUsageMenu.addSeparator()
|
||||
self.songUsageMenu.addAction(self.songUsageDelete)
|
||||
self.songUsageMenu.addAction(self.songUsageReport)
|
||||
self.songUsageMenu.addAction(self.songUsageDelete)
|
||||
self.songUsageActiveButton = QtGui.QToolButton(
|
||||
self.formparent.statusBar)
|
||||
self.songUsageActiveButton.setCheckable(True)
|
||||
self.songUsageActiveButton.setStatusTip(translate('SongUsagePlugin',
|
||||
'Toggle the tracking of song usage.'))
|
||||
self.songUsageActiveButton.setObjectName(u'songUsageActiveButton')
|
||||
self.formparent.statusBar.insertPermanentWidget(1,
|
||||
self.songUsageActiveButton)
|
||||
self.songUsageActiveButton.hide()
|
||||
# Signals and slots
|
||||
QtCore.QObject.connect(self.songUsageStatus,
|
||||
QtCore.SIGNAL(u'visibilityChanged(bool)'),
|
||||
self.songUsageStatus.setChecked)
|
||||
QtCore.QObject.connect(self.songUsageActiveButton,
|
||||
QtCore.SIGNAL(u'toggled(bool)'),
|
||||
self.toggleSongUsageState)
|
||||
QtCore.QObject.connect(self.songUsageDelete,
|
||||
QtCore.SIGNAL(u'triggered()'), self.onSongUsageDelete)
|
||||
QtCore.QObject.connect(self.songUsageReport,
|
||||
@ -107,23 +121,25 @@ class SongUsagePlugin(Plugin):
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'slidecontroller_live_started'),
|
||||
self.onReceiveSongUsage)
|
||||
self.SongUsageActive = QtCore.QSettings().value(
|
||||
self.songUsageActive = QtCore.QSettings().value(
|
||||
self.settingsSection + u'/active',
|
||||
QtCore.QVariant(False)).toBool()
|
||||
self.songUsageStatus.setChecked(self.SongUsageActive)
|
||||
# Set the button and checkbox state
|
||||
self.setButtonState()
|
||||
action_list = ActionList.get_instance()
|
||||
action_list.add_action(self.songUsageStatus,
|
||||
translate('SongUsagePlugin', 'Song Usage'))
|
||||
action_list.add_action(self.songUsageDelete,
|
||||
translate('SongUsagePlugin', 'Song Usage'))
|
||||
action_list.add_action(self.songUsageReport,
|
||||
translate('SongUsagePlugin', 'Song Usage'))
|
||||
action_list.add_action(self.songUsageStatus,
|
||||
translate('SongUsagePlugin', 'Song Usage'))
|
||||
if self.manager is None:
|
||||
self.manager = Manager(u'songusage', init_schema)
|
||||
self.songUsageDeleteForm = SongUsageDeleteForm(self.manager,
|
||||
self.formparent)
|
||||
self.songUsageDetailForm = SongUsageDetailForm(self, self.formparent)
|
||||
self.songUsageMenu.menuAction().setVisible(True)
|
||||
self.songUsageActiveButton.show()
|
||||
|
||||
def finalise(self):
|
||||
"""
|
||||
@ -134,26 +150,55 @@ class SongUsagePlugin(Plugin):
|
||||
Plugin.finalise(self)
|
||||
self.songUsageMenu.menuAction().setVisible(False)
|
||||
action_list = ActionList.get_instance()
|
||||
action_list.remove_action(self.songUsageStatus,
|
||||
translate('SongUsagePlugin', 'Song Usage'))
|
||||
action_list.remove_action(self.songUsageDelete,
|
||||
translate('SongUsagePlugin', 'Song Usage'))
|
||||
action_list.remove_action(self.songUsageReport,
|
||||
translate('SongUsagePlugin', 'Song Usage'))
|
||||
action_list.remove_action(self.songUsageStatus,
|
||||
translate('SongUsagePlugin', 'Song Usage'))
|
||||
#stop any events being processed
|
||||
self.SongUsageActive = False
|
||||
self.songUsageActiveButton.hide()
|
||||
# stop any events being processed
|
||||
self.songUsageActive = False
|
||||
|
||||
def toggleSongUsageState(self):
|
||||
self.SongUsageActive = not self.SongUsageActive
|
||||
"""
|
||||
Manage the state of the audit collection and amend
|
||||
the UI when necessary,
|
||||
"""
|
||||
self.songUsageActive = not self.songUsageActive
|
||||
QtCore.QSettings().setValue(self.settingsSection + u'/active',
|
||||
QtCore.QVariant(self.SongUsageActive))
|
||||
QtCore.QVariant(self.songUsageActive))
|
||||
self.setButtonState()
|
||||
|
||||
def setButtonState(self):
|
||||
"""
|
||||
Keep buttons inline. Turn of signals to stop dead loop but we need the
|
||||
button and check box set correctly.
|
||||
"""
|
||||
self.songUsageActiveButton.blockSignals(True)
|
||||
self.songUsageStatus.blockSignals(True)
|
||||
if self.songUsageActive:
|
||||
self.songUsageActiveButton.setIcon(self.activeIcon)
|
||||
self.songUsageStatus.setChecked(True)
|
||||
self.songUsageActiveButton.setChecked(True)
|
||||
self.songUsageActiveButton.setToolTip(translate('SongUsagePlugin',
|
||||
'Song usage tracking is active.'))
|
||||
else:
|
||||
self.songUsageActiveButton.setIcon(self.inactiveIcon)
|
||||
self.songUsageStatus.setChecked(False)
|
||||
self.songUsageActiveButton.setChecked(False)
|
||||
self.songUsageActiveButton.setToolTip(translate('SongUsagePlugin',
|
||||
'Song usage tracking is inactive.'))
|
||||
self.songUsageActiveButton.blockSignals(False)
|
||||
self.songUsageStatus.blockSignals(False)
|
||||
|
||||
|
||||
def onReceiveSongUsage(self, item):
|
||||
"""
|
||||
Song Usage for live song from SlideController
|
||||
"""
|
||||
audit = item[0].audit
|
||||
if self.SongUsageActive and audit:
|
||||
if self.songUsageActive and audit:
|
||||
song_usage_item = SongUsageItem()
|
||||
song_usage_item.usagedate = datetime.today()
|
||||
song_usage_item.usagetime = datetime.now().time()
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 97 KiB |
6
resources/images/README.txt
Normal file
6
resources/images/README.txt
Normal file
@ -0,0 +1,6 @@
|
||||
OpenLP.ico
|
||||
|
||||
This Windows icon contains several images with different resolution.
|
||||
It can be recreated by command:
|
||||
|
||||
icotool -c -o OpenLP.ico openlp-logo-16x16.png openlp-logo-32x32.png openlp-logo-48x48.png openlp-logo-64x64.png openlp-logo-128x128.png
|
BIN
resources/images/general_clone.png
Normal file
BIN
resources/images/general_clone.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 527 B |
@ -56,6 +56,7 @@
|
||||
<file>general_save.png</file>
|
||||
<file>general_email.png</file>
|
||||
<file>general_revert.png</file>
|
||||
<file>general_clone.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="slides">
|
||||
<file>slide_close.png</file>
|
||||
@ -137,6 +138,10 @@
|
||||
<file>messagebox_info.png</file>
|
||||
<file>messagebox_warning.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="songusage">
|
||||
<file>song_usage_active.png</file>
|
||||
<file>song_usage_inactive.png</file>
|
||||
</qresource>
|
||||
<qresource prefix="tools">
|
||||
<file>tools_add.png</file>
|
||||
<file>tools_alert.png</file>
|
||||
|
BIN
resources/images/song_usage_active.png
Normal file
BIN
resources/images/song_usage_active.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 757 B |
BIN
resources/images/song_usage_inactive.png
Normal file
BIN
resources/images/song_usage_inactive.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 716 B |
@ -2,6 +2,99 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtension</key>
|
||||
<array>
|
||||
<string>osz</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array>
|
||||
<string>openlp-logo-with-text.icns</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>OpenLP Service</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>org.openlp.osz</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleTypeExtension</key>
|
||||
<array>
|
||||
<string>otz</string>
|
||||
</array>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array>
|
||||
<string>openlp-logo-with-text.icns</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>OpenLP Theme</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Owner</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>org.openlp.otz</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
|
||||
<key>UTExportedTypeDeclarations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>org.openlp.osz</string>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>OpenLP Service</string>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
<string>public.content</string>
|
||||
</array>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>osz</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
<string>application/x-openlp-service</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>UTTypeIdentifier</key>
|
||||
<string>org.openlp.otz</string>
|
||||
<key>UTTypeDescription</key>
|
||||
<string>OpenLP Theme</string>
|
||||
<key>UTTypeConformsTo</key>
|
||||
<array>
|
||||
<string>public.data</string>
|
||||
<string>public.content</string>
|
||||
</array>
|
||||
<key>UTTypeTagSpecification</key>
|
||||
<dict>
|
||||
<key>public.filename-extension</key>
|
||||
<array>
|
||||
<string>otz</string>
|
||||
</array>
|
||||
<key>public.mime-type</key>
|
||||
<array>
|
||||
<string>application/x-openlp-theme</string>
|
||||
</array>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.openlp</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
|
@ -48,7 +48,7 @@ on run
|
||||
set theViewOptions to the icon view options of container window
|
||||
set arrangement of theViewOptions to not arranged
|
||||
set icon size of theViewOptions to 128
|
||||
set background picture of theViewOptions to file ".installer-background.png"
|
||||
set background picture of theViewOptions to file ".background:installer-background.png"
|
||||
if not exists file "Applications" then
|
||||
make new alias file at container window to POSIX file "/Applications" with properties {name:"Applications"}
|
||||
end if
|
||||
|
@ -49,15 +49,19 @@ on run
|
||||
set theViewOptions to the icon view options of container window
|
||||
set arrangement of theViewOptions to not arranged
|
||||
set icon size of theViewOptions to 128
|
||||
set background picture of theViewOptions to file ".installer-background.png"
|
||||
make new alias file at container window to POSIX file "/Applications" with properties {name:"Applications"}
|
||||
delay 5
|
||||
set background picture of theViewOptions to file ".background:installer-background.png"
|
||||
if not exists file "Applications" then
|
||||
make new alias file at container window to POSIX file "/Applications" with properties {name:"Applications"}
|
||||
end if
|
||||
delay 1
|
||||
set position of item "%s" of container window to {160, 200}
|
||||
set position of item ".Trashes" of container window to {100, 500}
|
||||
set position of item ".installer-background.png" of container window to {200, 500}
|
||||
set position of item ".background" of container window to {200, 500}
|
||||
set position of item ".DS_Store" of container window to {400, 500}
|
||||
set position of item "Applications" of container window to {550, 200}
|
||||
set position of item ".VolumeIcon.icns" of container window to {500, 500}
|
||||
if exists file ".VolumeIcon.icns" then
|
||||
set position of item ".VolumeIcon.icns" of container window to {500, 500}
|
||||
end if
|
||||
set position of item ".fseventsd" of container window to {300, 500}
|
||||
if exists POSIX file ".SymAVx86QSFile" then
|
||||
set position of item ".SymAVx86QSFile" of container window to {600, 500}
|
||||
|
@ -93,8 +93,12 @@ script_name = "build"
|
||||
def build_application(settings, app_name_lower, app_dir):
|
||||
logging.info('[%s] now building the app with pyinstaller at "%s"...',
|
||||
script_name, settings['pyinstaller_basedir'])
|
||||
result = os.system('python %s/pyinstaller.py openlp.spec' \
|
||||
% settings['pyinstaller_basedir'])
|
||||
full_python_dir = os.path.join('/opt/local/Library/Frameworks',
|
||||
'Python.framework/Versions/2.6/Resources/',
|
||||
'Python.app/Contents/MacOS/Python')
|
||||
result = os.system('arch -i386 %s %s/pyinstaller.py openlp.spec' \
|
||||
% ( full_python_dir,
|
||||
settings['pyinstaller_basedir']) )
|
||||
if (result != 0):
|
||||
logging.error('[%s] The pyinstaller build reported an error, cannot \
|
||||
continue!', script_name)
|
||||
@ -219,10 +223,10 @@ def create_dmg(settings):
|
||||
sys.exit(1)
|
||||
|
||||
logging.info('[%s] copying the background image...', script_name)
|
||||
# os.mkdir(volume_basedir + '/.background')
|
||||
os.mkdir(volume_basedir + '/.background')
|
||||
result = os.system('CpMac %s %s'
|
||||
% (settings['installer_backgroundimage_file'],
|
||||
volume_basedir + '/.installer-background.png'))
|
||||
volume_basedir + '/.background/installer-background.png'))
|
||||
if (result != 0):
|
||||
logging.error('[%s] could not copy the background image, dmg creation\
|
||||
failed!', script_name)
|
||||
|
0
resources/osx/openlp-logo-with-text.icns
Executable file → Normal file
0
resources/osx/openlp-logo-with-text.icns
Executable file → Normal file
@ -1,8 +1,8 @@
|
||||
[openlp]
|
||||
openlp_appname = OpenLP
|
||||
openlp_dmgname = OpenLP-1.9.4-bzrXXXX
|
||||
openlp_dmgname = OpenLP-1.9.6-bzrXXXX
|
||||
openlp_version = XXXX
|
||||
openlp_basedir = /Users/openlp/trunk
|
||||
openlp_basedir = /Users/openlp/repo/trunk
|
||||
openlp_icon_file = openlp-logo-with-text.icns
|
||||
openlp_dmg_icon_file = openlp-logo-420x420.png
|
||||
installer_backgroundimage_file = installation-background.png
|
@ -1,5 +1,5 @@
|
||||
# -*- mode: python -*-
|
||||
a = Analysis([os.path.join(HOMEPATH,'support/_mountzlib.py'), os.path.join(HOMEPATH,'support/useUnicode.py'), '%(openlp_basedir)s/openlp.pyw'],
|
||||
a = Analysis([os.path.join(HOMEPATH,'support/_mountzlib.py'), os.path.join(CONFIGDIR,'support/useUnicode.py'), '%(openlp_basedir)s/openlp.pyw'],
|
||||
pathex=['%(pyinstaller_basedir)s'], hookspath=['%(openlp_basedir)s/resources/pyinstaller'])
|
||||
pyz = PYZ(a.pure)
|
||||
exe = EXE(pyz,
|
||||
|
@ -1,14 +0,0 @@
|
||||
# -*- mode: python -*-
|
||||
a = Analysis([
|
||||
os.path.join(HOMEPATH, 'support\\_mountzlib.py'),
|
||||
os.path.join(HOMEPATH, 'support\\useUnicode.py'),
|
||||
os.path.abspath('openlp.pyw')],
|
||||
pathex=[os.path.abspath('.')])
|
||||
pyz = PYZ(a.pure)
|
||||
exe = EXE(pyz, a.scripts, exclude_binaries=1,
|
||||
name=os.path.abspath(os.path.join('build', 'pyi.win32', 'OpenLP',
|
||||
'OpenLP.exe')),
|
||||
debug=False, strip=False, upx=True, console=False,
|
||||
icon=os.path.abspath(os.path.join('resources', 'images', 'OpenLP.ico')))
|
||||
coll = COLLECT(exe, a.binaries, a.zipfiles, a.datas, strip=False, upx=True,
|
||||
name=os.path.abspath(os.path.join('dist', 'OpenLP')))
|
185
scripts/check_dependencies.py
Executable file
185
scripts/check_dependencies.py
Executable file
@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2011 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||
# Armin Köhler, Joshua Millar, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
"""
|
||||
This script is used to check dependencies of OpenLP. It checks availability
|
||||
of required python modules and their version. To verify availability of Python
|
||||
modules, simply run this script::
|
||||
|
||||
@:~$ ./check_dependencies.py
|
||||
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
is_win = sys.platform.startswith('win')
|
||||
|
||||
VERS = {
|
||||
'Python': '2.6',
|
||||
'PyQt4': '4.6',
|
||||
'Qt4': '4.6',
|
||||
'sqlalchemy': '0.5',
|
||||
# pyenchant 1.6 required on Windows
|
||||
'enchant': '1.6' if is_win else '1.3'
|
||||
}
|
||||
|
||||
# pywin32
|
||||
WIN32_MODULES = [
|
||||
'win32com',
|
||||
'win32ui',
|
||||
'pywintypes',
|
||||
]
|
||||
|
||||
MODULES = [
|
||||
'PyQt4',
|
||||
'PyQt4.QtCore',
|
||||
'PyQt4.QtGui',
|
||||
'PyQt4.QtNetwork',
|
||||
'PyQt4.QtOpenGL',
|
||||
'PyQt4.QtSvg',
|
||||
'PyQt4.QtTest',
|
||||
'PyQt4.QtWebKit',
|
||||
'PyQt4.phonon',
|
||||
'sqlalchemy',
|
||||
'sqlite3',
|
||||
'lxml',
|
||||
'chardet',
|
||||
'enchant',
|
||||
'BeautifulSoup',
|
||||
'mako',
|
||||
]
|
||||
|
||||
|
||||
OPTIONAL_MODULES = [
|
||||
('sqlite', ' (SQLite 2 support)'),
|
||||
('MySQLdb', ' (MySQL support)'),
|
||||
('psycopg2', ' (PostgreSQL support)'),
|
||||
]
|
||||
|
||||
w = sys.stdout.write
|
||||
|
||||
def check_vers(version, required, text):
|
||||
if type(version) is str:
|
||||
version = version.split('.')
|
||||
version = map(int, version)
|
||||
if type(required) is str:
|
||||
required = required.split('.')
|
||||
required = map(int, required)
|
||||
w(' %s >= %s ... ' % (text, '.'.join(map(str, required))))
|
||||
if version >= required:
|
||||
w('.'.join(map(str, version)) + os.linesep)
|
||||
return True
|
||||
else:
|
||||
w('FAIL' + os.linesep)
|
||||
return False
|
||||
|
||||
def print_vers_fail(required, text):
|
||||
print(' %s >= %s ... FAIL' % (text, required))
|
||||
|
||||
def verify_python():
|
||||
if not check_vers(list(sys.version_info), VERS['Python'], text='Python'):
|
||||
exit(1)
|
||||
|
||||
def verify_versions():
|
||||
print('Verifying version of modules...')
|
||||
try:
|
||||
from PyQt4 import QtCore
|
||||
check_vers(QtCore.PYQT_VERSION_STR, VERS['PyQt4'], 'PyQt4')
|
||||
check_vers(QtCore.qVersion(), VERS['Qt4'], 'Qt4')
|
||||
except ImportError:
|
||||
print_vers_fail(VERS['PyQt4'], 'PyQt4')
|
||||
print_vers_fail(VERS['Qt4'], 'Qt4')
|
||||
try:
|
||||
import sqlalchemy
|
||||
check_vers(sqlalchemy.__version__, VERS['sqlalchemy'], 'sqlalchemy')
|
||||
except ImportError:
|
||||
print_vers_fail(VERS['sqlalchemy'], 'sqlalchemy')
|
||||
try:
|
||||
import enchant
|
||||
check_vers(enchant.__version__, VERS['enchant'], 'enchant')
|
||||
except ImportError:
|
||||
print_vers_fail(VERS['enchant'], 'enchant')
|
||||
|
||||
def check_module(mod, text='', indent=' '):
|
||||
space = (30 - len(mod) - len(text)) * ' '
|
||||
w(indent + '%s%s... ' % (mod, text) + space)
|
||||
try:
|
||||
__import__(mod)
|
||||
w('OK')
|
||||
except ImportError:
|
||||
w('FAIL')
|
||||
w(os.linesep)
|
||||
|
||||
def verify_pyenchant():
|
||||
w('Enchant (spell checker)... ')
|
||||
try:
|
||||
import enchant
|
||||
w(os.linesep)
|
||||
backends = ', '.join([x.name for x in enchant.Broker().describe()])
|
||||
print(' available backends: %s' % backends)
|
||||
langs = ', '.join(enchant.list_languages())
|
||||
print(' available languages: %s' % langs)
|
||||
except ImportError:
|
||||
w('FAIL' + os.linesep)
|
||||
|
||||
def verify_pyqt():
|
||||
w('Qt4 image formats... ')
|
||||
try:
|
||||
from PyQt4 import QtGui
|
||||
read_f = ', '.join([unicode(format).lower()
|
||||
for format in QtGui.QImageReader.supportedImageFormats()])
|
||||
write_f = ', '.join([unicode(format).lower()
|
||||
for format in QtGui.QImageWriter.supportedImageFormats()])
|
||||
w(os.linesep)
|
||||
print(' read: %s' % read_f)
|
||||
print(' write: %s' % write_f)
|
||||
except ImportError:
|
||||
w('FAIL' + os.linesep)
|
||||
|
||||
def main():
|
||||
verify_python()
|
||||
|
||||
print('Checking for modules...')
|
||||
for m in MODULES:
|
||||
check_module(m)
|
||||
|
||||
print('Checking for optional modules...')
|
||||
for m in OPTIONAL_MODULES:
|
||||
check_module(m[0], text=m[1])
|
||||
|
||||
if is_win:
|
||||
print('Checking for Windows specific modules...')
|
||||
for m in WIN32_MODULES:
|
||||
check_module(m)
|
||||
|
||||
verify_versions()
|
||||
verify_pyqt()
|
||||
verify_pyenchant()
|
||||
|
||||
if __name__ == u'__main__':
|
||||
main()
|
@ -32,8 +32,7 @@ Windows Build Script
|
||||
This script is used to build the Windows binary and the accompanying installer.
|
||||
For this script to work out of the box, it depends on a number of things:
|
||||
|
||||
Python 2.6
|
||||
This build script only works with Python 2.6.
|
||||
Python 2.6/2.7
|
||||
|
||||
PyQt4
|
||||
You should already have this installed, OpenLP doesn't work without it. The
|
||||
@ -47,12 +46,6 @@ PyEnchant
|
||||
Inno Setup 5
|
||||
Inno Setup should be installed into "C:\%PROGRAMFILES%\Inno Setup 5"
|
||||
|
||||
UPX
|
||||
This is used to compress DLLs and EXEs so that they take up less space, but
|
||||
still function exactly the same. To install UPS, download it from
|
||||
http://upx.sourceforge.net/, extract it into C:\%PROGRAMFILES%\UPX, and then
|
||||
add that directory to your PATH environment variable.
|
||||
|
||||
Sphinx
|
||||
This is used to build the documentation. The documentation trunk must be at
|
||||
the same directory level as Openlp trunk and named "documentation"
|
||||
@ -61,7 +54,7 @@ HTML Help Workshop
|
||||
This is used to create the help file
|
||||
|
||||
PyInstaller
|
||||
PyInstaller should be a checkout of revision 844 of trunk, and in a
|
||||
PyInstaller should be a checkout of revision 1470 of trunk, and in a
|
||||
directory called, "pyinstaller" on the same level as OpenLP's Bazaar shared
|
||||
repository directory. The revision is very important as there is currently
|
||||
a major regression in HEAD.
|
||||
@ -73,13 +66,8 @@ PyInstaller
|
||||
http://svn.pyinstaller.org/trunk
|
||||
|
||||
Then you need to copy the two hook-*.py files from the "pyinstaller"
|
||||
subdirectory in OpenLP's "resources" directory into PyInstaller's "hooks"
|
||||
directory.
|
||||
|
||||
Once you've done that, open a command prompt (DOS shell), navigate to the
|
||||
PyInstaller directory and run::
|
||||
|
||||
C:\Projects\pyinstaller>python Configure.py
|
||||
subdirectory in OpenLP's "resources" directory into PyInstaller's
|
||||
"PyInstaller/hooks" directory.
|
||||
|
||||
Bazaar
|
||||
You need the command line "bzr" client installed.
|
||||
@ -102,7 +90,7 @@ psvince.dll
|
||||
the install will fail. The dll can be obtained from here:
|
||||
http://www.vincenzo.net/isxkb/index.php?title=PSVince)
|
||||
|
||||
Mako
|
||||
Mako
|
||||
Mako Templates for Python. This package is required for building the
|
||||
remote plugin. It can be installed by going to your
|
||||
python_directory\scripts\.. and running "easy_install Mako". If you do not
|
||||
@ -137,9 +125,18 @@ site_packages = os.path.join(os.path.split(python_exe)[0], u'Lib',
|
||||
|
||||
# Files and executables
|
||||
pyi_build = os.path.abspath(os.path.join(branch_path, u'..', u'..',
|
||||
u'pyinstaller', u'Build.py'))
|
||||
lrelease_exe = os.path.join(site_packages, u'PyQt4', u'bin', u'lrelease.exe')
|
||||
u'pyinstaller', u'pyinstaller.py'))
|
||||
openlp_main_script = os.path.abspath(os.path.join(branch_path, 'openlp.pyw'))
|
||||
if os.path.exists(os.path.join(site_packages, u'PyQt4', u'bin')):
|
||||
# Older versions of the PyQt4 Windows installer put their binaries in the
|
||||
# "bin" directory
|
||||
lrelease_exe = os.path.join(site_packages, u'PyQt4', u'bin', u'lrelease.exe')
|
||||
else:
|
||||
# Newer versions of the PyQt4 Windows installer put their binaries in the
|
||||
# base directory of the installation
|
||||
lrelease_exe = os.path.join(site_packages, u'PyQt4', u'lrelease.exe')
|
||||
i18n_utils = os.path.join(script_path, u'translation_utils.py')
|
||||
win32_icon = os.path.join(branch_path, u'resources', u'images', 'OpenLP.ico')
|
||||
|
||||
# Paths
|
||||
source_path = os.path.join(branch_path, u'openlp')
|
||||
@ -148,9 +145,8 @@ manual_build_path = os.path.join(manual_path, u'build')
|
||||
helpfile_path = os.path.join(manual_build_path, u'htmlhelp')
|
||||
i18n_path = os.path.join(branch_path, u'resources', u'i18n')
|
||||
winres_path = os.path.join(branch_path, u'resources', u'windows')
|
||||
build_path = os.path.join(branch_path, u'build', u'pyi.win32', u'OpenLP')
|
||||
build_path = os.path.join(branch_path, u'build')
|
||||
dist_path = os.path.join(branch_path, u'dist', u'OpenLP')
|
||||
enchant_path = os.path.join(site_packages, u'enchant')
|
||||
pptviewlib_path = os.path.join(source_path, u'plugins', u'presentations',
|
||||
u'lib', u'pptviewlib')
|
||||
|
||||
@ -174,8 +170,16 @@ def update_code():
|
||||
def run_pyinstaller():
|
||||
print u'Running PyInstaller...'
|
||||
os.chdir(branch_path)
|
||||
pyinstaller = Popen((python_exe, pyi_build, u'-y', u'-o', build_path,
|
||||
os.path.join(winres_path, u'OpenLP.spec')), stdout=PIPE)
|
||||
pyinstaller = Popen((python_exe, pyi_build,
|
||||
u'--noconfirm',
|
||||
u'--windowed',
|
||||
u'--noupx',
|
||||
u'-o', branch_path,
|
||||
u'-i', win32_icon,
|
||||
u'-p', branch_path,
|
||||
u'-n', 'OpenLP',
|
||||
openlp_main_script),
|
||||
stdout=PIPE)
|
||||
output, error = pyinstaller.communicate()
|
||||
code = pyinstaller.wait()
|
||||
if code != 0:
|
||||
@ -208,19 +212,6 @@ def write_version_file():
|
||||
f.write(versionstring)
|
||||
f.close()
|
||||
|
||||
def copy_enchant():
|
||||
print u'Copying enchant/pyenchant...'
|
||||
source = enchant_path
|
||||
dest = os.path.join(dist_path, u'enchant')
|
||||
for root, dirs, files in os.walk(source):
|
||||
for filename in files:
|
||||
if not filename.endswith(u'.pyc') and not filename.endswith(u'.pyo'):
|
||||
dest_path = os.path.join(dest, root[len(source) + 1:])
|
||||
if not os.path.exists(dest_path):
|
||||
os.makedirs(dest_path)
|
||||
copy(os.path.join(root, filename),
|
||||
os.path.join(dest_path, filename))
|
||||
|
||||
def copy_plugins():
|
||||
print u'Copying plugins...'
|
||||
source = os.path.join(source_path, u'plugins')
|
||||
@ -242,10 +233,10 @@ def copy_windows_files():
|
||||
os.path.join(dist_path, u'LICENSE.txt'))
|
||||
copy(os.path.join(winres_path, u'psvince.dll'),
|
||||
os.path.join(dist_path, u'psvince.dll'))
|
||||
if os.path.isfile(os.path.join(helpfile_path, u'Openlp.chm')):
|
||||
if os.path.isfile(os.path.join(helpfile_path, u'OpenLP.chm')):
|
||||
print u' Windows help file found'
|
||||
copy(os.path.join(helpfile_path, u'Openlp.chm'),
|
||||
os.path.join(dist_path, u'Openlp.chm'))
|
||||
copy(os.path.join(helpfile_path, u'OpenLP.chm'),
|
||||
os.path.join(dist_path, u'OpenLP.chm'))
|
||||
else:
|
||||
print u' WARNING ---- Windows help file not found ---- WARNING'
|
||||
|
||||
@ -330,17 +321,19 @@ def main():
|
||||
import sys
|
||||
for arg in sys.argv:
|
||||
if arg == u'-v' or arg == u'--verbose':
|
||||
print "Script path:", script_path
|
||||
print "Branch path:", branch_path
|
||||
print "Source path:", source_path
|
||||
print "\"dist\" path:", dist_path
|
||||
print "PyInstaller:", pyi_build
|
||||
print "OpenLP main script: ......", openlp_main_script
|
||||
print "Script path: .............", script_path
|
||||
print "Branch path: .............", branch_path
|
||||
print "Source path: .............", source_path
|
||||
print "\"dist\" path: .............", dist_path
|
||||
print "PyInstaller: .............", pyi_build
|
||||
print "Documentation branch path:", doc_branch_path
|
||||
print "Help file build path;", helpfile_path
|
||||
print "Inno Setup path:", innosetup_exe
|
||||
print "Windows resources:", winres_path
|
||||
print "VCBuild path:", vcbuild_exe
|
||||
print "PPTVIEWLIB path:", pptviewlib_path
|
||||
print "Help file build path: ....", helpfile_path
|
||||
print "Inno Setup path: .........", innosetup_exe
|
||||
print "Windows resources: .......", winres_path
|
||||
print "VCBuild path: ............", vcbuild_exe
|
||||
print "PPTVIEWLIB path: .........", pptviewlib_path
|
||||
print ""
|
||||
elif arg == u'--skip-update':
|
||||
skip_update = True
|
||||
elif arg == u'/?' or arg == u'-h' or arg == u'--help':
|
||||
@ -353,7 +346,6 @@ def main():
|
||||
build_pptviewlib()
|
||||
run_pyinstaller()
|
||||
write_version_file()
|
||||
copy_enchant()
|
||||
copy_plugins()
|
||||
if os.path.exists(manual_path):
|
||||
run_sphinx()
|
||||
|
Loading…
Reference in New Issue
Block a user