forked from openlp/openlp
r1968
This commit is contained in:
commit
ba8dc0d075
47
documentation/openlp.1
Normal file
47
documentation/openlp.1
Normal file
@ -0,0 +1,47 @@
|
||||
.\" DO NOT MODIFY THIS FILE! It was generated by help2man 1.40.9.
|
||||
.TH OPENLP "1" "May 2012" "OpenLP 1.9.9" "User Commands"
|
||||
.SH NAME
|
||||
OpenLP \- Church worship presentation software
|
||||
.SH SYNOPSIS
|
||||
.B openlp
|
||||
[\fIoptions\fR] [\fIqt-options\fR]
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
\fB\-\-version\fR
|
||||
show program's version number and exit
|
||||
.TP
|
||||
\fB\-h\fR, \fB\-\-help\fR
|
||||
show this help message and exit
|
||||
.TP
|
||||
\fB\-e\fR, \fB\-\-no\-error\-form\fR
|
||||
Disable the error notification form.
|
||||
.TP
|
||||
\fB\-l\fR LEVEL, \fB\-\-log\-level\fR=\fILEVEL\fR
|
||||
Set logging to LEVEL level. Valid values are "debug",
|
||||
"info", "warning".
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-portable\fR
|
||||
Specify if this should be run as a portable app, off a
|
||||
USB flash drive (not implemented).
|
||||
.TP
|
||||
\fB\-d\fR, \fB\-\-dev\-version\fR
|
||||
Ignore the version file and pull the version directly
|
||||
from Bazaar
|
||||
.TP
|
||||
\fB\-s\fR STYLE, \fB\-\-style\fR=\fISTYLE\fR
|
||||
Set the Qt4 style (passed directly to Qt4).
|
||||
.TP
|
||||
\fB\-\-testing\fR
|
||||
Run by testing framework
|
||||
.SH "SEE ALSO"
|
||||
The full documentation for
|
||||
.B OpenLP
|
||||
is maintained as a Texinfo manual. If the
|
||||
.B info
|
||||
and
|
||||
.B OpenLP
|
||||
programs are properly installed at your site, the command
|
||||
.IP
|
||||
.B info OpenLP
|
||||
.PP
|
||||
should give you access to the complete manual.
|
@ -1 +1 @@
|
||||
1.9.5-bzr1421
|
||||
1.9.9-bzr1956
|
||||
|
@ -49,7 +49,7 @@ from openlp.core.ui.firsttimeform import FirstTimeForm
|
||||
from openlp.core.ui.exceptionform import ExceptionForm
|
||||
from openlp.core.ui import SplashScreen, ScreenList
|
||||
from openlp.core.utils import AppLocation, LanguageManager, VersionThread, \
|
||||
get_application_version, DelayStartThread
|
||||
get_application_version
|
||||
|
||||
|
||||
__all__ = [u'OpenLP', u'main']
|
||||
@ -145,7 +145,6 @@ class OpenLP(QtGui.QApplication):
|
||||
VersionThread(self.mainWindow).start()
|
||||
Receiver.send_message(u'live_display_blank_check')
|
||||
self.mainWindow.appStartup()
|
||||
DelayStartThread(self.mainWindow).start()
|
||||
# Skip exec_() for gui tests
|
||||
if not testing:
|
||||
return self.exec_()
|
||||
|
@ -62,6 +62,7 @@ class ServiceItemAction(object):
|
||||
PreviousLastSlide = 2
|
||||
Next = 3
|
||||
|
||||
|
||||
def translate(context, text, comment=None,
|
||||
encoding=QtCore.QCoreApplication.CodecForTr, n=-1,
|
||||
translate=QtCore.QCoreApplication.translate):
|
||||
@ -83,6 +84,7 @@ def translate(context, text, comment=None,
|
||||
"""
|
||||
return translate(context, text, comment, encoding, n)
|
||||
|
||||
|
||||
def get_text_file_string(text_file):
|
||||
"""
|
||||
Open a file and return its content as unicode string. If the supplied file
|
||||
@ -111,6 +113,7 @@ def get_text_file_string(text_file):
|
||||
file_handle.close()
|
||||
return content_string
|
||||
|
||||
|
||||
def str_to_bool(stringvalue):
|
||||
"""
|
||||
Convert a string version of a boolean into a real boolean.
|
||||
@ -122,6 +125,7 @@ def str_to_bool(stringvalue):
|
||||
return stringvalue
|
||||
return unicode(stringvalue).strip().lower() in (u'true', u'yes', u'y')
|
||||
|
||||
|
||||
def build_icon(icon):
|
||||
"""
|
||||
Build a QIcon instance from an existing QIcon, a resource location, or a
|
||||
@ -148,6 +152,7 @@ def build_icon(icon):
|
||||
QtGui.QIcon.Normal, QtGui.QIcon.Off)
|
||||
return button_icon
|
||||
|
||||
|
||||
def image_to_byte(image):
|
||||
"""
|
||||
Resize an image to fit on the current screen for the web and returns
|
||||
@ -166,6 +171,7 @@ def image_to_byte(image):
|
||||
# convert to base64 encoding so does not get missed!
|
||||
return byte_array.toBase64()
|
||||
|
||||
|
||||
def create_thumb(image_path, thumb_path, return_icon=True, size=None):
|
||||
"""
|
||||
Create a thumbnail from the given image path and depending on
|
||||
@ -201,6 +207,7 @@ def create_thumb(image_path, thumb_path, return_icon=True, size=None):
|
||||
# Fallback for files with animation support.
|
||||
return build_icon(unicode(image_path))
|
||||
|
||||
|
||||
def validate_thumb(file_path, thumb_path):
|
||||
"""
|
||||
Validates whether an file's thumb still exists and if is up to date.
|
||||
@ -219,6 +226,7 @@ def validate_thumb(file_path, thumb_path):
|
||||
thumb_date = os.stat(thumb_path).st_mtime
|
||||
return image_date <= thumb_date
|
||||
|
||||
|
||||
def resize_image(image_path, width, height, background=u'#000000'):
|
||||
"""
|
||||
Resize an image to fit on the current screen.
|
||||
@ -266,6 +274,7 @@ def resize_image(image_path, width, height, background=u'#000000'):
|
||||
painter.drawImage((width - realw) / 2, (height - realh) / 2, preview)
|
||||
return new_image
|
||||
|
||||
|
||||
def check_item_selected(list_widget, message):
|
||||
"""
|
||||
Check if a list item is selected so an action may be performed on it
|
||||
@ -282,6 +291,7 @@ def check_item_selected(list_widget, message):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def clean_tags(text):
|
||||
"""
|
||||
Remove Tags from text for display
|
||||
@ -294,6 +304,7 @@ def clean_tags(text):
|
||||
text = text.replace(tag[u'end tag'], u'')
|
||||
return text
|
||||
|
||||
|
||||
def expand_tags(text):
|
||||
"""
|
||||
Expand tags HTML for display
|
||||
@ -303,6 +314,7 @@ def expand_tags(text):
|
||||
text = text.replace(tag[u'end tag'], tag[u'end html'])
|
||||
return text
|
||||
|
||||
|
||||
def check_directory_exists(dir):
|
||||
"""
|
||||
Check a theme directory exists and if not create it
|
||||
@ -317,6 +329,7 @@ def check_directory_exists(dir):
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
|
||||
def create_separated_list(stringlist):
|
||||
"""
|
||||
Returns a string that represents a join of a list of strings with a
|
||||
@ -345,6 +358,7 @@ def create_separated_list(stringlist):
|
||||
return unicode(translate('OpenLP.core.lib', '%1, %2',
|
||||
u'Locale list separator: start').arg(stringlist[0], merged))
|
||||
|
||||
|
||||
from eventreceiver import Receiver
|
||||
from listwidgetwithdnd import ListWidgetWithDnD
|
||||
from formattingtags import FormattingTags
|
||||
|
@ -53,8 +53,7 @@ class OpenLPDockWidget(QtGui.QDockWidget):
|
||||
self.setWindowIcon(build_icon(icon))
|
||||
# Sort out the minimum width.
|
||||
screens = ScreenList.get_instance()
|
||||
screen_width = screens.current[u'size'].width()
|
||||
mainwindow_docbars = screen_width / 5
|
||||
mainwindow_docbars = screens.current[u'size'].width() / 5
|
||||
if mainwindow_docbars > 300:
|
||||
self.setMinimumWidth(300)
|
||||
else:
|
||||
|
@ -46,14 +46,36 @@ class FormattingTags(object):
|
||||
"""
|
||||
Provide access to the html_expands list.
|
||||
"""
|
||||
# Load user defined tags otherwise user defined tags are not present.
|
||||
FormattingTags.load_tags()
|
||||
return FormattingTags.html_expands
|
||||
|
||||
@staticmethod
|
||||
def reset_html_tags():
|
||||
def save_html_tags():
|
||||
"""
|
||||
Resets the html_expands list.
|
||||
Saves all formatting tags except protected ones.
|
||||
"""
|
||||
tags = []
|
||||
for tag in FormattingTags.html_expands:
|
||||
if not tag[u'protected'] and not tag.get(u'temporary'):
|
||||
# Using dict ensures that copy is made and encoding of values
|
||||
# a little later does not affect tags in the original list
|
||||
tags.append(dict(tag))
|
||||
tag = tags[-1]
|
||||
# Remove key 'temporary' from tags.
|
||||
# It is not needed to be saved.
|
||||
if u'temporary' in tag:
|
||||
del tag[u'temporary']
|
||||
for element in tag:
|
||||
if isinstance(tag[element], unicode):
|
||||
tag[element] = tag[element].encode('utf8')
|
||||
# Formatting Tags were also known as display tags.
|
||||
QtCore.QSettings().setValue(u'displayTags/html_tags',
|
||||
QtCore.QVariant(cPickle.dumps(tags) if tags else u''))
|
||||
|
||||
@staticmethod
|
||||
def load_tags():
|
||||
"""
|
||||
Load the Tags from store so can be used in the system or used to
|
||||
update the display.
|
||||
"""
|
||||
temporary_tags = [tag for tag in FormattingTags.html_expands
|
||||
if tag.get(u'temporary')]
|
||||
@ -141,38 +163,6 @@ class FormattingTags(object):
|
||||
FormattingTags.add_html_tags(base_tags)
|
||||
FormattingTags.add_html_tags(temporary_tags)
|
||||
|
||||
@staticmethod
|
||||
def save_html_tags():
|
||||
"""
|
||||
Saves all formatting tags except protected ones.
|
||||
"""
|
||||
tags = []
|
||||
for tag in FormattingTags.html_expands:
|
||||
if not tag[u'protected'] and not tag.get(u'temporary'):
|
||||
# Using dict ensures that copy is made and encoding of values
|
||||
# a little later does not affect tags in the original list
|
||||
tags.append(dict(tag))
|
||||
tag = tags[-1]
|
||||
# Remove key 'temporary' from tags.
|
||||
# It is not needed to be saved.
|
||||
if u'temporary' in tag:
|
||||
del tag[u'temporary']
|
||||
for element in tag:
|
||||
if isinstance(tag[element], unicode):
|
||||
tag[element] = tag[element].encode('utf8')
|
||||
# Formatting Tags were also known as display tags.
|
||||
QtCore.QSettings().setValue(u'displayTags/html_tags',
|
||||
QtCore.QVariant(cPickle.dumps(tags) if tags else u''))
|
||||
|
||||
@staticmethod
|
||||
def load_tags():
|
||||
"""
|
||||
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
|
||||
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()
|
||||
@ -188,17 +178,13 @@ class FormattingTags(object):
|
||||
FormattingTags.add_html_tags(user_tags)
|
||||
|
||||
@staticmethod
|
||||
def add_html_tags(tags, save=False):
|
||||
def add_html_tags(tags):
|
||||
"""
|
||||
Add a list of tags to the list.
|
||||
|
||||
``tags``
|
||||
The list with tags to add.
|
||||
|
||||
``save``
|
||||
Defaults to ``False``. If set to ``True`` the given ``tags`` are
|
||||
saved to the config.
|
||||
|
||||
Each **tag** has to be a ``dict`` and should have the following keys:
|
||||
|
||||
* desc
|
||||
@ -226,8 +212,6 @@ class FormattingTags(object):
|
||||
displaying text containing the tag. It has to be a ``boolean``.
|
||||
"""
|
||||
FormattingTags.html_expands.extend(tags)
|
||||
if save:
|
||||
FormattingTags.save_html_tags()
|
||||
|
||||
@staticmethod
|
||||
def remove_html_tag(tag_id):
|
||||
|
@ -100,6 +100,7 @@ class Image(object):
|
||||
variables ``image`` and ``image_bytes`` to ``None`` and add the image object
|
||||
to the queue of images to process.
|
||||
"""
|
||||
secondary_priority = 0
|
||||
def __init__(self, name, path, source, background):
|
||||
self.name = name
|
||||
self.path = path
|
||||
@ -108,25 +109,40 @@ class Image(object):
|
||||
self.priority = Priority.Normal
|
||||
self.source = source
|
||||
self.background = background
|
||||
self.secondary_priority = Image.secondary_priority
|
||||
Image.secondary_priority += 1
|
||||
|
||||
|
||||
class PriorityQueue(Queue.PriorityQueue):
|
||||
"""
|
||||
Customised ``Queue.PriorityQueue``.
|
||||
|
||||
Each item in the queue must be tuple with three values. The first value
|
||||
is the :class:`Image`'s ``priority`` attribute, the second value
|
||||
the :class:`Image`'s ``secondary_priority`` attribute. The last value the
|
||||
:class:`Image` instance itself::
|
||||
|
||||
(image.priority, image.secondary_priority, image)
|
||||
|
||||
Doing this, the :class:`Queue.PriorityQueue` will sort the images according
|
||||
to their priorities, but also according to there number. However, the number
|
||||
only has an impact on the result if there are more images with the same
|
||||
priority. In such case the image which has been added earlier is privileged.
|
||||
"""
|
||||
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.
|
||||
The image to remove. This should be an :class:`Image` instance.
|
||||
|
||||
``new_priority``
|
||||
The image's new priority.
|
||||
The image's new priority. See the :class:`Priority` class for
|
||||
priorities.
|
||||
"""
|
||||
self.remove(image)
|
||||
image.priority = new_priority
|
||||
self.put((image.priority, image))
|
||||
self.put((image.priority, image.secondary_priority, image))
|
||||
|
||||
def remove(self, image):
|
||||
"""
|
||||
@ -135,8 +151,8 @@ class PriorityQueue(Queue.PriorityQueue):
|
||||
``image``
|
||||
The image to remove. This should be an ``Image`` instance.
|
||||
"""
|
||||
if (image.priority, image) in self.queue:
|
||||
self.queue.remove((image.priority, image))
|
||||
if (image.priority, image.secondary_priority, image) in self.queue:
|
||||
self.queue.remove((image.priority, image.secondary_priority, image))
|
||||
|
||||
|
||||
class ImageManager(QtCore.QObject):
|
||||
@ -261,7 +277,8 @@ class ImageManager(QtCore.QObject):
|
||||
if not name in self._cache:
|
||||
image = Image(name, path, source, background)
|
||||
self._cache[name] = image
|
||||
self._conversion_queue.put((image.priority, image))
|
||||
self._conversion_queue.put(
|
||||
(image.priority, image.secondary_priority, image))
|
||||
else:
|
||||
log.debug(u'Image in cache %s:%s' % (name, path))
|
||||
# We want only one thread.
|
||||
@ -282,7 +299,7 @@ class ImageManager(QtCore.QObject):
|
||||
Actually does the work.
|
||||
"""
|
||||
log.debug(u'_process_cache')
|
||||
image = self._conversion_queue.get()[1]
|
||||
image = self._conversion_queue.get()[2]
|
||||
# Generate the QImage for the image.
|
||||
if image.image is None:
|
||||
image.image = resize_image(image.path, self.width, self.height,
|
||||
|
@ -373,12 +373,12 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
Process a list for files either from the File Dialog or from Drag and
|
||||
Drop
|
||||
|
||||
``files``
|
||||
The files to be loaded
|
||||
``files``
|
||||
The files to be loaded.
|
||||
"""
|
||||
names = []
|
||||
fullList = []
|
||||
for count in range(0, self.listView.count()):
|
||||
for count in range(self.listView.count()):
|
||||
names.append(unicode(self.listView.item(count).text()))
|
||||
fullList.append(unicode(self.listView.item(count).
|
||||
data(QtCore.Qt.UserRole).toString()))
|
||||
@ -582,7 +582,7 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
Common method for generating a service item
|
||||
"""
|
||||
serviceItem = ServiceItem(self.plugin)
|
||||
serviceItem.add_icon(self.plugin.icon_path)
|
||||
serviceItem.add_icon(self.plugin.iconPath)
|
||||
if self.generateSlideData(serviceItem, item, xmlVersion, remote):
|
||||
return serviceItem
|
||||
else:
|
||||
|
@ -155,9 +155,9 @@ class Plugin(QtCore.QObject):
|
||||
self.version = get_application_version()[u'version']
|
||||
self.settingsSection = self.name
|
||||
self.icon = None
|
||||
self.media_item_class = media_item_class
|
||||
self.settings_tab_class = settings_tab_class
|
||||
self.settings_tab = None
|
||||
self.mediaItemClass = media_item_class
|
||||
self.settingsTabClass = settings_tab_class
|
||||
self.settingsTab = None
|
||||
self.mediaItem = None
|
||||
self.weight = 0
|
||||
self.status = PluginStatus.Inactive
|
||||
@ -166,9 +166,9 @@ class Plugin(QtCore.QObject):
|
||||
self.renderer = plugin_helpers[u'renderer']
|
||||
self.serviceManager = plugin_helpers[u'service']
|
||||
self.settingsForm = plugin_helpers[u'settings form']
|
||||
self.mediadock = plugin_helpers[u'toolbox']
|
||||
self.mediaDock = plugin_helpers[u'toolbox']
|
||||
self.pluginManager = plugin_helpers[u'pluginmanager']
|
||||
self.formparent = plugin_helpers[u'formparent']
|
||||
self.formParent = plugin_helpers[u'formparent']
|
||||
self.mediaController = plugin_helpers[u'mediacontroller']
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'%s_add_service_item' % self.name),
|
||||
@ -219,8 +219,8 @@ class Plugin(QtCore.QObject):
|
||||
Construct a MediaManagerItem object with all the buttons and things
|
||||
you need, and return it for integration into OpenLP.
|
||||
"""
|
||||
if self.media_item_class:
|
||||
self.mediaItem = self.media_item_class(self.mediadock.media_dock,
|
||||
if self.mediaItemClass:
|
||||
self.mediaItem = self.mediaItemClass(self.mediaDock.media_dock,
|
||||
self, self.icon)
|
||||
|
||||
def addImportMenuItem(self, importMenu):
|
||||
@ -255,10 +255,10 @@ class Plugin(QtCore.QObject):
|
||||
Create a tab for the settings window to display the configurable options
|
||||
for this plugin to the user.
|
||||
"""
|
||||
if self.settings_tab_class:
|
||||
self.settings_tab = self.settings_tab_class(parent, self.name,
|
||||
if self.settingsTabClass:
|
||||
self.settingsTab = self.settingsTabClass(parent, self.name,
|
||||
self.getString(StringContent.VisibleName)[u'title'],
|
||||
self.icon_path)
|
||||
self.iconPath)
|
||||
|
||||
def addToMenu(self, menubar):
|
||||
"""
|
||||
@ -294,14 +294,14 @@ class Plugin(QtCore.QObject):
|
||||
"""
|
||||
if self.mediaItem:
|
||||
self.mediaItem.initialise()
|
||||
self.mediadock.insert_dock(self.mediaItem, self.icon, self.weight)
|
||||
self.mediaDock.insert_dock(self.mediaItem, self.icon, self.weight)
|
||||
|
||||
def finalise(self):
|
||||
"""
|
||||
Called by the plugin Manager to cleanup things.
|
||||
"""
|
||||
if self.mediaItem:
|
||||
self.mediadock.remove_dock(self.mediaItem)
|
||||
self.mediaDock.remove_dock(self.mediaItem)
|
||||
|
||||
def appStartup(self):
|
||||
"""
|
||||
|
@ -125,13 +125,12 @@ class Renderer(object):
|
||||
Set the appropriate theme depending on the theme level.
|
||||
Called by the service item when building a display frame
|
||||
|
||||
``theme``
|
||||
``override_theme``
|
||||
The name of the song-level theme. None means the service
|
||||
item wants to use the given value.
|
||||
|
||||
``override_levels``
|
||||
Used to force the theme data passed in to be used.
|
||||
|
||||
"""
|
||||
log.debug(u'set override theme to %s', override_theme)
|
||||
theme_level = self.theme_level
|
||||
@ -236,8 +235,8 @@ class Renderer(object):
|
||||
# the first two slides (and neglect the last for now).
|
||||
if len(slides) == 3:
|
||||
html_text = expand_tags(u'\n'.join(slides[:2]))
|
||||
# We check both slides to determine if the optional break is
|
||||
# needed (there is only one optional break).
|
||||
# We check both slides to determine if the optional split is
|
||||
# needed (there is only one optional split).
|
||||
else:
|
||||
html_text = expand_tags(u'\n'.join(slides))
|
||||
html_text = html_text.replace(u'\n', u'<br>')
|
||||
@ -248,14 +247,18 @@ class Renderer(object):
|
||||
else:
|
||||
# The first optional slide fits, which means we have to
|
||||
# render the first optional slide.
|
||||
text_contains_break = u'[---]' in text
|
||||
if text_contains_break:
|
||||
text_contains_split = u'[---]' in text
|
||||
if text_contains_split:
|
||||
try:
|
||||
text_to_render, text = \
|
||||
text.split(u'\n[---]\n', 1)
|
||||
except:
|
||||
text_to_render = text.split(u'\n[---]\n')[0]
|
||||
text = u''
|
||||
text_to_render, raw_tags, html_tags = \
|
||||
self._get_start_tags(text_to_render)
|
||||
if text:
|
||||
text = raw_tags + text
|
||||
else:
|
||||
text_to_render = text
|
||||
text = u''
|
||||
@ -264,7 +267,7 @@ class Renderer(object):
|
||||
if len(slides) > 1 and text:
|
||||
# Add all slides apart from the last one the list.
|
||||
pages.extend(slides[:-1])
|
||||
if text_contains_break:
|
||||
if text_contains_split:
|
||||
text = slides[-1] + u'\n[---]\n' + text
|
||||
else:
|
||||
text = slides[-1] + u'\n'+ text
|
||||
@ -364,7 +367,7 @@ class Renderer(object):
|
||||
self.web.setVisible(False)
|
||||
self.web.resize(self.page_width, self.page_height)
|
||||
self.web_frame = self.web.page().mainFrame()
|
||||
# Adjust width and height to account for shadow. outline done in css
|
||||
# Adjust width and height to account for shadow. outline done in css.
|
||||
html = u"""<!DOCTYPE html><html><head><script>
|
||||
function show_text(newtext) {
|
||||
var main = document.getElementById('main');
|
||||
@ -493,19 +496,22 @@ class Renderer(object):
|
||||
(raw_text.find(tag[u'start tag']), tag[u'start tag'],
|
||||
tag[u'end tag']))
|
||||
html_tags.append(
|
||||
(raw_text.find(tag[u'start tag']), tag[u'start html']))
|
||||
(raw_text.find(tag[u'start tag']), tag[u'start html']))
|
||||
# Sort the lists, so that the tags which were opened first on the first
|
||||
# slide (the text we are checking) will be opened first on the next
|
||||
# slide as well.
|
||||
raw_tags.sort(key=lambda tag: tag[0])
|
||||
html_tags.sort(key=lambda tag: tag[0])
|
||||
# Create a list with closing tags for the raw_text.
|
||||
end_tags = [tag[2] for tag in raw_tags]
|
||||
end_tags = []
|
||||
start_tags = []
|
||||
for tag in raw_tags:
|
||||
start_tags.append(tag[1])
|
||||
end_tags.append(tag[2])
|
||||
end_tags.reverse()
|
||||
# Remove the indexes.
|
||||
raw_tags = [tag[1] for tag in raw_tags]
|
||||
html_tags = [tag[1] for tag in html_tags]
|
||||
return raw_text + u''.join(end_tags), u''.join(raw_tags), \
|
||||
return raw_text + u''.join(end_tags), u''.join(start_tags), \
|
||||
u''.join(html_tags)
|
||||
|
||||
def _binary_chop(self, formatted, previous_html, previous_raw, html_list,
|
||||
|
@ -195,8 +195,7 @@ class ServiceItem(object):
|
||||
# avoid tracebacks.
|
||||
if self.raw_footer is None:
|
||||
self.raw_footer = []
|
||||
self.foot_text = \
|
||||
u'<br>'.join([footer for footer in self.raw_footer if footer])
|
||||
self.foot_text = u'<br>'.join(filter(None, self.raw_footer))
|
||||
|
||||
def add_from_image(self, path, title, background=None):
|
||||
"""
|
||||
@ -314,17 +313,12 @@ class ServiceItem(object):
|
||||
self.from_plugin = header[u'from_plugin']
|
||||
self.capabilities = header[u'capabilities']
|
||||
# Added later so may not be present in older services.
|
||||
if u'search' in header:
|
||||
self.search_string = header[u'search']
|
||||
self.data_string = header[u'data']
|
||||
if u'xml_version' in header:
|
||||
self.xml_version = header[u'xml_version']
|
||||
if u'start_time' in header:
|
||||
self.start_time = header[u'start_time']
|
||||
if u'end_time' in header:
|
||||
self.end_time = header[u'end_time']
|
||||
if u'media_length' in header:
|
||||
self.media_length = header[u'media_length']
|
||||
self.search_string = header.get(u'search', u'')
|
||||
self.data_string = header.get(u'data', u'')
|
||||
self.xml_version = header.get(u'xml_version')
|
||||
self.start_time = header.get(u'start_time', 0)
|
||||
self.end_time = header.get(u'end_time', 0)
|
||||
self.media_length = header.get(u'media_length', 0)
|
||||
if u'background_audio' in header:
|
||||
self.background_audio = []
|
||||
for filename in header[u'background_audio']:
|
||||
|
@ -104,7 +104,7 @@ class SettingsManager(object):
|
||||
u'%s count' % name, QtCore.QVariant(0)).toInt()[0]
|
||||
new_count = len(list)
|
||||
settings.setValue(u'%s count' % name, QtCore.QVariant(new_count))
|
||||
for counter in range (0, new_count):
|
||||
for counter in range(new_count):
|
||||
settings.setValue(
|
||||
u'%s %d' % (name, counter), QtCore.QVariant(list[counter-1]))
|
||||
if old_count > new_count:
|
||||
@ -130,7 +130,7 @@ class SettingsManager(object):
|
||||
u'%s count' % name, QtCore.QVariant(0)).toInt()[0]
|
||||
list = []
|
||||
if list_count:
|
||||
for counter in range(0, list_count):
|
||||
for counter in range(list_count):
|
||||
item = unicode(
|
||||
settings.value(u'%s %d' % (name, counter)).toString())
|
||||
if item:
|
||||
|
@ -47,7 +47,7 @@ class SettingsTab(QtGui.QWidget):
|
||||
self.tabTitleVisible = visible_title
|
||||
self.settingsSection = self.tabTitle.lower()
|
||||
if icon_path:
|
||||
self.icon_path = icon_path
|
||||
self.iconPath = icon_path
|
||||
self.setupUi()
|
||||
self.retranslateUi()
|
||||
self.initialise()
|
||||
|
@ -143,6 +143,7 @@ class UiStrings(object):
|
||||
self.View = translate('OpenLP.Ui', 'View')
|
||||
self.ViewMode = translate('OpenLP.Ui', 'View Mode')
|
||||
|
||||
|
||||
def add_welcome_page(parent, image):
|
||||
"""
|
||||
Generate an opening welcome page for a wizard using a provided image.
|
||||
@ -170,6 +171,7 @@ def add_welcome_page(parent, image):
|
||||
parent.welcomeLayout.addStretch()
|
||||
parent.addPage(parent.welcomePage)
|
||||
|
||||
|
||||
def create_button_box(dialog, name, standard_buttons, custom_buttons=[]):
|
||||
"""
|
||||
Creates a QDialogButtonBox with the given buttons. The ``accepted()`` and
|
||||
@ -216,6 +218,7 @@ def create_button_box(dialog, name, standard_buttons, custom_buttons=[]):
|
||||
dialog.reject)
|
||||
return button_box
|
||||
|
||||
|
||||
def critical_error_message_box(title=None, message=None, parent=None,
|
||||
question=False):
|
||||
"""
|
||||
@ -242,6 +245,7 @@ def critical_error_message_box(title=None, message=None, parent=None,
|
||||
data[u'title'] = title if title else UiStrings().Error
|
||||
return Receiver.send_message(u'openlp_error_message', data)
|
||||
|
||||
|
||||
def create_horizontal_adjusting_combo_box(parent, name):
|
||||
"""
|
||||
Creates a QComboBox with adapting width for media items.
|
||||
@ -258,6 +262,7 @@ def create_horizontal_adjusting_combo_box(parent, name):
|
||||
combo.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed)
|
||||
return combo
|
||||
|
||||
|
||||
def create_button(parent, name, **kwargs):
|
||||
"""
|
||||
Return an button with the object name set and the given parameters.
|
||||
@ -324,6 +329,7 @@ def create_button(parent, name, **kwargs):
|
||||
log.warn(u'Parameter %s was not consumed in create_button().', key)
|
||||
return button
|
||||
|
||||
|
||||
def create_action(parent, name, **kwargs):
|
||||
"""
|
||||
Return an action with the object name set and the given parameters.
|
||||
@ -411,6 +417,7 @@ def create_action(parent, name, **kwargs):
|
||||
log.warn(u'Parameter %s was not consumed in create_action().', key)
|
||||
return action
|
||||
|
||||
|
||||
def create_widget_action(parent, name=u'', **kwargs):
|
||||
"""
|
||||
Return a new QAction by calling ``create_action(parent, name, **kwargs)``.
|
||||
@ -422,6 +429,7 @@ def create_widget_action(parent, name=u'', **kwargs):
|
||||
parent.addAction(action)
|
||||
return action
|
||||
|
||||
|
||||
def set_case_insensitive_completer(cache, widget):
|
||||
"""
|
||||
Sets a case insensitive text completer for a widget.
|
||||
@ -436,6 +444,7 @@ def set_case_insensitive_completer(cache, widget):
|
||||
completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
|
||||
widget.setCompleter(completer)
|
||||
|
||||
|
||||
def create_valign_selection_widgets(parent):
|
||||
"""
|
||||
Creates a standard label and combo box for asking users to select a
|
||||
@ -455,6 +464,7 @@ def create_valign_selection_widgets(parent):
|
||||
label.setBuddy(combo_box)
|
||||
return label, combo_box
|
||||
|
||||
|
||||
def find_and_set_in_combo_box(combo_box, value_to_find):
|
||||
"""
|
||||
Find a string in a combo box and set it as the selected item if present
|
||||
|
@ -209,7 +209,7 @@ class Theme(object):
|
||||
val = int(element_text[1:], 16)
|
||||
except ValueError: # nope
|
||||
pass
|
||||
elif DELPHI_COLORS.has_key(element_text):
|
||||
elif element_text in DELPHI_COLORS:
|
||||
val = DELPHI_COLORS[element_text]
|
||||
delphi_color_change = True
|
||||
else:
|
||||
|
@ -59,7 +59,7 @@ class AdvancedTab(SettingsTab):
|
||||
'#strftime-strptime-behavior for more information.'))
|
||||
self.defaultImage = u':/graphics/openlp-splash-screen.png'
|
||||
self.defaultColor = u'#ffffff'
|
||||
self.icon_path = u':/system/system_settings.png'
|
||||
self.iconPath = u':/system/system_settings.png'
|
||||
advanced_translated = translate('OpenLP.AdvancedTab', 'Advanced')
|
||||
SettingsTab.__init__(self, parent, u'Advanced', advanced_translated)
|
||||
|
||||
|
@ -77,6 +77,8 @@ try:
|
||||
UNO_VERSION = node.getByName(u'ooSetupVersion')
|
||||
except ImportError:
|
||||
UNO_VERSION = u'-'
|
||||
except:
|
||||
UNO_VERSION = u'- (Possible non-standard UNO installation)'
|
||||
try:
|
||||
WEBKIT_VERSION = QtWebKit.qWebKitVersion()
|
||||
except AttributeError:
|
||||
|
@ -57,6 +57,14 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
QtCore.SIGNAL(u'clicked()'), self.onDeleteClicked)
|
||||
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'rejected()'),
|
||||
self.close)
|
||||
QtCore.QObject.connect(self.descriptionLineEdit,
|
||||
QtCore.SIGNAL(u'textEdited(QString)'), self.onTextEdited)
|
||||
QtCore.QObject.connect(self.tagLineEdit,
|
||||
QtCore.SIGNAL(u'textEdited(QString)'), self.onTextEdited)
|
||||
QtCore.QObject.connect(self.startTagLineEdit,
|
||||
QtCore.SIGNAL(u'textEdited(QString)'), self.onTextEdited)
|
||||
QtCore.QObject.connect(self.endTagLineEdit,
|
||||
QtCore.SIGNAL(u'textEdited(QString)'), self.onTextEdited)
|
||||
# Forces reloading of tags from openlp configuration.
|
||||
FormattingTags.load_tags()
|
||||
|
||||
@ -65,7 +73,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
Load Display and set field state.
|
||||
"""
|
||||
# Create initial copy from master
|
||||
self._resetTable()
|
||||
self._reloadTable()
|
||||
self.selected = -1
|
||||
return QtGui.QDialog.exec_(self)
|
||||
|
||||
@ -73,9 +81,9 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
"""
|
||||
Table Row selected so display items and set field state.
|
||||
"""
|
||||
row = self.tagTableWidget.currentRow()
|
||||
html = FormattingTags.html_expands[row]
|
||||
self.selected = row
|
||||
self.savePushButton.setEnabled(False)
|
||||
self.selected = self.tagTableWidget.currentRow()
|
||||
html = FormattingTags.get_html_tags()[self.selected]
|
||||
self.descriptionLineEdit.setText(html[u'desc'])
|
||||
self.tagLineEdit.setText(self._strip(html[u'start tag']))
|
||||
self.startTagLineEdit.setText(html[u'start html'])
|
||||
@ -85,21 +93,26 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
self.tagLineEdit.setEnabled(False)
|
||||
self.startTagLineEdit.setEnabled(False)
|
||||
self.endTagLineEdit.setEnabled(False)
|
||||
self.savePushButton.setEnabled(False)
|
||||
self.deletePushButton.setEnabled(False)
|
||||
else:
|
||||
self.descriptionLineEdit.setEnabled(True)
|
||||
self.tagLineEdit.setEnabled(True)
|
||||
self.startTagLineEdit.setEnabled(True)
|
||||
self.endTagLineEdit.setEnabled(True)
|
||||
self.savePushButton.setEnabled(True)
|
||||
self.deletePushButton.setEnabled(True)
|
||||
|
||||
def onTextEdited(self, text):
|
||||
"""
|
||||
Enable the ``savePushButton`` when any of the selected tag's properties
|
||||
has been changed.
|
||||
"""
|
||||
self.savePushButton.setEnabled(True)
|
||||
|
||||
def onNewClicked(self):
|
||||
"""
|
||||
Add a new tag to list only if it is not a duplicate.
|
||||
"""
|
||||
for html in FormattingTags.html_expands:
|
||||
for html in FormattingTags.get_html_tags():
|
||||
if self._strip(html[u'start tag']) == u'n':
|
||||
critical_error_message_box(
|
||||
translate('OpenLP.FormattingTagForm', 'Update Error'),
|
||||
@ -117,11 +130,13 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
u'temporary': False
|
||||
}
|
||||
FormattingTags.add_html_tags([tag])
|
||||
self._resetTable()
|
||||
FormattingTags.save_html_tags()
|
||||
self._reloadTable()
|
||||
# Highlight new row
|
||||
self.tagTableWidget.selectRow(self.tagTableWidget.rowCount() - 1)
|
||||
self.onRowSelected()
|
||||
self.tagTableWidget.scrollToBottom()
|
||||
#self.savePushButton.setEnabled(False)
|
||||
|
||||
def onDeleteClicked(self):
|
||||
"""
|
||||
@ -130,14 +145,14 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
if self.selected != -1:
|
||||
FormattingTags.remove_html_tag(self.selected)
|
||||
self.selected = -1
|
||||
self._resetTable()
|
||||
FormattingTags.save_html_tags()
|
||||
FormattingTags.save_html_tags()
|
||||
self._reloadTable()
|
||||
|
||||
def onSavedClicked(self):
|
||||
"""
|
||||
Update Custom Tag details if not duplicate and save the data.
|
||||
"""
|
||||
html_expands = FormattingTags.html_expands
|
||||
html_expands = FormattingTags.get_html_tags()
|
||||
if self.selected != -1:
|
||||
html = html_expands[self.selected]
|
||||
tag = unicode(self.tagLineEdit.text())
|
||||
@ -157,10 +172,10 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
# Keep temporary tags when the user changes one.
|
||||
html[u'temporary'] = False
|
||||
self.selected = -1
|
||||
self._resetTable()
|
||||
FormattingTags.save_html_tags()
|
||||
self._reloadTable()
|
||||
|
||||
def _resetTable(self):
|
||||
def _reloadTable(self):
|
||||
"""
|
||||
Reset List for loading.
|
||||
"""
|
||||
@ -169,7 +184,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
self.newPushButton.setEnabled(True)
|
||||
self.savePushButton.setEnabled(False)
|
||||
self.deletePushButton.setEnabled(False)
|
||||
for linenumber, html in enumerate(FormattingTags.html_expands):
|
||||
for linenumber, html in enumerate(FormattingTags.get_html_tags()):
|
||||
self.tagTableWidget.setRowCount(self.tagTableWidget.rowCount() + 1)
|
||||
self.tagTableWidget.setItem(linenumber, 0,
|
||||
QtGui.QTableWidgetItem(html[u'desc']))
|
||||
|
@ -43,7 +43,7 @@ class GeneralTab(SettingsTab):
|
||||
Initialise the general settings tab
|
||||
"""
|
||||
self.screens = ScreenList.get_instance()
|
||||
self.icon_path = u':/icon/openlp-logo-16x16.png'
|
||||
self.iconPath = u':/icon/openlp-logo-16x16.png'
|
||||
generalTranslated = translate('OpenLP.GeneralTab', 'General')
|
||||
SettingsTab.__init__(self, parent, u'General', generalTranslated)
|
||||
|
||||
|
@ -554,9 +554,9 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
# (not for use by plugins)
|
||||
self.uiSettingsSection = u'user interface'
|
||||
self.generalSettingsSection = u'general'
|
||||
self.advancedlSettingsSection = u'advanced'
|
||||
self.advancedSettingsSection = u'advanced'
|
||||
self.shortcutsSettingsSection = u'shortcuts'
|
||||
self.servicemanagerSettingsSection = u'servicemanager'
|
||||
self.serviceManagerSettingsSection = u'servicemanager'
|
||||
self.songsSettingsSection = u'songs'
|
||||
self.themesSettingsSection = u'themes'
|
||||
self.displayTagsSection = u'displayTags'
|
||||
@ -568,8 +568,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
self.shortcutForm = ShortcutListForm(self)
|
||||
self.recentFiles = QtCore.QStringList()
|
||||
# Set up the path with plugins
|
||||
pluginpath = AppLocation.get_directory(AppLocation.PluginsDir)
|
||||
self.pluginManager = PluginManager(pluginpath)
|
||||
plugin_path = AppLocation.get_directory(AppLocation.PluginsDir)
|
||||
self.pluginManager = PluginManager(plugin_path)
|
||||
self.pluginHelpers = {}
|
||||
self.imageManager = ImageManager()
|
||||
self.mediaController = MediaController(self)
|
||||
@ -661,7 +661,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
self.pluginHelpers[u'pluginmanager'] = self.pluginManager
|
||||
self.pluginHelpers[u'formparent'] = self
|
||||
self.pluginHelpers[u'mediacontroller'] = self.mediaController
|
||||
self.pluginManager.find_plugins(pluginpath, self.pluginHelpers)
|
||||
self.pluginManager.find_plugins(plugin_path, self.pluginHelpers)
|
||||
# hook methods have to happen after find_plugins. Find plugins needs
|
||||
# the controllers hence the hooks have moved from setupUI() to here
|
||||
# Find and insert settings tabs
|
||||
@ -730,7 +730,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
if self.liveController.display.isVisible():
|
||||
self.liveController.display.setFocus()
|
||||
self.activateWindow()
|
||||
if len(self.arguments):
|
||||
if self.arguments:
|
||||
args = []
|
||||
for a in self.arguments:
|
||||
args.extend([a])
|
||||
@ -944,10 +944,10 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
setting_sections = []
|
||||
# Add main sections.
|
||||
setting_sections.extend([self.generalSettingsSection])
|
||||
setting_sections.extend([self.advancedlSettingsSection])
|
||||
setting_sections.extend([self.advancedSettingsSection])
|
||||
setting_sections.extend([self.uiSettingsSection])
|
||||
setting_sections.extend([self.shortcutsSettingsSection])
|
||||
setting_sections.extend([self.servicemanagerSettingsSection])
|
||||
setting_sections.extend([self.serviceManagerSettingsSection])
|
||||
setting_sections.extend([self.themesSettingsSection])
|
||||
setting_sections.extend([self.displayTagsSection])
|
||||
setting_sections.extend([self.headerSection])
|
||||
@ -1027,10 +1027,10 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
setting_sections = []
|
||||
# Add main sections.
|
||||
setting_sections.extend([self.generalSettingsSection])
|
||||
setting_sections.extend([self.advancedlSettingsSection])
|
||||
setting_sections.extend([self.advancedSettingsSection])
|
||||
setting_sections.extend([self.uiSettingsSection])
|
||||
setting_sections.extend([self.shortcutsSettingsSection])
|
||||
setting_sections.extend([self.servicemanagerSettingsSection])
|
||||
setting_sections.extend([self.serviceManagerSettingsSection])
|
||||
setting_sections.extend([self.themesSettingsSection])
|
||||
setting_sections.extend([self.displayTagsSection])
|
||||
# Add plugin sections.
|
||||
@ -1314,10 +1314,10 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
||||
u'/enable slide loop'):
|
||||
if QtCore.QSettings().value(self.generalSettingsSection +
|
||||
u'/enable slide loop', QtCore.QVariant(True)).toBool():
|
||||
QtCore.QSettings().setValue(self.advancedlSettingsSection +
|
||||
QtCore.QSettings().setValue(self.advancedSettingsSection +
|
||||
u'/slide limits', QtCore.QVariant(SlideLimits.Wrap))
|
||||
else:
|
||||
QtCore.QSettings().setValue(self.advancedlSettingsSection +
|
||||
QtCore.QSettings().setValue(self.advancedSettingsSection +
|
||||
u'/slide limits', QtCore.QVariant(SlideLimits.End))
|
||||
QtCore.QSettings().remove(self.generalSettingsSection +
|
||||
u'/enable slide loop')
|
||||
|
@ -69,12 +69,13 @@ class MediaInfo(object):
|
||||
def get_media_players():
|
||||
"""
|
||||
This method extract the configured media players and overridden player from
|
||||
the settings
|
||||
the settings.
|
||||
|
||||
``players_list``
|
||||
this is a python list with all active media players
|
||||
A list with all active media players.
|
||||
|
||||
``overridden_player``
|
||||
here an special media player is choosen for all media actions
|
||||
Here an special media player is chosen for all media actions.
|
||||
"""
|
||||
log.debug(u'get_media_players')
|
||||
players = unicode(QtCore.QSettings().value(u'media/players').toString())
|
||||
@ -92,15 +93,17 @@ def get_media_players():
|
||||
players_list = players.replace(u'[', u'').replace(u']', u'').split(u',')
|
||||
return players_list, overridden_player
|
||||
|
||||
|
||||
def set_media_players(players_list, overridden_player=u'auto'):
|
||||
"""
|
||||
This method saves the configured media players and overridden player to the
|
||||
settings
|
||||
|
||||
``players_list``
|
||||
this is a python list with all active media players
|
||||
A list with all active media players.
|
||||
|
||||
``overridden_player``
|
||||
here an special media player is choosen for all media actions
|
||||
Here an special media player is chosen for all media actions.
|
||||
"""
|
||||
log.debug(u'set_media_players')
|
||||
players = u','.join(players_list)
|
||||
|
@ -84,10 +84,7 @@ class MediaController(object):
|
||||
def set_active_players(self):
|
||||
savedPlayers = get_media_players()[0]
|
||||
for player in self.mediaPlayers.keys():
|
||||
if player in savedPlayers:
|
||||
self.mediaPlayers[player].isActive = True
|
||||
else:
|
||||
self.mediaPlayers[player].isActive = False
|
||||
self.mediaPlayers[player].isActive = player in savedPlayers
|
||||
|
||||
def register_controllers(self, controller):
|
||||
"""
|
||||
@ -106,8 +103,8 @@ class MediaController(object):
|
||||
AppLocation.get_directory(AppLocation.AppDir),
|
||||
u'core', u'ui', u'media')
|
||||
for filename in os.listdir(controller_dir):
|
||||
if filename.endswith(u'player.py') and \
|
||||
not filename == 'media_player.py':
|
||||
if filename.endswith(u'player.py') and not \
|
||||
filename == 'media_player.py':
|
||||
path = os.path.join(controller_dir, filename)
|
||||
if os.path.isfile(path):
|
||||
modulename = u'openlp.core.ui.media.' + \
|
||||
@ -122,38 +119,36 @@ class MediaController(object):
|
||||
for controller_class in controller_classes:
|
||||
controller = controller_class(self)
|
||||
self.register_controllers(controller)
|
||||
if self.mediaPlayers:
|
||||
savedPlayers, overriddenPlayer = get_media_players()
|
||||
invalidMediaPlayers = [mediaPlayer for mediaPlayer in savedPlayers \
|
||||
if not mediaPlayer in self.mediaPlayers or \
|
||||
not self.mediaPlayers[mediaPlayer].check_available()]
|
||||
if len(invalidMediaPlayers) > 0:
|
||||
for invalidPlayer in invalidMediaPlayers:
|
||||
savedPlayers.remove(invalidPlayer)
|
||||
set_media_players(savedPlayers, overriddenPlayer)
|
||||
self.set_active_players()
|
||||
return True
|
||||
else:
|
||||
if not self.mediaPlayers:
|
||||
return False
|
||||
savedPlayers, overriddenPlayer = get_media_players()
|
||||
invalidMediaPlayers = [mediaPlayer for mediaPlayer in savedPlayers
|
||||
if not mediaPlayer in self.mediaPlayers or not
|
||||
self.mediaPlayers[mediaPlayer].check_available()]
|
||||
if invalidMediaPlayers:
|
||||
for invalidPlayer in invalidMediaPlayers:
|
||||
savedPlayers.remove(invalidPlayer)
|
||||
set_media_players(savedPlayers, overriddenPlayer)
|
||||
self.set_active_players()
|
||||
return True
|
||||
|
||||
def video_state(self):
|
||||
"""
|
||||
Check if there is a running media Player and do updating stuff (e.g.
|
||||
update the UI)
|
||||
"""
|
||||
if len(self.curDisplayMediaPlayer.keys()) == 0:
|
||||
if not self.curDisplayMediaPlayer.keys():
|
||||
self.timer.stop()
|
||||
else:
|
||||
for display in self.curDisplayMediaPlayer.keys():
|
||||
self.curDisplayMediaPlayer[display].resize(display)
|
||||
self.curDisplayMediaPlayer[display].update_ui(display)
|
||||
if self.curDisplayMediaPlayer[display] \
|
||||
.state == MediaState.Playing:
|
||||
if self.curDisplayMediaPlayer[display].state == \
|
||||
MediaState.Playing:
|
||||
return
|
||||
# no players are active anymore
|
||||
for display in self.curDisplayMediaPlayer.keys():
|
||||
if self.curDisplayMediaPlayer[display] \
|
||||
.state != MediaState.Paused:
|
||||
if self.curDisplayMediaPlayer[display].state != MediaState.Paused:
|
||||
display.controller.seekSlider.setSliderPosition(0)
|
||||
self.timer.stop()
|
||||
|
||||
@ -333,8 +328,7 @@ class MediaController(object):
|
||||
'Unsupported File')))
|
||||
return False
|
||||
# dont care about actual theme, set a black background
|
||||
if controller.isLive and ( \
|
||||
controller.media_info.is_background == False):
|
||||
if controller.isLive and not controller.media_info.is_background:
|
||||
display.frame.evaluateJavaScript(u'show_video( \
|
||||
"setBackBoard", null, null, null,"visible");')
|
||||
# now start playing
|
||||
@ -395,7 +389,7 @@ class MediaController(object):
|
||||
"""
|
||||
Responds to the request to play a loaded video
|
||||
|
||||
``msg``
|
||||
``msg``
|
||||
First element is the controller which should be used
|
||||
"""
|
||||
log.debug(u'video_play')
|
||||
@ -497,15 +491,15 @@ class MediaController(object):
|
||||
First element is the boolean for Live indication
|
||||
"""
|
||||
isLive = msg[1]
|
||||
if isLive:
|
||||
controller = self.parent.liveController
|
||||
for display in self.curDisplayMediaPlayer.keys():
|
||||
if display.controller == controller:
|
||||
if self.curDisplayMediaPlayer[display] \
|
||||
.state == MediaState.Playing:
|
||||
self.curDisplayMediaPlayer[display].pause(display)
|
||||
self.curDisplayMediaPlayer[display] \
|
||||
.set_visible(display, False)
|
||||
if not isLive:
|
||||
return
|
||||
controller = self.parent.liveController
|
||||
for display in self.curDisplayMediaPlayer.keys():
|
||||
if display.controller != controller or \
|
||||
self.curDisplayMediaPlayer[display].state != MediaState.Playing:
|
||||
continue
|
||||
self.curDisplayMediaPlayer[display].pause(display)
|
||||
self.curDisplayMediaPlayer[display].set_visible(display, False)
|
||||
|
||||
def video_blank(self, msg):
|
||||
"""
|
||||
@ -517,16 +511,16 @@ class MediaController(object):
|
||||
"""
|
||||
isLive = msg[1]
|
||||
hide_mode = msg[2]
|
||||
if isLive:
|
||||
Receiver.send_message(u'live_display_hide', hide_mode)
|
||||
controller = self.parent.liveController
|
||||
for display in self.curDisplayMediaPlayer.keys():
|
||||
if display.controller == controller:
|
||||
if self.curDisplayMediaPlayer[display] \
|
||||
.state == MediaState.Playing:
|
||||
self.curDisplayMediaPlayer[display].pause(display)
|
||||
self.curDisplayMediaPlayer[display] \
|
||||
.set_visible(display, False)
|
||||
if not isLive:
|
||||
return
|
||||
Receiver.send_message(u'live_display_hide', hide_mode)
|
||||
controller = self.parent.liveController
|
||||
for display in self.curDisplayMediaPlayer.keys():
|
||||
if display.controller != controller or \
|
||||
self.curDisplayMediaPlayer[display].state != MediaState.Playing:
|
||||
continue
|
||||
self.curDisplayMediaPlayer[display].pause(display)
|
||||
self.curDisplayMediaPlayer[display].set_visible(display, False)
|
||||
|
||||
def video_unblank(self, msg):
|
||||
"""
|
||||
@ -538,19 +532,18 @@ class MediaController(object):
|
||||
"""
|
||||
Receiver.send_message(u'live_display_show')
|
||||
isLive = msg[1]
|
||||
if isLive:
|
||||
controller = self.parent.liveController
|
||||
for display in self.curDisplayMediaPlayer.keys():
|
||||
if display.controller == controller:
|
||||
if self.curDisplayMediaPlayer[display] \
|
||||
.state == MediaState.Paused:
|
||||
if self.curDisplayMediaPlayer[display].play(display):
|
||||
self.curDisplayMediaPlayer[display] \
|
||||
.set_visible(display, True)
|
||||
# Start Timer for ui updates
|
||||
if not self.timer.isActive():
|
||||
self.timer.start()
|
||||
|
||||
if not isLive:
|
||||
return
|
||||
controller = self.parent.liveController
|
||||
for display in self.curDisplayMediaPlayer.keys():
|
||||
if display.controller != controller or \
|
||||
self.curDisplayMediaPlayer[display].state != MediaState.Paused:
|
||||
continue
|
||||
if self.curDisplayMediaPlayer[display].play(display):
|
||||
self.curDisplayMediaPlayer[display].set_visible(display, True)
|
||||
# Start Timer for ui updates
|
||||
if not self.timer.isActive():
|
||||
self.timer.start()
|
||||
|
||||
def get_audio_extensions_list(self):
|
||||
audio_list = []
|
||||
@ -565,9 +558,8 @@ class MediaController(object):
|
||||
video_list = []
|
||||
for player in self.mediaPlayers.values():
|
||||
if player.isActive:
|
||||
for item in player.video_extensions_list:
|
||||
if not item in video_list:
|
||||
video_list.append(item)
|
||||
video_list.extend([item for item in player.video_extensions_list
|
||||
if item not in video_list])
|
||||
return video_list
|
||||
|
||||
def finalise(self):
|
||||
|
@ -2,25 +2,26 @@
|
||||
|
||||
# Python ctypes bindings for VLC
|
||||
#
|
||||
# Copyright (C) 2009-2010 the VideoLAN team
|
||||
# Copyright (C) 2009-2012 the VideoLAN team
|
||||
# $Id: $
|
||||
#
|
||||
# Authors: Olivier Aubert <olivier.aubert at liris.cnrs.fr>
|
||||
# Jean Brouwers <MrJean1 at gmail.com>
|
||||
# Geoff Salmon <geoff.salmon at gmail.com>
|
||||
#
|
||||
# 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; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
# This library is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as
|
||||
# published by the Free Software Foundation; either version 2.1 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# 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.
|
||||
# This library 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
|
||||
# Lesser 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA
|
||||
|
||||
"""This module provides bindings for the LibVLC public API, see
|
||||
U{http://wiki.videolan.org/LibVLC}.
|
||||
@ -47,7 +48,7 @@ import sys
|
||||
from inspect import getargspec
|
||||
|
||||
__version__ = "N/A"
|
||||
build_date = "Tue Jan 17 12:20:48 2012"
|
||||
build_date = "Fri Apr 27 16:47:21 2012"
|
||||
|
||||
# Internal guard to prevent internal classes to be directly
|
||||
# instanciated.
|
||||
@ -126,7 +127,7 @@ class VLCException(Exception):
|
||||
try:
|
||||
_Ints = (int, long)
|
||||
except NameError: # no long in Python 3+
|
||||
_Ints = int
|
||||
_Ints = int
|
||||
_Seqs = (list, tuple)
|
||||
|
||||
# Default instance. It is used to instanciate classes directly in the
|
||||
@ -904,6 +905,11 @@ class Instance(_Ctype):
|
||||
def media_new(self, mrl, *options):
|
||||
"""Create a new Media instance.
|
||||
|
||||
If mrl contains a colon (:), it will be treated as a
|
||||
URL. Else, it will be considered as a local path. If you need
|
||||
more control, directly use media_new_location/media_new_path
|
||||
methods.
|
||||
|
||||
Options can be specified as supplementary string parameters, e.g.
|
||||
|
||||
C{m = i.media_new('foo.avi', 'sub-filter=marq{marquee=Hello}', 'vout-filter=invert')}
|
||||
@ -914,7 +920,12 @@ class Instance(_Ctype):
|
||||
|
||||
@param options: optional media option=value strings
|
||||
"""
|
||||
m = libvlc_media_new_location(self, mrl)
|
||||
if ':' in mrl:
|
||||
# Assume it is a URL
|
||||
m = libvlc_media_new_location(self, mrl)
|
||||
else:
|
||||
# Else it should be a local path.
|
||||
m = libvlc_media_new_path(self, mrl)
|
||||
for o in options:
|
||||
libvlc_media_add_option(m, o)
|
||||
m._instance = self
|
||||
@ -1511,7 +1522,7 @@ class Media(_Ctype):
|
||||
|
||||
def save_meta(self):
|
||||
'''Save the meta previously set.
|
||||
@return: true if the write operation was successfull.
|
||||
@return: true if the write operation was successful.
|
||||
'''
|
||||
return libvlc_media_save_meta(self)
|
||||
|
||||
@ -2230,7 +2241,7 @@ class MediaPlayer(_Ctype):
|
||||
@param format: a four-characters string identifying the sample format (e.g. "S16N" or "FL32").
|
||||
@param rate: sample rate (expressed in Hz).
|
||||
@param channels: channels count.
|
||||
@version: LibVLC 1.2.0 or later.
|
||||
@version: LibVLC 2.0.0 or later.
|
||||
'''
|
||||
return libvlc_audio_set_format(self, format, rate, channels)
|
||||
|
||||
@ -2378,7 +2389,7 @@ class MediaPlayer(_Ctype):
|
||||
def navigate(self, navigate):
|
||||
'''Navigate through DVD Menu.
|
||||
@param navigate: the Navigation mode.
|
||||
@version: libVLC 1.2.0 or later.
|
||||
@version: libVLC 2.0.0 or later.
|
||||
'''
|
||||
return libvlc_media_player_navigate(self, navigate)
|
||||
|
||||
@ -2489,7 +2500,7 @@ class MediaPlayer(_Ctype):
|
||||
'''Get the current subtitle delay. Positive values means subtitles are being
|
||||
displayed later, negative values earlier.
|
||||
@return: time (in microseconds) the display of subtitles is being delayed.
|
||||
@version: LibVLC 1.2.0 or later.
|
||||
@version: LibVLC 2.0.0 or later.
|
||||
'''
|
||||
return libvlc_video_get_spu_delay(self)
|
||||
|
||||
@ -2500,7 +2511,7 @@ class MediaPlayer(_Ctype):
|
||||
The subtitle delay will be reset to zero each time the media changes.
|
||||
@param i_delay: time (in microseconds) the display of subtitles should be delayed.
|
||||
@return: 0 on success, -1 on error.
|
||||
@version: LibVLC 1.2.0 or later.
|
||||
@version: LibVLC 2.0.0 or later.
|
||||
'''
|
||||
return libvlc_video_set_spu_delay(self, i_delay)
|
||||
|
||||
@ -3247,7 +3258,7 @@ def libvlc_media_set_meta(p_md, e_meta, psz_value):
|
||||
def libvlc_media_save_meta(p_md):
|
||||
'''Save the meta previously set.
|
||||
@param p_md: the media desriptor.
|
||||
@return: true if the write operation was successfull.
|
||||
@return: true if the write operation was successful.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_media_save_meta', None) or \
|
||||
_Cfunction('libvlc_media_save_meta', ((1,),), None,
|
||||
@ -4084,7 +4095,7 @@ def libvlc_audio_set_format(mp, format, rate, channels):
|
||||
@param format: a four-characters string identifying the sample format (e.g. "S16N" or "FL32").
|
||||
@param rate: sample rate (expressed in Hz).
|
||||
@param channels: channels count.
|
||||
@version: LibVLC 1.2.0 or later.
|
||||
@version: LibVLC 2.0.0 or later.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_audio_set_format', None) or \
|
||||
_Cfunction('libvlc_audio_set_format', ((1,), (1,), (1,), (1,),), None,
|
||||
@ -4328,7 +4339,7 @@ def libvlc_media_player_navigate(p_mi, navigate):
|
||||
'''Navigate through DVD Menu.
|
||||
@param p_mi: the Media Player.
|
||||
@param navigate: the Navigation mode.
|
||||
@version: libVLC 1.2.0 or later.
|
||||
@version: libVLC 2.0.0 or later.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_media_player_navigate', None) or \
|
||||
_Cfunction('libvlc_media_player_navigate', ((1,), (1,),), None,
|
||||
@ -4554,7 +4565,7 @@ def libvlc_video_get_spu_delay(p_mi):
|
||||
displayed later, negative values earlier.
|
||||
@param p_mi: media player.
|
||||
@return: time (in microseconds) the display of subtitles is being delayed.
|
||||
@version: LibVLC 1.2.0 or later.
|
||||
@version: LibVLC 2.0.0 or later.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_video_get_spu_delay', None) or \
|
||||
_Cfunction('libvlc_video_get_spu_delay', ((1,),), None,
|
||||
@ -4569,7 +4580,7 @@ def libvlc_video_set_spu_delay(p_mi, i_delay):
|
||||
@param p_mi: media player.
|
||||
@param i_delay: time (in microseconds) the display of subtitles should be delayed.
|
||||
@return: 0 on success, -1 on error.
|
||||
@version: LibVLC 1.2.0 or later.
|
||||
@version: LibVLC 2.0.0 or later.
|
||||
'''
|
||||
f = _Cfunctions.get('libvlc_video_set_spu_delay', None) or \
|
||||
_Cfunction('libvlc_video_set_spu_delay', ((1,), (1,),), None,
|
||||
|
@ -25,30 +25,42 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
import logging
|
||||
import sys, os
|
||||
from datetime import datetime
|
||||
try:
|
||||
import vlc
|
||||
vlc_available = bool(vlc.get_default_instance())
|
||||
except (ImportError, NameError):
|
||||
vlc_available = False
|
||||
except OSError, e:
|
||||
if sys.platform.startswith('win'):
|
||||
if isinstance(e, WindowsError) and e.winerror == 126:
|
||||
vlc_available = False
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
raise
|
||||
from distutils.version import LooseVersion
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import Receiver
|
||||
from openlp.core.lib.mediaplayer import MediaPlayer
|
||||
from openlp.core.ui.media import MediaState
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
VLC_AVAILABLE = False
|
||||
try:
|
||||
import vlc
|
||||
VLC_AVAILABLE = bool(vlc.get_default_instance())
|
||||
except (ImportError, NameError):
|
||||
pass
|
||||
except OSError, e:
|
||||
if sys.platform.startswith('win'):
|
||||
if not isinstance(e, WindowsError) and e.winerror != 126:
|
||||
raise
|
||||
else:
|
||||
raise
|
||||
|
||||
if VLC_AVAILABLE:
|
||||
try:
|
||||
version = vlc.libvlc_get_version()
|
||||
except:
|
||||
version = u'0.0.0'
|
||||
if LooseVersion(version) < LooseVersion('1.1.0'):
|
||||
VLC_AVAILABLE = False
|
||||
log.debug(u'VLC could not be loaded: %s' % version)
|
||||
|
||||
AUDIO_EXT = [
|
||||
u'*.mp3'
|
||||
, u'*.wav'
|
||||
@ -128,7 +140,7 @@ class VlcPlayer(MediaPlayer):
|
||||
self.hasOwnWidget = True
|
||||
|
||||
def check_available(self):
|
||||
return vlc_available
|
||||
return VLC_AVAILABLE
|
||||
|
||||
def load(self, display):
|
||||
log.debug(u'load vid in Vlc Controller')
|
||||
|
@ -64,7 +64,7 @@ class MediaDockManager(object):
|
||||
visible_title = media_item.plugin.getString(StringContent.VisibleName)
|
||||
log.debug(u'Inserting %s dock' % visible_title[u'title'])
|
||||
match = False
|
||||
for dock_index in range(0, self.media_dock.count()):
|
||||
for dock_index in range(self.media_dock.count()):
|
||||
if self.media_dock.widget(dock_index).settingsSection == \
|
||||
media_item.plugin.name:
|
||||
match = True
|
||||
@ -81,7 +81,7 @@ class MediaDockManager(object):
|
||||
"""
|
||||
visible_title = media_item.plugin.getString(StringContent.VisibleName)
|
||||
log.debug(u'remove %s dock' % visible_title[u'title'])
|
||||
for dock_index in range(0, self.media_dock.count()):
|
||||
for dock_index in range(self.media_dock.count()):
|
||||
if self.media_dock.widget(dock_index):
|
||||
if self.media_dock.widget(dock_index).settingsSection == \
|
||||
media_item.plugin.name:
|
||||
|
@ -106,13 +106,13 @@ class ScreenList(object):
|
||||
"""
|
||||
# Do not log at start up.
|
||||
if changed_screen != -1:
|
||||
log.info(u'screen_count_changed %d' % self.desktop.numScreens())
|
||||
log.info(u'screen_count_changed %d' % self.desktop.screenCount())
|
||||
# Remove unplugged screens.
|
||||
for screen in copy.deepcopy(self.screen_list):
|
||||
if screen[u'number'] == self.desktop.numScreens():
|
||||
if screen[u'number'] == self.desktop.screenCount():
|
||||
self.remove_screen(screen[u'number'])
|
||||
# Add new screens.
|
||||
for number in xrange(0, self.desktop.numScreens()):
|
||||
for number in xrange(self.desktop.screenCount()):
|
||||
if not self.screen_exists(number):
|
||||
self.add_screen({
|
||||
u'number': number,
|
||||
|
@ -51,7 +51,7 @@ class ServiceManagerList(QtGui.QTreeWidget):
|
||||
"""
|
||||
Set up key bindings and mouse behaviour for the service list
|
||||
"""
|
||||
def __init__(self, serviceManager, parent=None, name=None):
|
||||
def __init__(self, serviceManager, parent=None):
|
||||
QtGui.QTreeWidget.__init__(self, parent)
|
||||
self.serviceManager = serviceManager
|
||||
|
||||
@ -101,7 +101,6 @@ class ServiceManager(QtGui.QWidget):
|
||||
QtGui.QWidget.__init__(self, parent)
|
||||
self.mainwindow = mainwindow
|
||||
self.serviceItems = []
|
||||
self.serviceName = u''
|
||||
self.suffixes = []
|
||||
self.dropPosition = 0
|
||||
self.expandTabs = False
|
||||
@ -272,7 +271,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
QtCore.SIGNAL(u'service_item_update'), self.serviceItemUpdate)
|
||||
# Last little bits of setting up
|
||||
self.service_theme = unicode(QtCore.QSettings().value(
|
||||
self.mainwindow.servicemanagerSettingsSection + u'/service theme',
|
||||
self.mainwindow.serviceManagerSettingsSection + u'/service theme',
|
||||
QtCore.QVariant(u'')).toString())
|
||||
self.servicePath = AppLocation.get_section_data_path(u'servicemanager')
|
||||
# build the drag and drop context menu
|
||||
@ -411,7 +410,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
self.mainwindow,
|
||||
translate('OpenLP.ServiceManager', 'Open File'),
|
||||
SettingsManager.get_last_dir(
|
||||
self.mainwindow.servicemanagerSettingsSection),
|
||||
self.mainwindow.serviceManagerSettingsSection),
|
||||
translate('OpenLP.ServiceManager',
|
||||
'OpenLP Service Files (*.osz)')))
|
||||
if not fileName:
|
||||
@ -419,7 +418,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
else:
|
||||
fileName = loadFile
|
||||
SettingsManager.set_last_dir(
|
||||
self.mainwindow.servicemanagerSettingsSection,
|
||||
self.mainwindow.serviceManagerSettingsSection,
|
||||
split_filename(fileName)[0])
|
||||
self.loadFile(fileName)
|
||||
|
||||
@ -469,7 +468,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
service_file_name = '%s.osd' % basename
|
||||
log.debug(u'ServiceManager.saveFile - %s', path_file_name)
|
||||
SettingsManager.set_last_dir(
|
||||
self.mainwindow.servicemanagerSettingsSection,
|
||||
self.mainwindow.serviceManagerSettingsSection,
|
||||
path)
|
||||
service = []
|
||||
write_list = []
|
||||
@ -483,7 +482,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
service_item = item[u'service_item'].get_service_repr()
|
||||
# Get all the audio files, and ready them for embedding in the
|
||||
# service file.
|
||||
if len(service_item[u'header'][u'background_audio']) > 0:
|
||||
if service_item[u'header'][u'background_audio']:
|
||||
for i, filename in \
|
||||
enumerate(service_item[u'header'][u'background_audio']):
|
||||
new_file = os.path.join(u'audio',
|
||||
@ -622,7 +621,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
else:
|
||||
default_filename = u''
|
||||
directory = unicode(SettingsManager.get_last_dir(
|
||||
self.mainwindow.servicemanagerSettingsSection))
|
||||
self.mainwindow.serviceManagerSettingsSection))
|
||||
path = os.path.join(directory, default_filename)
|
||||
fileName = unicode(QtGui.QFileDialog.getSaveFileName(self.mainwindow,
|
||||
UiStrings().SaveService, path,
|
||||
@ -822,7 +821,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
"""
|
||||
Called by the SlideController to select the next service item.
|
||||
"""
|
||||
if len(self.serviceManagerList.selectedItems()) == 0:
|
||||
if not self.serviceManagerList.selectedItems():
|
||||
return
|
||||
selected = self.serviceManagerList.selectedItems()[0]
|
||||
lookFor = 0
|
||||
@ -840,7 +839,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
"""
|
||||
Called by the SlideController to select the previous service item.
|
||||
"""
|
||||
if len(self.serviceManagerList.selectedItems()) == 0:
|
||||
if not self.serviceManagerList.selectedItems():
|
||||
return
|
||||
selected = self.serviceManagerList.selectedItems()[0]
|
||||
prevItem = None
|
||||
@ -1107,7 +1106,7 @@ class ServiceManager(QtGui.QWidget):
|
||||
self.service_theme = unicode(self.themeComboBox.currentText())
|
||||
self.mainwindow.renderer.set_service_theme(self.service_theme)
|
||||
QtCore.QSettings().setValue(
|
||||
self.mainwindow.servicemanagerSettingsSection +
|
||||
self.mainwindow.serviceManagerSettingsSection +
|
||||
u'/service theme',
|
||||
QtCore.QVariant(self.service_theme))
|
||||
self.regenerateServiceItems(True)
|
||||
|
@ -65,8 +65,8 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
|
||||
self.insertTab(self.advancedTab, 2, PluginStatus.Active)
|
||||
count = 3
|
||||
for plugin in self.plugins:
|
||||
if plugin.settings_tab:
|
||||
self.insertTab(plugin.settings_tab, count, plugin.status)
|
||||
if plugin.settingsTab:
|
||||
self.insertTab(plugin.settingsTab, count, plugin.status)
|
||||
count += 1
|
||||
self.settingListWidget.setCurrentRow(0)
|
||||
return QtGui.QDialog.exec_(self)
|
||||
@ -80,7 +80,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
|
||||
pos = self.stackedLayout.addWidget(tab)
|
||||
if is_active:
|
||||
item_name = QtGui.QListWidgetItem(tab.tabTitleVisible)
|
||||
icon = build_icon(tab.icon_path)
|
||||
icon = build_icon(tab.iconPath)
|
||||
item_name.setIcon(icon)
|
||||
self.settingListWidget.insertItem(location, item_name)
|
||||
else:
|
||||
@ -92,7 +92,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
|
||||
"""
|
||||
Process the form saving the settings
|
||||
"""
|
||||
for tabIndex in range(0, self.stackedLayout.count()):
|
||||
for tabIndex in range(self.stackedLayout.count()):
|
||||
self.stackedLayout.widget(tabIndex).save()
|
||||
# Must go after all settings are save
|
||||
Receiver.send_message(u'config_updated')
|
||||
@ -102,7 +102,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
|
||||
"""
|
||||
Process the form saving the settings
|
||||
"""
|
||||
for tabIndex in range(0, self.stackedLayout.count()):
|
||||
for tabIndex in range(self.stackedLayout.count()):
|
||||
self.stackedLayout.widget(tabIndex).cancel()
|
||||
return QtGui.QDialog.reject(self)
|
||||
|
||||
@ -114,8 +114,8 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
|
||||
self.themesTab.postSetUp()
|
||||
self.advancedTab.postSetUp()
|
||||
for plugin in self.plugins:
|
||||
if plugin.settings_tab:
|
||||
plugin.settings_tab.postSetUp()
|
||||
if plugin.settingsTab:
|
||||
plugin.settingsTab.postSetUp()
|
||||
|
||||
def tabChanged(self, tabIndex):
|
||||
"""
|
||||
|
@ -151,7 +151,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog):
|
||||
if action is None:
|
||||
continue
|
||||
shortcuts = self._actionShortcuts(action)
|
||||
if len(shortcuts) == 0:
|
||||
if not shortcuts:
|
||||
item.setText(1, u'')
|
||||
item.setText(2, u'')
|
||||
elif len(shortcuts) == 1:
|
||||
@ -195,7 +195,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog):
|
||||
return
|
||||
shortcuts = self._actionShortcuts(action)
|
||||
new_shortcuts = []
|
||||
if len(shortcuts) != 0:
|
||||
if shortcuts:
|
||||
new_shortcuts.append(shortcuts[0])
|
||||
new_shortcuts.append(
|
||||
QtGui.QKeySequence(self.alternatePushButton.text()))
|
||||
@ -241,7 +241,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog):
|
||||
self.primaryPushButton.setChecked(False)
|
||||
self.alternatePushButton.setChecked(False)
|
||||
else:
|
||||
if len(action.defaultShortcuts) != 0:
|
||||
if action.defaultShortcuts:
|
||||
primary_label_text = action.defaultShortcuts[0].toString()
|
||||
if len(action.defaultShortcuts) == 2:
|
||||
alternate_label_text = action.defaultShortcuts[1].toString()
|
||||
@ -313,7 +313,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog):
|
||||
self.refreshShortcutList()
|
||||
primary_button_text = u''
|
||||
alternate_button_text = u''
|
||||
if len(temp_shortcuts) != 0:
|
||||
if temp_shortcuts:
|
||||
primary_button_text = temp_shortcuts[0].toString()
|
||||
if len(temp_shortcuts) == 2:
|
||||
alternate_button_text = temp_shortcuts[1].toString()
|
||||
@ -363,7 +363,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog):
|
||||
return
|
||||
shortcuts = self._actionShortcuts(action)
|
||||
new_shortcuts = []
|
||||
if len(action.defaultShortcuts) != 0:
|
||||
if action.defaultShortcuts:
|
||||
new_shortcuts.append(action.defaultShortcuts[0])
|
||||
# We have to check if the primary default shortcut is available. But
|
||||
# we only have to check, if the action has a default primary
|
||||
@ -391,7 +391,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog):
|
||||
return
|
||||
shortcuts = self._actionShortcuts(action)
|
||||
new_shortcuts = []
|
||||
if len(shortcuts) != 0:
|
||||
if shortcuts:
|
||||
new_shortcuts.append(shortcuts[0])
|
||||
if len(action.defaultShortcuts) == 2:
|
||||
new_shortcuts.append(action.defaultShortcuts[1])
|
||||
|
@ -373,7 +373,7 @@ class SlideController(Controller):
|
||||
u'text': translate('OpenLP.SlideController', 'Go to "Ending"')},
|
||||
{u'key': u'O', u'configurable': True,
|
||||
u'text': translate('OpenLP.SlideController', 'Go to "Other"')}]
|
||||
shortcuts += [{u'key': unicode(number)} for number in range(0, 10)]
|
||||
shortcuts += [{u'key': unicode(number)} for number in range(10)]
|
||||
self.previewListWidget.addActions([create_action(self,
|
||||
u'shortcutAction_%s' % s[u'key'], text=s.get(u'text'),
|
||||
shortcuts=[QtGui.QKeySequence(s[u'key'])],
|
||||
@ -555,7 +555,7 @@ class SlideController(Controller):
|
||||
Process the service item request queue. The key presses can arrive
|
||||
faster than the processing so implement a FIFO queue.
|
||||
"""
|
||||
if len(self.keypress_queue):
|
||||
if self.keypress_queue:
|
||||
while len(self.keypress_queue) and not self.keypress_loop:
|
||||
self.keypress_loop = True
|
||||
keypressCommand = self.keypress_queue.popleft()
|
||||
@ -648,8 +648,8 @@ class SlideController(Controller):
|
||||
|
||||
def onSongBarHandler(self):
|
||||
request = unicode(self.sender().text())
|
||||
slideno = self.slideList[request]
|
||||
self.__updatePreviewSelection(slideno)
|
||||
slide_no = self.slideList[request]
|
||||
self.__updatePreviewSelection(slide_no)
|
||||
self.slideSelected()
|
||||
|
||||
def receiveSpinDelay(self, value):
|
||||
@ -663,7 +663,7 @@ class SlideController(Controller):
|
||||
Updates the Slide Limits variable from the settings.
|
||||
"""
|
||||
self.slide_limits = QtCore.QSettings().value(
|
||||
self.parent().advancedlSettingsSection + u'/slide limits',
|
||||
self.parent().advancedSettingsSection + u'/slide limits',
|
||||
QtCore.QVariant(SlideLimits.End)).toInt()[0]
|
||||
|
||||
def enableToolBar(self, item):
|
||||
@ -694,7 +694,7 @@ class SlideController(Controller):
|
||||
if item.is_text():
|
||||
if QtCore.QSettings().value(
|
||||
self.parent().songsSettingsSection + u'/display songbar',
|
||||
QtCore.QVariant(True)).toBool() and len(self.slideList) > 0:
|
||||
QtCore.QVariant(True)).toBool() and self.slideList:
|
||||
self.songMenu.show()
|
||||
if item.is_capable(ItemCapabilities.CanLoop) and \
|
||||
len(item.get_frames()) > 1:
|
||||
|
@ -444,7 +444,7 @@ class ThemeManager(QtGui.QWidget):
|
||||
self.firstTime()
|
||||
files = SettingsManager.get_files(self.settingsSection, u'.png')
|
||||
# No themes have been found so create one
|
||||
if len(files) == 0:
|
||||
if not files:
|
||||
theme = ThemeXML()
|
||||
theme.theme_name = UiStrings().Default
|
||||
self._writeTheme(theme, None, None)
|
||||
|
@ -39,7 +39,7 @@ class ThemesTab(SettingsTab):
|
||||
self.mainwindow = mainwindow
|
||||
generalTranslated = translate('OpenLP.ThemesTab', 'Themes')
|
||||
SettingsTab.__init__(self, parent, u'Themes', generalTranslated)
|
||||
self.icon_path = u':/themes/theme_new.png'
|
||||
self.iconPath = u':/themes/theme_new.png'
|
||||
|
||||
def setupUi(self):
|
||||
self.setObjectName(u'ThemesTab')
|
||||
|
@ -46,12 +46,14 @@ class WizardStrings(object):
|
||||
# the writers translating their own product name.
|
||||
CCLI = u'CCLI/SongSelect'
|
||||
CSV = u'CSV'
|
||||
DB = u'DreamBeam'
|
||||
EW = u'EasyWorship'
|
||||
ES = u'EasySlides'
|
||||
FP = u'Foilpresenter'
|
||||
OL = u'OpenLyrics'
|
||||
OS = u'OpenSong'
|
||||
OSIS = u'OSIS'
|
||||
PS = u'PowerSong 1.0'
|
||||
SB = u'SongBeamer'
|
||||
SoF = u'Songs of Fellowship'
|
||||
SSP = u'SongShow Plus'
|
||||
|
@ -27,14 +27,15 @@
|
||||
"""
|
||||
The :mod:`openlp.core.utils` module provides the utility libraries for OpenLP.
|
||||
"""
|
||||
from datetime import datetime
|
||||
from distutils.version import LooseVersion
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from subprocess import Popen, PIPE
|
||||
import sys
|
||||
import time
|
||||
import urllib2
|
||||
from datetime import datetime
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
from PyQt4 import QtGui, QtCore
|
||||
|
||||
@ -55,7 +56,6 @@ UNO_CONNECTION_TYPE = u'pipe'
|
||||
#UNO_CONNECTION_TYPE = u'socket'
|
||||
CONTROL_CHARS = re.compile(r'[\x00-\x1F\x7F-\x9F]', re.UNICODE)
|
||||
INVALID_FILE_CHARS = re.compile(r'[\\/:\*\?"<>\|\+\[\]%]', re.UNICODE)
|
||||
VERSION_SPLITTER = re.compile(r'([0-9]+).([0-9]+).([0-9]+)(?:-bzr([0-9]+))?')
|
||||
|
||||
class VersionThread(QtCore.QThread):
|
||||
"""
|
||||
@ -72,48 +72,8 @@ class VersionThread(QtCore.QThread):
|
||||
time.sleep(1)
|
||||
app_version = get_application_version()
|
||||
version = check_latest_version(app_version)
|
||||
remote_version = {}
|
||||
local_version = {}
|
||||
match = VERSION_SPLITTER.match(version)
|
||||
if match:
|
||||
remote_version[u'major'] = int(match.group(1))
|
||||
remote_version[u'minor'] = int(match.group(2))
|
||||
remote_version[u'release'] = int(match.group(3))
|
||||
if len(match.groups()) > 3 and match.group(4):
|
||||
remote_version[u'revision'] = int(match.group(4))
|
||||
else:
|
||||
return
|
||||
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))
|
||||
local_version[u'release'] = int(match.group(3))
|
||||
if len(match.groups()) > 3 and match.group(4):
|
||||
local_version[u'revision'] = int(match.group(4))
|
||||
else:
|
||||
return
|
||||
if remote_version[u'major'] > local_version[u'major'] or \
|
||||
remote_version[u'minor'] > local_version[u'minor'] or \
|
||||
remote_version[u'release'] > local_version[u'release']:
|
||||
if LooseVersion(str(version)) > LooseVersion(str(app_version[u'full'])):
|
||||
Receiver.send_message(u'openlp_version_check', u'%s' % version)
|
||||
elif remote_version.get(u'revision') and \
|
||||
local_version.get(u'revision') and \
|
||||
remote_version[u'revision'] > local_version[u'revision']:
|
||||
Receiver.send_message(u'openlp_version_check', u'%s' % version)
|
||||
|
||||
|
||||
class DelayStartThread(QtCore.QThread):
|
||||
"""
|
||||
A special Qt thread class to build things after OpenLP has started
|
||||
"""
|
||||
def __init__(self, parent):
|
||||
QtCore.QThread.__init__(self, parent)
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Run the thread.
|
||||
"""
|
||||
Receiver.send_message(u'openlp_phonon_creation')
|
||||
|
||||
|
||||
class AppLocation(object):
|
||||
@ -181,6 +141,7 @@ class AppLocation(object):
|
||||
check_directory_exists(path)
|
||||
return path
|
||||
|
||||
|
||||
def _get_os_dir_path(dir_type):
|
||||
"""
|
||||
Return a path based on which OS and environment we are running in.
|
||||
@ -220,6 +181,7 @@ def _get_os_dir_path(dir_type):
|
||||
u'.openlp', u'data')
|
||||
return os.path.join(unicode(os.getenv(u'HOME'), encoding), u'.openlp')
|
||||
|
||||
|
||||
def _get_frozen_path(frozen_option, non_frozen_option):
|
||||
"""
|
||||
Return a path based on the system status.
|
||||
@ -228,6 +190,7 @@ def _get_frozen_path(frozen_option, non_frozen_option):
|
||||
return frozen_option
|
||||
return non_frozen_option
|
||||
|
||||
|
||||
def get_application_version():
|
||||
"""
|
||||
Returns the application version of the running instance of OpenLP::
|
||||
@ -267,7 +230,7 @@ def get_application_version():
|
||||
if code != 0:
|
||||
raise Exception(u'Error running bzr tags')
|
||||
lines = output.splitlines()
|
||||
if len(lines) == 0:
|
||||
if not lines:
|
||||
tag = u'0.0.0'
|
||||
revision = u'0'
|
||||
else:
|
||||
@ -307,6 +270,7 @@ def get_application_version():
|
||||
log.info(u'Openlp version %s' % APPLICATION_VERSION[u'version'])
|
||||
return APPLICATION_VERSION
|
||||
|
||||
|
||||
def check_latest_version(current_version):
|
||||
"""
|
||||
Check the latest version of OpenLP against the version file on the OpenLP
|
||||
@ -340,6 +304,7 @@ def check_latest_version(current_version):
|
||||
version_string = remote_version
|
||||
return version_string
|
||||
|
||||
|
||||
def add_actions(target, actions):
|
||||
"""
|
||||
Adds multiple actions to a menu or toolbar in one command.
|
||||
@ -357,6 +322,7 @@ def add_actions(target, actions):
|
||||
else:
|
||||
target.addAction(action)
|
||||
|
||||
|
||||
def get_filesystem_encoding():
|
||||
"""
|
||||
Returns the name of the encoding used to convert Unicode filenames into
|
||||
@ -367,6 +333,7 @@ def get_filesystem_encoding():
|
||||
encoding = sys.getdefaultencoding()
|
||||
return encoding
|
||||
|
||||
|
||||
def get_images_filter():
|
||||
"""
|
||||
Returns a filter string for a file dialog containing all the supported
|
||||
@ -383,6 +350,7 @@ def get_images_filter():
|
||||
visible_formats, actual_formats)
|
||||
return IMAGES_FILTER
|
||||
|
||||
|
||||
def split_filename(path):
|
||||
"""
|
||||
Return a list of the parts in a given path.
|
||||
@ -393,6 +361,7 @@ def split_filename(path):
|
||||
else:
|
||||
return os.path.split(path)
|
||||
|
||||
|
||||
def clean_filename(filename):
|
||||
"""
|
||||
Removes invalid characters from the given ``filename``.
|
||||
@ -404,6 +373,7 @@ def clean_filename(filename):
|
||||
filename = unicode(filename, u'utf-8')
|
||||
return INVALID_FILE_CHARS.sub(u'_', CONTROL_CHARS.sub(u'', filename))
|
||||
|
||||
|
||||
def delete_file(file_path_name):
|
||||
"""
|
||||
Deletes a file from the system.
|
||||
@ -421,6 +391,7 @@ def delete_file(file_path_name):
|
||||
log.exception("Unable to delete file %s" % file_path_name)
|
||||
return False
|
||||
|
||||
|
||||
def get_web_page(url, header=None, update_openlp=False):
|
||||
"""
|
||||
Attempts to download the webpage at url and returns that page or None.
|
||||
@ -457,6 +428,7 @@ def get_web_page(url, header=None, update_openlp=False):
|
||||
log.debug(page)
|
||||
return page
|
||||
|
||||
|
||||
def get_uno_command():
|
||||
"""
|
||||
Returns the UNO command to launch an openoffice.org instance.
|
||||
@ -469,6 +441,7 @@ def get_uno_command():
|
||||
CONNECTION = u'"-accept=socket,host=localhost,port=2002;urp;"'
|
||||
return u'%s %s %s' % (COMMAND, OPTIONS, CONNECTION)
|
||||
|
||||
|
||||
def get_uno_instance(resolver):
|
||||
"""
|
||||
Returns a running openoffice.org instance.
|
||||
|
@ -90,7 +90,7 @@ class CategoryActionList(object):
|
||||
|
||||
def append(self, name):
|
||||
weight = 0
|
||||
if len(self.actions) > 0:
|
||||
if self.actions:
|
||||
weight = self.actions[-1][0] + 1
|
||||
self.add(name, weight)
|
||||
|
||||
@ -156,7 +156,7 @@ class CategoryList(object):
|
||||
|
||||
def append(self, name, actions=None):
|
||||
weight = 0
|
||||
if len(self.categories) > 0:
|
||||
if self.categories:
|
||||
weight = self.categories[-1].weight + 1
|
||||
if actions:
|
||||
self.add(name, weight, actions)
|
||||
|
@ -117,8 +117,8 @@ class AlertsPlugin(Plugin):
|
||||
Plugin.__init__(self, u'alerts', plugin_helpers,
|
||||
settings_tab_class=AlertsTab)
|
||||
self.weight = -3
|
||||
self.icon_path = u':/plugins/plugin_alerts.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
self.iconPath = u':/plugins/plugin_alerts.png'
|
||||
self.icon = build_icon(self.iconPath)
|
||||
self.alertsmanager = AlertsManager(self)
|
||||
self.manager = Manager(u'alerts', init_schema)
|
||||
self.alertForm = AlertForm(self)
|
||||
@ -197,10 +197,10 @@ class AlertsPlugin(Plugin):
|
||||
"""
|
||||
Add CSS to the main display.
|
||||
"""
|
||||
align = VerticalType.Names[self.settings_tab.location]
|
||||
return CSS % (align, self.settings_tab.font_face,
|
||||
self.settings_tab.font_size, self.settings_tab.font_color,
|
||||
self.settings_tab.bg_color)
|
||||
align = VerticalType.Names[self.settingsTab.location]
|
||||
return CSS % (align, self.settingsTab.font_face,
|
||||
self.settingsTab.font_size, self.settingsTab.font_color,
|
||||
self.settingsTab.bg_color)
|
||||
|
||||
def getDisplayHtml(self):
|
||||
"""
|
||||
@ -215,7 +215,7 @@ class AlertsPlugin(Plugin):
|
||||
``frame``
|
||||
The Web frame holding the page.
|
||||
"""
|
||||
align = VerticalType.Names[self.settings_tab.location]
|
||||
align = VerticalType.Names[self.settingsTab.location]
|
||||
frame.evaluateJavaScript(u'update_css("%s", "%s", "%s", "%s", "%s")' %
|
||||
(align, self.settings_tab.font_face, self.settings_tab.font_size,
|
||||
self.settings_tab.font_color, self.settings_tab.bg_color))
|
||||
(align, self.settingsTab.font_face, self.settingsTab.font_size,
|
||||
self.settingsTab.font_color, self.settingsTab.bg_color))
|
||||
|
@ -43,7 +43,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
||||
self.manager = plugin.manager
|
||||
self.plugin = plugin
|
||||
self.item_id = None
|
||||
QtGui.QDialog.__init__(self, plugin.formparent)
|
||||
QtGui.QDialog.__init__(self, plugin.formParent)
|
||||
self.setupUi(self)
|
||||
QtCore.QObject.connect(self.displayButton,
|
||||
QtCore.SIGNAL(u'clicked()'), self.onDisplayClicked)
|
||||
@ -101,7 +101,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
|
||||
self.alertTextEdit.setText(u'')
|
||||
|
||||
def onNewClick(self):
|
||||
if len(self.alertTextEdit.text()) == 0:
|
||||
if not self.alertTextEdit.text():
|
||||
QtGui.QMessageBox.information(self,
|
||||
translate('AlertsPlugin.AlertForm', 'New Alert'),
|
||||
translate('AlertsPlugin.AlertForm', 'You haven\'t specified '
|
||||
|
@ -84,7 +84,7 @@ class AlertsManager(QtCore.QObject):
|
||||
if not self.alertList:
|
||||
return
|
||||
text = self.alertList.pop(0)
|
||||
alertTab = self.parent().settings_tab
|
||||
alertTab = self.parent().settingsTab
|
||||
self.parent().liveController.display.alert(text, alertTab.location)
|
||||
# Check to see if we have a timer running.
|
||||
if self.timer_id == 0:
|
||||
@ -100,7 +100,7 @@ class AlertsManager(QtCore.QObject):
|
||||
"""
|
||||
log.debug(u'timer event')
|
||||
if event.timerId() == self.timer_id:
|
||||
alertTab = self.parent().settings_tab
|
||||
alertTab = self.parent().settingsTab
|
||||
self.parent().liveController.display.alert(u'', alertTab.location)
|
||||
self.killTimer(self.timer_id)
|
||||
self.timer_id = 0
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtGui
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import Plugin, StringContent, build_icon, translate
|
||||
from openlp.core.lib.ui import create_action, UiStrings
|
||||
@ -44,8 +44,8 @@ class BiblePlugin(Plugin):
|
||||
Plugin.__init__(self, u'bibles', plugin_helpers,
|
||||
BibleMediaItem, BiblesTab)
|
||||
self.weight = -9
|
||||
self.icon_path = u':/plugins/plugin_bibles.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
self.iconPath = u':/plugins/plugin_bibles.png'
|
||||
self.icon = build_icon(self.iconPath)
|
||||
self.manager = None
|
||||
|
||||
def initialise(self):
|
||||
@ -62,7 +62,7 @@ class BiblePlugin(Plugin):
|
||||
# unicode(UiStrings().Export))
|
||||
# Set to invisible until we can export bibles
|
||||
self.exportBibleItem.setVisible(False)
|
||||
if len(self.manager.old_bible_databases):
|
||||
if self.manager.old_bible_databases:
|
||||
self.toolsUpgradeItem.setVisible(True)
|
||||
|
||||
def finalise(self):
|
||||
@ -81,16 +81,23 @@ class BiblePlugin(Plugin):
|
||||
|
||||
def appStartup(self):
|
||||
"""
|
||||
Perform tasks on application starup
|
||||
Perform tasks on application startup
|
||||
"""
|
||||
if len(self.manager.old_bible_databases):
|
||||
if QtGui.QMessageBox.information(self.formparent,
|
||||
if self.manager.old_bible_databases:
|
||||
if QtGui.QMessageBox.information(self.formParent,
|
||||
translate('OpenLP', 'Information'), translate('OpenLP',
|
||||
'Bible format has changed.\nYou have to upgrade your '
|
||||
'existing Bibles.\nShould OpenLP upgrade now?'),
|
||||
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Yes |
|
||||
QtGui.QMessageBox.No)) == QtGui.QMessageBox.Yes:
|
||||
self.onToolsUpgradeItemTriggered()
|
||||
settings = QtCore.QSettings()
|
||||
settings.beginGroup(self.settingsSection)
|
||||
if settings.contains(u'bookname language'):
|
||||
settings.setValue(u'book name language', settings.value(
|
||||
u'bookname language', QtCore.QVariant(0)).toInt()[0])
|
||||
settings.remove(u'bookname language')
|
||||
settings.endGroup()
|
||||
|
||||
def addImportMenuItem(self, import_menu):
|
||||
self.importBibleItem = create_action(import_menu, u'importBibleItem',
|
||||
@ -126,7 +133,7 @@ class BiblePlugin(Plugin):
|
||||
Upgrade older bible databases.
|
||||
"""
|
||||
if not hasattr(self, u'upgrade_wizard'):
|
||||
self.upgrade_wizard = BibleUpgradeForm(self.formparent,
|
||||
self.upgrade_wizard = BibleUpgradeForm(self.formParent,
|
||||
self.manager, self)
|
||||
# If the import was not cancelled then reload.
|
||||
if self.upgrade_wizard.exec_():
|
||||
@ -145,11 +152,10 @@ class BiblePlugin(Plugin):
|
||||
def usesTheme(self, theme):
|
||||
"""
|
||||
Called to find out if the bible plugin is currently using a theme.
|
||||
Returns True if the theme is being used, otherwise returns False.
|
||||
Returns ``True`` if the theme is being used, otherwise returns
|
||||
``False``.
|
||||
"""
|
||||
if unicode(self.settings_tab.bible_theme) == theme:
|
||||
return True
|
||||
return False
|
||||
return unicode(self.settingsTab.bible_theme) == theme
|
||||
|
||||
def renameTheme(self, oldTheme, newTheme):
|
||||
"""
|
||||
@ -163,8 +169,8 @@ class BiblePlugin(Plugin):
|
||||
``newTheme``
|
||||
The new name the plugin should now use.
|
||||
"""
|
||||
self.settings_tab.bible_theme = newTheme
|
||||
self.settings_tab.save()
|
||||
self.settingsTab.bible_theme = newTheme
|
||||
self.settingsTab.save()
|
||||
|
||||
def setPluginTextStrings(self):
|
||||
"""
|
||||
|
@ -371,7 +371,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
"""
|
||||
Perform the actual upgrade.
|
||||
"""
|
||||
self.include_webbible = False
|
||||
self.includeWebBible = False
|
||||
proxy_server = None
|
||||
if not self.files:
|
||||
self.progressLabel.setText(
|
||||
@ -383,14 +383,14 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
for number, file in enumerate(self.files):
|
||||
if self.checkBox[number].checkState() == QtCore.Qt.Checked:
|
||||
max_bibles += 1
|
||||
oldBible = None
|
||||
old_bible = None
|
||||
for number, filename in enumerate(self.files):
|
||||
# Close the previous bible's connection.
|
||||
if oldBible is not None:
|
||||
oldBible.close_connection()
|
||||
if old_bible is not None:
|
||||
old_bible.close_connection()
|
||||
# Set to None to make obvious that we have already closed the
|
||||
# database.
|
||||
oldBible = None
|
||||
old_bible = None
|
||||
if self.stop_import_flag:
|
||||
self.success[number] = False
|
||||
break
|
||||
@ -398,7 +398,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
self.success[number] = False
|
||||
continue
|
||||
self.progressBar.reset()
|
||||
oldBible = OldBibleDB(self.mediaItem, path=self.temp_dir,
|
||||
old_bible = OldBibleDB(self.mediaItem, path=self.temp_dir,
|
||||
file=filename[0])
|
||||
name = filename[1]
|
||||
self.progressLabel.setText(unicode(translate(
|
||||
@ -408,33 +408,38 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
self.newbibles[number] = BibleDB(self.mediaItem, path=self.path,
|
||||
name=name, file=filename[0])
|
||||
self.newbibles[number].register(self.plugin.upgrade_wizard)
|
||||
metadata = oldBible.get_metadata()
|
||||
webbible = False
|
||||
metadata = old_bible.get_metadata()
|
||||
web_bible = False
|
||||
meta_data = {}
|
||||
for meta in metadata:
|
||||
# Upgrade the names of the metadata keys
|
||||
if meta[u'key'] == u'Version':
|
||||
meta[u'key'] = u'name'
|
||||
if meta[u'key'] == u'Bookname language':
|
||||
meta[u'key'] = 'book_name_language'
|
||||
meta[u'key'] = meta[u'key'].lower().replace(' ', '_')
|
||||
# Copy the metadata
|
||||
meta_data[meta[u'key']] = meta[u'value']
|
||||
if not meta[u'key'] == u'Version' and not meta[u'key'] == \
|
||||
u'dbversion':
|
||||
if meta[u'key'] != u'name' and meta[u'key'] != u'dbversion':
|
||||
self.newbibles[number].save_meta(meta[u'key'],
|
||||
meta[u'value'])
|
||||
if meta[u'key'] == u'download source':
|
||||
webbible = True
|
||||
self.include_webbible = True
|
||||
if meta.has_key(u'proxy server'):
|
||||
proxy_server = meta[u'proxy server']
|
||||
if webbible:
|
||||
if meta_data[u'download source'].lower() == u'crosswalk':
|
||||
if meta[u'key'] == u'download_source':
|
||||
web_bible = True
|
||||
self.includeWebBible = True
|
||||
proxy_server = meta.get(u'proxy_server')
|
||||
if web_bible:
|
||||
if meta_data[u'download_source'].lower() == u'crosswalk':
|
||||
handler = CWExtract(proxy_server)
|
||||
elif meta_data[u'download source'].lower() == u'biblegateway':
|
||||
elif meta_data[u'download_source'].lower() == u'biblegateway':
|
||||
handler = BGExtract(proxy_server)
|
||||
elif meta_data[u'download source'].lower() == u'bibleserver':
|
||||
elif meta_data[u'download_source'].lower() == u'bibleserver':
|
||||
handler = BSExtract(proxy_server)
|
||||
books = handler.get_books_from_http(meta_data[u'download name'])
|
||||
books = handler.get_books_from_http(meta_data[u'download_name'])
|
||||
if not books:
|
||||
log.error(u'Upgrading books from %s - download '\
|
||||
u'name: "%s" failed' % (
|
||||
meta_data[u'download source'],
|
||||
meta_data[u'download name']))
|
||||
meta_data[u'download_source'],
|
||||
meta_data[u'download_name']))
|
||||
self.newbibles[number].session.close()
|
||||
del self.newbibles[number]
|
||||
critical_error_message_box(
|
||||
@ -451,8 +456,8 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
self.success[number] = False
|
||||
continue
|
||||
bible = BiblesResourcesDB.get_webbible(
|
||||
meta_data[u'download name'],
|
||||
meta_data[u'download source'].lower())
|
||||
meta_data[u'download_name'],
|
||||
meta_data[u'download_source'].lower())
|
||||
if bible and bible[u'language_id']:
|
||||
language_id = bible[u'language_id']
|
||||
self.newbibles[number].save_meta(u'language_id',
|
||||
@ -485,8 +490,8 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
if not book_ref_id:
|
||||
log.warn(u'Upgrading books from %s - download '\
|
||||
u'name: "%s" aborted by user' % (
|
||||
meta_data[u'download source'],
|
||||
meta_data[u'download name']))
|
||||
meta_data[u'download_source'],
|
||||
meta_data[u'download_name']))
|
||||
self.newbibles[number].session.close()
|
||||
del self.newbibles[number]
|
||||
self.success[number] = False
|
||||
@ -495,9 +500,9 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
db_book = self.newbibles[number].create_book(book,
|
||||
book_ref_id, book_details[u'testament_id'])
|
||||
# Try to import already downloaded verses.
|
||||
oldbook = oldBible.get_book(book)
|
||||
oldbook = old_bible.get_book(book)
|
||||
if oldbook:
|
||||
verses = oldBible.get_verses(oldbook[u'id'])
|
||||
verses = old_bible.get_verses(oldbook[u'id'])
|
||||
if not verses:
|
||||
log.warn(u'No verses found to import for book '
|
||||
u'"%s"', book)
|
||||
@ -527,7 +532,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
self.progressBar.maximum() - self.progressBar.value())
|
||||
self.success[number] = False
|
||||
continue
|
||||
books = oldBible.get_books()
|
||||
books = old_bible.get_books()
|
||||
self.progressBar.setMaximum(len(books))
|
||||
for book in books:
|
||||
if self.stop_import_flag:
|
||||
@ -551,7 +556,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
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 = old_bible.get_verses(book[u'id'])
|
||||
if not verses:
|
||||
log.warn(u'No verses found to import for book '
|
||||
u'"%s"', book[u'name'])
|
||||
@ -566,7 +571,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
int(verse[u'verse']), unicode(verse[u'text']))
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
self.newbibles[number].session.commit()
|
||||
if self.success.has_key(number) and not self.success[number]:
|
||||
if not self.success.get(number, True):
|
||||
self.incrementProgressBar(unicode(translate(
|
||||
'BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\nFailed')) %
|
||||
@ -574,17 +579,17 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
self.progressBar.maximum() - self.progressBar.value())
|
||||
else:
|
||||
self.success[number] = True
|
||||
self.newbibles[number].save_meta(u'Version', name)
|
||||
self.newbibles[number].save_meta(u'name', name)
|
||||
self.incrementProgressBar(unicode(translate(
|
||||
'BiblesPlugin.UpgradeWizardForm',
|
||||
'Upgrading Bible %s of %s: "%s"\n'
|
||||
'Complete')) %
|
||||
(number + 1, max_bibles, name))
|
||||
if self.newbibles.has_key(number):
|
||||
if number in self.newbibles:
|
||||
self.newbibles[number].session.close()
|
||||
# Close the last bible's connection if possible.
|
||||
if oldBible is not None:
|
||||
oldBible.close_connection()
|
||||
if old_bible is not None:
|
||||
old_bible.close_connection()
|
||||
|
||||
def postWizard(self):
|
||||
"""
|
||||
@ -593,7 +598,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
successful_import = 0
|
||||
failed_import = 0
|
||||
for number, filename in enumerate(self.files):
|
||||
if self.success.has_key(number) and self.success[number]:
|
||||
if self.success.get(number):
|
||||
successful_import += 1
|
||||
elif self.checkBox[number].checkState() == QtCore.Qt.Checked:
|
||||
failed_import += 1
|
||||
@ -608,7 +613,7 @@ class BibleUpgradeForm(OpenLPWizard):
|
||||
else:
|
||||
failed_import_text = u''
|
||||
if successful_import > 0:
|
||||
if self.include_webbible:
|
||||
if self.includeWebBible:
|
||||
self.progressLabel.setText(unicode(
|
||||
translate('BiblesPlugin.UpgradeWizardForm', 'Upgrading '
|
||||
'Bible(s): %s successful%s\nPlease note that verses from '
|
||||
|
@ -90,7 +90,7 @@ class Ui_BookNameDialog(object):
|
||||
'Select Book Name'))
|
||||
self.infoLabel.setText(translate('BiblesPlugin.BookNameDialog',
|
||||
'The following book name cannot be matched up internally. Please '
|
||||
'select the corresponding English name from the list.'))
|
||||
'select the corresponding name from the list.'))
|
||||
self.currentLabel.setText(translate('BiblesPlugin.BookNameDialog',
|
||||
'Current name:'))
|
||||
self.correspondingLabel.setText(translate(
|
||||
|
@ -28,6 +28,7 @@
|
||||
Module implementing BookNameForm.
|
||||
"""
|
||||
import logging
|
||||
import re
|
||||
|
||||
from PyQt4.QtGui import QDialog
|
||||
from PyQt4 import QtCore
|
||||
@ -36,6 +37,7 @@ from openlp.core.lib import translate
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.plugins.bibles.forms.booknamedialog import \
|
||||
Ui_BookNameDialog
|
||||
from openlp.plugins.bibles.lib import BibleStrings
|
||||
from openlp.plugins.bibles.lib.db import BiblesResourcesDB
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -54,6 +56,8 @@ class BookNameForm(QDialog, Ui_BookNameDialog):
|
||||
QDialog.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
self.customSignals()
|
||||
self.book_names = BibleStrings().BookNames
|
||||
self.book_id = False
|
||||
|
||||
def customSignals(self):
|
||||
"""
|
||||
@ -97,7 +101,8 @@ class BookNameForm(QDialog, Ui_BookNameDialog):
|
||||
and item[u'testament_id'] == 3:
|
||||
addBook = False
|
||||
if addBook:
|
||||
self.correspondingComboBox.addItem(item[u'name'])
|
||||
self.correspondingComboBox.addItem(
|
||||
self.book_names[item[u'abbreviation']])
|
||||
|
||||
def exec_(self, name, books, maxbooks):
|
||||
self.books = books
|
||||
@ -120,4 +125,13 @@ class BookNameForm(QDialog, Ui_BookNameDialog):
|
||||
self.correspondingComboBox.setFocus()
|
||||
return False
|
||||
else:
|
||||
cor_book = unicode(self.correspondingComboBox.currentText())
|
||||
for character in u'\\.^$*+?{}[]()':
|
||||
cor_book = cor_book.replace(character, u'\\' + character)
|
||||
books = filter(lambda key:
|
||||
re.match(cor_book, unicode(self.book_names[key]), re.UNICODE),
|
||||
self.book_names.keys())
|
||||
books = filter(None, map(BiblesResourcesDB.get_book, books))
|
||||
if books:
|
||||
self.book_id = books[0][u'id']
|
||||
return QDialog.accept(self)
|
||||
|
@ -141,7 +141,7 @@ class Ui_EditBibleDialog(object):
|
||||
QtCore.QMetaObject.connectSlotsByName(editBibleDialog)
|
||||
|
||||
def retranslateUi(self, editBibleDialog):
|
||||
self.booknames = BibleStrings().Booknames
|
||||
self.book_names = BibleStrings().BookNames
|
||||
editBibleDialog.setWindowTitle(
|
||||
translate('BiblesPlugin.EditBibleForm', 'Bible Editor'))
|
||||
# Meta tab
|
||||
@ -178,4 +178,4 @@ class Ui_EditBibleDialog(object):
|
||||
translate('SongsPlugin.EditBibleForm', 'Custom Book Names'))
|
||||
for book in BiblesResourcesDB.get_books():
|
||||
self.bookNameLabel[book[u'abbreviation']].setText(
|
||||
u'%s:' % unicode(self.booknames[book[u'abbreviation']]))
|
||||
u'%s:' % unicode(self.book_names[book[u'abbreviation']]))
|
||||
|
@ -50,7 +50,7 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
|
||||
"""
|
||||
QtGui.QDialog.__init__(self, parent)
|
||||
self.mediaitem = mediaitem
|
||||
self.booknames = BibleStrings().Booknames
|
||||
self.book_names = BibleStrings().BookNames
|
||||
self.setupUi(self)
|
||||
self.manager = manager
|
||||
|
||||
@ -64,19 +64,19 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
|
||||
log.debug(u'Load Bible')
|
||||
self.bible = bible
|
||||
self.versionNameEdit.setText(
|
||||
self.manager.get_meta_data(self.bible, u'Version').value)
|
||||
self.manager.get_meta_data(self.bible, u'name').value)
|
||||
self.copyrightEdit.setText(
|
||||
self.manager.get_meta_data(self.bible, u'Copyright').value)
|
||||
self.manager.get_meta_data(self.bible, u'copyright').value)
|
||||
self.permissionsEdit.setText(
|
||||
self.manager.get_meta_data(self.bible, u'Permissions').value)
|
||||
bookname_language = self.manager.get_meta_data(self.bible,
|
||||
u'Bookname language')
|
||||
if bookname_language and bookname_language.value != u'None':
|
||||
self.manager.get_meta_data(self.bible, u'permissions').value)
|
||||
book_name_language = self.manager.get_meta_data(self.bible,
|
||||
u'book_name_language')
|
||||
if book_name_language and book_name_language.value != u'None':
|
||||
self.languageSelectionComboBox.setCurrentIndex(
|
||||
int(bookname_language.value) + 1)
|
||||
int(book_name_language.value) + 1)
|
||||
self.books = {}
|
||||
self.webbible = self.manager.get_meta_data(self.bible,
|
||||
u'download source')
|
||||
u'download_source')
|
||||
if self.webbible:
|
||||
self.bookNameNotice.setText(translate('BiblesPlugin.EditBibleForm',
|
||||
'This is a Web Download Bible.\nIt is not possible to '
|
||||
@ -119,9 +119,9 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
|
||||
version = unicode(self.versionNameEdit.text())
|
||||
copyright = unicode(self.copyrightEdit.text())
|
||||
permissions = unicode(self.permissionsEdit.text())
|
||||
bookname_language = self.languageSelectionComboBox.currentIndex() - 1
|
||||
if bookname_language == -1:
|
||||
bookname_language = None
|
||||
book_name_language = self.languageSelectionComboBox.currentIndex() - 1
|
||||
if book_name_language == -1:
|
||||
book_name_language = None
|
||||
if not self.validateMeta(version, copyright):
|
||||
return
|
||||
if not self.webbible:
|
||||
@ -135,7 +135,7 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
Receiver.send_message(u'cursor_busy')
|
||||
self.manager.save_meta_data(self.bible, version, copyright, permissions,
|
||||
bookname_language)
|
||||
book_name_language)
|
||||
if not self.webbible:
|
||||
for abbr, book in self.books.iteritems():
|
||||
if book:
|
||||
@ -146,11 +146,11 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
|
||||
Receiver.send_message(u'cursor_normal')
|
||||
QtGui.QDialog.accept(self)
|
||||
|
||||
def validateMeta(self, version, copyright):
|
||||
def validateMeta(self, name, copyright):
|
||||
"""
|
||||
Validate the Meta before saving.
|
||||
"""
|
||||
if not version:
|
||||
if not name:
|
||||
self.versionNameEdit.setFocus()
|
||||
critical_error_message_box(UiStrings().EmptyField,
|
||||
translate('BiblesPlugin.BibleEditForm',
|
||||
@ -163,9 +163,9 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
|
||||
'You need to set a copyright for your Bible. '
|
||||
'Bibles in the Public Domain need to be marked as such.'))
|
||||
return False
|
||||
elif self.manager.exists(version) and \
|
||||
self.manager.get_meta_data(self.bible, u'Version').value != \
|
||||
version:
|
||||
elif self.manager.exists(name) and \
|
||||
self.manager.get_meta_data(self.bible, u'name').value != \
|
||||
name:
|
||||
self.versionNameEdit.setFocus()
|
||||
critical_error_message_box(
|
||||
translate('BiblesPlugin.BibleEditForm', 'Bible Exists'),
|
||||
@ -175,37 +175,37 @@ class EditBibleForm(QtGui.QDialog, Ui_EditBibleDialog):
|
||||
return False
|
||||
return True
|
||||
|
||||
def validateBook(self, new_bookname, abbreviation):
|
||||
def validateBook(self, new_book_name, abbreviation):
|
||||
"""
|
||||
Validate a book.
|
||||
"""
|
||||
book_regex = re.compile(u'[\d]*[^\d]+$')
|
||||
if not new_bookname:
|
||||
if not new_book_name:
|
||||
self.bookNameEdit[abbreviation].setFocus()
|
||||
critical_error_message_box(UiStrings().EmptyField,
|
||||
unicode(translate('BiblesPlugin.BibleEditForm',
|
||||
'You need to specify a book name for "%s".')) %
|
||||
self.booknames[abbreviation])
|
||||
self.book_names[abbreviation])
|
||||
return False
|
||||
elif not book_regex.match(new_bookname):
|
||||
elif not book_regex.match(new_book_name):
|
||||
self.bookNameEdit[abbreviation].setFocus()
|
||||
critical_error_message_box(UiStrings().EmptyField,
|
||||
unicode(translate('BiblesPlugin.BibleEditForm',
|
||||
'The book name "%s" is not correct.\nNumbers can only be used '
|
||||
'at the beginning and must\nbe followed by one or more '
|
||||
'non-numeric characters.')) % new_bookname)
|
||||
'non-numeric characters.')) % new_book_name)
|
||||
return False
|
||||
for abbr, book in self.books.iteritems():
|
||||
if book:
|
||||
if abbr == abbreviation:
|
||||
continue
|
||||
if unicode(self.bookNameEdit[abbr].text()) == new_bookname:
|
||||
if unicode(self.bookNameEdit[abbr].text()) == new_book_name:
|
||||
self.bookNameEdit[abbreviation].setFocus()
|
||||
critical_error_message_box(
|
||||
translate('BiblesPlugin.BibleEditForm',
|
||||
'Duplicate Book Name'),
|
||||
unicode(translate('BiblesPlugin.BibleEditForm',
|
||||
'The Book Name "%s" has been entered more than once.'))
|
||||
% new_bookname)
|
||||
% new_book_name)
|
||||
return False
|
||||
return True
|
||||
|
@ -88,7 +88,7 @@ class BibleStrings(object):
|
||||
"""
|
||||
These strings should need a good reason to be retranslated elsewhere.
|
||||
"""
|
||||
self.Booknames = {
|
||||
self.BookNames = {
|
||||
u'Gen': translate('BiblesPlugin', 'Genesis'),
|
||||
u'Exod': translate('BiblesPlugin', 'Exodus'),
|
||||
u'Lev': translate('BiblesPlugin', 'Leviticus'),
|
||||
@ -236,7 +236,7 @@ def get_reference_separator(separator_type):
|
||||
``separator_type``
|
||||
The role and format of the separator.
|
||||
"""
|
||||
if len(REFERENCE_SEPARATORS) == 0:
|
||||
if not REFERENCE_SEPARATORS:
|
||||
update_reference_separators()
|
||||
return REFERENCE_SEPARATORS[separator_type]
|
||||
|
||||
@ -247,7 +247,7 @@ def get_reference_match(match_type):
|
||||
``match_type``
|
||||
The type of match is ``range_separator``, ``range`` or ``full``.
|
||||
"""
|
||||
if len(REFERENCE_MATCHES) == 0:
|
||||
if not REFERENCE_MATCHES:
|
||||
update_reference_separators()
|
||||
return REFERENCE_MATCHES[match_type]
|
||||
|
||||
@ -355,7 +355,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
|
||||
log.debug(u'Matched reference %s' % reference)
|
||||
book = match.group(u'book')
|
||||
if not book_ref_id:
|
||||
booknames = BibleStrings().Booknames
|
||||
book_names = BibleStrings().BookNames
|
||||
# escape reserved characters
|
||||
book_escaped = book
|
||||
for character in u'\\.^$*+?{}[]()':
|
||||
@ -369,7 +369,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
|
||||
book_ref_id = db_book.book_reference_id
|
||||
elif language_selection == LanguageSelection.Application:
|
||||
books = filter(lambda key:
|
||||
regex_book.match(unicode(booknames[key])), booknames.keys())
|
||||
regex_book.match(unicode(book_names[key])), book_names.keys())
|
||||
books = filter(None, map(BiblesResourcesDB.get_book, books))
|
||||
for value in books:
|
||||
if bible.get_book_by_book_ref_id(value[u'id']):
|
||||
|
@ -483,7 +483,7 @@ class BiblesTab(SettingsTab):
|
||||
self.getGreyTextPalette(False))
|
||||
self.endSeparatorCheckBox.setChecked(True)
|
||||
self.language_selection = settings.value(
|
||||
u'bookname language', QtCore.QVariant(0)).toInt()[0]
|
||||
u'book name language', QtCore.QVariant(0)).toInt()[0]
|
||||
self.languageSelectionComboBox.setCurrentIndex(self.language_selection)
|
||||
settings.endGroup()
|
||||
|
||||
@ -496,7 +496,7 @@ class BiblesTab(SettingsTab):
|
||||
QtCore.QVariant(self.display_style))
|
||||
settings.setValue(u'verse layout style',
|
||||
QtCore.QVariant(self.layout_style))
|
||||
settings.setValue(u'bookname language',
|
||||
settings.setValue(u'book name language',
|
||||
QtCore.QVariant(self.language_selection))
|
||||
settings.setValue(u'second bibles', QtCore.QVariant(self.second_bibles))
|
||||
settings.setValue(u'bible theme', QtCore.QVariant(self.bible_theme))
|
||||
|
@ -39,6 +39,7 @@ 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, clean_filename
|
||||
import upgrade
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -147,7 +148,7 @@ class BibleDB(QtCore.QObject, Manager):
|
||||
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)
|
||||
Manager.__init__(self, u'bibles', init_schema, self.file, upgrade)
|
||||
if u'file' in kwargs:
|
||||
self.get_name()
|
||||
if u'path' in kwargs:
|
||||
@ -167,7 +168,7 @@ class BibleDB(QtCore.QObject, Manager):
|
||||
"""
|
||||
Returns the version name of the Bible.
|
||||
"""
|
||||
version_name = self.get_object(BibleMeta, u'Version')
|
||||
version_name = self.get_object(BibleMeta, u'name')
|
||||
self.name = version_name.value if version_name else None
|
||||
return self.name
|
||||
|
||||
@ -182,7 +183,6 @@ class BibleDB(QtCore.QObject, Manager):
|
||||
The actual Qt wizard form.
|
||||
"""
|
||||
self.wizard = wizard
|
||||
self.save_meta(u'dbversion', u'2')
|
||||
return self.name
|
||||
|
||||
def create_book(self, name, bk_ref_id, testament=1):
|
||||
@ -332,6 +332,7 @@ class BibleDB(QtCore.QObject, Manager):
|
||||
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,
|
||||
language_id)
|
||||
book_id = None
|
||||
if BiblesResourcesDB.get_book(book, True):
|
||||
book_temp = BiblesResourcesDB.get_book(book, True)
|
||||
book_id = book_temp[u'id']
|
||||
@ -341,26 +342,13 @@ class BibleDB(QtCore.QObject, Manager):
|
||||
book_id = AlternativeBookNamesDB.get_book_reference_id(book)
|
||||
else:
|
||||
from openlp.plugins.bibles.forms import BookNameForm
|
||||
book_ref = None
|
||||
book_name = BookNameForm(self.wizard)
|
||||
if book_name.exec_(book, self.get_books(), maxbooks):
|
||||
book_ref = unicode(
|
||||
book_name.correspondingComboBox.currentText())
|
||||
if not book_ref:
|
||||
return None
|
||||
else:
|
||||
book_temp = BiblesResourcesDB.get_book(book_ref)
|
||||
if book_temp:
|
||||
book_id = book_temp[u'id']
|
||||
else:
|
||||
return None
|
||||
book_id = book_name.book_id
|
||||
if book_id:
|
||||
AlternativeBookNamesDB.create_alternative_book_name(
|
||||
book, book_id, language_id)
|
||||
if book_id:
|
||||
return book_id
|
||||
else:
|
||||
return None
|
||||
return book_id
|
||||
|
||||
def get_verses(self, reference_list, show_error=True):
|
||||
"""
|
||||
@ -495,7 +483,7 @@ class BibleDB(QtCore.QObject, Manager):
|
||||
prior to 1.9.6.
|
||||
"""
|
||||
try:
|
||||
columns = self.session.query(Book).all()
|
||||
self.session.query(Book).all()
|
||||
except:
|
||||
return True
|
||||
return False
|
||||
@ -562,16 +550,13 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
log.debug(u'BiblesResourcesDB.get_books()')
|
||||
books = BiblesResourcesDB.run_sql(u'SELECT id, testament_id, name, '
|
||||
u'abbreviation, chapters FROM book_reference ORDER BY id')
|
||||
return [
|
||||
{
|
||||
u'id': book[0],
|
||||
u'testament_id': book[1],
|
||||
u'name': unicode(book[2]),
|
||||
u'abbreviation': unicode(book[3]),
|
||||
u'chapters': book[4]
|
||||
}
|
||||
for book in books
|
||||
]
|
||||
return [{
|
||||
u'id': book[0],
|
||||
u'testament_id': book[1],
|
||||
u'name': unicode(book[2]),
|
||||
u'abbreviation': unicode(book[3]),
|
||||
u'chapters': book[4]
|
||||
} for book in books]
|
||||
|
||||
@staticmethod
|
||||
def get_book(name, lower=False):
|
||||
@ -613,7 +598,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
Return the books which include string.
|
||||
|
||||
``string``
|
||||
The string to search for in the booknames or abbreviations.
|
||||
The string to search for in the book names or abbreviations.
|
||||
"""
|
||||
log.debug(u'BiblesResourcesDB.get_book_like("%s")', string)
|
||||
if not isinstance(string, unicode):
|
||||
@ -623,16 +608,13 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
u'LOWER(name) LIKE ? OR LOWER(abbreviation) LIKE ?',
|
||||
(u'%' + string.lower() + u'%', u'%' + string.lower() + u'%'))
|
||||
if books:
|
||||
return [
|
||||
{
|
||||
return [{
|
||||
u'id': book[0],
|
||||
u'testament_id': book[1],
|
||||
u'name': unicode(book[2]),
|
||||
u'abbreviation': unicode(book[3]),
|
||||
u'chapters': book[4]
|
||||
}
|
||||
for book in books
|
||||
]
|
||||
} for book in books]
|
||||
else:
|
||||
return None
|
||||
|
||||
@ -759,16 +741,13 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
u'language_id, download_source_id FROM webbibles WHERE '
|
||||
u'download_source_id = ?', (source[u'id'],))
|
||||
if bibles:
|
||||
return [
|
||||
{
|
||||
return [{
|
||||
u'id': bible[0],
|
||||
u'name': bible[1],
|
||||
u'abbreviation': bible[2],
|
||||
u'language_id': bible[3],
|
||||
u'download_source_id': bible[4]
|
||||
}
|
||||
for bible in bibles
|
||||
]
|
||||
} for bible in bibles]
|
||||
else:
|
||||
return None
|
||||
|
||||
@ -801,7 +780,7 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
u'abbreviation': bible[0][2],
|
||||
u'language_id': bible[0][3],
|
||||
u'download_source_id': bible[0][4]
|
||||
}
|
||||
}
|
||||
except (IndexError, TypeError):
|
||||
return None
|
||||
|
||||
@ -862,14 +841,11 @@ class BiblesResourcesDB(QtCore.QObject, Manager):
|
||||
languages = BiblesResourcesDB.run_sql(u'SELECT id, name, code FROM '
|
||||
u'language ORDER by name')
|
||||
if languages:
|
||||
return [
|
||||
{
|
||||
return [{
|
||||
u'id': language[0],
|
||||
u'name': unicode(language[1]),
|
||||
u'code': unicode(language[2])
|
||||
}
|
||||
for language in languages
|
||||
]
|
||||
} for language in languages]
|
||||
else:
|
||||
return None
|
||||
|
||||
@ -1048,7 +1024,7 @@ class OldBibleDB(QtCore.QObject, Manager):
|
||||
Returns the version name of the Bible.
|
||||
"""
|
||||
version_name = self.run_sql(u'SELECT value FROM '
|
||||
u'metadata WHERE key = "Version"')
|
||||
u'metadata WHERE key = "name"')
|
||||
if version_name:
|
||||
self.name = version_name[0][0]
|
||||
else:
|
||||
@ -1062,13 +1038,10 @@ class OldBibleDB(QtCore.QObject, Manager):
|
||||
metadata = self.run_sql(u'SELECT key, value FROM metadata '
|
||||
u'ORDER BY rowid')
|
||||
if metadata:
|
||||
return [
|
||||
{
|
||||
return [{
|
||||
u'key': unicode(meta[0]),
|
||||
u'value': unicode(meta[1])
|
||||
}
|
||||
for meta in metadata
|
||||
]
|
||||
} for meta in metadata]
|
||||
else:
|
||||
return None
|
||||
|
||||
@ -1100,13 +1073,10 @@ class OldBibleDB(QtCore.QObject, Manager):
|
||||
"""
|
||||
books = self.run_sql(u'SELECT name, id FROM book ORDER BY id')
|
||||
if books:
|
||||
return [
|
||||
{
|
||||
return [{
|
||||
u'name': unicode(book[0]),
|
||||
u'id':int(book[1])
|
||||
}
|
||||
for book in books
|
||||
]
|
||||
} for book in books]
|
||||
else:
|
||||
return None
|
||||
|
||||
@ -1117,15 +1087,12 @@ class OldBibleDB(QtCore.QObject, Manager):
|
||||
verses = self.run_sql(u'SELECT book_id, chapter, verse, text FROM '
|
||||
u'verse WHERE book_id = ? ORDER BY id', (book_id, ))
|
||||
if verses:
|
||||
return [
|
||||
{
|
||||
return [{
|
||||
u'book_id': int(verse[0]),
|
||||
u'chapter': int(verse[1]),
|
||||
u'verse': int(verse[2]),
|
||||
u'text': unicode(verse[3])
|
||||
}
|
||||
for verse in verses
|
||||
]
|
||||
} for verse in verses]
|
||||
else:
|
||||
return None
|
||||
|
||||
|
@ -49,28 +49,28 @@ class BGExtract(object):
|
||||
"""
|
||||
Extract verses from BibleGateway
|
||||
"""
|
||||
def __init__(self, proxyurl=None):
|
||||
log.debug(u'BGExtract.init("%s")', proxyurl)
|
||||
self.proxyurl = proxyurl
|
||||
def __init__(self, proxy_url=None):
|
||||
log.debug(u'BGExtract.init("%s")', proxy_url)
|
||||
self.proxy_url = proxy_url
|
||||
socket.setdefaulttimeout(30)
|
||||
|
||||
def get_bible_chapter(self, version, bookname, chapter):
|
||||
def get_bible_chapter(self, version, book_name, chapter):
|
||||
"""
|
||||
Access and decode Bibles via the BibleGateway website.
|
||||
|
||||
``version``
|
||||
The version of the Bible like 31 for New International version.
|
||||
|
||||
``bookname``
|
||||
``book_name``
|
||||
Name of the Book.
|
||||
|
||||
``chapter``
|
||||
Chapter number.
|
||||
"""
|
||||
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,
|
||||
book_name, chapter)
|
||||
url_book_name = urllib.quote(book_name.encode("utf-8"))
|
||||
url_params = u'search=%s+%s&version=%s' % (url_book_name, chapter,
|
||||
version)
|
||||
cleaner = [(re.compile(' |<br />|\'\+\''), lambda match: '')]
|
||||
soup = get_soup_for_bible_ref(
|
||||
@ -106,7 +106,7 @@ class BGExtract(object):
|
||||
verse_list = {}
|
||||
# Cater for inconsistent mark up in the first verse of a chapter.
|
||||
first_verse = verses.find(u'versenum')
|
||||
if first_verse and len(first_verse.contents):
|
||||
if first_verse and first_verse.contents:
|
||||
verse_list[1] = unicode(first_verse.contents[0])
|
||||
for verse in verses(u'sup', u'versenum'):
|
||||
raw_verse_num = verse.next
|
||||
@ -120,7 +120,7 @@ class BGExtract(object):
|
||||
clean_verse_num = int(str(raw_verse_num))
|
||||
except ValueError:
|
||||
log.warn(u'Illegal verse number in %s %s %s:%s',
|
||||
version, bookname, chapter, unicode(raw_verse_num))
|
||||
version, book_name, chapter, unicode(raw_verse_num))
|
||||
if clean_verse_num:
|
||||
verse_text = raw_verse_num.next
|
||||
part = raw_verse_num.next.next
|
||||
@ -138,7 +138,7 @@ class BGExtract(object):
|
||||
log.debug(u'No content found in the BibleGateway response.')
|
||||
send_error_message(u'parse')
|
||||
return None
|
||||
return SearchResults(bookname, chapter, verse_list)
|
||||
return SearchResults(book_name, chapter, verse_list)
|
||||
|
||||
def get_books_from_http(self, version):
|
||||
"""
|
||||
@ -195,30 +195,30 @@ class BSExtract(object):
|
||||
"""
|
||||
Extract verses from Bibleserver.com
|
||||
"""
|
||||
def __init__(self, proxyurl=None):
|
||||
log.debug(u'BSExtract.init("%s")', proxyurl)
|
||||
self.proxyurl = proxyurl
|
||||
def __init__(self, proxy_url=None):
|
||||
log.debug(u'BSExtract.init("%s")', proxy_url)
|
||||
self.proxy_url = proxy_url
|
||||
socket.setdefaulttimeout(30)
|
||||
|
||||
def get_bible_chapter(self, version, bookname, chapter):
|
||||
def get_bible_chapter(self, version, book_name, chapter):
|
||||
"""
|
||||
Access and decode bibles via Bibleserver mobile website
|
||||
|
||||
``version``
|
||||
The version of the bible like NIV for New International Version
|
||||
|
||||
``bookname``
|
||||
``book_name``
|
||||
Text name of bible book e.g. Genesis, 1. John, 1John or Offenbarung
|
||||
|
||||
``chapter``
|
||||
Chapter number
|
||||
"""
|
||||
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"))
|
||||
book_name, chapter)
|
||||
url_version = urllib.quote(version.encode("utf-8"))
|
||||
url_book_name = urllib.quote(book_name.encode("utf-8"))
|
||||
chapter_url = u'http://m.bibleserver.com/text/%s/%s%d' % \
|
||||
(urlversion, urlbookname, chapter)
|
||||
(url_version, url_book_name, chapter)
|
||||
header = (u'Accept-Language', u'en')
|
||||
soup = get_soup_for_bible_ref(chapter_url, header)
|
||||
if not soup:
|
||||
@ -236,7 +236,7 @@ class BSExtract(object):
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
versenumber = int(verse_number.sub(r'\3', verse[u'class']))
|
||||
verses[versenumber] = verse.contents[1].rstrip(u'\n')
|
||||
return SearchResults(bookname, chapter, verses)
|
||||
return SearchResults(book_name, chapter, verses)
|
||||
|
||||
def get_books_from_http(self, version):
|
||||
"""
|
||||
@ -268,72 +268,72 @@ class CWExtract(object):
|
||||
"""
|
||||
Extract verses from CrossWalk/BibleStudyTools
|
||||
"""
|
||||
def __init__(self, proxyurl=None):
|
||||
log.debug(u'CWExtract.init("%s")', proxyurl)
|
||||
self.proxyurl = proxyurl
|
||||
def __init__(self, proxy_url=None):
|
||||
log.debug(u'CWExtract.init("%s")', proxy_url)
|
||||
self.proxy_url = proxy_url
|
||||
socket.setdefaulttimeout(30)
|
||||
|
||||
def get_bible_chapter(self, version, bookname, chapter):
|
||||
def get_bible_chapter(self, version, book_name, chapter):
|
||||
"""
|
||||
Access and decode bibles via the Crosswalk website
|
||||
|
||||
``version``
|
||||
The version of the Bible like niv for New International Version
|
||||
|
||||
``bookname``
|
||||
``book_name``
|
||||
Text name of in english e.g. 'gen' for Genesis
|
||||
|
||||
``chapter``
|
||||
Chapter number
|
||||
"""
|
||||
log.debug(u'CWExtract.get_bible_chapter("%s", "%s", "%s")', version,
|
||||
bookname, chapter)
|
||||
urlbookname = bookname.replace(u' ', u'-')
|
||||
urlbookname = urlbookname.lower()
|
||||
urlbookname = urllib.quote(urlbookname.encode("utf-8"))
|
||||
book_name, chapter)
|
||||
url_book_name = book_name.replace(u' ', u'-')
|
||||
url_book_name = url_book_name.lower()
|
||||
url_book_name = urllib.quote(url_book_name.encode("utf-8"))
|
||||
chapter_url = u'http://www.biblestudytools.com/%s/%s/%s.html' % \
|
||||
(version, urlbookname, chapter)
|
||||
(version, url_book_name, chapter)
|
||||
soup = get_soup_for_bible_ref(chapter_url)
|
||||
if not soup:
|
||||
return None
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
htmlverses = soup.findAll(u'span', u'versetext')
|
||||
if not htmlverses:
|
||||
html_verses = soup.findAll(u'span', u'versetext')
|
||||
if not html_verses:
|
||||
log.error(u'No verses found in the CrossWalk response.')
|
||||
send_error_message(u'parse')
|
||||
return None
|
||||
verses = {}
|
||||
reduce_spaces = re.compile(r'[ ]{2,}')
|
||||
fix_punctuation = re.compile(r'[ ]+([.,;])')
|
||||
for verse in htmlverses:
|
||||
for verse in html_verses:
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
versenumber = int(verse.contents[0].contents[0])
|
||||
versetext = u''
|
||||
verse_number = int(verse.contents[0].contents[0])
|
||||
verse_text = u''
|
||||
for part in verse.contents:
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
if isinstance(part, NavigableString):
|
||||
versetext = versetext + part
|
||||
verse_text = verse_text + part
|
||||
elif part and part.attrMap and \
|
||||
(part.attrMap[u'class'] == u'WordsOfChrist' or \
|
||||
part.attrMap[u'class'] == u'strongs'):
|
||||
for subpart in part.contents:
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
if isinstance(subpart, NavigableString):
|
||||
versetext = versetext + subpart
|
||||
verse_text = verse_text + subpart
|
||||
elif subpart and subpart.attrMap and \
|
||||
subpart.attrMap[u'class'] == u'strongs':
|
||||
for subsub in subpart.contents:
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
if isinstance(subsub, NavigableString):
|
||||
versetext = versetext + subsub
|
||||
verse_text = verse_text + subsub
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
# Fix up leading and trailing spaces, multiple spaces, and spaces
|
||||
# between text and , and .
|
||||
versetext = versetext.strip(u'\n\r\t ')
|
||||
versetext = reduce_spaces.sub(u' ', versetext)
|
||||
versetext = fix_punctuation.sub(r'\1', versetext)
|
||||
verses[versenumber] = versetext
|
||||
return SearchResults(bookname, chapter, verses)
|
||||
verse_text = verse_text.strip(u'\n\r\t ')
|
||||
verse_text = reduce_spaces.sub(u' ', verse_text)
|
||||
verse_text = fix_punctuation.sub(r'\1', verse_text)
|
||||
verses[verse_number] = verse_text
|
||||
return SearchResults(book_name, chapter, verses)
|
||||
|
||||
def get_books_from_http(self, version):
|
||||
"""
|
||||
@ -401,16 +401,16 @@ class HTTPBible(BibleDB):
|
||||
self.wizard.incrementProgressBar(unicode(translate(
|
||||
'BiblesPlugin.HTTPBible',
|
||||
'Registering Bible and loading books...')))
|
||||
self.save_meta(u'download source', self.download_source)
|
||||
self.save_meta(u'download name', self.download_name)
|
||||
self.save_meta(u'download_source', self.download_source)
|
||||
self.save_meta(u'download_name', self.download_name)
|
||||
if self.proxy_server:
|
||||
self.save_meta(u'proxy server', self.proxy_server)
|
||||
self.save_meta(u'proxy_server', self.proxy_server)
|
||||
if self.proxy_username:
|
||||
# Store the proxy userid.
|
||||
self.save_meta(u'proxy username', self.proxy_username)
|
||||
self.save_meta(u'proxy_username', self.proxy_username)
|
||||
if self.proxy_password:
|
||||
# Store the proxy password.
|
||||
self.save_meta(u'proxy password', self.proxy_password)
|
||||
self.save_meta(u'proxy_password', self.proxy_password)
|
||||
if self.download_source.lower() == u'crosswalk':
|
||||
handler = CWExtract(self.proxy_server)
|
||||
elif self.download_source.lower() == u'biblegateway':
|
||||
@ -499,10 +499,10 @@ class HTTPBible(BibleDB):
|
||||
## if it was there. By reusing the returned book name
|
||||
## we get a correct book. For example it is possible
|
||||
## to request ac and get Acts back.
|
||||
bookname = search_results.book
|
||||
book_name = search_results.book
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
# Check to see if book/chapter exists.
|
||||
db_book = self.get_book(bookname)
|
||||
db_book = self.get_book(book_name)
|
||||
self.create_chapter(db_book.id, search_results.chapter,
|
||||
search_results.verselist)
|
||||
Receiver.send_message(u'openlp_process_events')
|
||||
|
@ -164,12 +164,12 @@ class BibleManager(object):
|
||||
self.db_cache[name] = bible
|
||||
# Look to see if lazy load bible exists and get create getter.
|
||||
source = self.db_cache[name].get_object(BibleMeta,
|
||||
u'download source')
|
||||
u'download_source')
|
||||
if source:
|
||||
download_name = self.db_cache[name].get_object(BibleMeta,
|
||||
u'download name').value
|
||||
u'download_name').value
|
||||
meta_proxy = self.db_cache[name].get_object(BibleMeta,
|
||||
u'proxy url')
|
||||
u'proxy_server')
|
||||
web_bible = HTTPBible(self.parent, path=self.path,
|
||||
file=filename, download_source=source.value,
|
||||
download_name=download_name)
|
||||
@ -330,7 +330,7 @@ class BibleManager(object):
|
||||
'Import Wizard to install one or more Bibles.')
|
||||
})
|
||||
return None
|
||||
language_selection = self.get_meta_data(bible, u'Bookname language')
|
||||
language_selection = self.get_meta_data(bible, u'book_name_language')
|
||||
if language_selection:
|
||||
language_selection = int(language_selection.value)
|
||||
if language_selection is None or language_selection == -1:
|
||||
@ -377,7 +377,7 @@ class BibleManager(object):
|
||||
Unicode. The Bible to get the language selection from.
|
||||
"""
|
||||
log.debug(u'BibleManager.get_language_selection("%s")', bible)
|
||||
language_selection = self.get_meta_data(bible, u'Bookname language')
|
||||
language_selection = self.get_meta_data(bible, u'book_name_language')
|
||||
if language_selection and language_selection.value != u'None':
|
||||
return int(language_selection.value)
|
||||
if language_selection is None or language_selection.value == u'None':
|
||||
@ -410,17 +410,17 @@ class BibleManager(object):
|
||||
return None
|
||||
# Check if the bible or second_bible is a web bible.
|
||||
webbible = self.db_cache[bible].get_object(BibleMeta,
|
||||
u'download source')
|
||||
u'download_source')
|
||||
second_webbible = u''
|
||||
if second_bible:
|
||||
second_webbible = self.db_cache[second_bible].get_object(BibleMeta,
|
||||
u'download source')
|
||||
u'download_source')
|
||||
if webbible or second_webbible:
|
||||
Receiver.send_message(u'openlp_information_message', {
|
||||
u'title': translate('BiblesPlugin.BibleManager',
|
||||
'Web Bible cannot be used'),
|
||||
'Web Bible cannot be used'),
|
||||
u'message': translate('BiblesPlugin.BibleManager',
|
||||
'Text Search is not available with Web Bibles.')
|
||||
'Text Search is not available with Web Bibles.')
|
||||
})
|
||||
return None
|
||||
if text:
|
||||
@ -428,27 +428,27 @@ class BibleManager(object):
|
||||
else:
|
||||
Receiver.send_message(u'openlp_information_message', {
|
||||
u'title': translate('BiblesPlugin.BibleManager',
|
||||
'Scripture Reference Error'),
|
||||
'Scripture Reference Error'),
|
||||
u'message': translate('BiblesPlugin.BibleManager',
|
||||
'You did not enter a search keyword.\n'
|
||||
'You can separate different keywords by a space to search for '
|
||||
'all of your keywords and you can separate them by a comma to '
|
||||
'search for one of them.')
|
||||
'You did not enter a search keyword.\n'
|
||||
'You can separate different keywords by a space to '
|
||||
'search for all of your keywords and you can separate '
|
||||
'them by a comma to search for one of them.')
|
||||
})
|
||||
return None
|
||||
|
||||
def save_meta_data(self, bible, version, copyright, permissions,
|
||||
bookname_language=None):
|
||||
book_name_language=None):
|
||||
"""
|
||||
Saves the bibles meta data.
|
||||
"""
|
||||
log.debug(u'save_meta data %s, %s, %s, %s',
|
||||
bible, version, copyright, permissions)
|
||||
self.db_cache[bible].save_meta(u'Version', version)
|
||||
self.db_cache[bible].save_meta(u'Copyright', copyright)
|
||||
self.db_cache[bible].save_meta(u'Permissions', permissions)
|
||||
self.db_cache[bible].save_meta(u'Bookname language',
|
||||
bookname_language)
|
||||
self.db_cache[bible].save_meta(u'name', version)
|
||||
self.db_cache[bible].save_meta(u'copyright', copyright)
|
||||
self.db_cache[bible].save_meta(u'permissions', permissions)
|
||||
self.db_cache[bible].save_meta(u'book_name_language',
|
||||
book_name_language)
|
||||
|
||||
def get_meta_data(self, bible, key):
|
||||
"""
|
||||
|
@ -64,7 +64,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
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
|
||||
self.settings = self.plugin.settingsTab
|
||||
self.quickPreviewAllowed = True
|
||||
self.hasSearch = True
|
||||
self.search_results = {}
|
||||
@ -392,7 +392,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
if bible in bibles:
|
||||
find_and_set_in_combo_box(self.advancedVersionComboBox, bible)
|
||||
self.initialiseAdvancedBible(unicode(bible))
|
||||
elif len(bibles):
|
||||
elif bibles:
|
||||
self.initialiseAdvancedBible(bibles[0])
|
||||
bible = QtCore.QSettings().value(
|
||||
self.settingsSection + u'/quick bible', QtCore.QVariant(
|
||||
@ -408,7 +408,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
self.plugin.appStartup()
|
||||
self.updateAutoCompleter()
|
||||
|
||||
def initialiseAdvancedBible(self, bible):
|
||||
def initialiseAdvancedBible(self, bible, last_book_id=None):
|
||||
"""
|
||||
This initialises the given bible, which means that its book names and
|
||||
their chapter numbers is added to the combo boxes on the
|
||||
@ -417,8 +417,12 @@ class BibleMediaItem(MediaManagerItem):
|
||||
|
||||
``bible``
|
||||
The bible to initialise (unicode).
|
||||
|
||||
``last_book_id``
|
||||
The "book reference id" of the book which is choosen at the moment.
|
||||
(int)
|
||||
"""
|
||||
log.debug(u'initialiseAdvancedBible %s', bible)
|
||||
log.debug(u'initialiseAdvancedBible %s, %s', bible, last_book_id)
|
||||
book_data = self.plugin.manager.get_books(bible)
|
||||
secondbible = unicode(self.advancedSecondComboBox.currentText())
|
||||
if secondbible != u'':
|
||||
@ -432,8 +436,9 @@ class BibleMediaItem(MediaManagerItem):
|
||||
book_data = book_data_temp
|
||||
self.advancedBookComboBox.clear()
|
||||
first = True
|
||||
initialise_chapter_verse = False
|
||||
language_selection = self.plugin.manager.get_language_selection(bible)
|
||||
booknames = BibleStrings().Booknames
|
||||
book_names = BibleStrings().BookNames
|
||||
for book in book_data:
|
||||
row = self.advancedBookComboBox.count()
|
||||
if language_selection == LanguageSelection.Bible:
|
||||
@ -442,7 +447,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
data = BiblesResourcesDB.get_book_by_id(
|
||||
book[u'book_reference_id'])
|
||||
self.advancedBookComboBox.addItem(
|
||||
booknames[data[u'abbreviation']])
|
||||
book_names[data[u'abbreviation']])
|
||||
elif language_selection == LanguageSelection.English:
|
||||
data = BiblesResourcesDB.get_book_by_id(
|
||||
book[u'book_reference_id'])
|
||||
@ -451,8 +456,19 @@ class BibleMediaItem(MediaManagerItem):
|
||||
row, QtCore.QVariant(book[u'book_reference_id']))
|
||||
if first:
|
||||
first = False
|
||||
self.initialiseChapterVerse(bible, book[u'name'],
|
||||
book[u'book_reference_id'])
|
||||
first_book = book
|
||||
initialise_chapter_verse = True
|
||||
if last_book_id and last_book_id == int(book[u'book_reference_id']):
|
||||
index = self.advancedBookComboBox.findData(
|
||||
QtCore.QVariant(book[u'book_reference_id']))
|
||||
if index == -1:
|
||||
# Not Found.
|
||||
index = 0
|
||||
self.advancedBookComboBox.setCurrentIndex(index)
|
||||
initialise_chapter_verse = False
|
||||
if initialise_chapter_verse:
|
||||
self.initialiseChapterVerse(bible, first_book[u'name'],
|
||||
first_book[u'book_reference_id'])
|
||||
|
||||
def initialiseChapterVerse(self, bible, book, book_ref_id):
|
||||
log.debug(u'initialiseChapterVerse %s, %s, %s', bible, book,
|
||||
@ -509,12 +525,12 @@ class BibleMediaItem(MediaManagerItem):
|
||||
if language_selection == LanguageSelection.Bible:
|
||||
books = [book.name + u' ' for book in book_data]
|
||||
elif language_selection == LanguageSelection.Application:
|
||||
booknames = BibleStrings().Booknames
|
||||
book_names = BibleStrings().BookNames
|
||||
for book in book_data:
|
||||
data = BiblesResourcesDB.get_book_by_id(
|
||||
book.book_reference_id)
|
||||
books.append(unicode(
|
||||
booknames[data[u'abbreviation']]) + u' ')
|
||||
book_names[data[u'abbreviation']]) + u' ')
|
||||
elif language_selection == LanguageSelection.English:
|
||||
for book in book_data:
|
||||
data = BiblesResourcesDB.get_book_by_id(
|
||||
@ -537,7 +553,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
elif self.advancedTab.isVisible():
|
||||
bible = unicode(self.advancedVersionComboBox.currentText())
|
||||
if bible:
|
||||
self.editBibleForm = EditBibleForm(self, self.plugin.formparent,
|
||||
self.editBibleForm = EditBibleForm(self, self.plugin.formParent,
|
||||
self.plugin.manager)
|
||||
self.editBibleForm.loadBible(bible)
|
||||
if self.editBibleForm.exec_():
|
||||
@ -597,11 +613,15 @@ class BibleMediaItem(MediaManagerItem):
|
||||
QtCore.QSettings().setValue(self.settingsSection + u'/advanced bible',
|
||||
QtCore.QVariant(self.advancedVersionComboBox.currentText()))
|
||||
self.initialiseAdvancedBible(
|
||||
unicode(self.advancedVersionComboBox.currentText()))
|
||||
unicode(self.advancedVersionComboBox.currentText()),
|
||||
self.advancedBookComboBox.itemData(
|
||||
int(self.advancedBookComboBox.currentIndex())))
|
||||
|
||||
def onAdvancedSecondComboBox(self):
|
||||
self.initialiseAdvancedBible(
|
||||
unicode(self.advancedVersionComboBox.currentText()))
|
||||
unicode(self.advancedVersionComboBox.currentText()),
|
||||
self.advancedBookComboBox.itemData(
|
||||
int(self.advancedBookComboBox.currentIndex())))
|
||||
|
||||
def onAdvancedBookComboBox(self):
|
||||
item = int(self.advancedBookComboBox.currentIndex())
|
||||
@ -806,30 +826,30 @@ class BibleMediaItem(MediaManagerItem):
|
||||
further action is saved for/in each row.
|
||||
"""
|
||||
verse_separator = get_reference_separator(u'sep_v_display')
|
||||
version = self.plugin.manager.get_meta_data(bible, u'Version').value
|
||||
copyright = self.plugin.manager.get_meta_data(bible, u'Copyright').value
|
||||
version = self.plugin.manager.get_meta_data(bible, u'name').value
|
||||
copyright = self.plugin.manager.get_meta_data(bible, u'copyright').value
|
||||
permissions = \
|
||||
self.plugin.manager.get_meta_data(bible, u'Permissions').value
|
||||
self.plugin.manager.get_meta_data(bible, u'permissions').value
|
||||
second_version = u''
|
||||
second_copyright = u''
|
||||
second_permissions = u''
|
||||
if second_bible:
|
||||
second_version = self.plugin.manager.get_meta_data(
|
||||
second_bible, u'Version').value
|
||||
second_bible, u'name').value
|
||||
second_copyright = self.plugin.manager.get_meta_data(
|
||||
second_bible, u'Copyright').value
|
||||
second_bible, u'copyright').value
|
||||
second_permissions = self.plugin.manager.get_meta_data(
|
||||
second_bible, u'Permissions').value
|
||||
second_bible, u'permissions').value
|
||||
items = []
|
||||
language_selection = self.plugin.manager.get_language_selection(bible)
|
||||
for count, verse in enumerate(search_results):
|
||||
if language_selection == LanguageSelection.Bible:
|
||||
book = verse.book.name
|
||||
elif language_selection == LanguageSelection.Application:
|
||||
booknames = BibleStrings().Booknames
|
||||
book_names = BibleStrings().Booknames
|
||||
data = BiblesResourcesDB.get_book_by_id(
|
||||
verse.book.book_reference_id)
|
||||
book = unicode(booknames[data[u'abbreviation']])
|
||||
book = unicode(book_names[data[u'abbreviation']])
|
||||
elif language_selection == LanguageSelection.English:
|
||||
data = BiblesResourcesDB.get_book_by_id(
|
||||
verse.book.book_reference_id)
|
||||
@ -878,7 +898,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
items = item
|
||||
else:
|
||||
items = self.listView.selectedItems()
|
||||
if len(items) == 0:
|
||||
if not items:
|
||||
return False
|
||||
bible_text = u''
|
||||
old_item = None
|
||||
@ -949,7 +969,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
# Service Item: Title
|
||||
service_item.title = create_separated_list(raw_title)
|
||||
# Service Item: Theme
|
||||
if len(self.settings.bible_theme) == 0:
|
||||
if not self.settings.bible_theme:
|
||||
service_item.theme = None
|
||||
else:
|
||||
service_item.theme = self.settings.bible_theme
|
||||
|
@ -50,8 +50,6 @@ class OSISBible(BibleDB):
|
||||
log.debug(self.__class__.__name__)
|
||||
BibleDB.__init__(self, parent, **kwargs)
|
||||
self.filename = kwargs[u'filename']
|
||||
fbibles = None
|
||||
self.books = {}
|
||||
self.language_regex = re.compile(r'<language.*>(.*?)</language>')
|
||||
self.verse_regex = re.compile(
|
||||
r'<verse osisID="([a-zA-Z0-9 ]*).([0-9]*).([0-9]*)">(.*?)</verse>')
|
||||
@ -74,16 +72,6 @@ class OSISBible(BibleDB):
|
||||
filepath = os.path.join(
|
||||
AppLocation.get_directory(AppLocation.PluginsDir), u'bibles',
|
||||
u'resources', u'osisbooks.csv')
|
||||
try:
|
||||
fbibles = open(filepath, u'r')
|
||||
for line in fbibles:
|
||||
book = line.split(u',')
|
||||
self.books[book[0]] = (book[1].strip(), book[2].strip())
|
||||
except IOError:
|
||||
log.exception(u'OSIS bible import failed')
|
||||
finally:
|
||||
if fbibles:
|
||||
fbibles.close()
|
||||
|
||||
def do_import(self, bible_name=None):
|
||||
"""
|
||||
@ -101,6 +89,8 @@ class OSISBible(BibleDB):
|
||||
try:
|
||||
detect_file = open(self.filename, u'r')
|
||||
details = chardet.detect(detect_file.read(1048576))
|
||||
detect_file.seek(0)
|
||||
lines_in_file = int(len(detect_file.readlines()))
|
||||
except IOError:
|
||||
log.exception(u'Failed to detect OSIS file encoding')
|
||||
return
|
||||
@ -111,6 +101,17 @@ class OSISBible(BibleDB):
|
||||
osis = codecs.open(self.filename, u'r', details['encoding'])
|
||||
repl = replacement
|
||||
language_id = False
|
||||
# Decide if the bible propably contains only NT or AT and NT or
|
||||
# AT, NT and Apocrypha
|
||||
if lines_in_file < 11500:
|
||||
book_count = 27
|
||||
chapter_count = 260
|
||||
elif lines_in_file < 34200:
|
||||
book_count = 66
|
||||
chapter_count = 1188
|
||||
else:
|
||||
book_count = 67
|
||||
chapter_count = 1336
|
||||
for file_record in osis:
|
||||
if self.stop_import_flag:
|
||||
break
|
||||
@ -134,36 +135,32 @@ class OSISBible(BibleDB):
|
||||
% self.filename)
|
||||
return False
|
||||
match_count += 1
|
||||
book = match.group(1)
|
||||
book = unicode(match.group(1))
|
||||
chapter = int(match.group(2))
|
||||
verse = int(match.group(3))
|
||||
verse_text = match.group(4)
|
||||
if not db_book or db_book.name != self.books[book][0]:
|
||||
log.debug(u'New book: "%s"' % self.books[book][0])
|
||||
book_ref_id = self.get_book_ref_id_by_name(unicode(
|
||||
self.books[book][0]), 67, language_id)
|
||||
if not book_ref_id:
|
||||
log.exception(u'Importing books from "%s" '\
|
||||
'failed' % self.filename)
|
||||
return False
|
||||
book_details = BiblesResourcesDB.get_book_by_id(
|
||||
book_ref_id)
|
||||
book_ref_id = self.get_book_ref_id_by_name(book, book_count,
|
||||
language_id)
|
||||
if not book_ref_id:
|
||||
log.exception(u'Importing books from "%s" failed' %
|
||||
self.filename)
|
||||
return False
|
||||
book_details = BiblesResourcesDB.get_book_by_id(book_ref_id)
|
||||
if not db_book or db_book.name != book_details[u'name']:
|
||||
log.debug(u'New book: "%s"' % book_details[u'name'])
|
||||
db_book = self.create_book(
|
||||
unicode(self.books[book][0]),
|
||||
book_details[u'name'],
|
||||
book_ref_id,
|
||||
book_details[u'testament_id'])
|
||||
if last_chapter == 0:
|
||||
if book == u'Gen':
|
||||
self.wizard.progressBar.setMaximum(1188)
|
||||
else:
|
||||
self.wizard.progressBar.setMaximum(260)
|
||||
self.wizard.progressBar.setMaximum(chapter_count)
|
||||
if last_chapter != chapter:
|
||||
if last_chapter != 0:
|
||||
self.session.commit()
|
||||
self.wizard.incrementProgressBar(unicode(translate(
|
||||
'BiblesPlugin.OsisImport', 'Importing %s %s...',
|
||||
'Importing <book name> <chapter>...')) %
|
||||
(self.books[match.group(1)][0], chapter))
|
||||
(book_details[u'name'], chapter))
|
||||
last_chapter = chapter
|
||||
# All of this rigmarol below is because the mod2osis
|
||||
# tool from the Sword library embeds XML in the OSIS
|
||||
|
191
openlp/plugins/bibles/lib/upgrade.py
Normal file
191
openlp/plugins/bibles/lib/upgrade.py
Normal file
@ -0,0 +1,191 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2012 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||
# Armin Köhler, Joshua Miller, 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 #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`upgrade` module provides a way for the database and schema that is the
|
||||
backend for the Bibles plugin
|
||||
"""
|
||||
import logging
|
||||
|
||||
from sqlalchemy import Table, func, select, insert
|
||||
|
||||
__version__ = 1
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def upgrade_setup(metadata):
|
||||
"""
|
||||
Set up the latest revision all tables, with reflection, needed for the
|
||||
upgrade process. If you want to drop a table, you need to remove it from
|
||||
here, and add it to your upgrade function.
|
||||
"""
|
||||
# Don't define the "metadata" table, as the upgrade mechanism already
|
||||
# defines it.
|
||||
tables = {
|
||||
u'book': Table(u'book', metadata, autoload=True),
|
||||
u'verse': Table(u'verse', metadata, autoload=True)
|
||||
}
|
||||
return tables
|
||||
|
||||
|
||||
def upgrade_1(session, metadata, tables):
|
||||
"""
|
||||
Version 1 upgrade.
|
||||
|
||||
This upgrade renames a number of keys to a single naming convention..
|
||||
"""
|
||||
metadata_table = metadata.tables[u'metadata']
|
||||
# Copy "Version" to "name" ("version" used by upgrade system)
|
||||
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
|
||||
session.execute(insert(metadata_table).values(
|
||||
key=u'name',
|
||||
value=select(
|
||||
[metadata_table.c.value],
|
||||
metadata_table.c.key == u'Version'
|
||||
).as_scalar()
|
||||
))
|
||||
# Copy "Copyright" to "copyright"
|
||||
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
|
||||
session.execute(insert(metadata_table).values(
|
||||
key=u'copyright',
|
||||
value=select(
|
||||
[metadata_table.c.value],
|
||||
metadata_table.c.key == u'Copyright'
|
||||
).as_scalar()
|
||||
))
|
||||
# Copy "Permissions" to "permissions"
|
||||
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
|
||||
session.execute(insert(metadata_table).values(
|
||||
key=u'permissions',
|
||||
value=select(
|
||||
[metadata_table.c.value],
|
||||
metadata_table.c.key == u'Permissions'
|
||||
).as_scalar()
|
||||
))
|
||||
# Copy "Bookname language" to "book_name_language"
|
||||
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
|
||||
value_count = session.execute(
|
||||
select(
|
||||
[func.count(metadata_table.c.value)],
|
||||
metadata_table.c.key == u'Bookname language'
|
||||
)
|
||||
).scalar()
|
||||
if value_count > 0:
|
||||
session.execute(insert(metadata_table).values(
|
||||
key=u'book_name_language',
|
||||
value=select(
|
||||
[metadata_table.c.value],
|
||||
metadata_table.c.key == u'Bookname language'
|
||||
).as_scalar()
|
||||
))
|
||||
# Copy "download source" to "download_source"
|
||||
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
|
||||
value_count = session.execute(
|
||||
select(
|
||||
[func.count(metadata_table.c.value)],
|
||||
metadata_table.c.key == u'download source'
|
||||
)
|
||||
).scalar()
|
||||
log.debug(u'download source: %s', value_count)
|
||||
if value_count > 0:
|
||||
session.execute(insert(metadata_table).values(
|
||||
key=u'download_source',
|
||||
value=select(
|
||||
[metadata_table.c.value],
|
||||
metadata_table.c.key == u'download source'
|
||||
).as_scalar()
|
||||
))
|
||||
# Copy "download name" to "download_name"
|
||||
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
|
||||
value_count = session.execute(
|
||||
select(
|
||||
[func.count(metadata_table.c.value)],
|
||||
metadata_table.c.key == u'download name'
|
||||
)
|
||||
).scalar()
|
||||
log.debug(u'download name: %s', value_count)
|
||||
if value_count > 0:
|
||||
session.execute(insert(metadata_table).values(
|
||||
key=u'download_name',
|
||||
value=select(
|
||||
[metadata_table.c.value],
|
||||
metadata_table.c.key == u'download name'
|
||||
).as_scalar()
|
||||
))
|
||||
# Copy "proxy server" to "proxy_server"
|
||||
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
|
||||
value_count = session.execute(
|
||||
select(
|
||||
[func.count(metadata_table.c.value)],
|
||||
metadata_table.c.key == u'proxy server'
|
||||
)
|
||||
).scalar()
|
||||
log.debug(u'proxy server: %s', value_count)
|
||||
if value_count > 0:
|
||||
session.execute(insert(metadata_table).values(
|
||||
key=u'proxy_server',
|
||||
value=select(
|
||||
[metadata_table.c.value],
|
||||
metadata_table.c.key == u'proxy server'
|
||||
).as_scalar()
|
||||
))
|
||||
# Copy "proxy username" to "proxy_username"
|
||||
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
|
||||
value_count = session.execute(
|
||||
select(
|
||||
[func.count(metadata_table.c.value)],
|
||||
metadata_table.c.key == u'proxy username'
|
||||
)
|
||||
).scalar()
|
||||
log.debug(u'proxy username: %s', value_count)
|
||||
if value_count > 0:
|
||||
session.execute(insert(metadata_table).values(
|
||||
key=u'proxy_username',
|
||||
value=select(
|
||||
[metadata_table.c.value],
|
||||
metadata_table.c.key == u'proxy username'
|
||||
).as_scalar()
|
||||
))
|
||||
# Copy "proxy password" to "proxy_password"
|
||||
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
|
||||
value_count = session.execute(
|
||||
select(
|
||||
[func.count(metadata_table.c.value)],
|
||||
metadata_table.c.key == u'proxy password'
|
||||
)
|
||||
).scalar()
|
||||
log.debug(u'proxy password: %s', value_count)
|
||||
if value_count > 0:
|
||||
session.execute(insert(metadata_table).values(
|
||||
key=u'proxy_password',
|
||||
value=select(
|
||||
[metadata_table.c.value],
|
||||
metadata_table.c.key == u'proxy password'
|
||||
).as_scalar()
|
||||
))
|
||||
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
|
||||
#session.execute(delete(metadata_table)\
|
||||
# .where(metadata_table.c.key == u'dbversion'))
|
||||
session.commit()
|
@ -1,76 +0,0 @@
|
||||
Gen,Genesis,Gen
|
||||
Exod,Exodus,Exod
|
||||
Lev,Leviticus,Lev
|
||||
Num,Numbers,Num
|
||||
Deut,Deuteronomy,Deut
|
||||
Josh,Joshua,Josh
|
||||
Judg,Judges,Judg
|
||||
Ruth,Ruth,Ruth
|
||||
1Sam,1 Samuel,1Sam
|
||||
2Sam,2 Samuel,2Sam
|
||||
1Kgs,1 Kings,1Kgs
|
||||
2Kgs,2 Kings,2Kgs
|
||||
1Chr,1 Chronicles,1Chr
|
||||
2Chr,2 Chronicles,2Chr
|
||||
Ezra,Ezra,Ezra
|
||||
Neh,Nehemiah,Neh
|
||||
Esth,Esther,Esth
|
||||
Job,Job,Job
|
||||
Ps,Psalms,Ps
|
||||
Prov,Proverbs,Prov
|
||||
Eccl,Ecclesiastes,Eccl
|
||||
Song,Song of Songs,Song
|
||||
Isa,Isaiah,Isa
|
||||
Jer,Jeremiah,Jer
|
||||
Lam,Lamentations,Lam
|
||||
Ezek,Ezekiel,Ezek
|
||||
Dan,Daniel,Dan
|
||||
Hos,Hosea,Hos
|
||||
Joel,Joel,Joel
|
||||
Amos,Amos,Amos
|
||||
Obad,Obad,Obad
|
||||
Jonah,Jonah,Jonah
|
||||
Mic,Micah,Mic
|
||||
Nah,Naham,Nah
|
||||
Hab,Habakkuk,Hab
|
||||
Zeph,Zephaniah,Zeph
|
||||
Hag,Haggai,Hag
|
||||
Zech,Zechariah,Zech
|
||||
Mal,Malachi,Mal
|
||||
Matt,Matthew,Matt
|
||||
Mark,Mark,Mark
|
||||
Luke,Luke,Luke
|
||||
John,John,John
|
||||
Acts,Acts,Acts
|
||||
Rom,Romans,Rom
|
||||
1Cor,1 Corinthians,1Cor
|
||||
2Cor,2 Corinthians,2Cor
|
||||
Gal,Galatians,Gal
|
||||
Eph,Ephesians,Eph
|
||||
Phil,Philippians,Phil
|
||||
Col,Colossians,Col
|
||||
1Thess,1 Thessalonians,1Thess
|
||||
2Thess,2 Thessalonians,2Thess
|
||||
1Tim,1 Timothy,1Tim
|
||||
2Tim,2 Timothy,2Tim
|
||||
Titus,Titus,Titus
|
||||
Phlm,Philemon,Phlm
|
||||
Heb,Hebrews,Heb
|
||||
Jas,James,Jas
|
||||
1Pet,1 Peter,1Pet
|
||||
2Pet,2 Peter,2Pet
|
||||
1John,1 John,1John
|
||||
2John,2 John,2John
|
||||
3John,3 John,3John
|
||||
Jude,Jude,Jude
|
||||
Rev,Revelation,Rev
|
||||
Jdt,Judith,Jdt
|
||||
Wis,Wisdom,Wis
|
||||
Tob,Tobit,Tob
|
||||
Sir,Sirach,Sir
|
||||
Bar,Baruch,Bar
|
||||
1Macc,1 Maccabees,1Macc
|
||||
2Macc,2 Maccabees,2Macc
|
||||
AddDan,Rest of Daniel,AddDan
|
||||
AddEsth,Rest of Esther,AddEsth
|
||||
PrMan,Prayer of Manasses,PrMan
|
|
@ -50,8 +50,8 @@ class CustomPlugin(Plugin):
|
||||
CustomMediaItem, CustomTab)
|
||||
self.weight = -5
|
||||
self.manager = Manager(u'custom', init_schema)
|
||||
self.icon_path = u':/plugins/plugin_custom.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
self.iconPath = u':/plugins/plugin_custom.png'
|
||||
self.icon = build_icon(self.iconPath)
|
||||
|
||||
def about(self):
|
||||
about_text = translate('CustomPlugin', '<strong>Custom Slide Plugin'
|
||||
|
@ -127,7 +127,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
sxml.new_document()
|
||||
sxml.add_lyrics_to_song()
|
||||
count = 1
|
||||
for i in range(0, self.slideListView.count()):
|
||||
for i in range(self.slideListView.count()):
|
||||
sxml.add_verse_to_lyrics(u'custom', unicode(count),
|
||||
unicode(self.slideListView.item(i).text()))
|
||||
count += 1
|
||||
@ -170,7 +170,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
Edits all slides.
|
||||
"""
|
||||
slide_list = u''
|
||||
for row in range(0, self.slideListView.count()):
|
||||
for row in range(self.slideListView.count()):
|
||||
item = self.slideListView.item(row)
|
||||
slide_list += item.text()
|
||||
if row != self.slideListView.count() - 1:
|
||||
@ -206,7 +206,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
old_row = self.slideListView.currentRow()
|
||||
# Create a list with all (old/unedited) slides.
|
||||
old_slides = [self.slideListView.item(row).text() for row in \
|
||||
range(0, self.slideListView.count())]
|
||||
range(self.slideListView.count())]
|
||||
self.slideListView.clear()
|
||||
old_slides.pop(old_row)
|
||||
# Insert all slides to make the old_slides list complete.
|
||||
@ -254,7 +254,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
||||
Checks whether a custom is valid or not.
|
||||
"""
|
||||
# We must have a title.
|
||||
if len(self.titleEdit.displayText()) == 0:
|
||||
if not self.titleEdit.displayText():
|
||||
self.titleEdit.setFocus()
|
||||
critical_error_message_box(
|
||||
message=translate('CustomPlugin.EditCustomForm',
|
||||
|
@ -57,7 +57,7 @@ class CustomMediaItem(MediaManagerItem):
|
||||
def __init__(self, parent, plugin, icon):
|
||||
self.IconPath = u'custom/custom'
|
||||
MediaManagerItem.__init__(self, parent, plugin, icon)
|
||||
self.edit_custom_form = EditCustomForm(self, self.plugin.formparent,
|
||||
self.edit_custom_form = EditCustomForm(self, self.plugin.formParent,
|
||||
self.plugin.manager)
|
||||
self.singleServiceItem = False
|
||||
self.quickPreviewAllowed = True
|
||||
@ -258,7 +258,7 @@ class CustomMediaItem(MediaManagerItem):
|
||||
search_length = 2
|
||||
if len(text) > search_length:
|
||||
self.onSearchTextButtonClicked()
|
||||
elif len(text) == 0:
|
||||
elif not text:
|
||||
self.onClearTextButtonClick()
|
||||
|
||||
def onClearTextButtonClick(self):
|
||||
|
@ -42,8 +42,8 @@ class ImagePlugin(Plugin):
|
||||
Plugin.__init__(self, u'images', plugin_helpers, ImageMediaItem,
|
||||
ImageTab)
|
||||
self.weight = -7
|
||||
self.icon_path = u':/plugins/plugin_images.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
self.iconPath = u':/plugins/plugin_images.png'
|
||||
self.icon = build_icon(self.iconPath)
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'image_updated'), self.image_updated)
|
||||
|
||||
|
@ -106,24 +106,24 @@ class ImageMediaItem(MediaManagerItem):
|
||||
row_list = [item.row() for item in self.listView.selectedIndexes()]
|
||||
row_list.sort(reverse=True)
|
||||
Receiver.send_message(u'cursor_busy')
|
||||
self.plugin.formparent.displayProgressBar(len(row_list))
|
||||
self.plugin.formParent.displayProgressBar(len(row_list))
|
||||
for row in row_list:
|
||||
text = self.listView.item(row)
|
||||
if text:
|
||||
delete_file(os.path.join(self.servicePath,
|
||||
unicode(text.text())))
|
||||
self.listView.takeItem(row)
|
||||
self.plugin.formparent.incrementProgressBar()
|
||||
self.plugin.formParent.incrementProgressBar()
|
||||
SettingsManager.set_list(self.settingsSection,
|
||||
u'images', self.getFileList())
|
||||
self.plugin.formparent.finishedProgressBar()
|
||||
self.plugin.formParent.finishedProgressBar()
|
||||
Receiver.send_message(u'cursor_normal')
|
||||
self.listView.blockSignals(False)
|
||||
|
||||
def loadList(self, images, initialLoad=False):
|
||||
if not initialLoad:
|
||||
Receiver.send_message(u'cursor_busy')
|
||||
self.plugin.formparent.displayProgressBar(len(images))
|
||||
self.plugin.formParent.displayProgressBar(len(images))
|
||||
# Sort the themes by its filename considering language specific
|
||||
# characters. lower() is needed for windows!
|
||||
images.sort(cmp=locale.strcoll,
|
||||
@ -144,9 +144,9 @@ class ImageMediaItem(MediaManagerItem):
|
||||
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(imageFile))
|
||||
self.listView.addItem(item_name)
|
||||
if not initialLoad:
|
||||
self.plugin.formparent.incrementProgressBar()
|
||||
self.plugin.formParent.incrementProgressBar()
|
||||
if not initialLoad:
|
||||
self.plugin.formparent.finishedProgressBar()
|
||||
self.plugin.formParent.finishedProgressBar()
|
||||
Receiver.send_message(u'cursor_normal')
|
||||
|
||||
def generateSlideData(self, service_item, item=None, xmlVersion=False,
|
||||
|
@ -41,8 +41,8 @@ class MediaPlugin(Plugin):
|
||||
Plugin.__init__(self, u'media', plugin_helpers,
|
||||
MediaMediaItem)
|
||||
self.weight = -6
|
||||
self.icon_path = u':/plugins/plugin_media.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
self.iconPath = u':/plugins/plugin_media.png'
|
||||
self.icon = build_icon(self.iconPath)
|
||||
# passed with drag and drop messages
|
||||
self.dnd_id = u'Media'
|
||||
self.audio_extensions_list = \
|
||||
@ -59,8 +59,8 @@ class MediaPlugin(Plugin):
|
||||
Create the settings Tab
|
||||
"""
|
||||
visible_name = self.getString(StringContent.VisibleName)
|
||||
self.settings_tab = MediaTab(parent, self.name, visible_name[u'title'],
|
||||
self.mediaController.mediaPlayers, self.icon_path)
|
||||
self.settingsTab = MediaTab(parent, self.name, visible_name[u'title'],
|
||||
self.mediaController.mediaPlayers, self.iconPath)
|
||||
|
||||
def about(self):
|
||||
about_text = translate('MediaPlugin', '<strong>Media Plugin</strong>'
|
||||
@ -142,6 +142,6 @@ class MediaPlugin(Plugin):
|
||||
self.mediaController.mediaPlayers[u'phonon'].isActive = True
|
||||
settings.setValue(u'players', \
|
||||
QtCore.QVariant(u','.join(new_players)))
|
||||
self.settings_tab.load()
|
||||
self.settingsTab.load()
|
||||
settings.remove(u'use phonon')
|
||||
settings.endGroup()
|
||||
|
@ -42,6 +42,9 @@ import time
|
||||
if os.name == u'nt':
|
||||
from win32com.client import Dispatch
|
||||
import pywintypes
|
||||
# Declare an empty exception to match the exception imported from UNO
|
||||
class ErrorCodeIOException(Exception):
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
import uno
|
||||
@ -339,7 +342,6 @@ class ImpressDocument(PresentationDocument):
|
||||
Returns true if a presentation is loaded
|
||||
"""
|
||||
log.debug(u'is loaded OpenOffice')
|
||||
#print "is_loaded "
|
||||
if self.presentation is None or self.document is None:
|
||||
log.debug("is_loaded: no presentation or document")
|
||||
return False
|
||||
@ -357,14 +359,9 @@ class ImpressDocument(PresentationDocument):
|
||||
Returns true if a presentation is active and running
|
||||
"""
|
||||
log.debug(u'is active OpenOffice')
|
||||
#print "is_active "
|
||||
if not self.is_loaded():
|
||||
#print "False "
|
||||
return False
|
||||
#print "self.con ", self.control
|
||||
if self.control is None:
|
||||
return False
|
||||
return True
|
||||
return self.control is not None
|
||||
|
||||
def unblank_screen(self):
|
||||
"""
|
||||
|
@ -166,14 +166,14 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
Receiver.send_message(u'cursor_busy')
|
||||
if not initialLoad:
|
||||
Receiver.send_message(u'cursor_busy')
|
||||
self.plugin.formparent.displayProgressBar(len(files))
|
||||
self.plugin.formParent.displayProgressBar(len(files))
|
||||
# Sort the themes by its filename considering language specific
|
||||
# characters. lower() is needed for windows!
|
||||
files.sort(cmp=locale.strcoll,
|
||||
key=lambda filename: os.path.split(unicode(filename))[1].lower())
|
||||
for file in files:
|
||||
if not initialLoad:
|
||||
self.plugin.formparent.incrementProgressBar()
|
||||
self.plugin.formParent.incrementProgressBar()
|
||||
if currlist.count(file) > 0:
|
||||
continue
|
||||
filename = os.path.split(unicode(file))[1]
|
||||
@ -217,7 +217,7 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
self.listView.addItem(item_name)
|
||||
Receiver.send_message(u'cursor_normal')
|
||||
if not initialLoad:
|
||||
self.plugin.formparent.finishedProgressBar()
|
||||
self.plugin.formParent.finishedProgressBar()
|
||||
Receiver.send_message(u'cursor_normal')
|
||||
|
||||
def onDeleteClick(self):
|
||||
@ -229,7 +229,7 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
row_list = [item.row() for item in items]
|
||||
row_list.sort(reverse=True)
|
||||
Receiver.send_message(u'cursor_busy')
|
||||
self.plugin.formparent.displayProgressBar(len(row_list))
|
||||
self.plugin.formParent.displayProgressBar(len(row_list))
|
||||
for item in items:
|
||||
filepath = unicode(item.data(
|
||||
QtCore.Qt.UserRole).toString())
|
||||
@ -237,8 +237,8 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
doc = self.controllers[cidx].add_document(filepath)
|
||||
doc.presentation_deleted()
|
||||
doc.close_presentation()
|
||||
self.plugin.formparent.incrementProgressBar()
|
||||
self.plugin.formparent.finishedProgressBar()
|
||||
self.plugin.formParent.incrementProgressBar()
|
||||
self.plugin.formParent.finishedProgressBar()
|
||||
Receiver.send_message(u'cursor_normal')
|
||||
for row in row_list:
|
||||
self.listView.takeItem(row)
|
||||
@ -263,50 +263,49 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay)
|
||||
service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay)
|
||||
shortname = service_item.shortname
|
||||
if shortname:
|
||||
for bitem in items:
|
||||
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
|
||||
if os.path.exists(filename):
|
||||
if shortname == self.Automatic:
|
||||
service_item.shortname = \
|
||||
self.findControllerByType(filename)
|
||||
if not service_item.shortname:
|
||||
return False
|
||||
controller = self.controllers[service_item.shortname]
|
||||
(path, name) = os.path.split(filename)
|
||||
doc = controller.add_document(filename)
|
||||
if doc.get_thumbnail_path(1, True) is None:
|
||||
doc.load_presentation()
|
||||
i = 1
|
||||
img = doc.get_thumbnail_path(i, True)
|
||||
if img:
|
||||
while img:
|
||||
service_item.add_from_command(path, name, img)
|
||||
i = i + 1
|
||||
img = doc.get_thumbnail_path(i, True)
|
||||
doc.close_presentation()
|
||||
return True
|
||||
else:
|
||||
# File is no longer present
|
||||
if not remote:
|
||||
critical_error_message_box(
|
||||
translate('PresentationPlugin.MediaItem',
|
||||
'Missing Presentation'),
|
||||
unicode(translate(
|
||||
'PresentationPlugin.MediaItem',
|
||||
'The Presentation %s is incomplete,'
|
||||
' please reload.')) % filename)
|
||||
if not shortname:
|
||||
return False
|
||||
for bitem in items:
|
||||
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
|
||||
if os.path.exists(filename):
|
||||
if shortname == self.Automatic:
|
||||
service_item.shortname = self.findControllerByType(filename)
|
||||
if not service_item.shortname:
|
||||
return False
|
||||
controller = self.controllers[service_item.shortname]
|
||||
(path, name) = os.path.split(filename)
|
||||
doc = controller.add_document(filename)
|
||||
if doc.get_thumbnail_path(1, True) is None:
|
||||
doc.load_presentation()
|
||||
i = 1
|
||||
img = doc.get_thumbnail_path(i, True)
|
||||
if img:
|
||||
while img:
|
||||
service_item.add_from_command(path, name, img)
|
||||
i = i + 1
|
||||
img = doc.get_thumbnail_path(i, True)
|
||||
doc.close_presentation()
|
||||
return True
|
||||
else:
|
||||
# File is no longer present
|
||||
if not remote:
|
||||
critical_error_message_box(
|
||||
translate('PresentationPlugin.MediaItem',
|
||||
'Missing Presentation'),
|
||||
unicode(translate(
|
||||
'PresentationPlugin.MediaItem',
|
||||
'The presentation %s is incomplete,'
|
||||
' please reload.')) % filename)
|
||||
return False
|
||||
else:
|
||||
# File is no longer present
|
||||
if not remote:
|
||||
critical_error_message_box(
|
||||
translate('PresentationPlugin.MediaItem',
|
||||
'Missing Presentation'),
|
||||
unicode(translate('PresentationPlugin.MediaItem',
|
||||
'The Presentation %s no longer exists.')) % filename)
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
'The presentation %s no longer exists.')) % filename)
|
||||
return False
|
||||
|
||||
def findControllerByType(self, filename):
|
||||
"""
|
||||
|
@ -152,7 +152,7 @@ class PowerpointDocument(PresentationDocument):
|
||||
log.debug(u'create_thumbnails')
|
||||
if self.check_thumbnails():
|
||||
return
|
||||
for num in range(0, self.presentation.Slides.Count):
|
||||
for num in range(self.presentation.Slides.Count):
|
||||
self.presentation.Slides(num + 1).Export(os.path.join(
|
||||
self.get_thumbnail_folder(), 'slide%d.png' % (num + 1)),
|
||||
'png', 320, 240)
|
||||
|
@ -54,16 +54,16 @@ class PresentationPlugin(Plugin):
|
||||
self.controllers = {}
|
||||
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)
|
||||
self.iconPath = u':/plugins/plugin_presentations.png'
|
||||
self.icon = build_icon(self.iconPath)
|
||||
|
||||
def createSettingsTab(self, parent):
|
||||
"""
|
||||
Create the settings Tab
|
||||
"""
|
||||
visible_name = self.getString(StringContent.VisibleName)
|
||||
self.settings_tab = PresentationTab(parent, self.name,
|
||||
visible_name[u'title'], self.controllers, self.icon_path)
|
||||
self.settingsTab = PresentationTab(parent, self.name,
|
||||
visible_name[u'title'], self.controllers, self.iconPath)
|
||||
|
||||
def initialise(self):
|
||||
"""
|
||||
@ -99,7 +99,7 @@ class PresentationPlugin(Plugin):
|
||||
Create the Media Manager List
|
||||
"""
|
||||
self.mediaItem = PresentationMediaItem(
|
||||
self.mediadock.media_dock, self, self.icon, self.controllers)
|
||||
self.mediaDock.media_dock, self, self.icon, self.controllers)
|
||||
|
||||
def registerControllers(self, controller):
|
||||
"""
|
||||
@ -134,10 +134,7 @@ class PresentationPlugin(Plugin):
|
||||
for controller_class in controller_classes:
|
||||
controller = controller_class(self)
|
||||
self.registerControllers(controller)
|
||||
if self.controllers:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
return bool(self.controllers)
|
||||
|
||||
def about(self):
|
||||
"""
|
||||
|
BIN
openlp/plugins/remotes/html/images/favicon.ico
Normal file
BIN
openlp/plugins/remotes/html/images/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
@ -31,6 +31,7 @@
|
||||
<title>${app_title}</title>
|
||||
<link rel="stylesheet" href="/files/jquery.mobile.css" />
|
||||
<link rel="stylesheet" href="/files/openlp.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/files/images/favicon.ico">
|
||||
<script type="text/javascript" src="/files/jquery.js"></script>
|
||||
<script type="text/javascript" src="/files/openlp.js"></script>
|
||||
<script type="text/javascript" src="/files/jquery.mobile.js"></script>
|
||||
|
9408
openlp/plugins/remotes/html/jquery.js
vendored
9408
openlp/plugins/remotes/html/jquery.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -29,6 +29,7 @@
|
||||
<meta charset="utf-8" />
|
||||
<title>${stage_title}</title>
|
||||
<link rel="stylesheet" href="/files/stage.css" />
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/files/images/favicon.ico">
|
||||
<script type="text/javascript" src="/files/jquery.js"></script>
|
||||
<script type="text/javascript" src="/files/stage.js"></script>
|
||||
</head>
|
||||
|
@ -41,8 +41,8 @@ class RemotesPlugin(Plugin):
|
||||
"""
|
||||
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)
|
||||
self.iconPath = u':/plugins/plugin_remote.png'
|
||||
self.icon = build_icon(self.iconPath)
|
||||
self.weight = -1
|
||||
self.server = None
|
||||
|
||||
|
@ -336,7 +336,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
Tag the Song List rows based on the verse list
|
||||
"""
|
||||
row_label = []
|
||||
for row in range(0, self.verseListWidget.rowCount()):
|
||||
for row in range(self.verseListWidget.rowCount()):
|
||||
item = self.verseListWidget.item(row, 0)
|
||||
verse_def = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||
verse_tag = VerseType.translated_tag(verse_def[0])
|
||||
@ -494,7 +494,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
if len(tempText.split(u'\n')) != len(after_text.split(u'\n')):
|
||||
tempList = {}
|
||||
tempId = {}
|
||||
for row in range(0, self.verseListWidget.rowCount()):
|
||||
for row in range(self.verseListWidget.rowCount()):
|
||||
tempList[row] = self.verseListWidget.item(row, 0)\
|
||||
.text()
|
||||
tempId[row] = self.verseListWidget.item(row, 0)\
|
||||
@ -511,7 +511,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
def onVerseEditAllButtonClicked(self):
|
||||
verse_list = u''
|
||||
if self.verseListWidget.rowCount() > 0:
|
||||
for row in range(0, self.verseListWidget.rowCount()):
|
||||
for row in range(self.verseListWidget.rowCount()):
|
||||
item = self.verseListWidget.item(row, 0)
|
||||
field = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||
verse_tag = VerseType.translated_name(field[0])
|
||||
@ -579,7 +579,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
verses = []
|
||||
verse_names = []
|
||||
order = self.__extractVerseOrder(text)
|
||||
for index in range(0, self.verseListWidget.rowCount()):
|
||||
for index in range(self.verseListWidget.rowCount()):
|
||||
verse = self.verseListWidget.item(index, 0)
|
||||
verse = unicode(verse.data(QtCore.Qt.UserRole).toString())
|
||||
if verse not in verse_names:
|
||||
@ -620,7 +620,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
verse_names = []
|
||||
order_names = unicode(verse_order).split()
|
||||
order = self.__extractVerseOrder(verse_order)
|
||||
for index in range(0, verse_count):
|
||||
for index in range(verse_count):
|
||||
verse = self.verseListWidget.item(index, 0)
|
||||
verse = unicode(verse.data(QtCore.Qt.UserRole).toString())
|
||||
if verse not in verse_names:
|
||||
@ -920,7 +920,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
try:
|
||||
sxml = SongXML()
|
||||
multiple = []
|
||||
for i in range(0, self.verseListWidget.rowCount()):
|
||||
for i in range(self.verseListWidget.rowCount()):
|
||||
item = self.verseListWidget.item(i, 0)
|
||||
verseId = unicode(item.data(QtCore.Qt.UserRole).toString())
|
||||
verse_tag = verseId[0]
|
||||
|
@ -191,13 +191,13 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
|
||||
else:
|
||||
log.debug(unicode(self.getVerse()[0]).split(u'\n'))
|
||||
value = unicode(self.getVerse()[0]).split(u'\n')[1]
|
||||
if len(value) == 0:
|
||||
if not value:
|
||||
lines = unicode(self.getVerse()[0]).split(u'\n')
|
||||
index = 2
|
||||
while index < len(lines) and len(value) == 0:
|
||||
while index < len(lines) and not value:
|
||||
value = lines[index]
|
||||
index += 1
|
||||
if len(value) == 0:
|
||||
if not value:
|
||||
critical_error_message_box(
|
||||
message=translate('SongsPlugin.EditSongForm',
|
||||
'You need to type some text in to the verse.'))
|
||||
|
@ -57,7 +57,7 @@ class SongImportForm(OpenLPWizard):
|
||||
``plugin``
|
||||
The songs plugin.
|
||||
"""
|
||||
self.clipboard = plugin.formparent.clipboard
|
||||
self.clipboard = plugin.formParent.clipboard
|
||||
OpenLPWizard.__init__(self, parent, plugin, u'songImportWizard',
|
||||
u':/wizards/wizard_importsong.bmp')
|
||||
|
||||
@ -129,6 +129,12 @@ class SongImportForm(OpenLPWizard):
|
||||
QtCore.QObject.connect(self.ccliRemoveButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onCCLIRemoveButtonClicked)
|
||||
QtCore.QObject.connect(self.dreamBeamAddButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onDreamBeamAddButtonClicked)
|
||||
QtCore.QObject.connect(self.dreamBeamRemoveButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onDreamBeamRemoveButtonClicked)
|
||||
QtCore.QObject.connect(self.songsOfFellowshipAddButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onSongsOfFellowshipAddButtonClicked)
|
||||
@ -165,6 +171,12 @@ class SongImportForm(OpenLPWizard):
|
||||
QtCore.QObject.connect(self.foilPresenterRemoveButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onFoilPresenterRemoveButtonClicked)
|
||||
QtCore.QObject.connect(self.powerSongAddButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onPowerSongAddButtonClicked)
|
||||
QtCore.QObject.connect(self.powerSongRemoveButton,
|
||||
QtCore.SIGNAL(u'clicked()'),
|
||||
self.onPowerSongRemoveButtonClicked)
|
||||
|
||||
def addCustomPages(self):
|
||||
"""
|
||||
@ -201,6 +213,8 @@ class SongImportForm(OpenLPWizard):
|
||||
self.addFileSelectItem(u'generic', None, True)
|
||||
# CCLI File import
|
||||
self.addFileSelectItem(u'ccli')
|
||||
# DreamBeam
|
||||
self.addFileSelectItem(u'dreamBeam')
|
||||
# EasySlides
|
||||
self.addFileSelectItem(u'easySlides', single_select=True)
|
||||
# EasyWorship
|
||||
@ -209,6 +223,8 @@ class SongImportForm(OpenLPWizard):
|
||||
self.addFileSelectItem(u'foilPresenter')
|
||||
# Open Song
|
||||
self.addFileSelectItem(u'openSong', u'OpenSong')
|
||||
# PowerSong
|
||||
self.addFileSelectItem(u'powerSong')
|
||||
# SongBeamer
|
||||
self.addFileSelectItem(u'songBeamer')
|
||||
# Song Show Plus
|
||||
@ -247,6 +263,8 @@ class SongImportForm(OpenLPWizard):
|
||||
translate('SongsPlugin.ImportWizardForm',
|
||||
'Generic Document/Presentation'))
|
||||
self.formatComboBox.setItemText(SongFormat.CCLI, WizardStrings.CCLI)
|
||||
self.formatComboBox.setItemText(
|
||||
SongFormat.DreamBeam, WizardStrings.DB)
|
||||
self.formatComboBox.setItemText(
|
||||
SongFormat.EasySlides, WizardStrings.ES)
|
||||
self.formatComboBox.setItemText(
|
||||
@ -254,6 +272,8 @@ class SongImportForm(OpenLPWizard):
|
||||
self.formatComboBox.setItemText(
|
||||
SongFormat.FoilPresenter, WizardStrings.FP)
|
||||
self.formatComboBox.setItemText(SongFormat.OpenSong, WizardStrings.OS)
|
||||
self.formatComboBox.setItemText(
|
||||
SongFormat.PowerSong, WizardStrings.PS)
|
||||
self.formatComboBox.setItemText(
|
||||
SongFormat.SongBeamer, WizardStrings.SB)
|
||||
self.formatComboBox.setItemText(
|
||||
@ -291,6 +311,14 @@ class SongImportForm(OpenLPWizard):
|
||||
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
|
||||
self.ccliRemoveButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
|
||||
self.dreamBeamAddButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
|
||||
self.dreamBeamRemoveButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
|
||||
self.powerSongAddButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
|
||||
self.powerSongRemoveButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
|
||||
self.songsOfFellowshipAddButton.setText(
|
||||
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
|
||||
self.songsOfFellowshipRemoveButton.setText(
|
||||
@ -397,6 +425,18 @@ class SongImportForm(OpenLPWizard):
|
||||
WizardStrings.YouSpecifyFile % WizardStrings.CCLI)
|
||||
self.ccliAddButton.setFocus()
|
||||
return False
|
||||
elif source_format == SongFormat.DreamBeam:
|
||||
if self.dreamBeamFileListWidget.count() == 0:
|
||||
critical_error_message_box(UiStrings().NFSp,
|
||||
WizardStrings.YouSpecifyFile % WizardStrings.DB)
|
||||
self.dreamBeamAddButton.setFocus()
|
||||
return False
|
||||
elif source_format == SongFormat.PowerSong:
|
||||
if self.powerSongFileListWidget.count() == 0:
|
||||
critical_error_message_box(UiStrings().NFSp,
|
||||
WizardStrings.YouSpecifyFile % WizardStrings.PS)
|
||||
self.powerSongAddButton.setFocus()
|
||||
return False
|
||||
elif source_format == SongFormat.SongsOfFellowship:
|
||||
if self.songsOfFellowshipFileListWidget.count() == 0:
|
||||
critical_error_message_box(UiStrings().NFSp,
|
||||
@ -433,7 +473,7 @@ class SongImportForm(OpenLPWizard):
|
||||
if self.songShowPlusFileListWidget.count() == 0:
|
||||
critical_error_message_box(UiStrings().NFSp,
|
||||
WizardStrings.YouSpecifyFile % WizardStrings.SSP)
|
||||
self.wordsOfWorshipAddButton.setFocus()
|
||||
self.songShowPlusAddButton.setFocus()
|
||||
return False
|
||||
elif source_format == SongFormat.FoilPresenter:
|
||||
if self.foilPresenterFileListWidget.count() == 0:
|
||||
@ -564,6 +604,38 @@ class SongImportForm(OpenLPWizard):
|
||||
"""
|
||||
self.removeSelectedItems(self.ccliFileListWidget)
|
||||
|
||||
def onDreamBeamAddButtonClicked(self):
|
||||
"""
|
||||
Get DreamBeam song database files
|
||||
"""
|
||||
self.getFiles(WizardStrings.OpenTypeFile % WizardStrings.DB,
|
||||
self.dreamBeamFileListWidget, u'%s (*.xml)'
|
||||
% translate('SongsPlugin.ImportWizardForm',
|
||||
'DreamBeam Song Files')
|
||||
)
|
||||
|
||||
def onDreamBeamRemoveButtonClicked(self):
|
||||
"""
|
||||
Remove selected DreamBeam files from the import list
|
||||
"""
|
||||
self.removeSelectedItems(self.dreamBeamFileListWidget)
|
||||
|
||||
def onPowerSongAddButtonClicked(self):
|
||||
"""
|
||||
Get PowerSong song database files
|
||||
"""
|
||||
self.getFiles(WizardStrings.OpenTypeFile % WizardStrings.PS,
|
||||
self.powerSongFileListWidget, u'%s (*.song)'
|
||||
% translate('SongsPlugin.ImportWizardForm',
|
||||
'PowerSong 1.0 Song Files')
|
||||
)
|
||||
|
||||
def onPowerSongRemoveButtonClicked(self):
|
||||
"""
|
||||
Remove selected PowerSong files from the import list
|
||||
"""
|
||||
self.removeSelectedItems(self.powerSongFileListWidget)
|
||||
|
||||
def onSongsOfFellowshipAddButtonClicked(self):
|
||||
"""
|
||||
Get Songs of Fellowship song database files
|
||||
@ -680,6 +752,8 @@ class SongImportForm(OpenLPWizard):
|
||||
self.openSongFileListWidget.clear()
|
||||
self.wordsOfWorshipFileListWidget.clear()
|
||||
self.ccliFileListWidget.clear()
|
||||
self.dreamBeamFileListWidget.clear()
|
||||
self.powerSongFileListWidget.clear()
|
||||
self.songsOfFellowshipFileListWidget.clear()
|
||||
self.genericFileListWidget.clear()
|
||||
self.easySlidesFilenameEdit.setText(u'')
|
||||
@ -741,6 +815,18 @@ class SongImportForm(OpenLPWizard):
|
||||
importer = self.plugin.importSongs(SongFormat.CCLI,
|
||||
filenames=self.getListOfFiles(self.ccliFileListWidget)
|
||||
)
|
||||
elif source_format == SongFormat.DreamBeam:
|
||||
# Import DreamBeam songs
|
||||
importer = self.plugin.importSongs(SongFormat.DreamBeam,
|
||||
filenames=self.getListOfFiles(
|
||||
self.dreamBeamFileListWidget)
|
||||
)
|
||||
elif source_format == SongFormat.PowerSong:
|
||||
# Import PowerSong songs
|
||||
importer = self.plugin.importSongs(SongFormat.PowerSong,
|
||||
filenames=self.getListOfFiles(
|
||||
self.powerSongFileListWidget)
|
||||
)
|
||||
elif source_format == SongFormat.SongsOfFellowship:
|
||||
# Import a Songs of Fellowship RTF file
|
||||
importer = self.plugin.importSongs(SongFormat.SongsOfFellowship,
|
||||
|
@ -108,7 +108,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
|
||||
item_id = self._getCurrentItemId(list_widget)
|
||||
if item_id != -1:
|
||||
item = self.manager.get_object(item_class, item_id)
|
||||
if item and len(item.songs) == 0:
|
||||
if item and not item.songs:
|
||||
if critical_error_message_box(dlg_title, del_text, self,
|
||||
True) == QtGui.QMessageBox.Yes:
|
||||
self.manager.delete_object(item_class, item.id)
|
||||
@ -191,7 +191,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
|
||||
``edit``
|
||||
If we edit an item, this should be *True*.
|
||||
"""
|
||||
if len(objects) > 0:
|
||||
if objects:
|
||||
# If we edit an existing object, we need to make sure that we do
|
||||
# not return False when nothing has changed.
|
||||
if edit:
|
||||
|
@ -316,7 +316,7 @@ def clean_song(manager, song):
|
||||
verse_type,
|
||||
verse[0][u'label'],
|
||||
verse[1],
|
||||
verse[0][u'lang'] if verse[0].has_key(u'lang') else None
|
||||
verse[0].get(u'lang')
|
||||
)
|
||||
compare_order.append((u'%s%s' % (verse_type, verse[0][u'label'])
|
||||
).upper())
|
||||
|
@ -185,7 +185,7 @@ class CCLIFileImport(SongImport):
|
||||
check_first_verse_line = False
|
||||
field_list = song_fields.split(u'/t')
|
||||
words_list = song_words.split(u'/t')
|
||||
for counter in range(0, len(field_list)):
|
||||
for counter in range(len(field_list)):
|
||||
if field_list[counter].startswith(u'Ver'):
|
||||
verse_type = VerseType.Tags[VerseType.Verse]
|
||||
elif field_list[counter].startswith(u'Ch'):
|
||||
@ -211,7 +211,7 @@ class CCLIFileImport(SongImport):
|
||||
elif verse_lines[0].startswith(u'('):
|
||||
verse_type = VerseType.Tags[VerseType.Other]
|
||||
verse_text = verse_lines[1]
|
||||
if len(verse_text) > 0:
|
||||
if verse_text:
|
||||
self.addVerse(verse_text, verse_type)
|
||||
check_first_verse_line = False
|
||||
# Handle multiple authors
|
||||
|
157
openlp/plugins/songs/lib/dreambeamimport.py
Normal file
157
openlp/plugins/songs/lib/dreambeamimport.py
Normal file
@ -0,0 +1,157 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2012 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||
# Armin Köhler, Joshua Miller, 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 #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`dreambeamimport` module provides the functionality for importing
|
||||
DreamBeam songs into the OpenLP database.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
|
||||
from lxml import etree, objectify
|
||||
|
||||
from openlp.core.lib import translate
|
||||
from openlp.plugins.songs.lib.songimport import SongImport
|
||||
from openlp.plugins.songs.lib.ui import SongStrings
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class DreamBeamImport(SongImport):
|
||||
"""
|
||||
The :class:`DreamBeamImport` class provides the ability to import song files from
|
||||
DreamBeam.
|
||||
|
||||
An example of DreamBeam xml mark-up::
|
||||
|
||||
<?xml version="1.0"?>
|
||||
<DreamSong xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<WordWrap>false</WordWrap>
|
||||
<Version>0.80</Version>
|
||||
<Title>Amazing Grace</Title>
|
||||
<Author>John Newton</Author>
|
||||
<Collection />
|
||||
<Number />
|
||||
<Notes />
|
||||
<KeyRangeLow>F</KeyRangeLow>
|
||||
<KeyRangeHigh>G</KeyRangeHigh>
|
||||
<MinorKey>false</MinorKey>
|
||||
<DualLanguage>false</DualLanguage>
|
||||
<SongLyrics>
|
||||
<LyricsItem Type="Verse" Number="1">Amazing Grace, how sweet the sound,
|
||||
That saved a wretch like me.
|
||||
I once was lost but now am found,
|
||||
Was blind, but now, I see.</LyricsItem>
|
||||
<LyricsItem Type="Verse" Number="2">T’was Grace that taught my heart to fear.
|
||||
And Grace, my fears relieved.
|
||||
How precious did that Grace appear…
|
||||
the hour I first believed.</LyricsItem>
|
||||
</SongLyrics>
|
||||
<Sequence>
|
||||
<LyricsSequenceItem Type="Verse" Number="1" />
|
||||
<LyricsSequenceItem Type="Verse" Number="2" />
|
||||
</Sequence>
|
||||
<ShowRectangles>false</ShowRectangles>
|
||||
</DreamSong>
|
||||
|
||||
Valid extensions for a DreamBeam song file are:
|
||||
|
||||
* \*.xml
|
||||
"""
|
||||
|
||||
def doImport(self):
|
||||
"""
|
||||
Receive a single file or a list of files to import.
|
||||
"""
|
||||
if isinstance(self.importSource, list):
|
||||
self.importWizard.progressBar.setMaximum(len(self.importSource))
|
||||
for file in self.importSource:
|
||||
if self.stopImportFlag:
|
||||
return
|
||||
self.setDefaults()
|
||||
parser = etree.XMLParser(remove_blank_text=True)
|
||||
try:
|
||||
parsed_file = etree.parse(open(file, u'r'), parser)
|
||||
except etree.XMLSyntaxError:
|
||||
log.exception(u'XML syntax error in file %s' % file)
|
||||
self.logError(file, SongStrings.XMLSyntaxError)
|
||||
continue
|
||||
xml = unicode(etree.tostring(parsed_file))
|
||||
song_xml = objectify.fromstring(xml)
|
||||
if song_xml.tag != u'DreamSong':
|
||||
self.logError(file, unicode(
|
||||
translate('SongsPlugin.DreamBeamImport',
|
||||
('Invalid DreamBeam song file. Missing '
|
||||
'DreamSong tag.'))))
|
||||
continue
|
||||
if hasattr(song_xml, u'Version'):
|
||||
self.version = float(song_xml.Version.text)
|
||||
else:
|
||||
self.version = 0
|
||||
# Version numbers found in DreamBeam Source /FileTypes/Song.cs
|
||||
if self.version >= 0.5:
|
||||
if hasattr(song_xml, u'Title'):
|
||||
self.title = unicode(song_xml.Title.text)
|
||||
if hasattr(song_xml, u'Author'):
|
||||
author_copyright = song_xml.Author.text
|
||||
if hasattr(song_xml, u'SongLyrics'):
|
||||
for lyrics_item in song_xml.SongLyrics.iterchildren():
|
||||
verse_type = lyrics_item.get(u'Type')
|
||||
verse_number = lyrics_item.get(u'Number')
|
||||
verse_text = unicode(lyrics_item.text)
|
||||
self.addVerse(verse_text,
|
||||
(u'%s%s' % (verse_type[:1], verse_number)))
|
||||
if hasattr(song_xml, u'Collection'):
|
||||
self.songBookName = unicode(song_xml.Collection.text)
|
||||
if hasattr(song_xml, u'Number'):
|
||||
self.songNumber = unicode(song_xml.Number.text)
|
||||
if hasattr(song_xml, u'Sequence'):
|
||||
for LyricsSequenceItem in (
|
||||
song_xml.Sequence.iterchildren()):
|
||||
self.verseOrderList.append(
|
||||
"%s%s" % (LyricsSequenceItem.get(u'Type')[:1],
|
||||
LyricsSequenceItem.get(u'Number')))
|
||||
if hasattr(song_xml, u'Notes'):
|
||||
self.comments = unicode(song_xml.Notes.text)
|
||||
else:
|
||||
if hasattr(song_xml.Text0, u'Text'):
|
||||
self.title = unicode(song_xml.Text0.Text.text)
|
||||
if hasattr(song_xml.Text1, u'Text'):
|
||||
self.lyrics = unicode(song_xml.Text1.Text.text)
|
||||
for verse in self.lyrics.split(u'\n\n\n'):
|
||||
self.addVerse(verse)
|
||||
if hasattr(song_xml.Text2, u'Text'):
|
||||
author_copyright = song_xml.Text2.Text.text
|
||||
if author_copyright:
|
||||
author_copyright = unicode(author_copyright)
|
||||
if author_copyright.find(
|
||||
unicode(SongStrings.CopyrightSymbol)) >= 0:
|
||||
self.addCopyright(author_copyright)
|
||||
else:
|
||||
self.parseAuthor(author_copyright)
|
||||
if not self.finish():
|
||||
self.logError(file)
|
@ -162,15 +162,12 @@ class EasySlidesImport(SongImport):
|
||||
separatorlines = 0
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if len(line) == 0:
|
||||
if not line:
|
||||
continue
|
||||
elif line[1:7] == u'region':
|
||||
# this is region separator, probably [region 2]
|
||||
region = self._extractRegion(line)
|
||||
if regionlines.has_key(region):
|
||||
regionlines[region] = regionlines[region] + 1
|
||||
else:
|
||||
regionlines[region] = 1
|
||||
regionlines[region] = 1 + regionlines.get(region, 0)
|
||||
elif line[0] == u'[':
|
||||
separatorlines = separatorlines + 1
|
||||
# if the song has separators
|
||||
@ -206,7 +203,7 @@ class EasySlidesImport(SongImport):
|
||||
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if len(line) == 0:
|
||||
if not line:
|
||||
if separators:
|
||||
# separators are used, so empty line means slide break
|
||||
# inside verse
|
||||
@ -215,15 +212,11 @@ class EasySlidesImport(SongImport):
|
||||
else:
|
||||
# separators are not used, so empty line starts a new verse
|
||||
vt = u'V'
|
||||
if verses[reg].has_key(vt):
|
||||
vn = len(verses[reg][vt].keys())+1
|
||||
else:
|
||||
vn = u'1'
|
||||
vn = len(verses[reg].get(vt, {})) + 1
|
||||
inst = 1
|
||||
elif line[0:7] == u'[region':
|
||||
reg = self._extractRegion(line)
|
||||
if not verses.has_key(reg):
|
||||
verses[reg] = {}
|
||||
verses.setdefault(reg, {})
|
||||
if not regionsInVerses:
|
||||
vt = u'V'
|
||||
vn = u'1'
|
||||
@ -238,28 +231,19 @@ class EasySlidesImport(SongImport):
|
||||
if match:
|
||||
marker = match.group(1).strip()
|
||||
vn = match.group(2)
|
||||
if len(marker) == 0:
|
||||
vt = u'V'
|
||||
elif MarkTypes.has_key(marker):
|
||||
vt = MarkTypes[marker]
|
||||
else:
|
||||
vt = u'O'
|
||||
vt = MarkTypes.get(marker, u'O') if marker else u'V'
|
||||
if regionsInVerses:
|
||||
region = defaultregion
|
||||
inst = 1
|
||||
if self._listHas(verses, [reg, vt, vn, inst]):
|
||||
inst = len(verses[reg][vt][vn])+1
|
||||
inst = len(verses[reg][vt][vn]) + 1
|
||||
else:
|
||||
if not [reg, vt, vn, inst] in our_verse_order:
|
||||
our_verse_order.append([reg, vt, vn, inst])
|
||||
if not verses[reg].has_key(vt):
|
||||
verses[reg][vt] = {}
|
||||
if not verses[reg][vt].has_key(vn):
|
||||
verses[reg][vt][vn] = {}
|
||||
if not verses[reg][vt][vn].has_key(inst):
|
||||
verses[reg][vt][vn][inst] = []
|
||||
words = self.tidyText(line)
|
||||
verses[reg][vt][vn][inst].append(words)
|
||||
verses[reg].setdefault(vt, {})
|
||||
verses[reg][vt].setdefault(vn, {})
|
||||
verses[reg][vt][vn].setdefault(inst, [])
|
||||
verses[reg][vt][vn][inst].append(self.tidyText(line))
|
||||
# done parsing
|
||||
|
||||
versetags = []
|
||||
@ -286,11 +270,11 @@ class EasySlidesImport(SongImport):
|
||||
try:
|
||||
order = unicode(song.Sequence).strip().split(u',')
|
||||
for tag in order:
|
||||
if len(tag) == 0:
|
||||
if not tag:
|
||||
continue
|
||||
elif tag[0].isdigit():
|
||||
tag = u'V' + tag
|
||||
elif SeqTypes.has_key(tag.lower()):
|
||||
elif tag.lower() in SeqTypes:
|
||||
tag = SeqTypes[tag.lower()]
|
||||
else:
|
||||
continue
|
||||
@ -307,9 +291,7 @@ class EasySlidesImport(SongImport):
|
||||
|
||||
def _listHas(self, lst, subitems):
|
||||
for subitem in subitems:
|
||||
if isinstance(lst, dict) and lst.has_key(subitem):
|
||||
lst = lst[subitem]
|
||||
elif isinstance(lst, list) and subitem in lst:
|
||||
if subitem in lst:
|
||||
lst = lst[subitem]
|
||||
else:
|
||||
return False
|
||||
|
@ -62,15 +62,15 @@ def strip_rtf(blob, encoding):
|
||||
if control:
|
||||
# for delimiters, set control to False
|
||||
if c == '{':
|
||||
if len(control_word) > 0:
|
||||
if control_word:
|
||||
depth += 1
|
||||
control = False
|
||||
elif c == '}':
|
||||
if len(control_word) > 0:
|
||||
if control_word:
|
||||
depth -= 1
|
||||
control = False
|
||||
elif c == '\\':
|
||||
new_control = (len(control_word) > 0)
|
||||
new_control = bool(control_word)
|
||||
control = False
|
||||
elif c.isspace():
|
||||
control = False
|
||||
@ -79,7 +79,7 @@ def strip_rtf(blob, encoding):
|
||||
if len(control_word) == 3 and control_word[0] == '\'':
|
||||
control = False
|
||||
if not control:
|
||||
if len(control_word) == 0:
|
||||
if not control_word:
|
||||
if c == '{' or c == '}' or c == '\\':
|
||||
clear_text.append(c)
|
||||
else:
|
||||
@ -360,7 +360,7 @@ class EasyWorshipSongImport(SongImport):
|
||||
field_desc = self.fieldDescs[field_desc_index]
|
||||
# Return None in case of 'blank' entries
|
||||
if isinstance(field, str):
|
||||
if len(field.rstrip('\0')) == 0:
|
||||
if not field.rstrip('\0'):
|
||||
return None
|
||||
elif field == 0:
|
||||
return None
|
||||
|
@ -413,7 +413,7 @@ class FoilPresenter(object):
|
||||
temp_verse_order_backup = []
|
||||
temp_sortnr_backup = 1
|
||||
temp_sortnr_liste = []
|
||||
versenumber = {
|
||||
verse_count = {
|
||||
VerseType.Tags[VerseType.Verse]: 1,
|
||||
VerseType.Tags[VerseType.Chorus]: 1,
|
||||
VerseType.Tags[VerseType.Bridge]: 1,
|
||||
@ -463,8 +463,8 @@ class FoilPresenter(object):
|
||||
verse_number = re.compile(u'[a-zA-Z.+-_ ]*').sub(u'', verse_name)
|
||||
# Foilpresenter allows e. g. "C", but we need "C1".
|
||||
if not verse_number:
|
||||
verse_number = unicode(versenumber[verse_type])
|
||||
versenumber[verse_type] += 1
|
||||
verse_number = unicode(verse_count[verse_type])
|
||||
verse_count[verse_type] += 1
|
||||
else:
|
||||
# test if foilpresenter have the same versenumber two times with
|
||||
# different parts raise the verse number
|
||||
@ -508,13 +508,13 @@ class FoilPresenter(object):
|
||||
song.song_number = u''
|
||||
try:
|
||||
for bucheintrag in foilpresenterfolie.buch.bucheintrag:
|
||||
bookname = self._child(bucheintrag.name)
|
||||
if bookname:
|
||||
book_name = self._child(bucheintrag.name)
|
||||
if book_name:
|
||||
book = self.manager.get_object_filtered(Book,
|
||||
Book.name == bookname)
|
||||
Book.name == book_name)
|
||||
if book is None:
|
||||
# We need to create a book, because it does not exist.
|
||||
book = Book.populate(name=bookname, publisher=u'')
|
||||
book = Book.populate(name=book_name, publisher=u'')
|
||||
self.manager.save_object(book)
|
||||
song.song_book_id = book.id
|
||||
try:
|
||||
@ -537,12 +537,12 @@ class FoilPresenter(object):
|
||||
``song``
|
||||
The song object.
|
||||
"""
|
||||
for titelstring in foilpresenterfolie.titel.titelstring:
|
||||
for title_string in foilpresenterfolie.titel.titelstring:
|
||||
if not song.title:
|
||||
song.title = self._child(titelstring)
|
||||
song.title = self._child(title_string)
|
||||
song.alternate_title = u''
|
||||
else:
|
||||
song.alternate_title = self._child(titelstring)
|
||||
song.alternate_title = self._child(title_string)
|
||||
|
||||
def _process_topics(self, foilpresenterfolie, song):
|
||||
"""
|
||||
@ -556,13 +556,13 @@ class FoilPresenter(object):
|
||||
"""
|
||||
try:
|
||||
for name in foilpresenterfolie.kategorien.name:
|
||||
topictext = self._child(name)
|
||||
if topictext:
|
||||
topic_text = self._child(name)
|
||||
if topic_text:
|
||||
topic = self.manager.get_object_filtered(Topic,
|
||||
Topic.name == topictext)
|
||||
Topic.name == topic_text)
|
||||
if topic is None:
|
||||
# We need to create a topic, because it does not exist.
|
||||
topic = Topic.populate(name=topictext)
|
||||
topic = Topic.populate(name=topic_text)
|
||||
self.manager.save_object(topic)
|
||||
song.topics.append(topic)
|
||||
except AttributeError:
|
||||
|
@ -35,6 +35,8 @@ from olpimport import OpenLPSongImport
|
||||
from openlyricsimport import OpenLyricsImport
|
||||
from wowimport import WowImport
|
||||
from cclifileimport import CCLIFileImport
|
||||
from dreambeamimport import DreamBeamImport
|
||||
from powersongimport import PowerSongImport
|
||||
from ewimport import EasyWorshipSongImport
|
||||
from songbeamerimport import SongBeamerImport
|
||||
from songshowplusimport import SongShowPlusImport
|
||||
@ -73,20 +75,22 @@ class SongFormat(object):
|
||||
OpenLP1 = 2
|
||||
Generic = 3
|
||||
CCLI = 4
|
||||
EasySlides = 5
|
||||
EasyWorship = 6
|
||||
FoilPresenter = 7
|
||||
OpenSong = 8
|
||||
SongBeamer = 9
|
||||
SongShowPlus = 10
|
||||
SongsOfFellowship = 11
|
||||
WordsOfWorship = 12
|
||||
#CSV = 13
|
||||
DreamBeam = 5
|
||||
EasySlides = 6
|
||||
EasyWorship = 7
|
||||
FoilPresenter = 8
|
||||
OpenSong = 9
|
||||
PowerSong = 10
|
||||
SongBeamer = 11
|
||||
SongShowPlus = 12
|
||||
SongsOfFellowship = 13
|
||||
WordsOfWorship = 14
|
||||
#CSV = 15
|
||||
|
||||
@staticmethod
|
||||
def get_class(format):
|
||||
"""
|
||||
Return the appropriate imeplementation class.
|
||||
Return the appropriate implementation class.
|
||||
|
||||
``format``
|
||||
The song format.
|
||||
@ -107,6 +111,10 @@ class SongFormat(object):
|
||||
return OooImport
|
||||
elif format == SongFormat.CCLI:
|
||||
return CCLIFileImport
|
||||
elif format == SongFormat.DreamBeam:
|
||||
return DreamBeamImport
|
||||
elif format == SongFormat.PowerSong:
|
||||
return PowerSongImport
|
||||
elif format == SongFormat.EasySlides:
|
||||
return EasySlidesImport
|
||||
elif format == SongFormat.EasyWorship:
|
||||
@ -130,10 +138,12 @@ class SongFormat(object):
|
||||
SongFormat.OpenLP1,
|
||||
SongFormat.Generic,
|
||||
SongFormat.CCLI,
|
||||
SongFormat.DreamBeam,
|
||||
SongFormat.EasySlides,
|
||||
SongFormat.EasyWorship,
|
||||
SongFormat.FoilPresenter,
|
||||
SongFormat.OpenSong,
|
||||
SongFormat.PowerSong,
|
||||
SongFormat.SongBeamer,
|
||||
SongFormat.SongShowPlus,
|
||||
SongFormat.SongsOfFellowship,
|
||||
|
@ -68,7 +68,7 @@ class SongMediaItem(MediaManagerItem):
|
||||
def __init__(self, parent, plugin, icon):
|
||||
self.IconPath = u'songs/song'
|
||||
MediaManagerItem.__init__(self, parent, plugin, icon)
|
||||
self.editSongForm = EditSongForm(self, self.plugin.formparent,
|
||||
self.editSongForm = EditSongForm(self, self.plugin.formParent,
|
||||
self.plugin.manager)
|
||||
self.openLyrics = OpenLyrics(self.plugin.manager)
|
||||
self.singleServiceItem = False
|
||||
@ -295,9 +295,8 @@ class SongMediaItem(MediaManagerItem):
|
||||
log.debug(u'display results Book')
|
||||
self.listView.clear()
|
||||
for book in searchresults:
|
||||
songs = sorted(book.songs, key=lambda song: int(
|
||||
re.sub(r'[^0-9]', u' ', song.song_number).partition(' ')[0])
|
||||
if len(re.sub(r'[^\w]', ' ', song.song_number)) else 0)
|
||||
songs = sorted(book.songs, key=lambda song:
|
||||
int(re.match(r'[0-9]+', u'0' + song.song_number).group()))
|
||||
for song in songs:
|
||||
# Do not display temporary songs
|
||||
if song.temporary:
|
||||
@ -331,7 +330,7 @@ class SongMediaItem(MediaManagerItem):
|
||||
search_length = 3
|
||||
if len(text) > search_length:
|
||||
self.onSearchTextButtonClicked()
|
||||
elif len(text) == 0:
|
||||
elif not text:
|
||||
self.onClearTextButtonClick()
|
||||
|
||||
def onImportClick(self):
|
||||
@ -409,7 +408,7 @@ class SongMediaItem(MediaManagerItem):
|
||||
QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No:
|
||||
return
|
||||
Receiver.send_message(u'cursor_busy')
|
||||
self.plugin.formparent.displayProgressBar(len(items))
|
||||
self.plugin.formParent.displayProgressBar(len(items))
|
||||
for item in items:
|
||||
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
|
||||
media_files = self.plugin.manager.get_all_objects(MediaFile,
|
||||
@ -428,8 +427,8 @@ class SongMediaItem(MediaManagerItem):
|
||||
except OSError:
|
||||
log.exception(u'Could not remove directory: %s', save_path)
|
||||
self.plugin.manager.delete_object(Song, item_id)
|
||||
self.plugin.formparent.incrementProgressBar()
|
||||
self.plugin.formparent.finishedProgressBar()
|
||||
self.plugin.formParent.incrementProgressBar()
|
||||
self.plugin.formParent.finishedProgressBar()
|
||||
Receiver.send_message(u'cursor_normal')
|
||||
self.onSearchTextButtonClicked()
|
||||
|
||||
@ -491,7 +490,7 @@ class SongMediaItem(MediaManagerItem):
|
||||
else:
|
||||
# Loop through the verse list and expand the song accordingly.
|
||||
for order in song.verse_order.lower().split():
|
||||
if len(order) == 0:
|
||||
if not order:
|
||||
break
|
||||
for verse in verseList:
|
||||
if verse[0][u'type'][0].lower() == order[0] and \
|
||||
@ -530,7 +529,7 @@ class SongMediaItem(MediaManagerItem):
|
||||
u'authors': u', '.join(author_list)}
|
||||
service_item.xml_version = self.openLyrics.song_to_xml(song)
|
||||
# Add the audio file to the service item.
|
||||
if len(song.media_files) > 0:
|
||||
if song.media_files:
|
||||
service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
|
||||
service_item.background_audio = \
|
||||
[m.file_name for m in song.media_files]
|
||||
@ -575,12 +574,12 @@ class SongMediaItem(MediaManagerItem):
|
||||
editId = song.id
|
||||
break
|
||||
# If there's any backing tracks, copy them over.
|
||||
if len(item.background_audio) > 0:
|
||||
if item.background_audio:
|
||||
self._updateBackgroundAudio(song, item)
|
||||
if add_song and self.addSongFromService:
|
||||
song = self.openLyrics.xml_to_song(item.xml_version)
|
||||
# If there's any backing tracks, copy them over.
|
||||
if len(item.background_audio) > 0:
|
||||
if item.background_audio:
|
||||
self._updateBackgroundAudio(song, item)
|
||||
editId = song.id
|
||||
self.onSearchTextButtonClicked()
|
||||
@ -588,7 +587,7 @@ class SongMediaItem(MediaManagerItem):
|
||||
# Make sure we temporary import formatting tags.
|
||||
song = self.openLyrics.xml_to_song(item.xml_version, True)
|
||||
# If there's any backing tracks, copy them over.
|
||||
if len(item.background_audio) > 0:
|
||||
if item.background_audio:
|
||||
self._updateBackgroundAudio(song, item)
|
||||
editId = song.id
|
||||
temporary = True
|
||||
|
@ -60,7 +60,7 @@ class OpenLP1SongImport(SongImport):
|
||||
"""
|
||||
SongImport.__init__(self, manager, **kwargs)
|
||||
self.availableThemes = \
|
||||
kwargs[u'plugin'].formparent.themeManagerContents.getThemes()
|
||||
kwargs[u'plugin'].formParent.themeManagerContents.getThemes()
|
||||
|
||||
def doImport(self):
|
||||
"""
|
||||
@ -122,8 +122,7 @@ class OpenLP1SongImport(SongImport):
|
||||
cursor.execute(
|
||||
u'SELECT settingsid FROM songs WHERE songid = %s' % song_id)
|
||||
theme_id = cursor.fetchone()[0]
|
||||
if themes.has_key(theme_id):
|
||||
self.themeName = themes[theme_id]
|
||||
self.themeName = themes.get(theme_id, u'')
|
||||
verses = lyrics.split(u'\n\n')
|
||||
for verse in verses:
|
||||
if verse.strip():
|
||||
@ -191,7 +190,7 @@ class OpenLP1SongImport(SongImport):
|
||||
# Detect charset by songs.
|
||||
cursor.execute(u'SELECT name FROM sqlite_master '
|
||||
u'WHERE type = \'table\' AND name = \'tracks\'')
|
||||
if len(cursor.fetchall()) > 0:
|
||||
if cursor.fetchall():
|
||||
cursor.execute(u'SELECT fulltrackname FROM tracks')
|
||||
tracks = cursor.fetchall()
|
||||
for track in tracks:
|
||||
|
@ -174,7 +174,7 @@ class OpenSongImport(SongImport):
|
||||
if semicolon >= 0:
|
||||
this_line = this_line[:semicolon]
|
||||
this_line = this_line.strip()
|
||||
if not len(this_line):
|
||||
if not this_line:
|
||||
continue
|
||||
# skip guitar chords and page and column breaks
|
||||
if this_line.startswith(u'.') or this_line.startswith(u'---') \
|
||||
@ -197,15 +197,12 @@ class OpenSongImport(SongImport):
|
||||
# the verse tag
|
||||
verse_tag = content
|
||||
verse_num = u'1'
|
||||
if len(verse_tag) == 0:
|
||||
verse_index = 0
|
||||
else:
|
||||
verse_index = VerseType.from_loose_input(verse_tag)
|
||||
verse_index = VerseType.from_loose_input(verse_tag) \
|
||||
if verse_tag else 0
|
||||
verse_tag = VerseType.Tags[verse_index]
|
||||
inst = 1
|
||||
if [verse_tag, verse_num, inst] in our_verse_order \
|
||||
and verses.has_key(verse_tag) \
|
||||
and verses[verse_tag].has_key(verse_num):
|
||||
and verse_num in verses.get(verse_tag, {}):
|
||||
inst = len(verses[verse_tag][verse_num]) + 1
|
||||
continue
|
||||
# number at start of line.. it's verse number
|
||||
@ -213,11 +210,9 @@ class OpenSongImport(SongImport):
|
||||
verse_num = this_line[0]
|
||||
this_line = this_line[1:].strip()
|
||||
our_verse_order.append([verse_tag, verse_num, inst])
|
||||
if not verses.has_key(verse_tag):
|
||||
verses[verse_tag] = {}
|
||||
if not verses[verse_tag].has_key(verse_num):
|
||||
verses[verse_tag][verse_num] = {}
|
||||
if not verses[verse_tag][verse_num].has_key(inst):
|
||||
verses.setdefault(verse_tag, {})
|
||||
verses[verse_tag].setdefault(verse_num, {})
|
||||
if inst not in verses[verse_tag][verse_num]:
|
||||
verses[verse_tag][verse_num][inst] = []
|
||||
our_verse_order.append([verse_tag, verse_num, inst])
|
||||
# Tidy text and remove the ____s from extended words
|
||||
@ -252,15 +247,14 @@ class OpenSongImport(SongImport):
|
||||
if match is not None:
|
||||
verse_tag = match.group(1)
|
||||
verse_num = match.group(2)
|
||||
if not len(verse_tag):
|
||||
if not verse_tag:
|
||||
verse_tag = VerseType.Tags[VerseType.Verse]
|
||||
else:
|
||||
# Assume it's no.1 if there are no digits
|
||||
verse_tag = verse_def
|
||||
verse_num = u'1'
|
||||
verse_def = u'%s%s' % (verse_tag, verse_num)
|
||||
if verses.has_key(verse_tag) and \
|
||||
verses[verse_tag].has_key(verse_num):
|
||||
if verse_num in verses.get(verse_tag, {}):
|
||||
self.verseOrderList.append(verse_def)
|
||||
else:
|
||||
log.info(u'Got order %s but not in verse tags, dropping'
|
||||
|
195
openlp/plugins/songs/lib/powersongimport.py
Normal file
195
openlp/plugins/songs/lib/powersongimport.py
Normal file
@ -0,0 +1,195 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2012 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||
# Armin Köhler, Joshua Miller, 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 #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`powersongimport` module provides the functionality for importing
|
||||
PowerSong songs into the OpenLP database.
|
||||
"""
|
||||
import logging
|
||||
|
||||
from openlp.core.lib import translate
|
||||
from openlp.plugins.songs.lib.songimport import SongImport
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class PowerSongImport(SongImport):
|
||||
"""
|
||||
The :class:`PowerSongImport` class provides the ability to import song files
|
||||
from PowerSong.
|
||||
|
||||
**PowerSong 1.0 Song File Format:**
|
||||
|
||||
The file has a number of label-field (think key-value) pairs.
|
||||
|
||||
Label and Field strings:
|
||||
|
||||
* Every label and field is a variable length string preceded by an
|
||||
integer specifying it's byte length.
|
||||
* Integer is 32-bit but is encoded in 7-bit format to save space. Thus
|
||||
if length will fit in 7 bits (ie <= 127) it takes up only one byte.
|
||||
|
||||
Metadata fields:
|
||||
|
||||
* Every PowerSong file has a TITLE field.
|
||||
* There is zero or more AUTHOR fields.
|
||||
* There is always a COPYRIGHTLINE label, but its field may be empty.
|
||||
This field may also contain a CCLI number: e.g. "CCLI 176263".
|
||||
|
||||
Lyrics fields:
|
||||
|
||||
* Each verse is contained in a PART field.
|
||||
* Lines have Windows line endings ``CRLF`` (0x0d, 0x0a).
|
||||
* There is no concept of verse types.
|
||||
|
||||
Valid extensions for a PowerSong song file are:
|
||||
|
||||
* .song
|
||||
"""
|
||||
|
||||
def doImport(self):
|
||||
"""
|
||||
Receive a list of files to import.
|
||||
"""
|
||||
if not isinstance(self.importSource, list):
|
||||
self.logError(unicode(translate('SongsPlugin.PowerSongImport',
|
||||
'No files to import.')))
|
||||
return
|
||||
self.importWizard.progressBar.setMaximum(len(self.importSource))
|
||||
for file in self.importSource:
|
||||
if self.stopImportFlag:
|
||||
return
|
||||
self.setDefaults()
|
||||
parse_error = False
|
||||
with open(file, 'rb') as song_data:
|
||||
while True:
|
||||
try:
|
||||
label = self._readString(song_data)
|
||||
if not label:
|
||||
break
|
||||
field = self._readString(song_data)
|
||||
except ValueError:
|
||||
parse_error = True
|
||||
self.logError(file, unicode(
|
||||
translate('SongsPlugin.PowerSongImport',
|
||||
'Invalid PowerSong file. Unexpected byte value.')))
|
||||
break
|
||||
else:
|
||||
if label == u'TITLE':
|
||||
self.title = field.replace(u'\n', u' ')
|
||||
elif label == u'AUTHOR':
|
||||
self.parseAuthor(field)
|
||||
elif label == u'COPYRIGHTLINE':
|
||||
found_copyright = True
|
||||
self._parseCopyrightCCLI(field)
|
||||
elif label == u'PART':
|
||||
self.addVerse(field)
|
||||
if parse_error:
|
||||
continue
|
||||
# Check that file had TITLE field
|
||||
if not self.title:
|
||||
self.logError(file, unicode(
|
||||
translate('SongsPlugin.PowerSongImport',
|
||||
'Invalid PowerSong file. Missing "TITLE" header.')))
|
||||
continue
|
||||
# Check that file had COPYRIGHTLINE label
|
||||
if not found_copyright:
|
||||
self.logError(file, unicode(
|
||||
translate('SongsPlugin.PowerSongImport',
|
||||
'"%s" Invalid PowerSong file. Missing "COPYRIGHTLINE" '
|
||||
'header.' % self.title)))
|
||||
continue
|
||||
# Check that file had at least one verse
|
||||
if not self.verses:
|
||||
self.logError(file, unicode(
|
||||
translate('SongsPlugin.PowerSongImport',
|
||||
'"%s" Verses not found. Missing "PART" header.'
|
||||
% self.title)))
|
||||
continue
|
||||
if not self.finish():
|
||||
self.logError(file)
|
||||
|
||||
def _readString(self, file_object):
|
||||
"""
|
||||
Reads in next variable-length string.
|
||||
"""
|
||||
string_len = self._read7BitEncodedInteger(file_object)
|
||||
return unicode(file_object.read(string_len), u'utf-8', u'ignore')
|
||||
|
||||
def _read7BitEncodedInteger(self, file_object):
|
||||
"""
|
||||
Reads in a 32-bit integer in compressed 7-bit format.
|
||||
|
||||
Accomplished by reading the integer 7 bits at a time. The high bit
|
||||
of the byte when set means to continue reading more bytes.
|
||||
If the integer will fit in 7 bits (ie <= 127), it only takes up one
|
||||
byte. Otherwise, it may take up to 5 bytes.
|
||||
|
||||
Reference: .NET method System.IO.BinaryReader.Read7BitEncodedInt
|
||||
"""
|
||||
val = 0
|
||||
shift = 0
|
||||
i = 0
|
||||
while True:
|
||||
# Check for corrupted stream (since max 5 bytes per 32-bit integer)
|
||||
if i == 5:
|
||||
raise ValueError
|
||||
byte = self._readByte(file_object)
|
||||
# Strip high bit and shift left
|
||||
val += (byte & 0x7f) << shift
|
||||
shift += 7
|
||||
high_bit_set = byte & 0x80
|
||||
if not high_bit_set:
|
||||
break
|
||||
i += 1
|
||||
return val
|
||||
|
||||
def _readByte(self, file_object):
|
||||
"""
|
||||
Reads in next byte as an unsigned integer
|
||||
|
||||
Note: returns 0 at end of file.
|
||||
"""
|
||||
byte_str = file_object.read(1)
|
||||
# If read result is empty, then reached end of file
|
||||
if not byte_str:
|
||||
return 0
|
||||
else:
|
||||
return ord(byte_str)
|
||||
|
||||
def _parseCopyrightCCLI(self, field):
|
||||
"""
|
||||
Look for CCLI song number, and get copyright
|
||||
"""
|
||||
copyright, sep, ccli_no = field.rpartition(u'CCLI')
|
||||
if not sep:
|
||||
copyright = ccli_no
|
||||
ccli_no = u''
|
||||
if copyright:
|
||||
self.addCopyright(copyright.rstrip(u'\n').replace(u'\n', u' '))
|
||||
if ccli_no:
|
||||
ccli_no = ccli_no.strip(u' :')
|
||||
if ccli_no.isdigit():
|
||||
self.ccliNumber = ccli_no
|
@ -61,9 +61,9 @@ class SongImport(QtCore.QObject):
|
||||
"""
|
||||
self.manager = manager
|
||||
QtCore.QObject.__init__(self)
|
||||
if kwargs.has_key(u'filename'):
|
||||
if u'filename' in kwargs:
|
||||
self.importSource = kwargs[u'filename']
|
||||
elif kwargs.has_key(u'filenames'):
|
||||
elif u'filenames' in kwargs:
|
||||
self.importSource = kwargs[u'filenames']
|
||||
else:
|
||||
raise KeyError(u'Keyword arguments "filename[s]" not supplied.')
|
||||
@ -107,11 +107,11 @@ class SongImport(QtCore.QObject):
|
||||
|
||||
``filepath``
|
||||
This should be the file path if ``self.importSource`` is a list
|
||||
with different files. If it is not a list, but a single file (for
|
||||
with different files. If it is not a list, but a single file (for
|
||||
instance a database), then this should be the song's title.
|
||||
|
||||
``reason``
|
||||
The reason, why the import failed. The string should be as
|
||||
The reason why the import failed. The string should be as
|
||||
informative as possible.
|
||||
"""
|
||||
self.setDefaults()
|
||||
@ -273,7 +273,7 @@ class SongImport(QtCore.QObject):
|
||||
Author not checked here, if no author then "Author unknown" is
|
||||
automatically added
|
||||
"""
|
||||
if not self.title or not len(self.verses):
|
||||
if not self.title or not self.verses:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
@ -314,13 +314,10 @@ class SongImport(QtCore.QObject):
|
||||
verse_def = new_verse_def
|
||||
sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], verse_text, lang)
|
||||
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
||||
if not len(self.verseOrderList) and \
|
||||
self.verseOrderListGeneratedUseful:
|
||||
if not self.verseOrderList and self.verseOrderListGeneratedUseful:
|
||||
self.verseOrderList = self.verseOrderListGenerated
|
||||
for i, current_verse_def in enumerate(self.verseOrderList):
|
||||
if verses_changed_to_other.has_key(current_verse_def):
|
||||
self.verseOrderList[i] = \
|
||||
verses_changed_to_other[current_verse_def]
|
||||
self.verseOrderList = map(lambda v: verses_changed_to_other.get(v, v),
|
||||
self.verseOrderList)
|
||||
song.verse_order = u' '.join(self.verseOrderList)
|
||||
song.copyright = self.copyright
|
||||
song.comments = self.comments
|
||||
|
@ -25,8 +25,8 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`wowimport` module provides the functionality for importing Words of
|
||||
Worship songs into the OpenLP database.
|
||||
The :mod:`songshowplusimport` module provides the functionality for importing
|
||||
SongShow Plus songs into the OpenLP database.
|
||||
"""
|
||||
import os
|
||||
import logging
|
||||
@ -204,7 +204,7 @@ class SongShowPlusImport(SongImport):
|
||||
elif verse_type == "pre-chorus":
|
||||
verse_tag = VerseType.Tags[VerseType.PreChorus]
|
||||
else:
|
||||
if not self.otherList.has_key(verse_name):
|
||||
if verse_name not in self.otherList:
|
||||
if ignore_unique:
|
||||
return None
|
||||
self.otherCount = self.otherCount + 1
|
||||
|
@ -75,6 +75,7 @@ def upgrade_1(session, metadata, tables):
|
||||
ForeignKeyConstraint([u'song_id'], [u'songs.id'],
|
||||
table=tables[u'media_files']).create()
|
||||
|
||||
|
||||
def upgrade_2(session, metadata, tables):
|
||||
"""
|
||||
Version 2 upgrade.
|
||||
@ -86,6 +87,7 @@ def upgrade_2(session, metadata, tables):
|
||||
Column(u'last_modified', types.DateTime(), default=func.now())\
|
||||
.create(table=tables[u'songs'])
|
||||
|
||||
|
||||
def upgrade_3(session, metadata, tables):
|
||||
"""
|
||||
Version 3 upgrade.
|
||||
|
@ -71,7 +71,7 @@ class WowImport(SongImport):
|
||||
* ``SOH`` (0x01) - Chorus
|
||||
* ``STX`` (0x02) - Bridge
|
||||
|
||||
Blocks are seperated by two bytes. The first byte is 0x01, and the
|
||||
Blocks are separated by two bytes. The first byte is 0x01, and the
|
||||
second byte is 0x80.
|
||||
|
||||
Lines:
|
||||
@ -126,7 +126,7 @@ class WowImport(SongImport):
|
||||
('Invalid Words of Worship song file. Missing '
|
||||
'"CSongDoc::CBlock" string.'))))
|
||||
continue
|
||||
# Seek to the beging of the first block
|
||||
# Seek to the beginning of the first block
|
||||
song_data.seek(82)
|
||||
for block in range(no_of_blocks):
|
||||
self.linesToRead = ord(song_data.read(4)[:1])
|
||||
@ -140,7 +140,7 @@ class WowImport(SongImport):
|
||||
block_text += self.lineText
|
||||
self.linesToRead -= 1
|
||||
block_type = BLOCK_TYPES[ord(song_data.read(4)[:1])]
|
||||
# Blocks are seperated by 2 bytes, skip them, but not if
|
||||
# Blocks are separated by 2 bytes, skip them, but not if
|
||||
# this is the last block!
|
||||
if block + 1 < no_of_blocks:
|
||||
song_data.seek(2, os.SEEK_CUR)
|
||||
|
@ -33,7 +33,7 @@ The basic XML for storing the lyrics in the song database looks like this::
|
||||
<song version="1.0">
|
||||
<lyrics>
|
||||
<verse type="c" label="1" lang="en">
|
||||
<![CDATA[Chorus virtual slide 1[---]Chorus virtual slide 2]]>
|
||||
<![CDATA[Chorus optional split 1[---]Chorus optional split 2]]>
|
||||
</verse>
|
||||
</lyrics>
|
||||
</song>
|
||||
@ -135,7 +135,7 @@ class SongXML(object):
|
||||
The returned list has the following format::
|
||||
|
||||
[[{'type': 'v', 'label': '1'},
|
||||
u"virtual slide 1[---]virtual slide 2"],
|
||||
u"optional slide split 1[---]optional slide split 2"],
|
||||
[{'lang': 'en', 'type': 'c', 'label': '1'}, u"English chorus"]]
|
||||
"""
|
||||
self.song_xml = None
|
||||
@ -317,9 +317,7 @@ class OpenLyrics(object):
|
||||
tags_element = None
|
||||
match = re.search(u'\{/?\w+\}', song.lyrics, re.UNICODE)
|
||||
if match:
|
||||
# Reset available tags.
|
||||
FormattingTags.reset_html_tags()
|
||||
# Named 'formatting' - 'format' is built-in fuction in Python.
|
||||
# Named 'format_' - 'format' is built-in fuction in Python.
|
||||
format_ = etree.SubElement(song_xml, u'format')
|
||||
tags_element = etree.SubElement(format_, u'tags')
|
||||
tags_element.set(u'application', u'OpenLP')
|
||||
@ -334,18 +332,59 @@ class OpenLyrics(object):
|
||||
self._add_text_to_element(u'verse', lyrics, None, verse_def)
|
||||
if u'lang' in verse[0]:
|
||||
verse_element.set(u'lang', verse[0][u'lang'])
|
||||
# Create a list with all "virtual" verses.
|
||||
virtual_verses = cgi.escape(verse[1])
|
||||
virtual_verses = virtual_verses.split(u'[---]')
|
||||
for index, virtual_verse in enumerate(virtual_verses):
|
||||
# Create a list with all "optional" verses.
|
||||
optional_verses = cgi.escape(verse[1])
|
||||
optional_verses = optional_verses.split(u'\n[---]\n')
|
||||
start_tags = u''
|
||||
end_tags = u''
|
||||
for index, optional_verse in enumerate(optional_verses):
|
||||
# Fix up missing end and start tags such as {r} or {/r}.
|
||||
optional_verse = start_tags + optional_verse
|
||||
start_tags, end_tags = self._get_missing_tags(optional_verse)
|
||||
optional_verse += end_tags
|
||||
# Add formatting tags to text
|
||||
lines_element = self._add_text_with_tags_to_lines(verse_element,
|
||||
virtual_verse, tags_element)
|
||||
optional_verse, tags_element)
|
||||
# Do not add the break attribute to the last lines element.
|
||||
if index < len(virtual_verses) - 1:
|
||||
if index < len(optional_verses) - 1:
|
||||
lines_element.set(u'break', u'optional')
|
||||
return self._extract_xml(song_xml)
|
||||
|
||||
def _get_missing_tags(self, text):
|
||||
"""
|
||||
Tests the given text for not closed formatting tags and returns a tuple
|
||||
consisting of two unicode strings::
|
||||
|
||||
(u'{st}{r}', u'{/r}{/st}')
|
||||
|
||||
The first unicode string are the start tags (for the next slide). The
|
||||
second unicode string are the end tags.
|
||||
|
||||
``text``
|
||||
The text to test. The text must **not** contain html tags, only
|
||||
OpenLP formatting tags are allowed::
|
||||
|
||||
{st}{r}Text text text
|
||||
"""
|
||||
tags = []
|
||||
for tag in FormattingTags.get_html_tags():
|
||||
if tag[u'start tag'] == u'{br}':
|
||||
continue
|
||||
if text.count(tag[u'start tag']) != text.count(tag[u'end tag']):
|
||||
tags.append((text.find(tag[u'start tag']),
|
||||
tag[u'start tag'], tag[u'end tag']))
|
||||
# Sort the lists, so that the tags which were opened first on the first
|
||||
# slide (the text we are checking) will be opened first on the next
|
||||
# slide as well.
|
||||
tags.sort(key=lambda tag: tag[0])
|
||||
end_tags = []
|
||||
start_tags = []
|
||||
for tag in tags:
|
||||
start_tags.append(tag[1])
|
||||
end_tags.append(tag[2])
|
||||
end_tags.reverse()
|
||||
return u''.join(start_tags), u''.join(end_tags)
|
||||
|
||||
def xml_to_song(self, xml, parse_and_temporary_save=False):
|
||||
"""
|
||||
Create and save a song from OpenLyrics format xml to the database. Since
|
||||
@ -521,9 +560,9 @@ class OpenLyrics(object):
|
||||
if hasattr(properties, u'comments'):
|
||||
comments_list = []
|
||||
for comment in properties.comments.comment:
|
||||
commenttext = self._text(comment)
|
||||
if commenttext:
|
||||
comments_list.append(commenttext)
|
||||
comment_text = self._text(comment)
|
||||
if comment_text:
|
||||
comments_list.append(comment_text)
|
||||
song.comments = u'\n'.join(comments_list)
|
||||
|
||||
def _process_copyright(self, properties, song):
|
||||
@ -572,7 +611,8 @@ class OpenLyrics(object):
|
||||
for tag in FormattingTags.get_html_tags()]
|
||||
new_tags = [tag for tag in found_tags
|
||||
if tag[u'start tag'] not in existing_tag_ids]
|
||||
FormattingTags.add_html_tags(new_tags, True)
|
||||
FormattingTags.add_html_tags(new_tags)
|
||||
FormattingTags.save_html_tags()
|
||||
|
||||
def _process_lines_mixed_content(self, element, newlines=True):
|
||||
"""
|
||||
@ -611,7 +651,7 @@ class OpenLyrics(object):
|
||||
text += u'{%s}' % element.get(u'name')
|
||||
# Some formattings may have only start tag.
|
||||
# Handle this case if element has no children and contains no text.
|
||||
if len(element) == 0 and not element.text:
|
||||
if not element and not element.text:
|
||||
use_endtag = False
|
||||
# Append text from element.
|
||||
if element.text:
|
||||
@ -746,13 +786,13 @@ class OpenLyrics(object):
|
||||
song.song_number = u''
|
||||
if hasattr(properties, u'songbooks'):
|
||||
for songbook in properties.songbooks.songbook:
|
||||
bookname = songbook.get(u'name', u'')
|
||||
if bookname:
|
||||
book_name = songbook.get(u'name', u'')
|
||||
if book_name:
|
||||
book = self.manager.get_object_filtered(Book,
|
||||
Book.name == bookname)
|
||||
Book.name == book_name)
|
||||
if book is None:
|
||||
# We need to create a book, because it does not exist.
|
||||
book = Book.populate(name=bookname, publisher=u'')
|
||||
book = Book.populate(name=book_name, publisher=u'')
|
||||
self.manager.save_object(book)
|
||||
song.song_book_id = book.id
|
||||
song.song_number = songbook.get(u'entry', u'')
|
||||
@ -787,14 +827,14 @@ class OpenLyrics(object):
|
||||
The song object.
|
||||
"""
|
||||
if hasattr(properties, u'themes'):
|
||||
for topictext in properties.themes.theme:
|
||||
topictext = self._text(topictext)
|
||||
if topictext:
|
||||
for topic_text in properties.themes.theme:
|
||||
topic_text = self._text(topic_text)
|
||||
if topic_text:
|
||||
topic = self.manager.get_object_filtered(Topic,
|
||||
Topic.name == topictext)
|
||||
Topic.name == topic_text)
|
||||
if topic is None:
|
||||
# We need to create a topic, because it does not exist.
|
||||
topic = Topic.populate(name=topictext)
|
||||
topic = Topic.populate(name=topic_text)
|
||||
self.manager.save_object(topic)
|
||||
song.topics.append(topic)
|
||||
|
||||
@ -813,6 +853,7 @@ class OpenLyricsError(Exception):
|
||||
VerseError = 2
|
||||
|
||||
def __init__(self, type, log_message, display_message):
|
||||
Exception.__init__(self)
|
||||
self.type = type
|
||||
self.log_message = log_message
|
||||
self.display_message = display_message
|
||||
|
@ -61,8 +61,8 @@ class SongsPlugin(Plugin):
|
||||
Plugin.__init__(self, u'songs', plugin_helpers, SongMediaItem, SongsTab)
|
||||
self.manager = Manager(u'songs', init_schema, upgrade_mod=upgrade)
|
||||
self.weight = -10
|
||||
self.icon_path = u':/plugins/plugin_songs.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
self.iconPath = u':/plugins/plugin_songs.png'
|
||||
self.icon = build_icon(self.iconPath)
|
||||
|
||||
def checkPreConditions(self):
|
||||
return self.manager.session is not None
|
||||
@ -144,7 +144,7 @@ class SongsPlugin(Plugin):
|
||||
return
|
||||
progressDialog = QtGui.QProgressDialog(
|
||||
translate('SongsPlugin', 'Reindexing songs...'), UiStrings().Cancel,
|
||||
0, maxSongs, self.formparent)
|
||||
0, maxSongs, self.formParent)
|
||||
progressDialog.setWindowModality(QtCore.Qt.WindowModal)
|
||||
songs = self.manager.get_all_objects(Song)
|
||||
for number, song in enumerate(songs):
|
||||
@ -239,9 +239,9 @@ class SongsPlugin(Plugin):
|
||||
for sfile in os.listdir(db_dir):
|
||||
if sfile.startswith(u'songs_') and sfile.endswith(u'.sqlite'):
|
||||
song_dbs.append(os.path.join(db_dir, sfile))
|
||||
if len(song_dbs) == 0:
|
||||
if not song_dbs:
|
||||
return
|
||||
progress = QtGui.QProgressDialog(self.formparent)
|
||||
progress = QtGui.QProgressDialog(self.formParent)
|
||||
progress.setWindowModality(QtCore.Qt.WindowModal)
|
||||
progress.setLabelText(translate('OpenLP.Ui', 'Starting import...'))
|
||||
progress.setCancelButton(None)
|
||||
|
@ -98,13 +98,13 @@ class SongUsagePlugin(Plugin):
|
||||
self.songUsageMenu.addAction(self.songUsageReport)
|
||||
self.songUsageMenu.addAction(self.songUsageDelete)
|
||||
self.songUsageActiveButton = QtGui.QToolButton(
|
||||
self.formparent.statusBar)
|
||||
self.formParent.statusBar)
|
||||
self.songUsageActiveButton.setCheckable(True)
|
||||
self.songUsageActiveButton.setAutoRaise(True)
|
||||
self.songUsageActiveButton.setStatusTip(translate('SongUsagePlugin',
|
||||
'Toggle the tracking of song usage.'))
|
||||
self.songUsageActiveButton.setObjectName(u'songUsageActiveButton')
|
||||
self.formparent.statusBar.insertPermanentWidget(1,
|
||||
self.formParent.statusBar.insertPermanentWidget(1,
|
||||
self.songUsageActiveButton)
|
||||
self.songUsageActiveButton.hide()
|
||||
# Signals and slots
|
||||
@ -138,8 +138,8 @@ class SongUsagePlugin(Plugin):
|
||||
action_list.add_action(self.songUsageReport,
|
||||
unicode(translate('SongUsagePlugin', 'Song Usage')))
|
||||
self.songUsageDeleteForm = SongUsageDeleteForm(self.manager,
|
||||
self.formparent)
|
||||
self.songUsageDetailForm = SongUsageDetailForm(self, self.formparent)
|
||||
self.formParent)
|
||||
self.songUsageDetailForm = SongUsageDetailForm(self, self.formParent)
|
||||
self.songUsageMenu.menuAction().setVisible(True)
|
||||
self.songUsageActiveButton.show()
|
||||
|
||||
|
@ -1,125 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!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>
|
||||
<string>%(openlp_version)s</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>%(openlp_version)s</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>%(openlp_appname)s</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>openlp-logo-with-text.icns</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>MacOS/openlp</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>%(openlp_appname)s</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>%(openlp_appname)s %(openlp_version)s</string>
|
||||
<key>LSHasLocalizedDisplayName</key>
|
||||
<false/>
|
||||
<key>NSAppleScriptEnabled</key>
|
||||
<false/>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>LSBackgroundOnly</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
@ -1,28 +0,0 @@
|
||||
all:
|
||||
python build.py -c openlp.cfg
|
||||
|
||||
view:
|
||||
python build.py -c openlp.cfg --package-view --compress-view
|
||||
|
||||
package:
|
||||
|
||||
python build.py -c openlp.cfg --package --package-view
|
||||
|
||||
bundle:
|
||||
|
||||
python build.py -c openlp.cfg --compress --compress-view
|
||||
|
||||
clean:
|
||||
# remove old configuration files
|
||||
rm -f openlp.spec
|
||||
rm -f Info.plist
|
||||
rm -f .version
|
||||
|
||||
# remove old build artifacts
|
||||
rm -rf build
|
||||
rm -rf dist
|
||||
rm -rf Macopenlp.app
|
||||
rm -rf OpenLP.app
|
||||
rm -f warnopenlp.txt
|
||||
rm -f *dmg
|
||||
|
@ -1,74 +0,0 @@
|
||||
on saveImageWithItselfAsIcon(icon_image_file)
|
||||
-- save icon_image_file with itself as icon
|
||||
set icon_image_file_string to icon_image_file as string
|
||||
tell application "Image Events"
|
||||
launch
|
||||
set icon_image to open file icon_image_file_string
|
||||
save icon_image with icon
|
||||
close icon_image
|
||||
end tell
|
||||
end saveImageWithItselfAsIcon
|
||||
|
||||
on copyIconOfTo(aFileOrFolderWithIcon, aFileOrFolder)
|
||||
tell application "Finder" to set f to aFileOrFolderWithIcon as alias
|
||||
-- grab the file's icon
|
||||
my CopyOrPaste(f, "c")
|
||||
-- now the icon is in the clipboard
|
||||
tell application "Finder" to set c to aFileOrFolder as alias
|
||||
my CopyOrPaste(result, "v")
|
||||
end copyIconOfTo
|
||||
|
||||
on CopyOrPaste(i, cv)
|
||||
tell application "Finder"
|
||||
activate
|
||||
open information window of i
|
||||
end tell
|
||||
tell application "System Events" to tell process "Finder" to tell window 1
|
||||
keystroke tab -- select icon button
|
||||
keystroke (cv & "w") using command down (* (copy or paste) + close window *)
|
||||
end tell -- window 1 then process Finder then System Events
|
||||
end CopyOrPaste
|
||||
|
||||
on run
|
||||
set icon_image_file to POSIX file "%s" as alias
|
||||
set dmg_file to POSIX file "/Volumes/%s" as alias
|
||||
|
||||
my saveImageWithItselfAsIcon(icon_image_file)
|
||||
-- wait for virus scanner
|
||||
delay 2
|
||||
my copyIconOfTo(icon_image_file, dmg_file)
|
||||
|
||||
tell application "Finder"
|
||||
tell disk "%s"
|
||||
open
|
||||
set current view of container window to icon view
|
||||
set toolbar visible of container window to false
|
||||
set statusbar visible of container window to false
|
||||
set the bounds of container window to {400, 100, 1100, 500}
|
||||
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 ".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 5
|
||||
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 ".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}
|
||||
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}
|
||||
end if
|
||||
open
|
||||
close
|
||||
update without registering applications
|
||||
-- wait until the virus scan completes
|
||||
delay 5
|
||||
-- eject
|
||||
end tell
|
||||
end tell
|
||||
end run
|
@ -1,77 +0,0 @@
|
||||
on saveImageWithItselfAsIcon(icon_image_file)
|
||||
-- save icon_image_file with itself as icon
|
||||
set icon_image_file_string to icon_image_file as string
|
||||
tell application "Image Events"
|
||||
launch
|
||||
set icon_image to open file icon_image_file_string
|
||||
save icon_image with icon
|
||||
close icon_image
|
||||
end tell
|
||||
end saveImageWithItselfAsIcon
|
||||
|
||||
on copyIconOfTo(aFileOrFolderWithIcon, aFileOrFolder)
|
||||
tell application "Finder" to set f to aFileOrFolderWithIcon as alias
|
||||
-- grab the file's icon
|
||||
my CopyOrPaste(f, "c")
|
||||
-- now the icon is in the clipboard
|
||||
tell application "Finder" to set c to aFileOrFolder as alias
|
||||
my CopyOrPaste(result, "v")
|
||||
end copyIconOfTo
|
||||
|
||||
on CopyOrPaste(i, cv)
|
||||
tell application "Finder"
|
||||
activate
|
||||
set infoWindow to open information window of i
|
||||
set infoWindowName to name of infoWindow
|
||||
end tell
|
||||
tell application "System Events" to tell process "Finder" to tell window infoWindowName
|
||||
keystroke tab -- select icon button
|
||||
keystroke (cv & "w") using command down (* (copy or paste) + close window *)
|
||||
end tell -- window 1 then process Finder then System Events
|
||||
end CopyOrPaste
|
||||
|
||||
on run
|
||||
set icon_image_file to POSIX file "%s" as alias
|
||||
set dmg_file to POSIX file "/Volumes/%s" as alias
|
||||
|
||||
my saveImageWithItselfAsIcon(icon_image_file)
|
||||
-- wait for virus scanner
|
||||
delay 2
|
||||
my copyIconOfTo(icon_image_file, dmg_file)
|
||||
|
||||
tell application "Finder"
|
||||
tell disk "%s"
|
||||
open
|
||||
set current view of container window to icon view
|
||||
set toolbar visible of container window to false
|
||||
set statusbar visible of container window to false
|
||||
set the bounds of container window to {400, 100, 1100, 500}
|
||||
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 ".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 ".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}
|
||||
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}
|
||||
end if
|
||||
open
|
||||
close
|
||||
update without registering applications
|
||||
-- wait until the virus scan completes
|
||||
delay 5
|
||||
-- eject
|
||||
end tell
|
||||
end tell
|
||||
end run
|
@ -1,40 +0,0 @@
|
||||
on saveImageWithItselfAsIcon(icon_image_file)
|
||||
-- save icon_image_file with itself as icon
|
||||
set icon_image_file_string to icon_image_file as string
|
||||
tell application "Image Events"
|
||||
launch
|
||||
set icon_image to open file icon_image_file_string
|
||||
save icon_image with icon
|
||||
close icon_image
|
||||
end tell
|
||||
end saveImageWithItselfAsIcon
|
||||
|
||||
on copyIconOfTo(aFileOrFolderWithIcon, aFileOrFolder)
|
||||
tell application "Finder" to set f to aFileOrFolderWithIcon as alias
|
||||
-- grab the file's icon
|
||||
my CopyOrPaste(f, "c")
|
||||
-- now the icon is in the clipboard
|
||||
tell application "Finder" to set c to aFileOrFolder as alias
|
||||
my CopyOrPaste(result, "v")
|
||||
end copyIconOfTo
|
||||
|
||||
on CopyOrPaste(i, cv)
|
||||
tell application "Finder"
|
||||
activate
|
||||
open information window of i
|
||||
end tell
|
||||
tell application "System Events" to tell process "Finder" to tell window 1
|
||||
keystroke tab -- select icon button
|
||||
keystroke (cv & "w") using command down (* (copy or paste) + close window *)
|
||||
end tell -- window 1 then process Finder then System Events
|
||||
end CopyOrPaste
|
||||
|
||||
on run
|
||||
set icon_image_file to POSIX file "%s" as alias
|
||||
set dmg_file to POSIX file "%s" as alias
|
||||
|
||||
my saveImageWithItselfAsIcon(icon_image_file)
|
||||
-- wait for virus scanner
|
||||
delay 2
|
||||
my copyIconOfTo(icon_image_file, dmg_file)
|
||||
end run
|
@ -1,41 +0,0 @@
|
||||
on saveImageWithItselfAsIcon(icon_image_file)
|
||||
-- save icon_image_file with itself as icon
|
||||
set icon_image_file_string to icon_image_file as string
|
||||
tell application "Image Events"
|
||||
launch
|
||||
set icon_image to open file icon_image_file_string
|
||||
save icon_image with icon
|
||||
close icon_image
|
||||
end tell
|
||||
end saveImageWithItselfAsIcon
|
||||
|
||||
on copyIconOfTo(aFileOrFolderWithIcon, aFileOrFolder)
|
||||
tell application "Finder" to set f to aFileOrFolderWithIcon as alias
|
||||
-- grab the file's icon
|
||||
my CopyOrPaste(f, "c")
|
||||
-- now the icon is in the clipboard
|
||||
tell application "Finder" to set c to aFileOrFolder as alias
|
||||
my CopyOrPaste(result, "v")
|
||||
end copyIconOfTo
|
||||
|
||||
on CopyOrPaste(i, cv)
|
||||
tell application "Finder"
|
||||
activate
|
||||
set infoWindow to open information window of i
|
||||
set infoWindowName to name of infoWindow
|
||||
end tell
|
||||
tell application "System Events" to tell process "Finder" to tell window infoWindowName
|
||||
keystroke tab -- select icon button
|
||||
keystroke (cv & "w") using command down (* (copy or paste) + close window *)
|
||||
end tell -- window 1 then process Finder then System Events
|
||||
end CopyOrPaste
|
||||
|
||||
on run
|
||||
set icon_image_file to POSIX file "%s" as alias
|
||||
set dmg_file to POSIX file "%s" as alias
|
||||
|
||||
my saveImageWithItselfAsIcon(icon_image_file)
|
||||
-- wait for virus scanner
|
||||
delay 2
|
||||
my copyIconOfTo(icon_image_file, dmg_file)
|
||||
end run
|
@ -1,426 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2011 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
|
||||
# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, #
|
||||
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
|
||||
# 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 #
|
||||
###############################################################################
|
||||
|
||||
"""
|
||||
Mac OS X Build Script
|
||||
---------------------
|
||||
|
||||
This script is used to build the OS X binary and the accompanying installer.
|
||||
For this script to work out of the box, it depends on a number of things:
|
||||
|
||||
Python 2.6/2.7
|
||||
This build script only works with Python 2.6/2.7
|
||||
|
||||
PyQt4
|
||||
You should already have this installed, OpenLP doesn't work without it.
|
||||
The version the script expects is the packaged one available from River
|
||||
Bank Computing.
|
||||
|
||||
PyInstaller
|
||||
PyInstaller should be a checkout of revision 1355 of trunk, and in a
|
||||
directory which is configured in the openlp.cfg. The revision is very
|
||||
important as there is just included a fix for builds on OS X.
|
||||
|
||||
To install PyInstaller, first checkout trunk from Subversion. The
|
||||
easiest way is to do a
|
||||
|
||||
svn co 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.
|
||||
|
||||
openlp.cfg
|
||||
The configuration file contains settings of the version string to include
|
||||
in the bundle as well as directory and file settings for different
|
||||
purposes (e.g. PyInstaller location or installer background image)
|
||||
|
||||
To start the build process do a
|
||||
|
||||
make
|
||||
|
||||
inside the resources/osx directory. The result should be a {openlp_dmgname}.dmg
|
||||
file in the same directory. If something went wrong - this sometimes happen
|
||||
with the graphical commands in the Apple script - do a
|
||||
|
||||
make clean
|
||||
|
||||
and start the build process again. If you want to execute only parts of the
|
||||
build process you can specify different make targets
|
||||
|
||||
make view -- runs the Apple scripts to set the icons
|
||||
make package -- creates the dmg file and copies the application files
|
||||
make bundle -- compresses the dmg file and sets the dmg file icon
|
||||
"""
|
||||
|
||||
import os
|
||||
import ConfigParser
|
||||
import logging
|
||||
import optparse
|
||||
import sys
|
||||
import glob
|
||||
import platform
|
||||
import re
|
||||
import subprocess as subp
|
||||
|
||||
from shutil import copy
|
||||
|
||||
# set the script name
|
||||
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('arch -i386 %s %s/pyinstaller.py openlp.spec' \
|
||||
% ( sys.executable,
|
||||
settings['pyinstaller_basedir']) )
|
||||
if (result != 0):
|
||||
logging.error('[%s] The pyinstaller build reported an error, cannot \
|
||||
continue!', script_name)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
dist_folder = os.getcwd() + '/dist/' + app_name_lower
|
||||
|
||||
logging.info('[%s] copying the new plugins...', script_name)
|
||||
result = os.system('cp -R %(openlp_directory)s/openlp/plugins \
|
||||
%(application_directory)s/Contents/MacOS' \
|
||||
% { 'openlp_directory' : settings['openlp_basedir'],
|
||||
'application_directory' : app_dir })
|
||||
if (result != 0):
|
||||
logging.error('[%s] could not copy plugins, dmg creation failed!',
|
||||
script_name)
|
||||
sys.exit(1)
|
||||
|
||||
logging.info('[%s] removing the presentations plugin...', script_name)
|
||||
result = os.system('rm -rf \
|
||||
%(application_directory)s/Contents/MacOS/plugins/presentations' \
|
||||
% { 'application_directory' : app_dir })
|
||||
if (result != 0):
|
||||
logging.error('[%s] could not remove presentations plugins, dmg \
|
||||
creation failed!', script_name)
|
||||
sys.exit(1)
|
||||
|
||||
logging.info('[%s] copying the icons to the resource directory...',
|
||||
script_name)
|
||||
result = os.system('cp %(icon_file)s \
|
||||
%(application_directory)s/Contents/Resources' \
|
||||
% { 'icon_file' : settings['openlp_icon_file'],
|
||||
'application_directory' : app_dir })
|
||||
if (result != 0):
|
||||
logging.error('[%s] could not copy the icon, dmg creation failed!',
|
||||
script_name)
|
||||
sys.exit(1)
|
||||
|
||||
logging.info('[%s] copying the version file...', script_name)
|
||||
result = os.system('CpMac %s/.version %s/Contents/MacOS' % (os.getcwd(),
|
||||
app_dir))
|
||||
if (result != 0):
|
||||
logging.error('[%s] could not copy the version file, dmg creation \
|
||||
failed!', script_name)
|
||||
sys.exit(1)
|
||||
|
||||
logging.info('[%s] copying the new Info.plist...', script_name)
|
||||
result = os.system('cp %(target_directory)s/Info.plist \
|
||||
%(application_directory)s/Contents' \
|
||||
% { 'target_directory' : os.getcwd(),
|
||||
'application_directory' : app_dir })
|
||||
if (result != 0):
|
||||
logging.error('[%s] could not copy the info file, dmg creation \
|
||||
failed!', script_name)
|
||||
sys.exit(1)
|
||||
|
||||
logging.info('[%s] copying the translations...', script_name)
|
||||
os.makedirs(app_dir + '/Contents/MacOS/i18n')
|
||||
for ts_file in glob.glob(os.path.join(settings['openlp_basedir']
|
||||
+ '/resources/i18n/', '*ts')):
|
||||
result = os.system('lconvert -i %(ts_file)s \
|
||||
-o %(target_directory)s/Contents/MacOS/i18n/%(base)s.qm' \
|
||||
% { 'ts_file' : ts_file, 'target_directory' : app_dir,
|
||||
'base': os.path.splitext(os.path.basename(ts_file))[0] })
|
||||
if (result != 0):
|
||||
logging.error('[%s] could not copy the translations, dmg \
|
||||
creation failed!', script_name)
|
||||
sys.exit(1)
|
||||
|
||||
# Backported from windows build script.
|
||||
logging.info('[%s] copying the media player...', script_name)
|
||||
os.makedirs(os.path.join(app_dir, 'Contents/MacOS/core/ui/media'))
|
||||
source = os.path.join(settings['openlp_basedir'], u'openlp', u'core', u'ui', u'media')
|
||||
dest = os.path.join(app_dir, u'Contents/MacOS/core/ui/media')
|
||||
for root, dirs, files in os.walk(source):
|
||||
for filename in files:
|
||||
print filename
|
||||
if not filename.endswith(u'.pyc'):
|
||||
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 create_dmg(settings):
|
||||
logging.info('[%s] creating the dmg...', script_name)
|
||||
dmg_file = os.getcwd() + '/' + settings['openlp_dmgname'] + '.dmg'
|
||||
result = os.system('hdiutil create %(dmg_file)s~ -ov -megabytes \
|
||||
%(vol_size)s -fs HFS+ -volname %(vol_name)s' \
|
||||
% { 'dmg_file' : dmg_file,
|
||||
'vol_size' : '250',
|
||||
'vol_name' : settings['openlp_appname'] })
|
||||
if (result != 0):
|
||||
logging.error('[%s] could not create dmg file!', script_name)
|
||||
sys.exit(1)
|
||||
|
||||
logging.info('[%s] mounting the dmg file...', script_name)
|
||||
output = subp.Popen(["hdiutil", "attach", dmg_file + "~.dmg"],
|
||||
stdout=subp.PIPE).communicate()[0]
|
||||
logging.debug(output)
|
||||
|
||||
p = re.compile('Apple_HFS\s+(.+?)\s*$')
|
||||
result = p.search(output, re.M)
|
||||
volume_basedir = ''
|
||||
if result:
|
||||
volume_basedir = result.group(1)
|
||||
else:
|
||||
logging.error('could not mount dmg file, cannot continue!')
|
||||
sys.exit(1)
|
||||
|
||||
logging.info('[%s] copying the app (from %s) to the dmg (at %s)...',
|
||||
script_name, app_dir, volume_basedir)
|
||||
result = os.system('CpMac -r %s %s' \
|
||||
% ( app_dir, volume_basedir ))
|
||||
if (result != 0):
|
||||
logging.error('[%s] could not copy application, dmg creation failed!',
|
||||
script_name)
|
||||
sys.exit(1)
|
||||
|
||||
logging.info('[%s] copying the background image...', script_name)
|
||||
os.mkdir(volume_basedir + '/.background')
|
||||
result = os.system('CpMac %s %s'
|
||||
% (settings['installer_backgroundimage_file'],
|
||||
volume_basedir + '/.background/installer-background.png'))
|
||||
if (result != 0):
|
||||
logging.error('[%s] could not copy the background image, dmg creation\
|
||||
failed!', script_name)
|
||||
sys.exit(1)
|
||||
|
||||
return (volume_basedir, dmg_file)
|
||||
|
||||
def unmount_dmg(settings, volume_basedir):
|
||||
logging.info('[%s] unmounting the dmg...', script_name)
|
||||
result = os.system('hdiutil detach %s' % volume_basedir)
|
||||
if (result != 0):
|
||||
logging.error('[%s] could not unmount the dmg file, dmg creation \
|
||||
failed!', script_name)
|
||||
sys.exit(1)
|
||||
|
||||
def compress_view(settings, seticon_scriptname, dmg_file):
|
||||
logging.info('[%s] setting icon of the dmg file...', script_name)
|
||||
try:
|
||||
f = open(seticon_scriptname)
|
||||
p = subp.Popen(["osascript"], stdin=subp.PIPE)
|
||||
p.communicate(f.read() % ((os.getcwd() + '/' +
|
||||
settings['openlp_dmg_icon_file']), dmg_file))
|
||||
f.close()
|
||||
result = p.returncode
|
||||
if (result != 0):
|
||||
logging.error('[%s] could not set the icon to the dmg file, \
|
||||
dmg creation failed!', script_name)
|
||||
sys.exit(1)
|
||||
except IOError, e:
|
||||
logging.error('[%s] could not adjust the view (%s), dmg creation \
|
||||
failed!', script_name, e)
|
||||
sys.exit(1)
|
||||
except OSError, e:
|
||||
logging.error('[%s] could not set the icon to the dmg file(%s), \
|
||||
dmg creation failed!', script_name, e)
|
||||
sys.exit(1)
|
||||
|
||||
def adjust_package_view(settings, adjustview_scriptname):
|
||||
logging.info('[%s] making adjustments to the view...', script_name)
|
||||
try:
|
||||
f = open(adjustview_scriptname)
|
||||
p = subp.Popen(["osascript"], stdin=subp.PIPE)
|
||||
p.communicate(f.read() % ((os.getcwd() + '/' + \
|
||||
settings['openlp_dmg_icon_file']),
|
||||
settings['openlp_appname'],
|
||||
settings['openlp_appname'],
|
||||
settings['openlp_appname']))
|
||||
f.close()
|
||||
result = p.returncode
|
||||
if (result != 0):
|
||||
logging.error('[%s] could not adjust the view, dmg creation \
|
||||
failed!', script_name)
|
||||
sys.exit(1)
|
||||
except IOError, e:
|
||||
logging.error('[%s] could not adjust the view (%s), dmg creation \
|
||||
failed!', script_name, e)
|
||||
sys.exit(1)
|
||||
except OSError, e:
|
||||
logging.error('[%s] could not adjust the view (%s), dmg creation \
|
||||
failed!', script_name, e)
|
||||
sys.exit(1)
|
||||
|
||||
def compress_dmg(settings):
|
||||
logging.info('[%s] compress the dmg file...', script_name)
|
||||
result = os.system('hdiutil convert %s~.dmg -format UDZO \
|
||||
-imagekey zlib-level=9 -o %s' \
|
||||
% (dmg_file, dmg_file))
|
||||
if (result != 0):
|
||||
logging.error('[%s] could not compress the dmg file, dmg creation \
|
||||
failed!', script_name)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
# set default actions
|
||||
do_build = True
|
||||
do_compress_view = True
|
||||
do_package_view = True
|
||||
do_create_dmg = True
|
||||
do_compress_dmg = True
|
||||
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option('-c', '--config', dest='config', help='config file',
|
||||
metavar='CONFIG')
|
||||
parser.add_option('-v', '--package-view', dest='package_view',
|
||||
help='triggers view adjustment scripts for package',
|
||||
metavar='PACKAGEVIEWONLY', action='store_true', default=False)
|
||||
parser.add_option('-y', '--compress-view', dest='compress_view',
|
||||
help='triggers view adjustment scripts for dmg',
|
||||
metavar='COMPRESSVIEWONLY', action='store_true', default=False)
|
||||
parser.add_option('-p', '--package', dest='package',
|
||||
help='package application folder to dmg', metavar='PACKAGE',
|
||||
action='store_true', default=False)
|
||||
parser.add_option('-z', '--compress', dest='compress',
|
||||
help='compresses the existing dmg', metavar='COMPRESS',
|
||||
action='store_true', default=False)
|
||||
parser.add_option('-b', '--basedir', dest='basedir',
|
||||
help='volume basedir like /Volumes/OpenLP', metavar='BASEDIR',
|
||||
default='/Volumes/OpenLP')
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
# if an option is set, false all
|
||||
if (options.package_view is True or options.compress_view is True
|
||||
or options.package is True or options.compress is True):
|
||||
do_build = False
|
||||
do_package_view = options.package_view
|
||||
do_compress_view = options.compress_view
|
||||
do_create_dmg = options.package
|
||||
do_compress_dmg = options.compress
|
||||
|
||||
if not options.config:
|
||||
parser.error('option --config|-c is required')
|
||||
|
||||
logHandler = logging.StreamHandler()
|
||||
logHandler.setFormatter(logging.Formatter(
|
||||
'%(asctime)s %(levelname)-8s %(message)s',
|
||||
'%a, %d %b %Y %H:%M:%S'))
|
||||
logging.getLogger().addHandler(logHandler)
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
|
||||
config = ConfigParser.RawConfigParser()
|
||||
config.readfp(open(options.config, 'r'))
|
||||
|
||||
if not config.has_section('openlp'):
|
||||
logging.error('[%s] config file "%s" lacks an [openlp] section',
|
||||
script_name, options.config)
|
||||
sys.exit(1)
|
||||
|
||||
if not sys.platform == "darwin":
|
||||
logging.error('[%s] this script only works on Macintosh OS X systems,'
|
||||
+ 'not on %s', script_name, sys.platform)
|
||||
sys.exit(1)
|
||||
|
||||
version = platform.mac_ver()[0]
|
||||
# we only need the differenciation between leopard and snow leopard
|
||||
if version.startswith("10.6") or version.startswith("10.7"):
|
||||
SNOWLEOPARD = True
|
||||
logging.info('[%s] using snow leopard scripts (version = %s)',
|
||||
script_name, version)
|
||||
adjustview_scriptname = "applescript-adjustview-10-6.master"
|
||||
seticon_scriptname = "applescript-seticon-10-6.master"
|
||||
else:
|
||||
SNOWLEOPARD = False
|
||||
logging.info('[%s] using leopard scripts (version = %s)', script_name,
|
||||
version)
|
||||
adjustview_scriptname = "applescript-adjustview-10-5.master"
|
||||
seticon_scriptname = "applescript-seticon-10-5.master"
|
||||
|
||||
if not os.path.isfile(adjustview_scriptname) \
|
||||
or not os.path.isfile(seticon_scriptname):
|
||||
logging.error('[%s] could not find apple scripts for given OS X '
|
||||
+ 'version %s', script_name, version)
|
||||
sys.exit(1)
|
||||
|
||||
settings = dict()
|
||||
for k in config.options('openlp'):
|
||||
settings[k] = config.get('openlp', k)
|
||||
|
||||
# prepare the configuration files
|
||||
os.system('python expander.py --config %(config_file)s \
|
||||
--template openlp.spec.master \
|
||||
--expandto %(target_directory)s/openlp.spec' \
|
||||
% { 'config_file' : options.config, 'target_directory' : os.getcwd() })
|
||||
os.system('python expander.py --config %(config_file)s \
|
||||
--template Info.plist.master \
|
||||
--expandto %(target_directory)s/Info.plist' \
|
||||
% { 'config_file' : options.config, 'target_directory' : os.getcwd() })
|
||||
os.system('python get_version.py > .version')
|
||||
|
||||
# prepare variables
|
||||
app_name_lower = settings['openlp_appname'].lower()
|
||||
app_dir = os.getcwd() + '/' + settings['openlp_appname'] + '.app'
|
||||
|
||||
# if the view option is set, skip the building steps
|
||||
if (do_build is True):
|
||||
build_application(settings, app_name_lower, app_dir)
|
||||
|
||||
if (do_create_dmg is True):
|
||||
(volume_basedir, dmg_file) = create_dmg(settings)
|
||||
else:
|
||||
# setting base dir
|
||||
volume_basedir = options.basedir
|
||||
dmg_file = os.getcwd() + '/' + settings['openlp_dmgname'] + '.dmg'
|
||||
|
||||
if (do_package_view is True):
|
||||
adjust_package_view(settings, adjustview_scriptname)
|
||||
|
||||
if (do_create_dmg is True):
|
||||
unmount_dmg(settings, volume_basedir)
|
||||
|
||||
if (do_compress_dmg is True):
|
||||
compress_dmg(settings)
|
||||
|
||||
if (do_compress_view is True):
|
||||
compress_view(settings, seticon_scriptname, dmg_file)
|
||||
|
||||
if (do_compress_dmg is True):
|
||||
logging.info('[%s] finished creating dmg file, resulting file is "%s"',
|
||||
script_name, dmg_file)
|
@ -1,227 +0,0 @@
|
||||
#!/usr/bin/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, Jonathan Corwin, Michael #
|
||||
# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, #
|
||||
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
|
||||
# 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 #
|
||||
###############################################################################
|
||||
|
||||
# TODOs:
|
||||
# - defaults for non-supplied expansions:
|
||||
# template contains
|
||||
|
||||
import ConfigParser
|
||||
import logging
|
||||
import optparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
# variable expansion:
|
||||
# - %(dog)s --- normal python expansion
|
||||
# - %(dog%)s --- no python expansion, leave as is (stripping the trailing %)
|
||||
# - %(dog:cat) --- if there is an expansion for dog, dog will be used;
|
||||
# otherwise if cat exists cat will be used
|
||||
# - %(dog=cat) --- if there is an expansion for dog, dog will be used;
|
||||
# otherwise "cat" will be used
|
||||
# re_conf = re.compile(r'(?<!%)%\((?P<key>[^\(]+?)\)s')
|
||||
re_conf = re.compile(r'(?P<verbatim>%?)%\((?P<key>[^+=:&\)]+?)'
|
||||
+ '(?:(?P<kind>[+=:&])(?P<default>[^\)]+))?\)(?P<type>s|d)')
|
||||
|
||||
def expand_variable(match, expansions, errors):
|
||||
key = match.group('key')
|
||||
kind = match.group('kind')
|
||||
default = match.group('default')
|
||||
typ = match.group('type')
|
||||
verbatim = match.group('verbatim')
|
||||
|
||||
if verbatim:
|
||||
return match.group(0)[1:]
|
||||
|
||||
# literal default
|
||||
if kind == '=':
|
||||
if key in expansions:
|
||||
return expansions[key]
|
||||
return default
|
||||
|
||||
# variable default
|
||||
if kind == ':' and default in expansions:
|
||||
return expansions[default]
|
||||
|
||||
if kind == '+' and default in expansions:
|
||||
if key in expansions:
|
||||
key = expansions[key]
|
||||
if typ == 's':
|
||||
return '%s%s' % (key, expansions[default])
|
||||
if typ == 'd':
|
||||
try:
|
||||
return str(int(key) + int(expansions[default]))
|
||||
except:
|
||||
pass
|
||||
|
||||
if kind == '&' and default in expansions:
|
||||
if typ == 's':
|
||||
return '%s%s' % (key, expansions[default])
|
||||
if typ == 'd':
|
||||
try:
|
||||
return str(int(key) + int(expansions[default]))
|
||||
except:
|
||||
pass
|
||||
|
||||
if key in expansions:
|
||||
return expansions[key]
|
||||
|
||||
if not match.group(0) in errors:
|
||||
errors.append(match.group(0))
|
||||
|
||||
return None
|
||||
|
||||
options = None
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
# get config file
|
||||
parser = optparse.OptionParser()
|
||||
parser.add_option('-c', '--config', dest='config',
|
||||
help='config file', metavar='CONFIG')
|
||||
parser.add_option('-t', '--template', dest='template',
|
||||
help='template file', metavar='TEMPLATE')
|
||||
parser.add_option('-x', '--expandto', dest='expanded',
|
||||
help='expanded file', metavar='EXPANDED')
|
||||
parser.add_option('-e', '--echo', dest='echo',
|
||||
help='echo variable', metavar='ECHOVAR')
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if not options.config:
|
||||
parser.error('option --config|-c is required')
|
||||
if not os.path.exists(options.config):
|
||||
parser.error('config file "%s" does not exist' % options.config)
|
||||
if not options.echo:
|
||||
if not options.template:
|
||||
parser.error('option --template|-t is required')
|
||||
if not os.path.exists(options.template):
|
||||
parser.error('template file "%s" does not exist' \
|
||||
% options.template)
|
||||
if not options.expanded:
|
||||
parser.error('option --expandto|-e is required')
|
||||
|
||||
logHandler = logging.StreamHandler()
|
||||
logHandler.setFormatter(logging.Formatter('%(asctime)s %(levelname)-8s '
|
||||
+ ' %(message)s', '%a, %d %b %Y %H:%M:%S'))
|
||||
logging.getLogger().addHandler(logHandler)
|
||||
logging.getLogger().setLevel(logging.DEBUG)
|
||||
|
||||
config = ConfigParser.RawConfigParser()
|
||||
config.readfp(open(options.config, 'r'))
|
||||
|
||||
if not config.has_section('openlp'):
|
||||
logging.error('[expander] %s: config file "%s" lacks an [openlp] '
|
||||
+ 'section', options.template, options.config)
|
||||
|
||||
expansions = dict()
|
||||
for k in config.options('openlp'):
|
||||
expansions[k] = config.get('openlp', k)
|
||||
|
||||
# commandline overrides?
|
||||
for override in args:
|
||||
if not '=' in override:
|
||||
continue
|
||||
|
||||
(k, v) = override.split('=', 2)
|
||||
expansions[k] = v
|
||||
|
||||
if options.echo:
|
||||
if options.echo in expansions:
|
||||
print expansions[options.echo]
|
||||
sys.exit(0)
|
||||
else:
|
||||
sys.exit(1)
|
||||
|
||||
# closure to capture expansions and errors variable
|
||||
errors = []
|
||||
expanded = []
|
||||
|
||||
try:
|
||||
# try to expand the template
|
||||
line = 0
|
||||
faulty = False
|
||||
|
||||
template = open(options.template, 'r')
|
||||
raw = template.readlines()
|
||||
template.close()
|
||||
|
||||
def _expand(m):
|
||||
return expand_variable(m, expansions = expansions, errors = errors)
|
||||
|
||||
for l in raw:
|
||||
line += 1
|
||||
exp = re_conf.sub(_expand, l)
|
||||
if errors:
|
||||
for key in errors:
|
||||
logging.error('[expander] %s: line %d: could not expand '
|
||||
+ 'key "%s"', options.template, line, key)
|
||||
faulty = True
|
||||
errors = []
|
||||
else:
|
||||
expanded.append(exp)
|
||||
|
||||
if faulty:
|
||||
sys.exit(1)
|
||||
|
||||
# successfully expanded template, now backup potentially existing
|
||||
# target file
|
||||
targetFile = options.expanded % expansions
|
||||
if os.path.exists(targetFile):
|
||||
if os.path.exists('%s~' % targetFile):
|
||||
os.unlink('%s~' % targetFile)
|
||||
os.rename(options.expanded, '%s~' % targetFile)
|
||||
logging.info('[expander] %s: backed up existing target file "%s" '
|
||||
+ 'to "%s"', options.template, targetFile,
|
||||
'%s~' % options.expanded)
|
||||
|
||||
# make sure that target directory exists
|
||||
targetDir = os.path.dirname(targetFile)
|
||||
if not os.path.exists(targetDir):
|
||||
os.makedirs(targetDir)
|
||||
|
||||
# write target file
|
||||
try:
|
||||
target = open(targetFile, 'w')
|
||||
for exp in expanded:
|
||||
target.write(exp)
|
||||
target.close()
|
||||
except Exception, e:
|
||||
logging.error('[expander] %s: could not expand to "%s"',
|
||||
options.template, options.expaned, e)
|
||||
|
||||
# copy over file access mode from template
|
||||
mode = os.stat(options.template)
|
||||
os.chmod(options.expanded, mode.st_mode)
|
||||
|
||||
logging.info('[expander] expanded "%s" to "%s"',
|
||||
options.template, options.expanded)
|
||||
|
||||
except:
|
||||
pass
|
||||
|
@ -1,61 +0,0 @@
|
||||
#!/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, Jonathan Corwin, Michael #
|
||||
# Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, Armin Köhler, #
|
||||
# Andreas Preikschat, Mattias Põldaru, Christian Richter, Philip Ridout, #
|
||||
# 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 #
|
||||
###############################################################################
|
||||
import sys
|
||||
import os
|
||||
from bzrlib.branch import Branch
|
||||
|
||||
def get_version(path):
|
||||
b = Branch.open_containing(path)[0]
|
||||
b.lock_read()
|
||||
result = '0.0.0'
|
||||
try:
|
||||
# Get the branch's latest revision number.
|
||||
revno = b.revno()
|
||||
# Convert said revision number into a bzr revision id.
|
||||
revision_id = b.dotted_revno_to_revision_id((revno,))
|
||||
# Get a dict of tags, with the revision id as the key.
|
||||
tags = b.tags.get_reverse_tag_dict()
|
||||
# Check if the latest
|
||||
if revision_id in tags:
|
||||
result = tags[revision_id][0]
|
||||
else:
|
||||
result = '%s-bzr%s' % (sorted(b.tags.get_tag_dict().keys())[-1], revno)
|
||||
finally:
|
||||
b.unlock()
|
||||
return result
|
||||
|
||||
def get_path():
|
||||
if len(sys.argv) > 1:
|
||||
return os.path.abspath(sys.argv[1])
|
||||
else:
|
||||
return os.path.abspath('.')
|
||||
|
||||
if __name__ == u'__main__':
|
||||
path = get_path()
|
||||
print get_version(path)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user