forked from openlp/openlp
r1759
This commit is contained in:
commit
b776015ae9
@ -144,7 +144,7 @@ def image_to_byte(image):
|
||||
# convert to base64 encoding so does not get missed!
|
||||
return byte_array.toBase64()
|
||||
|
||||
def resize_image(image_path, width, height, background):
|
||||
def resize_image(image_path, width, height, background=u'#000000'):
|
||||
"""
|
||||
Resize an image to fit on the current screen.
|
||||
|
||||
@ -159,6 +159,8 @@ def resize_image(image_path, width, height, background):
|
||||
|
||||
``background``
|
||||
The background colour defaults to black.
|
||||
|
||||
DO NOT REMOVE THE DEFAULT BACKGROUND VALUE!
|
||||
"""
|
||||
log.debug(u'resize_image - start')
|
||||
reader = QtGui.QImageReader(image_path)
|
||||
@ -185,7 +187,7 @@ def resize_image(image_path, width, height, background):
|
||||
new_image = QtGui.QImage(width, height,
|
||||
QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||
painter = QtGui.QPainter(new_image)
|
||||
painter.fillRect(new_image.rect(), background)
|
||||
painter.fillRect(new_image.rect(), QtGui.QColor(background))
|
||||
painter.drawImage((width - realw) / 2, (height - realh) / 2, preview)
|
||||
return new_image
|
||||
|
||||
|
@ -27,9 +27,13 @@
|
||||
"""
|
||||
Provide HTML Tag management and Formatting Tag access class
|
||||
"""
|
||||
import cPickle
|
||||
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.lib import translate
|
||||
|
||||
|
||||
class FormattingTags(object):
|
||||
"""
|
||||
Static Class to HTML Tags to be access around the code the list is managed
|
||||
@ -42,6 +46,8 @@ 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
|
||||
@ -49,6 +55,8 @@ class FormattingTags(object):
|
||||
"""
|
||||
Resets the html_expands list.
|
||||
"""
|
||||
temporary_tags = [tag for tag in FormattingTags.html_expands
|
||||
if tag.get(u'temporary')]
|
||||
FormattingTags.html_expands = []
|
||||
base_tags = []
|
||||
# Append the base tags.
|
||||
@ -56,75 +64,160 @@ class FormattingTags(object):
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Red'),
|
||||
u'start tag': u'{r}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:red">',
|
||||
u'end tag': u'{/r}', u'end html': u'</span>', u'protected': True})
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Black'),
|
||||
u'end tag': u'{/r}', u'end html': u'</span>', u'protected': True,
|
||||
u'temporary': False})
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Black'),
|
||||
u'start tag': u'{b}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:black">',
|
||||
u'end tag': u'{/b}', u'end html': u'</span>', u'protected': True})
|
||||
u'end tag': u'{/b}', u'end html': u'</span>', u'protected': True,
|
||||
u'temporary': False})
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Blue'),
|
||||
u'start tag': u'{bl}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:blue">',
|
||||
u'end tag': u'{/bl}', u'end html': u'</span>', u'protected': True})
|
||||
u'end tag': u'{/bl}', u'end html': u'</span>', u'protected': True,
|
||||
u'temporary': False})
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Yellow'),
|
||||
u'start tag': u'{y}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:yellow">',
|
||||
u'end tag': u'{/y}', u'end html': u'</span>', u'protected': True})
|
||||
u'end tag': u'{/y}', u'end html': u'</span>', u'protected': True,
|
||||
u'temporary': False})
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Green'),
|
||||
u'start tag': u'{g}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:green">',
|
||||
u'end tag': u'{/g}', u'end html': u'</span>', u'protected': True})
|
||||
u'end tag': u'{/g}', u'end html': u'</span>', u'protected': True,
|
||||
u'temporary': False})
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Pink'),
|
||||
u'start tag': u'{pk}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:#FFC0CB">',
|
||||
u'end tag': u'{/pk}', u'end html': u'</span>', u'protected': True})
|
||||
u'end tag': u'{/pk}', u'end html': u'</span>', u'protected': True,
|
||||
u'temporary': False})
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Orange'),
|
||||
u'start tag': u'{o}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:#FFA500">',
|
||||
u'end tag': u'{/o}', u'end html': u'</span>', u'protected': True})
|
||||
u'end tag': u'{/o}', u'end html': u'</span>', u'protected': True,
|
||||
u'temporary': False})
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Purple'),
|
||||
u'start tag': u'{pp}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:#800080">',
|
||||
u'end tag': u'{/pp}', u'end html': u'</span>', u'protected': True})
|
||||
u'end tag': u'{/pp}', u'end html': u'</span>', u'protected': True,
|
||||
u'temporary': False})
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'White'),
|
||||
u'start tag': u'{w}',
|
||||
u'start html': u'<span style="-webkit-text-fill-color:white">',
|
||||
u'end tag': u'{/w}', u'end html': u'</span>', u'protected': True})
|
||||
u'end tag': u'{/w}', u'end html': u'</span>', u'protected': True,
|
||||
u'temporary': False})
|
||||
base_tags.append({
|
||||
u'desc': translate('OpenLP.FormattingTags', 'Superscript'),
|
||||
u'start tag': u'{su}', u'start html': u'<sup>',
|
||||
u'end tag': u'{/su}', u'end html': u'</sup>', u'protected': True})
|
||||
u'end tag': u'{/su}', u'end html': u'</sup>', u'protected': True,
|
||||
u'temporary': False})
|
||||
base_tags.append({
|
||||
u'desc': translate('OpenLP.FormattingTags', 'Subscript'),
|
||||
u'start tag': u'{sb}', u'start html': u'<sub>',
|
||||
u'end tag': u'{/sb}', u'end html': u'</sub>', u'protected': True})
|
||||
u'end tag': u'{/sb}', u'end html': u'</sub>', u'protected': True,
|
||||
u'temporary': False})
|
||||
base_tags.append({
|
||||
u'desc': translate('OpenLP.FormattingTags', 'Paragraph'),
|
||||
u'start tag': u'{p}', u'start html': u'<p>', u'end tag': u'{/p}',
|
||||
u'end html': u'</p>', u'protected': True})
|
||||
u'end html': u'</p>', u'protected': True,
|
||||
u'temporary': False})
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Bold'),
|
||||
u'start tag': u'{st}', u'start html': u'<strong>',
|
||||
u'end tag': u'{/st}', u'end html': u'</strong>',
|
||||
u'protected': True})
|
||||
u'protected': True, u'temporary': False})
|
||||
base_tags.append({
|
||||
u'desc': translate('OpenLP.FormattingTags', 'Italics'),
|
||||
u'start tag': u'{it}', u'start html': u'<em>', u'end tag': u'{/it}',
|
||||
u'end html': u'</em>', u'protected': True})
|
||||
u'end html': u'</em>', u'protected': True, u'temporary': False})
|
||||
base_tags.append({
|
||||
u'desc': translate('OpenLP.FormattingTags', 'Underline'),
|
||||
u'start tag': u'{u}',
|
||||
u'start html': u'<span style="text-decoration: underline;">',
|
||||
u'end tag': u'{/u}', u'end html': u'</span>', u'protected': True})
|
||||
u'end tag': u'{/u}', u'end html': u'</span>', u'protected': True,
|
||||
u'temporary': False})
|
||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Break'),
|
||||
u'start tag': u'{br}', u'start html': u'<br>', u'end tag': u'',
|
||||
u'end html': u'', u'protected': True})
|
||||
u'end html': u'', u'protected': True, u'temporary': False})
|
||||
FormattingTags.add_html_tags(base_tags)
|
||||
FormattingTags.add_html_tags(temporary_tags)
|
||||
|
||||
@staticmethod
|
||||
def add_html_tags(tags):
|
||||
def save_html_tags():
|
||||
"""
|
||||
Add a list of tags to the 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'):
|
||||
tags.append(tag)
|
||||
# Remove key 'temporary' from tags. It is not needed to be saved.
|
||||
for tag in tags:
|
||||
if u'temporary' in tag:
|
||||
del tag[u'temporary']
|
||||
# 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()
|
||||
# cPickle only accepts str not unicode strings
|
||||
user_expands_string = str(unicode(user_expands).encode(u'utf8'))
|
||||
if user_expands_string:
|
||||
user_tags = cPickle.loads(user_expands_string)
|
||||
# If we have some user ones added them as well
|
||||
FormattingTags.add_html_tags(user_tags)
|
||||
|
||||
@staticmethod
|
||||
def add_html_tags(tags, save=False):
|
||||
"""
|
||||
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
|
||||
The formatting tag's description, e. g. **Red**
|
||||
|
||||
* start tag
|
||||
The start tag, e. g. ``{r}``
|
||||
|
||||
* end tag
|
||||
The end tag, e. g. ``{/r}``
|
||||
|
||||
* start html
|
||||
The start html tag. For instance ``<span style="
|
||||
-webkit-text-fill-color:red">``
|
||||
|
||||
* end html
|
||||
The end html tag. For example ``</span>``
|
||||
|
||||
* protected
|
||||
A boolean stating whether this is a build-in tag or not. Should be
|
||||
``True`` in most cases.
|
||||
|
||||
* temporary
|
||||
A temporary tag will not be saved, but is also considered when
|
||||
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):
|
||||
|
@ -215,6 +215,8 @@ class ImageManager(QtCore.QObject):
|
||||
image = self._cache[name]
|
||||
if image.image is None:
|
||||
self._conversion_queue.modify_priority(image, Priority.High)
|
||||
# make sure we are running and if not give it a kick
|
||||
self.process_updates()
|
||||
while image.image is None:
|
||||
log.debug(u'get_image - waiting')
|
||||
time.sleep(0.1)
|
||||
@ -235,6 +237,8 @@ class ImageManager(QtCore.QObject):
|
||||
image = self._cache[name]
|
||||
if image.image_bytes is None:
|
||||
self._conversion_queue.modify_priority(image, Priority.Urgent)
|
||||
# make sure we are running and if not give it a kick
|
||||
self.process_updates()
|
||||
while image.image_bytes is None:
|
||||
log.debug(u'get_image_bytes - waiting')
|
||||
time.sleep(0.1)
|
||||
|
@ -31,7 +31,7 @@ from PyQt4 import QtGui, QtCore, QtWebKit
|
||||
|
||||
from openlp.core.lib import ServiceItem, expand_tags, \
|
||||
build_lyrics_format_css, build_lyrics_outline_css, Receiver, \
|
||||
ItemCapabilities
|
||||
ItemCapabilities, FormattingTags
|
||||
from openlp.core.lib.theme import ThemeLevel
|
||||
from openlp.core.ui import MainDisplay, ScreenList
|
||||
|
||||
@ -439,6 +439,50 @@ class Renderer(object):
|
||||
log.debug(u'_paginate_slide_words - End')
|
||||
return formatted
|
||||
|
||||
def _get_start_tags(self, raw_text):
|
||||
"""
|
||||
Tests the given text for not closed formatting tags and returns a tuple
|
||||
consisting of three unicode strings::
|
||||
|
||||
(u'{st}{r}Text text text{/r}{/st}', u'{st}{r}', u'<strong>
|
||||
<span style="-webkit-text-fill-color:red">')
|
||||
|
||||
The first unicode string is the text, with correct closing tags. The
|
||||
second unicode string are OpenLP's opening formatting tags and the third
|
||||
unicode string the html opening formatting tags.
|
||||
|
||||
``raw_text``
|
||||
The text to test. The text must **not** contain html tags, only
|
||||
OpenLP formatting tags are allowed::
|
||||
|
||||
{st}{r}Text text text
|
||||
"""
|
||||
raw_tags = []
|
||||
html_tags = []
|
||||
for tag in FormattingTags.get_html_tags():
|
||||
if tag[u'start tag'] == u'{br}':
|
||||
continue
|
||||
if raw_text.count(tag[u'start tag']) != \
|
||||
raw_text.count(tag[u'end tag']):
|
||||
raw_tags.append(
|
||||
(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']))
|
||||
# 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.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), \
|
||||
u''.join(html_tags)
|
||||
|
||||
def _binary_chop(self, formatted, previous_html, previous_raw, html_list,
|
||||
raw_list, separator, line_end):
|
||||
"""
|
||||
@ -490,8 +534,10 @@ class Renderer(object):
|
||||
# We found the number of words which will fit.
|
||||
if smallest_index == index or highest_index == index:
|
||||
index = smallest_index
|
||||
formatted.append(previous_raw.rstrip(u'<br>') +
|
||||
separator.join(raw_list[:index + 1]))
|
||||
text = previous_raw.rstrip(u'<br>') + \
|
||||
separator.join(raw_list[:index + 1])
|
||||
text, raw_tags, html_tags = self._get_start_tags(text)
|
||||
formatted.append(text)
|
||||
previous_html = u''
|
||||
previous_raw = u''
|
||||
# Stop here as the theme line count was requested.
|
||||
@ -502,17 +548,19 @@ class Renderer(object):
|
||||
continue
|
||||
# Check if the remaining elements fit on the slide.
|
||||
if self._text_fits_on_slide(
|
||||
separator.join(html_list[index + 1:]).strip()):
|
||||
previous_html = separator.join(
|
||||
html_tags + separator.join(html_list[index + 1:]).strip()):
|
||||
previous_html = html_tags + separator.join(
|
||||
html_list[index + 1:]).strip() + line_end
|
||||
previous_raw = separator.join(
|
||||
previous_raw = raw_tags + separator.join(
|
||||
raw_list[index + 1:]).strip() + line_end
|
||||
break
|
||||
else:
|
||||
# The remaining elements do not fit, thus reset the indexes,
|
||||
# create a new list and continue.
|
||||
raw_list = raw_list[index + 1:]
|
||||
raw_list[0] = raw_tags + raw_list[0]
|
||||
html_list = html_list[index + 1:]
|
||||
html_list[0] = html_tags + html_list[0]
|
||||
smallest_index = 0
|
||||
highest_index = len(html_list) - 1
|
||||
index = int(highest_index / 2)
|
||||
|
@ -466,7 +466,7 @@ class ServiceItem(object):
|
||||
'<strong>Length</strong>: %s')) % \
|
||||
unicode(datetime.timedelta(seconds=self.media_length))
|
||||
if not start and not end:
|
||||
return None
|
||||
return u''
|
||||
elif start and not end:
|
||||
return start
|
||||
elif not start and end:
|
||||
|
@ -30,14 +30,13 @@ protected and included each time loaded. Custom tags can be defined and saved.
|
||||
The Custom Tag arrays are saved in a pickle so QSettings works on them. Base
|
||||
Tags cannot be changed.
|
||||
"""
|
||||
import cPickle
|
||||
|
||||
from PyQt4 import QtCore, QtGui
|
||||
|
||||
from openlp.core.lib import translate, FormattingTags
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui.formattingtagdialog import Ui_FormattingTagDialog
|
||||
|
||||
|
||||
class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
"""
|
||||
The :class:`FormattingTagForm` manages the settings tab .
|
||||
@ -48,7 +47,6 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
"""
|
||||
QtGui.QDialog.__init__(self, parent)
|
||||
self.setupUi(self)
|
||||
self._loadFormattingTags()
|
||||
QtCore.QObject.connect(self.tagTableWidget,
|
||||
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onRowSelected)
|
||||
QtCore.QObject.connect(self.newPushButton,
|
||||
@ -59,41 +57,24 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
QtCore.SIGNAL(u'pressed()'), self.onDeletePushed)
|
||||
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'rejected()'),
|
||||
self.close)
|
||||
# Forces reloading of tags from openlp configuration.
|
||||
FormattingTags.load_tags()
|
||||
|
||||
def exec_(self):
|
||||
"""
|
||||
Load Display and set field state.
|
||||
"""
|
||||
# Create initial copy from master
|
||||
self._loadFormattingTags()
|
||||
self._resetTable()
|
||||
self.selected = -1
|
||||
return QtGui.QDialog.exec_(self)
|
||||
|
||||
def _loadFormattingTags(self):
|
||||
"""
|
||||
Load the Tags from store so can be used in the system or used to
|
||||
update the display. If Cancel was selected this is needed to reset the
|
||||
dsiplay to the correct version.
|
||||
"""
|
||||
# Initial Load of the Tags
|
||||
FormattingTags.reset_html_tags()
|
||||
# Formatting Tags were also known as display tags.
|
||||
user_expands = QtCore.QSettings().value(u'displayTags/html_tags',
|
||||
QtCore.QVariant(u'')).toString()
|
||||
# cPickle only accepts str not unicode strings
|
||||
user_expands_string = str(unicode(user_expands).encode(u'utf8'))
|
||||
if user_expands_string:
|
||||
user_tags = cPickle.loads(user_expands_string)
|
||||
# If we have some user ones added them as well
|
||||
FormattingTags.add_html_tags(user_tags)
|
||||
|
||||
def onRowSelected(self):
|
||||
"""
|
||||
Table Row selected so display items and set field state.
|
||||
"""
|
||||
row = self.tagTableWidget.currentRow()
|
||||
html = FormattingTags.get_html_tags()[row]
|
||||
html = FormattingTags.html_expands[row]
|
||||
self.selected = row
|
||||
self.descriptionLineEdit.setText(html[u'desc'])
|
||||
self.tagLineEdit.setText(self._strip(html[u'start tag']))
|
||||
@ -118,7 +99,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
"""
|
||||
Add a new tag to list only if it is not a duplicate.
|
||||
"""
|
||||
for html in FormattingTags.get_html_tags():
|
||||
for html in FormattingTags.html_expands:
|
||||
if self._strip(html[u'start tag']) == u'n':
|
||||
critical_error_message_box(
|
||||
translate('OpenLP.FormattingTagForm', 'Update Error'),
|
||||
@ -132,7 +113,8 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
u'start html': translate('OpenLP.FormattingTagForm', '<HTML here>'),
|
||||
u'end tag': u'{/n}',
|
||||
u'end html': translate('OpenLP.FormattingTagForm', '</and here>'),
|
||||
u'protected': False
|
||||
u'protected': False,
|
||||
u'temporary': False
|
||||
}
|
||||
FormattingTags.add_html_tags([tag])
|
||||
self._resetTable()
|
||||
@ -149,13 +131,13 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
FormattingTags.remove_html_tag(self.selected)
|
||||
self.selected = -1
|
||||
self._resetTable()
|
||||
self._saveTable()
|
||||
FormattingTags.save_html_tags()
|
||||
|
||||
def onSavedPushed(self):
|
||||
"""
|
||||
Update Custom Tag details if not duplicate and save the data.
|
||||
"""
|
||||
html_expands = FormattingTags.get_html_tags()
|
||||
html_expands = FormattingTags.html_expands
|
||||
if self.selected != -1:
|
||||
html = html_expands[self.selected]
|
||||
tag = unicode(self.tagLineEdit.text())
|
||||
@ -172,21 +154,11 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
html[u'end html'] = unicode(self.endTagLineEdit.text())
|
||||
html[u'start tag'] = u'{%s}' % tag
|
||||
html[u'end tag'] = u'{/%s}' % tag
|
||||
# Keep temporary tags when the user changes one.
|
||||
html[u'temporary'] = False
|
||||
self.selected = -1
|
||||
self._resetTable()
|
||||
self._saveTable()
|
||||
|
||||
def _saveTable(self):
|
||||
"""
|
||||
Saves all formatting tags except protected ones.
|
||||
"""
|
||||
tags = []
|
||||
for tag in FormattingTags.get_html_tags():
|
||||
if not tag[u'protected']:
|
||||
tags.append(tag)
|
||||
# Formatting Tags were also known as display tags.
|
||||
QtCore.QSettings().setValue(u'displayTags/html_tags',
|
||||
QtCore.QVariant(cPickle.dumps(tags) if tags else u''))
|
||||
FormattingTags.save_html_tags()
|
||||
|
||||
def _resetTable(self):
|
||||
"""
|
||||
@ -197,9 +169,8 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
self.newPushButton.setEnabled(True)
|
||||
self.savePushButton.setEnabled(False)
|
||||
self.deletePushButton.setEnabled(False)
|
||||
for linenumber, html in enumerate(FormattingTags.get_html_tags()):
|
||||
self.tagTableWidget.setRowCount(
|
||||
self.tagTableWidget.rowCount() + 1)
|
||||
for linenumber, html in enumerate(FormattingTags.html_expands):
|
||||
self.tagTableWidget.setRowCount(self.tagTableWidget.rowCount() + 1)
|
||||
self.tagTableWidget.setItem(linenumber, 0,
|
||||
QtGui.QTableWidgetItem(html[u'desc']))
|
||||
self.tagTableWidget.setItem(linenumber, 1,
|
||||
@ -208,6 +179,9 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||
QtGui.QTableWidgetItem(html[u'start html']))
|
||||
self.tagTableWidget.setItem(linenumber, 3,
|
||||
QtGui.QTableWidgetItem(html[u'end html']))
|
||||
# Permanent (persistent) tags do not have this key.
|
||||
if u'temporary' not in html:
|
||||
html[u'temporary'] = False
|
||||
self.tagTableWidget.resizeRowsToContents()
|
||||
self.descriptionLineEdit.setText(u'')
|
||||
self.tagLineEdit.setText(u'')
|
||||
|
@ -606,7 +606,6 @@ class AudioPlayer(QtCore.QObject):
|
||||
self.stop()
|
||||
for path in self.mediaObject.outputPaths():
|
||||
path.disconnect()
|
||||
QtCore.QObject.__del__(self)
|
||||
|
||||
def onAboutToFinish(self):
|
||||
"""
|
||||
|
@ -554,6 +554,18 @@ class ServiceManager(QtGui.QWidget):
|
||||
for path_from in write_list:
|
||||
zip.write(path_from, path_from.encode(u'utf-8'))
|
||||
for path_from, path_to in audio_files:
|
||||
if path_from == path_to:
|
||||
# If this file has already been saved, let's use set the
|
||||
# from path to the real location of the files
|
||||
path_from = os.path.join(self.servicePath, path_from)
|
||||
else:
|
||||
# If this file has not yet been saved, let's copy the file
|
||||
# to the service manager path
|
||||
save_file = os.path.join(self.servicePath, path_to)
|
||||
save_path = os.path.split(save_file)[0]
|
||||
if not os.path.exists(save_path):
|
||||
os.makedirs(save_path)
|
||||
shutil.copy(path_from, save_file)
|
||||
zip.write(path_from, path_to.encode(u'utf-8'))
|
||||
except IOError:
|
||||
log.exception(u'Failed to save service to disk')
|
||||
|
@ -127,6 +127,9 @@ class AppLocation(object):
|
||||
CacheDir = 6
|
||||
LanguageDir = 7
|
||||
|
||||
# Base path where data/config/cache dir is located
|
||||
BaseDir = None
|
||||
|
||||
@staticmethod
|
||||
def get_directory(dir_type=1):
|
||||
"""
|
||||
@ -152,6 +155,8 @@ class AppLocation(object):
|
||||
os.path.abspath(os.path.split(sys.argv[0])[0]),
|
||||
_get_os_dir_path(dir_type))
|
||||
return os.path.join(app_path, u'i18n')
|
||||
elif dir_type == AppLocation.DataDir and AppLocation.BaseDir:
|
||||
return os.path.join(AppLocation.BaseDir, 'data')
|
||||
else:
|
||||
return _get_os_dir_path(dir_type)
|
||||
|
||||
|
@ -56,6 +56,7 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
MediaManagerItem.__init__(self, parent, plugin, icon)
|
||||
self.message_listener = MessageListener(self)
|
||||
self.hasSearch = True
|
||||
self.singleServiceItem = False
|
||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||
QtCore.SIGNAL(u'mediaitem_presentation_rebuild'), self.rebuild)
|
||||
# Allow DnD from the desktop
|
||||
|
@ -682,7 +682,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
||||
text = unicode(self.songBookComboBox.currentText())
|
||||
if item == 0 and text:
|
||||
temp_song_book = text
|
||||
self.mediaitem.song_maintenance_form.exec_()
|
||||
self.mediaitem.songMaintenanceForm.exec_()
|
||||
self.loadAuthors()
|
||||
self.loadBooks()
|
||||
self.loadTopics()
|
||||
|
@ -428,8 +428,11 @@ class SongMediaItem(MediaManagerItem):
|
||||
|
||||
def generateSlideData(self, service_item, item=None, xmlVersion=False,
|
||||
remote=False):
|
||||
log.debug(u'generateSlideData (%s:%s)' % (service_item, item))
|
||||
item_id = self._getIdOfItemToGenerate(item, self.remoteSong)
|
||||
log.debug(u'generateSlideData: %s, %s, %s' % (service_item, item, self.remoteSong))
|
||||
# The ``None`` below is a workaround for bug #812289 - I think that Qt
|
||||
# deletes the item somewhere along the line because the user is taking
|
||||
# so long to update their item (or something weird like that).
|
||||
item_id = self._getIdOfItemToGenerate(None, self.remoteSong)
|
||||
service_item.add_capability(ItemCapabilities.CanEdit)
|
||||
service_item.add_capability(ItemCapabilities.CanPreview)
|
||||
service_item.add_capability(ItemCapabilities.CanLoop)
|
||||
@ -559,6 +562,9 @@ class SongMediaItem(MediaManagerItem):
|
||||
self._updateBackgroundAudio(song, item)
|
||||
editId = song.id
|
||||
self.onSearchTextButtonClick()
|
||||
else:
|
||||
# Make sure we temporary import formatting tags.
|
||||
self.openLyrics.xml_to_song(item.xml_version, True)
|
||||
# Update service with correct song id.
|
||||
if editId:
|
||||
Receiver.send_message(u'service_item_update',
|
||||
|
@ -61,19 +61,21 @@ The XML of an `OpenLyrics <http://openlyrics.info/>`_ song looks like this::
|
||||
</song>
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import re
|
||||
|
||||
from lxml import etree, objectify
|
||||
|
||||
from openlp.core.lib import FormattingTags
|
||||
from openlp.plugins.songs.lib import clean_song, VerseType
|
||||
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic
|
||||
from openlp.core.utils import get_application_version
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
CHORD_REGEX = re.compile(u'<chord name=".*?"/>')
|
||||
NAMESPACE = u'http://openlyrics.info/namespace/2009/song'
|
||||
NSMAP = '{' + NAMESPACE + '}' + '%s'
|
||||
|
||||
|
||||
class SongXML(object):
|
||||
"""
|
||||
@ -173,7 +175,7 @@ class SongXML(object):
|
||||
|
||||
class OpenLyrics(object):
|
||||
"""
|
||||
This class represents the converter for OpenLyrics XML (version 0.7)
|
||||
This class represents the converter for OpenLyrics XML (version 0.8)
|
||||
to/from a song.
|
||||
|
||||
As OpenLyrics has a rich set of different features, we cannot support them
|
||||
@ -198,11 +200,15 @@ class OpenLyrics(object):
|
||||
``<key>``
|
||||
This property is not supported.
|
||||
|
||||
``<format>``
|
||||
The custom formatting tags are fully supported.
|
||||
|
||||
``<keywords>``
|
||||
This property is not supported.
|
||||
|
||||
``<lines>``
|
||||
The attribute *part* is not supported.
|
||||
The attribute *part* is not supported. The *break* attribute is
|
||||
supported.
|
||||
|
||||
``<publisher>``
|
||||
This property is not supported.
|
||||
@ -227,15 +233,35 @@ class OpenLyrics(object):
|
||||
|
||||
``<verse name="v1a" lang="he" translit="en">``
|
||||
The attribute *translit* is not supported. Note, the attribute *lang* is
|
||||
considered, but there is not further functionality implemented yet.
|
||||
considered, but there is not further functionality implemented yet. The
|
||||
following verse "types" are supported by OpenLP:
|
||||
|
||||
* v
|
||||
* c
|
||||
* b
|
||||
* p
|
||||
* i
|
||||
* e
|
||||
* o
|
||||
|
||||
The verse "types" stand for *Verse*, *Chorus*, *Bridge*, *Pre-Chorus*,
|
||||
*Intro*, *Ending* and *Other*. Any numeric value is allowed after the
|
||||
verse type. The complete verse name in OpenLP always consists of the
|
||||
verse type and the verse number. If not number is present *1* is
|
||||
assumed.
|
||||
OpenLP will merge verses which are split up by appending a letter to the
|
||||
verse name, such as *v1a*.
|
||||
|
||||
``<verseOrder>``
|
||||
OpenLP supports this property.
|
||||
|
||||
"""
|
||||
IMPLEMENTED_VERSION = u'0.7'
|
||||
IMPLEMENTED_VERSION = u'0.8'
|
||||
|
||||
def __init__(self, manager):
|
||||
self.manager = manager
|
||||
self.start_tags_regex = re.compile(r'\{(\w+)\}') # {abc} -> abc
|
||||
self.end_tags_regex = re.compile(r'\{\/(\w+)\}') # {/abc} -> abc
|
||||
|
||||
def song_to_xml(self, song):
|
||||
"""
|
||||
@ -244,13 +270,14 @@ class OpenLyrics(object):
|
||||
sxml = SongXML()
|
||||
song_xml = objectify.fromstring(u'<song/>')
|
||||
# Append the necessary meta data to the song.
|
||||
song_xml.set(u'xmlns', u'http://openlyrics.info/namespace/2009/song')
|
||||
song_xml.set(u'xmlns', NAMESPACE)
|
||||
song_xml.set(u'version', OpenLyrics.IMPLEMENTED_VERSION)
|
||||
application_name = u'OpenLP ' + get_application_version()[u'version']
|
||||
song_xml.set(u'createdIn', application_name)
|
||||
song_xml.set(u'modifiedIn', application_name)
|
||||
# "Convert" 2011-08-27 11:49:15 to 2011-08-27T11:49:15.
|
||||
song_xml.set(u'modifiedDate',
|
||||
datetime.datetime.now().strftime(u'%Y-%m-%dT%H:%M:%S'))
|
||||
unicode(song.last_modified).replace(u' ', u'T'))
|
||||
properties = etree.SubElement(song_xml, u'properties')
|
||||
titles = etree.SubElement(properties, u'titles')
|
||||
self._add_text_to_element(u'title', titles, song.title)
|
||||
@ -284,29 +311,40 @@ class OpenLyrics(object):
|
||||
themes = etree.SubElement(properties, u'themes')
|
||||
for topic in song.topics:
|
||||
self._add_text_to_element(u'theme', themes, topic.name)
|
||||
# Process the formatting tags.
|
||||
# Have we any tags in song lyrics?
|
||||
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.
|
||||
format_ = etree.SubElement(song_xml, u'format')
|
||||
tags_element = etree.SubElement(format_, u'tags')
|
||||
tags_element.set(u'application', u'OpenLP')
|
||||
# Process the song's lyrics.
|
||||
lyrics = etree.SubElement(song_xml, u'lyrics')
|
||||
verse_list = sxml.get_verses(song.lyrics)
|
||||
for verse in verse_list:
|
||||
verse_tag = verse[0][u'type'][0].lower()
|
||||
verse_number = verse[0][u'label']
|
||||
verse_def = verse_tag + verse_number
|
||||
verse_element = \
|
||||
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 = verse[1].split(u'[---]')
|
||||
for index, virtual_verse in enumerate(virtual_verses):
|
||||
verse_def = verse_tag + verse_number
|
||||
# We need "v1a" because we have more than one virtual verse.
|
||||
if len(virtual_verses) > 1:
|
||||
verse_def += list(u'abcdefghijklmnopqrstuvwxyz')[index]
|
||||
element = \
|
||||
self._add_text_to_element(u'verse', lyrics, None, verse_def)
|
||||
if verse[0].has_key(u'lang'):
|
||||
element.set(u'lang', verse[0][u'lang'])
|
||||
element = self._add_text_to_element(u'lines', element)
|
||||
for line in virtual_verse.strip(u'\n').split(u'\n'):
|
||||
self._add_text_to_element(u'line', element, line)
|
||||
# Add formatting tags to text
|
||||
lines_element = self._add_text_with_tags_to_lines(verse_element,
|
||||
virtual_verse, tags_element)
|
||||
# Do not add the break attribute to the last lines element.
|
||||
if index < len(virtual_verses) - 1:
|
||||
lines_element.set(u'break', u'optional')
|
||||
return self._extract_xml(song_xml)
|
||||
|
||||
def xml_to_song(self, xml):
|
||||
def xml_to_song(self, xml, parse_and_not_save=False):
|
||||
"""
|
||||
Create and save a song from OpenLyrics format xml to the database. Since
|
||||
we also export XML from external sources (e. g. OpenLyrics import), we
|
||||
@ -314,19 +352,26 @@ class OpenLyrics(object):
|
||||
|
||||
``xml``
|
||||
The XML to parse (unicode).
|
||||
|
||||
``parse_and_not_save``
|
||||
Switch to skip processing the whole song and to prevent storing the
|
||||
songs to the database. Defaults to ``False``.
|
||||
"""
|
||||
# No xml get out of here.
|
||||
if not xml:
|
||||
return None
|
||||
if xml[:5] == u'<?xml':
|
||||
xml = xml[38:]
|
||||
# Remove chords from xml.
|
||||
xml = CHORD_REGEX.sub(u'', xml)
|
||||
song_xml = objectify.fromstring(xml)
|
||||
if hasattr(song_xml, u'properties'):
|
||||
properties = song_xml.properties
|
||||
else:
|
||||
return None
|
||||
# Formatting tags are new in OpenLyrics 0.8
|
||||
if float(song_xml.get(u'version')) > 0.7:
|
||||
self._process_formatting_tags(song_xml, parse_and_not_save)
|
||||
if parse_and_not_save:
|
||||
return
|
||||
song = Song()
|
||||
# Values will be set when cleaning the song.
|
||||
song.search_lyrics = u''
|
||||
@ -336,7 +381,7 @@ class OpenLyrics(object):
|
||||
self._process_cclinumber(properties, song)
|
||||
self._process_titles(properties, song)
|
||||
# The verse order is processed with the lyrics!
|
||||
self._process_lyrics(properties, song_xml.lyrics, song)
|
||||
self._process_lyrics(properties, song_xml, song)
|
||||
self._process_comments(properties, song)
|
||||
self._process_authors(properties, song)
|
||||
self._process_songbooks(properties, song)
|
||||
@ -355,6 +400,57 @@ class OpenLyrics(object):
|
||||
parent.append(element)
|
||||
return element
|
||||
|
||||
def _add_tag_to_formatting(self, tag_name, tags_element):
|
||||
"""
|
||||
Add new formatting tag to the element ``<format>``
|
||||
if the tag is not present yet.
|
||||
"""
|
||||
available_tags = FormattingTags.get_html_tags()
|
||||
start_tag = '{%s}' % tag_name
|
||||
for t in available_tags:
|
||||
if t[u'start tag'] == start_tag:
|
||||
# Create new formatting tag in openlyrics xml.
|
||||
el = self._add_text_to_element(u'tag', tags_element)
|
||||
el.set(u'name', tag_name)
|
||||
el_open = self._add_text_to_element(u'open', el)
|
||||
el_open.text = etree.CDATA(t[u'start html'])
|
||||
# Check if formatting tag contains end tag. Some formatting
|
||||
# tags e.g. {br} has only start tag. If no end tag is present
|
||||
# <close> element has not to be in OpenLyrics xml.
|
||||
if t['end tag']:
|
||||
el_close = self._add_text_to_element(u'close', el)
|
||||
el_close.text = etree.CDATA(t[u'end html'])
|
||||
|
||||
def _add_text_with_tags_to_lines(self, verse_element, text, tags_element):
|
||||
"""
|
||||
Convert text with formatting tags from OpenLP format to OpenLyrics
|
||||
format and append it to element ``<lines>``.
|
||||
"""
|
||||
start_tags = self.start_tags_regex.findall(text)
|
||||
end_tags = self.end_tags_regex.findall(text)
|
||||
# Replace start tags with xml syntax.
|
||||
for tag in start_tags:
|
||||
# Tags already converted to xml structure.
|
||||
xml_tags = tags_element.xpath(u'tag/attribute::name')
|
||||
# Some formatting tag has only starting part e.g. <br>.
|
||||
# Handle this case.
|
||||
if tag in end_tags:
|
||||
text = text.replace(u'{%s}' % tag, u'<tag name="%s">' % tag)
|
||||
else:
|
||||
text = text.replace(u'{%s}' % tag, u'<tag name="%s"/>' % tag)
|
||||
# Add tag to <format> element if tag not present.
|
||||
if tag not in xml_tags:
|
||||
self._add_tag_to_formatting(tag, tags_element)
|
||||
# Replace end tags.
|
||||
for t in end_tags:
|
||||
text = text.replace(u'{/%s}' % t, u'</tag>')
|
||||
# Replace \n with <br/>.
|
||||
text = text.replace(u'\n', u'<br/>')
|
||||
text = u'<lines>' + text + u'</lines>'
|
||||
element = etree.XML(text)
|
||||
verse_element.append(element)
|
||||
return element
|
||||
|
||||
def _extract_xml(self, xml):
|
||||
"""
|
||||
Extract our newly created XML song.
|
||||
@ -362,20 +458,6 @@ class OpenLyrics(object):
|
||||
return etree.tostring(xml, encoding=u'UTF-8',
|
||||
xml_declaration=True)
|
||||
|
||||
def _get(self, element, attribute):
|
||||
"""
|
||||
This returns the element's attribute as unicode string.
|
||||
|
||||
``element``
|
||||
The element.
|
||||
|
||||
``attribute``
|
||||
The element's attribute (unicode).
|
||||
"""
|
||||
if element.get(attribute) is not None:
|
||||
return unicode(element.get(attribute))
|
||||
return u''
|
||||
|
||||
def _text(self, element):
|
||||
"""
|
||||
This returns the text of an element as unicode string.
|
||||
@ -457,29 +539,155 @@ class OpenLyrics(object):
|
||||
if hasattr(properties, u'copyright'):
|
||||
song.copyright = self._text(properties.copyright)
|
||||
|
||||
def _process_lyrics(self, properties, lyrics, song):
|
||||
def _process_formatting_tags(self, song_xml, temporary):
|
||||
"""
|
||||
Process the formatting tags from the song and either add missing tags
|
||||
temporary or permanently to the formatting tag list.
|
||||
"""
|
||||
if not hasattr(song_xml, u'format'):
|
||||
return
|
||||
found_tags = []
|
||||
for tag in song_xml.format.tags.getchildren():
|
||||
name = tag.get(u'name')
|
||||
if name is None:
|
||||
continue
|
||||
start_tag = u'{%s}' % name[:5]
|
||||
# Some tags have only start tag e.g. {br}
|
||||
end_tag = u'{/' + name[:5] + u'}' if hasattr(tag, 'close') else u''
|
||||
openlp_tag = {
|
||||
u'desc': name,
|
||||
u'start tag': start_tag,
|
||||
u'end tag': end_tag,
|
||||
u'start html': tag.open.text,
|
||||
# Some tags have only start html e.g. {br}
|
||||
u'end html': tag.close.text if hasattr(tag, 'close') else u'',
|
||||
u'protected': False,
|
||||
}
|
||||
# Add 'temporary' key in case the formatting tag should not be
|
||||
# saved otherwise it is supposed that formatting tag is permanent.
|
||||
if temporary:
|
||||
openlp_tag[u'temporary'] = temporary
|
||||
found_tags.append(openlp_tag)
|
||||
existing_tag_ids = [tag[u'start tag']
|
||||
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)
|
||||
|
||||
def _process_lines_mixed_content(self, element, newlines=True):
|
||||
"""
|
||||
Converts the xml text with mixed content to OpenLP representation.
|
||||
Chords are skipped and formatting tags are converted.
|
||||
|
||||
``element``
|
||||
The property object (lxml.etree.Element).
|
||||
|
||||
``newlines``
|
||||
The switch to enable/disable processing of line breaks <br/>.
|
||||
The <br/> is used since OpenLyrics 0.8.
|
||||
"""
|
||||
text = u''
|
||||
use_endtag = True
|
||||
# Skip <comment> elements - not yet supported.
|
||||
if element.tag == NSMAP % u'comment':
|
||||
if element.tail:
|
||||
# Append tail text at chord element.
|
||||
text += element.tail
|
||||
return text
|
||||
# Skip <chord> element - not yet supported.
|
||||
elif element.tag == NSMAP % u'chord':
|
||||
if element.tail:
|
||||
# Append tail text at chord element.
|
||||
text += element.tail
|
||||
return text
|
||||
# Convert line breaks <br/> to \n.
|
||||
elif newlines and element.tag == NSMAP % u'br':
|
||||
text += u'\n'
|
||||
if element.tail:
|
||||
text += element.tail
|
||||
return text
|
||||
# Start formatting tag.
|
||||
if element.tag == NSMAP % u'tag':
|
||||
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:
|
||||
use_endtag = False
|
||||
# Append text from element.
|
||||
if element.text:
|
||||
text += element.text
|
||||
# Process nested formatting tags.
|
||||
for child in element:
|
||||
# Use recursion since nested formatting tags are allowed.
|
||||
text += self._process_lines_mixed_content(child, newlines)
|
||||
# Append text from tail and add formatting end tag.
|
||||
if element.tag == NSMAP % 'tag' and use_endtag:
|
||||
text += u'{/%s}' % element.get(u'name')
|
||||
# Append text from tail.
|
||||
if element.tail:
|
||||
text += element.tail
|
||||
return text
|
||||
|
||||
def _process_verse_lines(self, lines, version):
|
||||
"""
|
||||
Converts lyrics lines to OpenLP representation.
|
||||
|
||||
``lines``
|
||||
The lines object (lxml.objectify.ObjectifiedElement).
|
||||
"""
|
||||
text = u''
|
||||
# Convert lxml.objectify to lxml.etree representation.
|
||||
lines = etree.tostring(lines)
|
||||
element = etree.XML(lines)
|
||||
|
||||
# OpenLyrics 0.8 uses <br/> for new lines.
|
||||
# Append text from "lines" element to verse text.
|
||||
if version > '0.7':
|
||||
text = self._process_lines_mixed_content(element)
|
||||
# OpenLyrics version <= 0.7 contais <line> elements to represent lines.
|
||||
# First child element is tested.
|
||||
else:
|
||||
# Loop over the "line" elements removing comments and chords.
|
||||
for line in element:
|
||||
# Skip comment lines.
|
||||
if line.tag == NSMAP % u'comment':
|
||||
continue
|
||||
if text:
|
||||
text += u'\n'
|
||||
text += self._process_lines_mixed_content(line, newlines=False)
|
||||
return text
|
||||
|
||||
def _process_lyrics(self, properties, song_xml, song_obj):
|
||||
"""
|
||||
Processes the verses and search_lyrics for the song.
|
||||
|
||||
``properties``
|
||||
The properties object (lxml.objectify.ObjectifiedElement).
|
||||
|
||||
``lyrics``
|
||||
The lyrics object (lxml.objectify.ObjectifiedElement).
|
||||
``song_xml``
|
||||
The objectified song (lxml.objectify.ObjectifiedElement).
|
||||
|
||||
``song``
|
||||
``song_obj``
|
||||
The song object.
|
||||
"""
|
||||
sxml = SongXML()
|
||||
verses = {}
|
||||
verse_def_list = []
|
||||
lyrics = song_xml.lyrics
|
||||
# Loop over the "verse" elements.
|
||||
for verse in lyrics.verse:
|
||||
text = u''
|
||||
# Loop over the "lines" elements.
|
||||
for lines in verse.lines:
|
||||
if text:
|
||||
text += u'\n'
|
||||
text += u'\n'.join([unicode(line) for line in lines.line])
|
||||
verse_def = self._get(verse, u'name').lower()
|
||||
# Append text from "lines" element to verse text.
|
||||
text += self._process_verse_lines(lines,
|
||||
version=song_xml.get(u'version'))
|
||||
# Add a virtual split to the verse text.
|
||||
if lines.get(u'break') is not None:
|
||||
text += u'\n[---]'
|
||||
verse_def = verse.get(u'name', u' ').lower()
|
||||
if verse_def[0] in VerseType.Tags:
|
||||
verse_tag = verse_def[0]
|
||||
else:
|
||||
@ -489,11 +697,16 @@ class OpenLyrics(object):
|
||||
# not correct the verse order.
|
||||
if not verse_number:
|
||||
verse_number = u'1'
|
||||
lang = None
|
||||
if self._get(verse, u'lang'):
|
||||
lang = self._get(verse, u'lang')
|
||||
if verses.has_key((verse_tag, verse_number, lang)):
|
||||
lang = verse.get(u'lang')
|
||||
# In OpenLP 1.9.6 we used v1a, v1b ... to represent visual slide
|
||||
# breaks. In OpenLyrics 0.7 an attribute has been added.
|
||||
if song_xml.get(u'modifiedIn') in (u'1.9.6', u'OpenLP 1.9.6') and \
|
||||
song_xml.get(u'version') == u'0.7' and \
|
||||
(verse_tag, verse_number, lang) in verses:
|
||||
verses[(verse_tag, verse_number, lang)] += u'\n[---]\n' + text
|
||||
# Merge v1a, v1b, .... to v1.
|
||||
elif (verse_tag, verse_number, lang) in verses:
|
||||
verses[(verse_tag, verse_number, lang)] += u'\n' + text
|
||||
else:
|
||||
verses[(verse_tag, verse_number, lang)] = text
|
||||
verse_def_list.append((verse_tag, verse_number, lang))
|
||||
@ -501,10 +714,10 @@ class OpenLyrics(object):
|
||||
for verse in verse_def_list:
|
||||
sxml.add_verse_to_lyrics(
|
||||
verse[0], verse[1], verses[verse], verse[2])
|
||||
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
||||
song_obj.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
||||
# Process verse order
|
||||
if hasattr(properties, u'verseOrder'):
|
||||
song.verse_order = self._text(properties.verseOrder)
|
||||
song_obj.verse_order = self._text(properties.verseOrder)
|
||||
|
||||
def _process_songbooks(self, properties, song):
|
||||
"""
|
||||
@ -520,7 +733,7 @@ class OpenLyrics(object):
|
||||
song.song_number = u''
|
||||
if hasattr(properties, u'songbooks'):
|
||||
for songbook in properties.songbooks.songbook:
|
||||
bookname = self._get(songbook, u'name')
|
||||
bookname = songbook.get(u'name', u'')
|
||||
if bookname:
|
||||
book = self.manager.get_object_filtered(Book,
|
||||
Book.name == bookname)
|
||||
@ -529,7 +742,7 @@ class OpenLyrics(object):
|
||||
book = Book.populate(name=bookname, publisher=u'')
|
||||
self.manager.save_object(book)
|
||||
song.song_book_id = book.id
|
||||
song.song_number = self._get(songbook, u'entry')
|
||||
song.song_number = songbook.get(u'entry', u'')
|
||||
# We only support one song book, so take the first one.
|
||||
break
|
||||
|
||||
|
1592
resources/i18n/af.ts
1592
resources/i18n/af.ts
File diff suppressed because it is too large
Load Diff
1154
resources/i18n/cs.ts
1154
resources/i18n/cs.ts
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1277
resources/i18n/es.ts
1277
resources/i18n/es.ts
File diff suppressed because it is too large
Load Diff
1199
resources/i18n/et.ts
1199
resources/i18n/et.ts
File diff suppressed because it is too large
Load Diff
1398
resources/i18n/fr.ts
1398
resources/i18n/fr.ts
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1583
resources/i18n/ja.ts
1583
resources/i18n/ja.ts
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1277
resources/i18n/nl.ts
1277
resources/i18n/nl.ts
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1028
resources/i18n/ru.ts
1028
resources/i18n/ru.ts
File diff suppressed because it is too large
Load Diff
2928
resources/i18n/sv.ts
2928
resources/i18n/sv.ts
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user