forked from openlp/openlp
Fixed bug #745636: Added export to openlyrics format with formatting tags.
bzr-revno: 1758 Fixes: https://launchpad.net/bugs/745636
This commit is contained in:
commit
b66e07e98b
@ -27,9 +27,13 @@
|
|||||||
"""
|
"""
|
||||||
Provide HTML Tag management and Formatting Tag access class
|
Provide HTML Tag management and Formatting Tag access class
|
||||||
"""
|
"""
|
||||||
|
import cPickle
|
||||||
|
|
||||||
|
from PyQt4 import QtCore
|
||||||
|
|
||||||
from openlp.core.lib import translate
|
from openlp.core.lib import translate
|
||||||
|
|
||||||
|
|
||||||
class FormattingTags(object):
|
class FormattingTags(object):
|
||||||
"""
|
"""
|
||||||
Static Class to HTML Tags to be access around the code the list is managed
|
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.
|
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
|
return FormattingTags.html_expands
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -49,6 +55,8 @@ class FormattingTags(object):
|
|||||||
"""
|
"""
|
||||||
Resets the html_expands list.
|
Resets the html_expands list.
|
||||||
"""
|
"""
|
||||||
|
temporary_tags = [tag for tag in FormattingTags.html_expands
|
||||||
|
if tag.get(u'temporary')]
|
||||||
FormattingTags.html_expands = []
|
FormattingTags.html_expands = []
|
||||||
base_tags = []
|
base_tags = []
|
||||||
# Append the base tags.
|
# Append the base tags.
|
||||||
@ -56,75 +64,160 @@ class FormattingTags(object):
|
|||||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Red'),
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Red'),
|
||||||
u'start tag': u'{r}',
|
u'start tag': u'{r}',
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:red">',
|
u'start html': u'<span style="-webkit-text-fill-color:red">',
|
||||||
u'end tag': u'{/r}', u'end html': u'</span>', u'protected': True})
|
u'end tag': u'{/r}', u'end html': u'</span>', u'protected': True,
|
||||||
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Black'),
|
u'temporary': False})
|
||||||
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Black'),
|
||||||
u'start tag': u'{b}',
|
u'start tag': u'{b}',
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:black">',
|
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'),
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Blue'),
|
||||||
u'start tag': u'{bl}',
|
u'start tag': u'{bl}',
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:blue">',
|
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'),
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Yellow'),
|
||||||
u'start tag': u'{y}',
|
u'start tag': u'{y}',
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:yellow">',
|
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'),
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Green'),
|
||||||
u'start tag': u'{g}',
|
u'start tag': u'{g}',
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:green">',
|
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'),
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Pink'),
|
||||||
u'start tag': u'{pk}',
|
u'start tag': u'{pk}',
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:#FFC0CB">',
|
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'),
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Orange'),
|
||||||
u'start tag': u'{o}',
|
u'start tag': u'{o}',
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:#FFA500">',
|
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'),
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Purple'),
|
||||||
u'start tag': u'{pp}',
|
u'start tag': u'{pp}',
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:#800080">',
|
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'),
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'White'),
|
||||||
u'start tag': u'{w}',
|
u'start tag': u'{w}',
|
||||||
u'start html': u'<span style="-webkit-text-fill-color:white">',
|
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({
|
base_tags.append({
|
||||||
u'desc': translate('OpenLP.FormattingTags', 'Superscript'),
|
u'desc': translate('OpenLP.FormattingTags', 'Superscript'),
|
||||||
u'start tag': u'{su}', u'start html': u'<sup>',
|
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({
|
base_tags.append({
|
||||||
u'desc': translate('OpenLP.FormattingTags', 'Subscript'),
|
u'desc': translate('OpenLP.FormattingTags', 'Subscript'),
|
||||||
u'start tag': u'{sb}', u'start html': u'<sub>',
|
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({
|
base_tags.append({
|
||||||
u'desc': translate('OpenLP.FormattingTags', 'Paragraph'),
|
u'desc': translate('OpenLP.FormattingTags', 'Paragraph'),
|
||||||
u'start tag': u'{p}', u'start html': u'<p>', u'end tag': u'{/p}',
|
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'),
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Bold'),
|
||||||
u'start tag': u'{st}', u'start html': u'<strong>',
|
u'start tag': u'{st}', u'start html': u'<strong>',
|
||||||
u'end tag': u'{/st}', u'end 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({
|
base_tags.append({
|
||||||
u'desc': translate('OpenLP.FormattingTags', 'Italics'),
|
u'desc': translate('OpenLP.FormattingTags', 'Italics'),
|
||||||
u'start tag': u'{it}', u'start html': u'<em>', u'end tag': u'{/it}',
|
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({
|
base_tags.append({
|
||||||
u'desc': translate('OpenLP.FormattingTags', 'Underline'),
|
u'desc': translate('OpenLP.FormattingTags', 'Underline'),
|
||||||
u'start tag': u'{u}',
|
u'start tag': u'{u}',
|
||||||
u'start html': u'<span style="text-decoration: underline;">',
|
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'),
|
base_tags.append({u'desc': translate('OpenLP.FormattingTags', 'Break'),
|
||||||
u'start tag': u'{br}', u'start html': u'<br>', u'end tag': u'',
|
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(base_tags)
|
||||||
|
FormattingTags.add_html_tags(temporary_tags)
|
||||||
|
|
||||||
@staticmethod
|
@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)
|
FormattingTags.html_expands.extend(tags)
|
||||||
|
if save:
|
||||||
|
FormattingTags.save_html_tags()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def remove_html_tag(tag_id):
|
def remove_html_tag(tag_id):
|
||||||
|
@ -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
|
The Custom Tag arrays are saved in a pickle so QSettings works on them. Base
|
||||||
Tags cannot be changed.
|
Tags cannot be changed.
|
||||||
"""
|
"""
|
||||||
import cPickle
|
|
||||||
|
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.lib import translate, FormattingTags
|
from openlp.core.lib import translate, FormattingTags
|
||||||
from openlp.core.lib.ui import critical_error_message_box
|
from openlp.core.lib.ui import critical_error_message_box
|
||||||
from openlp.core.ui.formattingtagdialog import Ui_FormattingTagDialog
|
from openlp.core.ui.formattingtagdialog import Ui_FormattingTagDialog
|
||||||
|
|
||||||
|
|
||||||
class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
||||||
"""
|
"""
|
||||||
The :class:`FormattingTagForm` manages the settings tab .
|
The :class:`FormattingTagForm` manages the settings tab .
|
||||||
@ -48,7 +47,6 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
|||||||
"""
|
"""
|
||||||
QtGui.QDialog.__init__(self, parent)
|
QtGui.QDialog.__init__(self, parent)
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self._loadFormattingTags()
|
|
||||||
QtCore.QObject.connect(self.tagTableWidget,
|
QtCore.QObject.connect(self.tagTableWidget,
|
||||||
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onRowSelected)
|
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onRowSelected)
|
||||||
QtCore.QObject.connect(self.newPushButton,
|
QtCore.QObject.connect(self.newPushButton,
|
||||||
@ -59,41 +57,24 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
|||||||
QtCore.SIGNAL(u'pressed()'), self.onDeletePushed)
|
QtCore.SIGNAL(u'pressed()'), self.onDeletePushed)
|
||||||
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'rejected()'),
|
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'rejected()'),
|
||||||
self.close)
|
self.close)
|
||||||
|
# Forces reloading of tags from openlp configuration.
|
||||||
|
FormattingTags.load_tags()
|
||||||
|
|
||||||
def exec_(self):
|
def exec_(self):
|
||||||
"""
|
"""
|
||||||
Load Display and set field state.
|
Load Display and set field state.
|
||||||
"""
|
"""
|
||||||
# Create initial copy from master
|
# Create initial copy from master
|
||||||
self._loadFormattingTags()
|
|
||||||
self._resetTable()
|
self._resetTable()
|
||||||
self.selected = -1
|
self.selected = -1
|
||||||
return QtGui.QDialog.exec_(self)
|
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):
|
def onRowSelected(self):
|
||||||
"""
|
"""
|
||||||
Table Row selected so display items and set field state.
|
Table Row selected so display items and set field state.
|
||||||
"""
|
"""
|
||||||
row = self.tagTableWidget.currentRow()
|
row = self.tagTableWidget.currentRow()
|
||||||
html = FormattingTags.get_html_tags()[row]
|
html = FormattingTags.html_expands[row]
|
||||||
self.selected = row
|
self.selected = row
|
||||||
self.descriptionLineEdit.setText(html[u'desc'])
|
self.descriptionLineEdit.setText(html[u'desc'])
|
||||||
self.tagLineEdit.setText(self._strip(html[u'start tag']))
|
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.
|
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':
|
if self._strip(html[u'start tag']) == u'n':
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
translate('OpenLP.FormattingTagForm', 'Update Error'),
|
translate('OpenLP.FormattingTagForm', 'Update Error'),
|
||||||
@ -132,7 +113,8 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
|||||||
u'start html': translate('OpenLP.FormattingTagForm', '<HTML here>'),
|
u'start html': translate('OpenLP.FormattingTagForm', '<HTML here>'),
|
||||||
u'end tag': u'{/n}',
|
u'end tag': u'{/n}',
|
||||||
u'end html': translate('OpenLP.FormattingTagForm', '</and here>'),
|
u'end html': translate('OpenLP.FormattingTagForm', '</and here>'),
|
||||||
u'protected': False
|
u'protected': False,
|
||||||
|
u'temporary': False
|
||||||
}
|
}
|
||||||
FormattingTags.add_html_tags([tag])
|
FormattingTags.add_html_tags([tag])
|
||||||
self._resetTable()
|
self._resetTable()
|
||||||
@ -149,13 +131,13 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
|||||||
FormattingTags.remove_html_tag(self.selected)
|
FormattingTags.remove_html_tag(self.selected)
|
||||||
self.selected = -1
|
self.selected = -1
|
||||||
self._resetTable()
|
self._resetTable()
|
||||||
self._saveTable()
|
FormattingTags.save_html_tags()
|
||||||
|
|
||||||
def onSavedPushed(self):
|
def onSavedPushed(self):
|
||||||
"""
|
"""
|
||||||
Update Custom Tag details if not duplicate and save the data.
|
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:
|
if self.selected != -1:
|
||||||
html = html_expands[self.selected]
|
html = html_expands[self.selected]
|
||||||
tag = unicode(self.tagLineEdit.text())
|
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'end html'] = unicode(self.endTagLineEdit.text())
|
||||||
html[u'start tag'] = u'{%s}' % tag
|
html[u'start tag'] = u'{%s}' % tag
|
||||||
html[u'end 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.selected = -1
|
||||||
self._resetTable()
|
self._resetTable()
|
||||||
self._saveTable()
|
FormattingTags.save_html_tags()
|
||||||
|
|
||||||
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''))
|
|
||||||
|
|
||||||
def _resetTable(self):
|
def _resetTable(self):
|
||||||
"""
|
"""
|
||||||
@ -197,9 +169,8 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
|||||||
self.newPushButton.setEnabled(True)
|
self.newPushButton.setEnabled(True)
|
||||||
self.savePushButton.setEnabled(False)
|
self.savePushButton.setEnabled(False)
|
||||||
self.deletePushButton.setEnabled(False)
|
self.deletePushButton.setEnabled(False)
|
||||||
for linenumber, html in enumerate(FormattingTags.get_html_tags()):
|
for linenumber, html in enumerate(FormattingTags.html_expands):
|
||||||
self.tagTableWidget.setRowCount(
|
self.tagTableWidget.setRowCount(self.tagTableWidget.rowCount() + 1)
|
||||||
self.tagTableWidget.rowCount() + 1)
|
|
||||||
self.tagTableWidget.setItem(linenumber, 0,
|
self.tagTableWidget.setItem(linenumber, 0,
|
||||||
QtGui.QTableWidgetItem(html[u'desc']))
|
QtGui.QTableWidgetItem(html[u'desc']))
|
||||||
self.tagTableWidget.setItem(linenumber, 1,
|
self.tagTableWidget.setItem(linenumber, 1,
|
||||||
@ -208,6 +179,9 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
|||||||
QtGui.QTableWidgetItem(html[u'start html']))
|
QtGui.QTableWidgetItem(html[u'start html']))
|
||||||
self.tagTableWidget.setItem(linenumber, 3,
|
self.tagTableWidget.setItem(linenumber, 3,
|
||||||
QtGui.QTableWidgetItem(html[u'end html']))
|
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.tagTableWidget.resizeRowsToContents()
|
||||||
self.descriptionLineEdit.setText(u'')
|
self.descriptionLineEdit.setText(u'')
|
||||||
self.tagLineEdit.setText(u'')
|
self.tagLineEdit.setText(u'')
|
||||||
|
@ -127,6 +127,9 @@ class AppLocation(object):
|
|||||||
CacheDir = 6
|
CacheDir = 6
|
||||||
LanguageDir = 7
|
LanguageDir = 7
|
||||||
|
|
||||||
|
# Base path where data/config/cache dir is located
|
||||||
|
BaseDir = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_directory(dir_type=1):
|
def get_directory(dir_type=1):
|
||||||
"""
|
"""
|
||||||
@ -152,6 +155,8 @@ class AppLocation(object):
|
|||||||
os.path.abspath(os.path.split(sys.argv[0])[0]),
|
os.path.abspath(os.path.split(sys.argv[0])[0]),
|
||||||
_get_os_dir_path(dir_type))
|
_get_os_dir_path(dir_type))
|
||||||
return os.path.join(app_path, u'i18n')
|
return os.path.join(app_path, u'i18n')
|
||||||
|
elif dir_type == AppLocation.DataDir and AppLocation.BaseDir:
|
||||||
|
return os.path.join(AppLocation.BaseDir, 'data')
|
||||||
else:
|
else:
|
||||||
return _get_os_dir_path(dir_type)
|
return _get_os_dir_path(dir_type)
|
||||||
|
|
||||||
|
@ -562,6 +562,9 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
self._updateBackgroundAudio(song, item)
|
self._updateBackgroundAudio(song, item)
|
||||||
editId = song.id
|
editId = song.id
|
||||||
self.onSearchTextButtonClick()
|
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.
|
# Update service with correct song id.
|
||||||
if editId:
|
if editId:
|
||||||
Receiver.send_message(u'service_item_update',
|
Receiver.send_message(u'service_item_update',
|
||||||
|
@ -61,19 +61,21 @@ The XML of an `OpenLyrics <http://openlyrics.info/>`_ song looks like this::
|
|||||||
</song>
|
</song>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import datetime
|
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from lxml import etree, objectify
|
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 import clean_song, VerseType
|
||||||
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic
|
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic
|
||||||
from openlp.core.utils import get_application_version
|
from openlp.core.utils import get_application_version
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
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):
|
class SongXML(object):
|
||||||
"""
|
"""
|
||||||
@ -173,7 +175,7 @@ class SongXML(object):
|
|||||||
|
|
||||||
class OpenLyrics(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.
|
to/from a song.
|
||||||
|
|
||||||
As OpenLyrics has a rich set of different features, we cannot support them
|
As OpenLyrics has a rich set of different features, we cannot support them
|
||||||
@ -198,11 +200,15 @@ class OpenLyrics(object):
|
|||||||
``<key>``
|
``<key>``
|
||||||
This property is not supported.
|
This property is not supported.
|
||||||
|
|
||||||
|
``<format>``
|
||||||
|
The custom formatting tags are fully supported.
|
||||||
|
|
||||||
``<keywords>``
|
``<keywords>``
|
||||||
This property is not supported.
|
This property is not supported.
|
||||||
|
|
||||||
``<lines>``
|
``<lines>``
|
||||||
The attribute *part* is not supported.
|
The attribute *part* is not supported. The *break* attribute is
|
||||||
|
supported.
|
||||||
|
|
||||||
``<publisher>``
|
``<publisher>``
|
||||||
This property is not supported.
|
This property is not supported.
|
||||||
@ -227,15 +233,35 @@ class OpenLyrics(object):
|
|||||||
|
|
||||||
``<verse name="v1a" lang="he" translit="en">``
|
``<verse name="v1a" lang="he" translit="en">``
|
||||||
The attribute *translit* is not supported. Note, the attribute *lang* is
|
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>``
|
``<verseOrder>``
|
||||||
OpenLP supports this property.
|
OpenLP supports this property.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
IMPLEMENTED_VERSION = u'0.7'
|
IMPLEMENTED_VERSION = u'0.8'
|
||||||
|
|
||||||
def __init__(self, manager):
|
def __init__(self, manager):
|
||||||
self.manager = 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):
|
def song_to_xml(self, song):
|
||||||
"""
|
"""
|
||||||
@ -244,13 +270,14 @@ class OpenLyrics(object):
|
|||||||
sxml = SongXML()
|
sxml = SongXML()
|
||||||
song_xml = objectify.fromstring(u'<song/>')
|
song_xml = objectify.fromstring(u'<song/>')
|
||||||
# Append the necessary meta data to the 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)
|
song_xml.set(u'version', OpenLyrics.IMPLEMENTED_VERSION)
|
||||||
application_name = u'OpenLP ' + get_application_version()[u'version']
|
application_name = u'OpenLP ' + get_application_version()[u'version']
|
||||||
song_xml.set(u'createdIn', application_name)
|
song_xml.set(u'createdIn', application_name)
|
||||||
song_xml.set(u'modifiedIn', 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',
|
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')
|
properties = etree.SubElement(song_xml, u'properties')
|
||||||
titles = etree.SubElement(properties, u'titles')
|
titles = etree.SubElement(properties, u'titles')
|
||||||
self._add_text_to_element(u'title', titles, song.title)
|
self._add_text_to_element(u'title', titles, song.title)
|
||||||
@ -284,29 +311,40 @@ class OpenLyrics(object):
|
|||||||
themes = etree.SubElement(properties, u'themes')
|
themes = etree.SubElement(properties, u'themes')
|
||||||
for topic in song.topics:
|
for topic in song.topics:
|
||||||
self._add_text_to_element(u'theme', themes, topic.name)
|
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.
|
# Process the song's lyrics.
|
||||||
lyrics = etree.SubElement(song_xml, u'lyrics')
|
lyrics = etree.SubElement(song_xml, u'lyrics')
|
||||||
verse_list = sxml.get_verses(song.lyrics)
|
verse_list = sxml.get_verses(song.lyrics)
|
||||||
for verse in verse_list:
|
for verse in verse_list:
|
||||||
verse_tag = verse[0][u'type'][0].lower()
|
verse_tag = verse[0][u'type'][0].lower()
|
||||||
verse_number = verse[0][u'label']
|
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.
|
# Create a list with all "virtual" verses.
|
||||||
virtual_verses = verse[1].split(u'[---]')
|
virtual_verses = verse[1].split(u'[---]')
|
||||||
for index, virtual_verse in enumerate(virtual_verses):
|
for index, virtual_verse in enumerate(virtual_verses):
|
||||||
verse_def = verse_tag + verse_number
|
# Add formatting tags to text
|
||||||
# We need "v1a" because we have more than one virtual verse.
|
lines_element = self._add_text_with_tags_to_lines(verse_element,
|
||||||
if len(virtual_verses) > 1:
|
virtual_verse, tags_element)
|
||||||
verse_def += list(u'abcdefghijklmnopqrstuvwxyz')[index]
|
# Do not add the break attribute to the last lines element.
|
||||||
element = \
|
if index < len(virtual_verses) - 1:
|
||||||
self._add_text_to_element(u'verse', lyrics, None, verse_def)
|
lines_element.set(u'break', u'optional')
|
||||||
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)
|
|
||||||
return self._extract_xml(song_xml)
|
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
|
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
|
we also export XML from external sources (e. g. OpenLyrics import), we
|
||||||
@ -314,19 +352,26 @@ class OpenLyrics(object):
|
|||||||
|
|
||||||
``xml``
|
``xml``
|
||||||
The XML to parse (unicode).
|
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.
|
# No xml get out of here.
|
||||||
if not xml:
|
if not xml:
|
||||||
return None
|
return None
|
||||||
if xml[:5] == u'<?xml':
|
if xml[:5] == u'<?xml':
|
||||||
xml = xml[38:]
|
xml = xml[38:]
|
||||||
# Remove chords from xml.
|
|
||||||
xml = CHORD_REGEX.sub(u'', xml)
|
|
||||||
song_xml = objectify.fromstring(xml)
|
song_xml = objectify.fromstring(xml)
|
||||||
if hasattr(song_xml, u'properties'):
|
if hasattr(song_xml, u'properties'):
|
||||||
properties = song_xml.properties
|
properties = song_xml.properties
|
||||||
else:
|
else:
|
||||||
return None
|
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()
|
song = Song()
|
||||||
# Values will be set when cleaning the song.
|
# Values will be set when cleaning the song.
|
||||||
song.search_lyrics = u''
|
song.search_lyrics = u''
|
||||||
@ -336,7 +381,7 @@ class OpenLyrics(object):
|
|||||||
self._process_cclinumber(properties, song)
|
self._process_cclinumber(properties, song)
|
||||||
self._process_titles(properties, song)
|
self._process_titles(properties, song)
|
||||||
# The verse order is processed with the lyrics!
|
# 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_comments(properties, song)
|
||||||
self._process_authors(properties, song)
|
self._process_authors(properties, song)
|
||||||
self._process_songbooks(properties, song)
|
self._process_songbooks(properties, song)
|
||||||
@ -355,6 +400,57 @@ class OpenLyrics(object):
|
|||||||
parent.append(element)
|
parent.append(element)
|
||||||
return 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):
|
def _extract_xml(self, xml):
|
||||||
"""
|
"""
|
||||||
Extract our newly created XML song.
|
Extract our newly created XML song.
|
||||||
@ -362,20 +458,6 @@ class OpenLyrics(object):
|
|||||||
return etree.tostring(xml, encoding=u'UTF-8',
|
return etree.tostring(xml, encoding=u'UTF-8',
|
||||||
xml_declaration=True)
|
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):
|
def _text(self, element):
|
||||||
"""
|
"""
|
||||||
This returns the text of an element as unicode string.
|
This returns the text of an element as unicode string.
|
||||||
@ -457,29 +539,155 @@ class OpenLyrics(object):
|
|||||||
if hasattr(properties, u'copyright'):
|
if hasattr(properties, u'copyright'):
|
||||||
song.copyright = self._text(properties.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.
|
Processes the verses and search_lyrics for the song.
|
||||||
|
|
||||||
``properties``
|
``properties``
|
||||||
The properties object (lxml.objectify.ObjectifiedElement).
|
The properties object (lxml.objectify.ObjectifiedElement).
|
||||||
|
|
||||||
``lyrics``
|
``song_xml``
|
||||||
The lyrics object (lxml.objectify.ObjectifiedElement).
|
The objectified song (lxml.objectify.ObjectifiedElement).
|
||||||
|
|
||||||
``song``
|
``song_obj``
|
||||||
The song object.
|
The song object.
|
||||||
"""
|
"""
|
||||||
sxml = SongXML()
|
sxml = SongXML()
|
||||||
verses = {}
|
verses = {}
|
||||||
verse_def_list = []
|
verse_def_list = []
|
||||||
|
lyrics = song_xml.lyrics
|
||||||
|
# Loop over the "verse" elements.
|
||||||
for verse in lyrics.verse:
|
for verse in lyrics.verse:
|
||||||
text = u''
|
text = u''
|
||||||
|
# Loop over the "lines" elements.
|
||||||
for lines in verse.lines:
|
for lines in verse.lines:
|
||||||
if text:
|
if text:
|
||||||
text += u'\n'
|
text += u'\n'
|
||||||
text += u'\n'.join([unicode(line) for line in lines.line])
|
# Append text from "lines" element to verse text.
|
||||||
verse_def = self._get(verse, u'name').lower()
|
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:
|
if verse_def[0] in VerseType.Tags:
|
||||||
verse_tag = verse_def[0]
|
verse_tag = verse_def[0]
|
||||||
else:
|
else:
|
||||||
@ -489,11 +697,16 @@ class OpenLyrics(object):
|
|||||||
# not correct the verse order.
|
# not correct the verse order.
|
||||||
if not verse_number:
|
if not verse_number:
|
||||||
verse_number = u'1'
|
verse_number = u'1'
|
||||||
lang = None
|
lang = verse.get(u'lang')
|
||||||
if self._get(verse, u'lang'):
|
# In OpenLP 1.9.6 we used v1a, v1b ... to represent visual slide
|
||||||
lang = self._get(verse, u'lang')
|
# breaks. In OpenLyrics 0.7 an attribute has been added.
|
||||||
if verses.has_key((verse_tag, verse_number, lang)):
|
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
|
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:
|
else:
|
||||||
verses[(verse_tag, verse_number, lang)] = text
|
verses[(verse_tag, verse_number, lang)] = text
|
||||||
verse_def_list.append((verse_tag, verse_number, lang))
|
verse_def_list.append((verse_tag, verse_number, lang))
|
||||||
@ -501,10 +714,10 @@ class OpenLyrics(object):
|
|||||||
for verse in verse_def_list:
|
for verse in verse_def_list:
|
||||||
sxml.add_verse_to_lyrics(
|
sxml.add_verse_to_lyrics(
|
||||||
verse[0], verse[1], verses[verse], verse[2])
|
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
|
# Process verse order
|
||||||
if hasattr(properties, u'verseOrder'):
|
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):
|
def _process_songbooks(self, properties, song):
|
||||||
"""
|
"""
|
||||||
@ -520,7 +733,7 @@ class OpenLyrics(object):
|
|||||||
song.song_number = u''
|
song.song_number = u''
|
||||||
if hasattr(properties, u'songbooks'):
|
if hasattr(properties, u'songbooks'):
|
||||||
for songbook in properties.songbooks.songbook:
|
for songbook in properties.songbooks.songbook:
|
||||||
bookname = self._get(songbook, u'name')
|
bookname = songbook.get(u'name', u'')
|
||||||
if bookname:
|
if bookname:
|
||||||
book = self.manager.get_object_filtered(Book,
|
book = self.manager.get_object_filtered(Book,
|
||||||
Book.name == bookname)
|
Book.name == bookname)
|
||||||
@ -529,7 +742,7 @@ class OpenLyrics(object):
|
|||||||
book = Book.populate(name=bookname, publisher=u'')
|
book = Book.populate(name=bookname, publisher=u'')
|
||||||
self.manager.save_object(book)
|
self.manager.save_object(book)
|
||||||
song.song_book_id = book.id
|
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.
|
# We only support one song book, so take the first one.
|
||||||
break
|
break
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user