forked from openlp/openlp
r1975
This commit is contained in:
commit
d2a38f676c
@ -26,12 +26,6 @@
|
|||||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
# Import uuid now, to avoid the rare bug described in the support system:
|
|
||||||
# http://support.openlp.org/issues/102
|
|
||||||
# If https://bugs.gentoo.org/show_bug.cgi?id=317557 is fixed, the import can be
|
|
||||||
# removed.
|
|
||||||
import uuid
|
|
||||||
|
|
||||||
from openlp.core import main
|
from openlp.core import main
|
||||||
|
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ class OpenLP(QtGui.QApplication):
|
|||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'cursor_normal'), self.setNormalCursor)
|
QtCore.SIGNAL(u'cursor_normal'), self.setNormalCursor)
|
||||||
# Decide how many screens we have and their size
|
# Decide how many screens we have and their size
|
||||||
screens = ScreenList(self.desktop())
|
screens = ScreenList.create(self.desktop())
|
||||||
# First time checks in settings
|
# First time checks in settings
|
||||||
has_run_wizard = QtCore.QSettings().value(
|
has_run_wizard = QtCore.QSettings().value(
|
||||||
u'general/has run wizard', QtCore.QVariant(False)).toBool()
|
u'general/has run wizard', QtCore.QVariant(False)).toBool()
|
||||||
|
@ -52,9 +52,8 @@ class OpenLPDockWidget(QtGui.QDockWidget):
|
|||||||
if icon:
|
if icon:
|
||||||
self.setWindowIcon(build_icon(icon))
|
self.setWindowIcon(build_icon(icon))
|
||||||
# Sort out the minimum width.
|
# Sort out the minimum width.
|
||||||
screens = ScreenList.get_instance()
|
screens = ScreenList()
|
||||||
screen_width = screens.current[u'size'].width()
|
mainwindow_docbars = screens.current[u'size'].width() / 5
|
||||||
mainwindow_docbars = screen_width / 5
|
|
||||||
if mainwindow_docbars > 300:
|
if mainwindow_docbars > 300:
|
||||||
self.setMinimumWidth(300)
|
self.setMinimumWidth(300)
|
||||||
else:
|
else:
|
||||||
|
@ -46,13 +46,36 @@ 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.
|
|
||||||
return FormattingTags.html_expands
|
return FormattingTags.html_expands
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def reset_html_tags():
|
def save_html_tags():
|
||||||
"""
|
"""
|
||||||
Resets the html_expands list.
|
Saves all formatting tags except protected ones.
|
||||||
|
"""
|
||||||
|
tags = []
|
||||||
|
for tag in FormattingTags.html_expands:
|
||||||
|
if not tag[u'protected'] and not tag.get(u'temporary'):
|
||||||
|
# Using dict ensures that copy is made and encoding of values
|
||||||
|
# a little later does not affect tags in the original list
|
||||||
|
tags.append(dict(tag))
|
||||||
|
tag = tags[-1]
|
||||||
|
# Remove key 'temporary' from tags.
|
||||||
|
# It is not needed to be saved.
|
||||||
|
if u'temporary' in tag:
|
||||||
|
del tag[u'temporary']
|
||||||
|
for element in tag:
|
||||||
|
if isinstance(tag[element], unicode):
|
||||||
|
tag[element] = tag[element].encode('utf8')
|
||||||
|
# Formatting Tags were also known as display tags.
|
||||||
|
QtCore.QSettings().setValue(u'displayTags/html_tags',
|
||||||
|
QtCore.QVariant(cPickle.dumps(tags) if tags else u''))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_tags():
|
||||||
|
"""
|
||||||
|
Load the Tags from store so can be used in the system or used to
|
||||||
|
update the display.
|
||||||
"""
|
"""
|
||||||
temporary_tags = [tag for tag in FormattingTags.html_expands
|
temporary_tags = [tag for tag in FormattingTags.html_expands
|
||||||
if tag.get(u'temporary')]
|
if tag.get(u'temporary')]
|
||||||
@ -140,38 +163,6 @@ class FormattingTags(object):
|
|||||||
FormattingTags.add_html_tags(base_tags)
|
FormattingTags.add_html_tags(base_tags)
|
||||||
FormattingTags.add_html_tags(temporary_tags)
|
FormattingTags.add_html_tags(temporary_tags)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def save_html_tags():
|
|
||||||
"""
|
|
||||||
Saves all formatting tags except protected ones.
|
|
||||||
"""
|
|
||||||
tags = []
|
|
||||||
for tag in FormattingTags.html_expands:
|
|
||||||
if not tag[u'protected'] and not tag.get(u'temporary'):
|
|
||||||
# Using dict ensures that copy is made and encoding of values
|
|
||||||
# a little later does not affect tags in the original list
|
|
||||||
tags.append(dict(tag))
|
|
||||||
tag = tags[-1]
|
|
||||||
# Remove key 'temporary' from tags.
|
|
||||||
# It is not needed to be saved.
|
|
||||||
if u'temporary' in tag:
|
|
||||||
del tag[u'temporary']
|
|
||||||
for element in tag:
|
|
||||||
if isinstance(tag[element], unicode):
|
|
||||||
tag[element] = tag[element].encode('utf8')
|
|
||||||
# Formatting Tags were also known as display tags.
|
|
||||||
QtCore.QSettings().setValue(u'displayTags/html_tags',
|
|
||||||
QtCore.QVariant(cPickle.dumps(tags) if tags else u''))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def load_tags():
|
|
||||||
"""
|
|
||||||
Load the Tags from store so can be used in the system or used to
|
|
||||||
update the display. If Cancel was selected this is needed to reset the
|
|
||||||
dsiplay to the correct version.
|
|
||||||
"""
|
|
||||||
# Initial Load of the Tags
|
|
||||||
FormattingTags.reset_html_tags()
|
|
||||||
# Formatting Tags were also known as display tags.
|
# Formatting Tags were also known as display tags.
|
||||||
user_expands = QtCore.QSettings().value(u'displayTags/html_tags',
|
user_expands = QtCore.QSettings().value(u'displayTags/html_tags',
|
||||||
QtCore.QVariant(u'')).toString()
|
QtCore.QVariant(u'')).toString()
|
||||||
@ -187,17 +178,13 @@ class FormattingTags(object):
|
|||||||
FormattingTags.add_html_tags(user_tags)
|
FormattingTags.add_html_tags(user_tags)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def add_html_tags(tags, save=False):
|
def add_html_tags(tags):
|
||||||
"""
|
"""
|
||||||
Add a list of tags to the list.
|
Add a list of tags to the list.
|
||||||
|
|
||||||
``tags``
|
``tags``
|
||||||
The list with tags to add.
|
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:
|
Each **tag** has to be a ``dict`` and should have the following keys:
|
||||||
|
|
||||||
* desc
|
* desc
|
||||||
@ -225,8 +212,6 @@ class FormattingTags(object):
|
|||||||
displaying text containing the tag. It has to be a ``boolean``.
|
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):
|
||||||
|
@ -163,7 +163,7 @@ class ImageManager(QtCore.QObject):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
QtCore.QObject.__init__(self)
|
QtCore.QObject.__init__(self)
|
||||||
current_screen = ScreenList.get_instance().current
|
current_screen = ScreenList().current
|
||||||
self.width = current_screen[u'size'].width()
|
self.width = current_screen[u'size'].width()
|
||||||
self.height = current_screen[u'size'].height()
|
self.height = current_screen[u'size'].height()
|
||||||
self._cache = {}
|
self._cache = {}
|
||||||
@ -177,7 +177,7 @@ class ImageManager(QtCore.QObject):
|
|||||||
Screen has changed size so rebuild the cache to new size.
|
Screen has changed size so rebuild the cache to new size.
|
||||||
"""
|
"""
|
||||||
log.debug(u'update_display')
|
log.debug(u'update_display')
|
||||||
current_screen = ScreenList.get_instance().current
|
current_screen = ScreenList().current
|
||||||
self.width = current_screen[u'size'].width()
|
self.width = current_screen[u'size'].width()
|
||||||
self.height = current_screen[u'size'].height()
|
self.height = current_screen[u'size'].height()
|
||||||
# Mark the images as dirty for a rebuild by setting the image and byte
|
# Mark the images as dirty for a rebuild by setting the image and byte
|
||||||
|
@ -69,7 +69,7 @@ class Renderer(object):
|
|||||||
log.debug(u'Initialisation started')
|
log.debug(u'Initialisation started')
|
||||||
self.themeManager = themeManager
|
self.themeManager = themeManager
|
||||||
self.imageManager = imageManager
|
self.imageManager = imageManager
|
||||||
self.screens = ScreenList.get_instance()
|
self.screens = ScreenList()
|
||||||
self.service_theme = u''
|
self.service_theme = u''
|
||||||
self.theme_level = u''
|
self.theme_level = u''
|
||||||
self.override_background = None
|
self.override_background = None
|
||||||
@ -125,7 +125,7 @@ class Renderer(object):
|
|||||||
Set the appropriate theme depending on the theme level.
|
Set the appropriate theme depending on the theme level.
|
||||||
Called by the service item when building a display frame
|
Called by the service item when building a display frame
|
||||||
|
|
||||||
``theme``
|
``override_theme``
|
||||||
The name of the song-level theme. None means the service
|
The name of the song-level theme. None means the service
|
||||||
item wants to use the given value.
|
item wants to use the given value.
|
||||||
|
|
||||||
@ -235,8 +235,8 @@ class Renderer(object):
|
|||||||
# the first two slides (and neglect the last for now).
|
# the first two slides (and neglect the last for now).
|
||||||
if len(slides) == 3:
|
if len(slides) == 3:
|
||||||
html_text = expand_tags(u'\n'.join(slides[:2]))
|
html_text = expand_tags(u'\n'.join(slides[:2]))
|
||||||
# We check both slides to determine if the optional break is
|
# We check both slides to determine if the optional split is
|
||||||
# needed (there is only one optional break).
|
# needed (there is only one optional split).
|
||||||
else:
|
else:
|
||||||
html_text = expand_tags(u'\n'.join(slides))
|
html_text = expand_tags(u'\n'.join(slides))
|
||||||
html_text = html_text.replace(u'\n', u'<br>')
|
html_text = html_text.replace(u'\n', u'<br>')
|
||||||
@ -247,14 +247,18 @@ class Renderer(object):
|
|||||||
else:
|
else:
|
||||||
# The first optional slide fits, which means we have to
|
# The first optional slide fits, which means we have to
|
||||||
# render the first optional slide.
|
# render the first optional slide.
|
||||||
text_contains_break = u'[---]' in text
|
text_contains_split = u'[---]' in text
|
||||||
if text_contains_break:
|
if text_contains_split:
|
||||||
try:
|
try:
|
||||||
text_to_render, text = \
|
text_to_render, text = \
|
||||||
text.split(u'\n[---]\n', 1)
|
text.split(u'\n[---]\n', 1)
|
||||||
except:
|
except:
|
||||||
text_to_render = text.split(u'\n[---]\n')[0]
|
text_to_render = text.split(u'\n[---]\n')[0]
|
||||||
text = u''
|
text = u''
|
||||||
|
text_to_render, raw_tags, html_tags = \
|
||||||
|
self._get_start_tags(text_to_render)
|
||||||
|
if text:
|
||||||
|
text = raw_tags + text
|
||||||
else:
|
else:
|
||||||
text_to_render = text
|
text_to_render = text
|
||||||
text = u''
|
text = u''
|
||||||
@ -263,7 +267,7 @@ class Renderer(object):
|
|||||||
if len(slides) > 1 and text:
|
if len(slides) > 1 and text:
|
||||||
# Add all slides apart from the last one the list.
|
# Add all slides apart from the last one the list.
|
||||||
pages.extend(slides[:-1])
|
pages.extend(slides[:-1])
|
||||||
if text_contains_break:
|
if text_contains_split:
|
||||||
text = slides[-1] + u'\n[---]\n' + text
|
text = slides[-1] + u'\n[---]\n' + text
|
||||||
else:
|
else:
|
||||||
text = slides[-1] + u'\n'+ text
|
text = slides[-1] + u'\n'+ text
|
||||||
|
@ -313,17 +313,12 @@ class ServiceItem(object):
|
|||||||
self.from_plugin = header[u'from_plugin']
|
self.from_plugin = header[u'from_plugin']
|
||||||
self.capabilities = header[u'capabilities']
|
self.capabilities = header[u'capabilities']
|
||||||
# Added later so may not be present in older services.
|
# Added later so may not be present in older services.
|
||||||
if u'search' in header:
|
self.search_string = header.get(u'search', u'')
|
||||||
self.search_string = header[u'search']
|
self.data_string = header.get(u'data', u'')
|
||||||
self.data_string = header[u'data']
|
self.xml_version = header.get(u'xml_version')
|
||||||
if u'xml_version' in header:
|
self.start_time = header.get(u'start_time', 0)
|
||||||
self.xml_version = header[u'xml_version']
|
self.end_time = header.get(u'end_time', 0)
|
||||||
if u'start_time' in header:
|
self.media_length = header.get(u'media_length', 0)
|
||||||
self.start_time = header[u'start_time']
|
|
||||||
if u'end_time' in header:
|
|
||||||
self.end_time = header[u'end_time']
|
|
||||||
if u'media_length' in header:
|
|
||||||
self.media_length = header[u'media_length']
|
|
||||||
if u'background_audio' in header:
|
if u'background_audio' in header:
|
||||||
self.background_audio = []
|
self.background_audio = []
|
||||||
for filename in header[u'background_audio']:
|
for filename in header[u'background_audio']:
|
||||||
|
@ -94,6 +94,7 @@ class UiStrings(object):
|
|||||||
self.NewService = translate('OpenLP.Ui', 'New Service')
|
self.NewService = translate('OpenLP.Ui', 'New Service')
|
||||||
self.NewTheme = translate('OpenLP.Ui', 'New Theme')
|
self.NewTheme = translate('OpenLP.Ui', 'New Theme')
|
||||||
self.NextTrack = translate('OpenLP.Ui', 'Next Track')
|
self.NextTrack = translate('OpenLP.Ui', 'Next Track')
|
||||||
|
self.NFdSs = translate('OpenLP.Ui', 'No Folder Selected', 'Singular')
|
||||||
self.NFSs = translate('OpenLP.Ui', 'No File Selected', 'Singular')
|
self.NFSs = translate('OpenLP.Ui', 'No File Selected', 'Singular')
|
||||||
self.NFSp = translate('OpenLP.Ui', 'No Files Selected', 'Plural')
|
self.NFSp = translate('OpenLP.Ui', 'No Files Selected', 'Plural')
|
||||||
self.NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular')
|
self.NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular')
|
||||||
|
@ -57,6 +57,14 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
|||||||
QtCore.SIGNAL(u'clicked()'), self.onDeleteClicked)
|
QtCore.SIGNAL(u'clicked()'), self.onDeleteClicked)
|
||||||
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'rejected()'),
|
QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(u'rejected()'),
|
||||||
self.close)
|
self.close)
|
||||||
|
QtCore.QObject.connect(self.descriptionLineEdit,
|
||||||
|
QtCore.SIGNAL(u'textEdited(QString)'), self.onTextEdited)
|
||||||
|
QtCore.QObject.connect(self.tagLineEdit,
|
||||||
|
QtCore.SIGNAL(u'textEdited(QString)'), self.onTextEdited)
|
||||||
|
QtCore.QObject.connect(self.startTagLineEdit,
|
||||||
|
QtCore.SIGNAL(u'textEdited(QString)'), self.onTextEdited)
|
||||||
|
QtCore.QObject.connect(self.endTagLineEdit,
|
||||||
|
QtCore.SIGNAL(u'textEdited(QString)'), self.onTextEdited)
|
||||||
# Forces reloading of tags from openlp configuration.
|
# Forces reloading of tags from openlp configuration.
|
||||||
FormattingTags.load_tags()
|
FormattingTags.load_tags()
|
||||||
|
|
||||||
@ -65,7 +73,7 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
|||||||
Load Display and set field state.
|
Load Display and set field state.
|
||||||
"""
|
"""
|
||||||
# Create initial copy from master
|
# Create initial copy from master
|
||||||
self._resetTable()
|
self._reloadTable()
|
||||||
self.selected = -1
|
self.selected = -1
|
||||||
return QtGui.QDialog.exec_(self)
|
return QtGui.QDialog.exec_(self)
|
||||||
|
|
||||||
@ -73,9 +81,9 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
|||||||
"""
|
"""
|
||||||
Table Row selected so display items and set field state.
|
Table Row selected so display items and set field state.
|
||||||
"""
|
"""
|
||||||
row = self.tagTableWidget.currentRow()
|
self.savePushButton.setEnabled(False)
|
||||||
html = FormattingTags.html_expands[row]
|
self.selected = self.tagTableWidget.currentRow()
|
||||||
self.selected = row
|
html = FormattingTags.get_html_tags()[self.selected]
|
||||||
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']))
|
||||||
self.startTagLineEdit.setText(html[u'start html'])
|
self.startTagLineEdit.setText(html[u'start html'])
|
||||||
@ -85,21 +93,26 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
|||||||
self.tagLineEdit.setEnabled(False)
|
self.tagLineEdit.setEnabled(False)
|
||||||
self.startTagLineEdit.setEnabled(False)
|
self.startTagLineEdit.setEnabled(False)
|
||||||
self.endTagLineEdit.setEnabled(False)
|
self.endTagLineEdit.setEnabled(False)
|
||||||
self.savePushButton.setEnabled(False)
|
|
||||||
self.deletePushButton.setEnabled(False)
|
self.deletePushButton.setEnabled(False)
|
||||||
else:
|
else:
|
||||||
self.descriptionLineEdit.setEnabled(True)
|
self.descriptionLineEdit.setEnabled(True)
|
||||||
self.tagLineEdit.setEnabled(True)
|
self.tagLineEdit.setEnabled(True)
|
||||||
self.startTagLineEdit.setEnabled(True)
|
self.startTagLineEdit.setEnabled(True)
|
||||||
self.endTagLineEdit.setEnabled(True)
|
self.endTagLineEdit.setEnabled(True)
|
||||||
self.savePushButton.setEnabled(True)
|
|
||||||
self.deletePushButton.setEnabled(True)
|
self.deletePushButton.setEnabled(True)
|
||||||
|
|
||||||
|
def onTextEdited(self, text):
|
||||||
|
"""
|
||||||
|
Enable the ``savePushButton`` when any of the selected tag's properties
|
||||||
|
has been changed.
|
||||||
|
"""
|
||||||
|
self.savePushButton.setEnabled(True)
|
||||||
|
|
||||||
def onNewClicked(self):
|
def onNewClicked(self):
|
||||||
"""
|
"""
|
||||||
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.html_expands:
|
for html in FormattingTags.get_html_tags():
|
||||||
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'),
|
||||||
@ -117,11 +130,13 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
|||||||
u'temporary': False
|
u'temporary': False
|
||||||
}
|
}
|
||||||
FormattingTags.add_html_tags([tag])
|
FormattingTags.add_html_tags([tag])
|
||||||
self._resetTable()
|
FormattingTags.save_html_tags()
|
||||||
|
self._reloadTable()
|
||||||
# Highlight new row
|
# Highlight new row
|
||||||
self.tagTableWidget.selectRow(self.tagTableWidget.rowCount() - 1)
|
self.tagTableWidget.selectRow(self.tagTableWidget.rowCount() - 1)
|
||||||
self.onRowSelected()
|
self.onRowSelected()
|
||||||
self.tagTableWidget.scrollToBottom()
|
self.tagTableWidget.scrollToBottom()
|
||||||
|
#self.savePushButton.setEnabled(False)
|
||||||
|
|
||||||
def onDeleteClicked(self):
|
def onDeleteClicked(self):
|
||||||
"""
|
"""
|
||||||
@ -130,14 +145,14 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
|||||||
if self.selected != -1:
|
if self.selected != -1:
|
||||||
FormattingTags.remove_html_tag(self.selected)
|
FormattingTags.remove_html_tag(self.selected)
|
||||||
self.selected = -1
|
self.selected = -1
|
||||||
self._resetTable()
|
|
||||||
FormattingTags.save_html_tags()
|
FormattingTags.save_html_tags()
|
||||||
|
self._reloadTable()
|
||||||
|
|
||||||
def onSavedClicked(self):
|
def onSavedClicked(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.html_expands
|
html_expands = FormattingTags.get_html_tags()
|
||||||
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())
|
||||||
@ -157,14 +172,13 @@ class FormattingTagForm(QtGui.QDialog, Ui_FormattingTagDialog):
|
|||||||
# Keep temporary tags when the user changes one.
|
# Keep temporary tags when the user changes one.
|
||||||
html[u'temporary'] = False
|
html[u'temporary'] = False
|
||||||
self.selected = -1
|
self.selected = -1
|
||||||
self._resetTable()
|
|
||||||
FormattingTags.save_html_tags()
|
FormattingTags.save_html_tags()
|
||||||
|
self._reloadTable()
|
||||||
|
|
||||||
def _resetTable(self):
|
def _reloadTable(self):
|
||||||
"""
|
"""
|
||||||
Reset List for loading.
|
Reset List for loading.
|
||||||
"""
|
"""
|
||||||
FormattingTags.load_tags()
|
|
||||||
self.tagTableWidget.clearContents()
|
self.tagTableWidget.clearContents()
|
||||||
self.tagTableWidget.setRowCount(0)
|
self.tagTableWidget.setRowCount(0)
|
||||||
self.newPushButton.setEnabled(True)
|
self.newPushButton.setEnabled(True)
|
||||||
|
@ -42,7 +42,7 @@ class GeneralTab(SettingsTab):
|
|||||||
"""
|
"""
|
||||||
Initialise the general settings tab
|
Initialise the general settings tab
|
||||||
"""
|
"""
|
||||||
self.screens = ScreenList.get_instance()
|
self.screens = ScreenList()
|
||||||
self.iconPath = u':/icon/openlp-logo-16x16.png'
|
self.iconPath = u':/icon/openlp-logo-16x16.png'
|
||||||
generalTranslated = translate('OpenLP.GeneralTab', 'General')
|
generalTranslated = translate('OpenLP.GeneralTab', 'General')
|
||||||
SettingsTab.__init__(self, parent, u'General', generalTranslated)
|
SettingsTab.__init__(self, parent, u'General', generalTranslated)
|
||||||
|
@ -119,7 +119,7 @@ class MainDisplay(Display):
|
|||||||
def __init__(self, parent, imageManager, live, controller):
|
def __init__(self, parent, imageManager, live, controller):
|
||||||
Display.__init__(self, parent, live, controller)
|
Display.__init__(self, parent, live, controller)
|
||||||
self.imageManager = imageManager
|
self.imageManager = imageManager
|
||||||
self.screens = ScreenList.get_instance()
|
self.screens = ScreenList()
|
||||||
self.plugins = PluginManager.get_instance().plugins
|
self.plugins = PluginManager.get_instance().plugins
|
||||||
self.rebuildCSS = False
|
self.rebuildCSS = False
|
||||||
self.hideMode = None
|
self.hideMode = None
|
||||||
|
@ -795,7 +795,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
|
|||||||
if answer == QtGui.QMessageBox.No:
|
if answer == QtGui.QMessageBox.No:
|
||||||
return
|
return
|
||||||
Receiver.send_message(u'cursor_busy')
|
Receiver.send_message(u'cursor_busy')
|
||||||
screens = ScreenList.get_instance()
|
screens = ScreenList()
|
||||||
FirstTimeForm(screens, self).exec_()
|
FirstTimeForm(screens, self).exec_()
|
||||||
self.firstTime()
|
self.firstTime()
|
||||||
for plugin in self.pluginManager.plugins:
|
for plugin in self.pluginManager.plugins:
|
||||||
|
@ -41,36 +41,40 @@ class ScreenList(object):
|
|||||||
"""
|
"""
|
||||||
Wrapper to handle the parameters of the display screen.
|
Wrapper to handle the parameters of the display screen.
|
||||||
|
|
||||||
To get access to the screen list call ``ScreenList.get_instance()``.
|
To get access to the screen list call ``ScreenList()``.
|
||||||
"""
|
"""
|
||||||
log.info(u'Screen loaded')
|
log.info(u'Screen loaded')
|
||||||
instance = None
|
__instance__ = None
|
||||||
|
|
||||||
@staticmethod
|
def __new__(cls):
|
||||||
def get_instance():
|
if not cls.__instance__:
|
||||||
return ScreenList.instance
|
cls.__instance__ = object.__new__(cls)
|
||||||
|
return cls.__instance__
|
||||||
|
|
||||||
def __init__(self, desktop):
|
@classmethod
|
||||||
|
def create(cls, desktop):
|
||||||
"""
|
"""
|
||||||
Initialise the screen list.
|
Initialise the screen list.
|
||||||
|
|
||||||
``desktop``
|
``desktop``
|
||||||
A ``QDesktopWidget`` object.
|
A ``QDesktopWidget`` object.
|
||||||
"""
|
"""
|
||||||
ScreenList.instance = self
|
screen_list = cls()
|
||||||
self.desktop = desktop
|
screen_list.desktop = desktop
|
||||||
self.preview = None
|
screen_list.preview = None
|
||||||
self.current = None
|
screen_list.current = None
|
||||||
self.override = None
|
screen_list.override = None
|
||||||
self.screen_list = []
|
screen_list.screen_list = []
|
||||||
self.display_count = 0
|
screen_list.display_count = 0
|
||||||
self.screen_count_changed()
|
screen_list.screen_count_changed()
|
||||||
self._load_screen_settings()
|
screen_list._load_screen_settings()
|
||||||
QtCore.QObject.connect(desktop,
|
QtCore.QObject.connect(desktop,
|
||||||
QtCore.SIGNAL(u'resized(int)'), self.screen_resolution_changed)
|
QtCore.SIGNAL(u'resized(int)'),
|
||||||
|
screen_list.screen_resolution_changed)
|
||||||
QtCore.QObject.connect(desktop,
|
QtCore.QObject.connect(desktop,
|
||||||
QtCore.SIGNAL(u'screenCountChanged(int)'),
|
QtCore.SIGNAL(u'screenCountChanged(int)'),
|
||||||
self.screen_count_changed)
|
screen_list.screen_count_changed)
|
||||||
|
return screen_list
|
||||||
|
|
||||||
def screen_resolution_changed(self, number):
|
def screen_resolution_changed(self, number):
|
||||||
"""
|
"""
|
||||||
@ -233,8 +237,8 @@ class ScreenList(object):
|
|||||||
y = window.y() + (window.height() / 2)
|
y = window.y() + (window.height() / 2)
|
||||||
for screen in self.screen_list:
|
for screen in self.screen_list:
|
||||||
size = screen[u'size']
|
size = screen[u'size']
|
||||||
if x >= size.x() and x <= (size.x() + size.width()) \
|
if x >= size.x() and x <= (size.x() + size.width()) and \
|
||||||
and y >= size.y() and y <= (size.y() + size.height()):
|
y >= size.y() and y <= (size.y() + size.height()):
|
||||||
return screen[u'number']
|
return screen[u'number']
|
||||||
|
|
||||||
def _load_screen_settings(self):
|
def _load_screen_settings(self):
|
||||||
|
@ -51,7 +51,7 @@ class ServiceManagerList(QtGui.QTreeWidget):
|
|||||||
"""
|
"""
|
||||||
Set up key bindings and mouse behaviour for the service list
|
Set up key bindings and mouse behaviour for the service list
|
||||||
"""
|
"""
|
||||||
def __init__(self, serviceManager, parent=None, name=None):
|
def __init__(self, serviceManager, parent=None):
|
||||||
QtGui.QTreeWidget.__init__(self, parent)
|
QtGui.QTreeWidget.__init__(self, parent)
|
||||||
self.serviceManager = serviceManager
|
self.serviceManager = serviceManager
|
||||||
|
|
||||||
@ -64,6 +64,9 @@ class ServiceManagerList(QtGui.QTreeWidget):
|
|||||||
elif event.key() == QtCore.Qt.Key_Down:
|
elif event.key() == QtCore.Qt.Key_Down:
|
||||||
self.serviceManager.onMoveSelectionDown()
|
self.serviceManager.onMoveSelectionDown()
|
||||||
event.accept()
|
event.accept()
|
||||||
|
elif event.key() == QtCore.Qt.Key_Delete:
|
||||||
|
self.serviceManager.onDeleteFromService()
|
||||||
|
event.accept()
|
||||||
event.ignore()
|
event.ignore()
|
||||||
else:
|
else:
|
||||||
event.ignore()
|
event.ignore()
|
||||||
@ -101,7 +104,6 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
QtGui.QWidget.__init__(self, parent)
|
QtGui.QWidget.__init__(self, parent)
|
||||||
self.mainwindow = mainwindow
|
self.mainwindow = mainwindow
|
||||||
self.serviceItems = []
|
self.serviceItems = []
|
||||||
self.serviceName = u''
|
|
||||||
self.suffixes = []
|
self.suffixes = []
|
||||||
self.dropPosition = 0
|
self.dropPosition = 0
|
||||||
self.expandTabs = False
|
self.expandTabs = False
|
||||||
@ -219,6 +221,7 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
icon=u':/general/general_delete.png',
|
icon=u':/general/general_delete.png',
|
||||||
tooltip=translate('OpenLP.ServiceManager',
|
tooltip=translate('OpenLP.ServiceManager',
|
||||||
'Delete the selected item from the service.'),
|
'Delete the selected item from the service.'),
|
||||||
|
shortcuts=[QtCore.Qt.Key_Delete],
|
||||||
triggers=self.onDeleteFromService)
|
triggers=self.onDeleteFromService)
|
||||||
self.orderToolbar.addSeparator()
|
self.orderToolbar.addSeparator()
|
||||||
self.serviceManagerList.expand = self.orderToolbar.addToolbarAction(
|
self.serviceManagerList.expand = self.orderToolbar.addToolbarAction(
|
||||||
@ -299,17 +302,14 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
self.timeAction = create_widget_action(self.menu,
|
self.timeAction = create_widget_action(self.menu,
|
||||||
text=translate('OpenLP.ServiceManager', '&Start Time'),
|
text=translate('OpenLP.ServiceManager', '&Start Time'),
|
||||||
icon=u':/media/media_time.png', triggers=self.onStartTimeForm)
|
icon=u':/media/media_time.png', triggers=self.onStartTimeForm)
|
||||||
self.deleteAction = create_widget_action(self.menu,
|
# Add already existing delete action to the menu.
|
||||||
text=translate('OpenLP.ServiceManager', '&Delete From Service'),
|
self.menu.addAction(self.serviceManagerList.delete)
|
||||||
icon=u':/general/general_delete.png',
|
|
||||||
triggers=self.onDeleteFromService)
|
|
||||||
self.menu.addSeparator()
|
self.menu.addSeparator()
|
||||||
self.previewAction = create_widget_action(self.menu,
|
self.previewAction = create_widget_action(self.menu,
|
||||||
text=translate('OpenLP.ServiceManager', 'Show &Preview'),
|
text=translate('OpenLP.ServiceManager', 'Show &Preview'),
|
||||||
icon=u':/general/general_preview.png', triggers=self.makePreview)
|
icon=u':/general/general_preview.png', triggers=self.makePreview)
|
||||||
self.liveAction = create_widget_action(self.menu,
|
# Add already existing make live action to the menu.
|
||||||
text=translate('OpenLP.ServiceManager', 'Show &Live'),
|
self.menu.addAction(self.serviceManagerList.makeLive)
|
||||||
icon=u':/general/general_live.png', triggers=self.makeLive)
|
|
||||||
self.menu.addSeparator()
|
self.menu.addSeparator()
|
||||||
self.themeMenu = QtGui.QMenu(
|
self.themeMenu = QtGui.QMenu(
|
||||||
translate('OpenLP.ServiceManager', '&Change Item Theme'))
|
translate('OpenLP.ServiceManager', '&Change Item Theme'))
|
||||||
@ -561,14 +561,12 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
zip.write(audio_from, audio_to.encode(u'utf-8'))
|
zip.write(audio_from, audio_to.encode(u'utf-8'))
|
||||||
except IOError:
|
except IOError:
|
||||||
log.exception(u'Failed to save service to disk: %s', temp_file_name)
|
log.exception(u'Failed to save service to disk: %s', temp_file_name)
|
||||||
# Add this line in after the release to notify the user that saving
|
Receiver.send_message(u'openlp_error_message', {
|
||||||
# their file failed. Commented out due to string freeze.
|
u'title': translate(u'OpenLP.ServiceManager',
|
||||||
#Receiver.send_message(u'openlp_error_message', {
|
u'Error Saving File'),
|
||||||
# u'title': translate(u'OpenLP.ServiceManager',
|
u'message': translate(u'OpenLP.ServiceManager',
|
||||||
# u'Error Saving File'),
|
u'There was an error saving your file.')
|
||||||
# u'message': translate(u'OpenLP.ServiceManager',
|
})
|
||||||
# u'There was an error saving your file.')
|
|
||||||
#})
|
|
||||||
success = False
|
success = False
|
||||||
finally:
|
finally:
|
||||||
if zip:
|
if zip:
|
||||||
@ -1318,15 +1316,15 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
|
|
||||||
def findServiceItem(self):
|
def findServiceItem(self):
|
||||||
"""
|
"""
|
||||||
Finds the selected ServiceItem in the list and returns the position of
|
Finds the first selected ServiceItem in the list and returns the
|
||||||
the serviceitem and its selected child item. For example, if the third
|
position of the serviceitem and its selected child item. For example,
|
||||||
child item (in the Slidecontroller known as slide) in the second service
|
if the third child item (in the Slidecontroller known as slide) in the
|
||||||
item is selected this will return::
|
second service item is selected this will return::
|
||||||
|
|
||||||
(1, 2)
|
(1, 2)
|
||||||
"""
|
"""
|
||||||
items = self.serviceManagerList.selectedItems()
|
items = self.serviceManagerList.selectedItems()
|
||||||
serviceItem = 0
|
serviceItem = -1
|
||||||
serviceItemChild = -1
|
serviceItemChild = -1
|
||||||
for item in items:
|
for item in items:
|
||||||
parentitem = item.parent()
|
parentitem = item.parent()
|
||||||
@ -1337,6 +1335,8 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
serviceItemChild = item.data(0, QtCore.Qt.UserRole).toInt()[0]
|
serviceItemChild = item.data(0, QtCore.Qt.UserRole).toInt()[0]
|
||||||
# Adjust for zero based arrays.
|
# Adjust for zero based arrays.
|
||||||
serviceItem -= 1
|
serviceItem -= 1
|
||||||
|
# Only process the first item on the list for this method.
|
||||||
|
break
|
||||||
return serviceItem, serviceItemChild
|
return serviceItem, serviceItemChild
|
||||||
|
|
||||||
def dragEnterEvent(self, event):
|
def dragEnterEvent(self, event):
|
||||||
|
@ -83,7 +83,7 @@ class SlideController(Controller):
|
|||||||
Set up the Slide Controller.
|
Set up the Slide Controller.
|
||||||
"""
|
"""
|
||||||
Controller.__init__(self, parent, isLive)
|
Controller.__init__(self, parent, isLive)
|
||||||
self.screens = ScreenList.get_instance()
|
self.screens = ScreenList()
|
||||||
try:
|
try:
|
||||||
self.ratio = float(self.screens.current[u'size'].width()) / \
|
self.ratio = float(self.screens.current[u'size'].width()) / \
|
||||||
float(self.screens.current[u'size'].height())
|
float(self.screens.current[u'size'].height())
|
||||||
|
@ -136,16 +136,14 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
|||||||
"""
|
"""
|
||||||
self.backgroundPage.registerField(
|
self.backgroundPage.registerField(
|
||||||
u'background_type', self.backgroundComboBox)
|
u'background_type', self.backgroundComboBox)
|
||||||
self.backgroundPage.registerField(
|
self.backgroundPage.registerField(u'color', self.colorButton)
|
||||||
u'color', self.colorButton)
|
|
||||||
self.backgroundPage.registerField(
|
self.backgroundPage.registerField(
|
||||||
u'grandient_start', self.gradientStartButton)
|
u'grandient_start', self.gradientStartButton)
|
||||||
self.backgroundPage.registerField(
|
self.backgroundPage.registerField(
|
||||||
u'grandient_end', self.gradientEndButton)
|
u'grandient_end', self.gradientEndButton)
|
||||||
self.backgroundPage.registerField(
|
self.backgroundPage.registerField(
|
||||||
u'background_image', self.imageFileEdit)
|
u'background_image', self.imageFileEdit)
|
||||||
self.backgroundPage.registerField(
|
self.backgroundPage.registerField(u'gradient', self.gradientComboBox)
|
||||||
u'gradient', self.gradientComboBox)
|
|
||||||
self.mainAreaPage.registerField(
|
self.mainAreaPage.registerField(
|
||||||
u'mainColorButton', self.mainColorButton)
|
u'mainColorButton', self.mainColorButton)
|
||||||
self.mainAreaPage.registerField(
|
self.mainAreaPage.registerField(
|
||||||
@ -158,8 +156,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
|||||||
u'outlineColorButton', self.outlineColorButton)
|
u'outlineColorButton', self.outlineColorButton)
|
||||||
self.mainAreaPage.registerField(
|
self.mainAreaPage.registerField(
|
||||||
u'outlineSizeSpinBox', self.outlineSizeSpinBox)
|
u'outlineSizeSpinBox', self.outlineSizeSpinBox)
|
||||||
self.mainAreaPage.registerField(
|
self.mainAreaPage.registerField(u'shadowCheckBox', self.shadowCheckBox)
|
||||||
u'shadowCheckBox', self.shadowCheckBox)
|
|
||||||
self.mainAreaPage.registerField(
|
self.mainAreaPage.registerField(
|
||||||
u'mainBoldCheckBox', self.mainBoldCheckBox)
|
u'mainBoldCheckBox', self.mainBoldCheckBox)
|
||||||
self.mainAreaPage.registerField(
|
self.mainAreaPage.registerField(
|
||||||
@ -170,10 +167,8 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
|||||||
u'shadowSizeSpinBox', self.shadowSizeSpinBox)
|
u'shadowSizeSpinBox', self.shadowSizeSpinBox)
|
||||||
self.mainAreaPage.registerField(
|
self.mainAreaPage.registerField(
|
||||||
u'footerSizeSpinBox', self.footerSizeSpinBox)
|
u'footerSizeSpinBox', self.footerSizeSpinBox)
|
||||||
self.areaPositionPage.registerField(
|
self.areaPositionPage.registerField(u'mainPositionX', self.mainXSpinBox)
|
||||||
u'mainPositionX', self.mainXSpinBox)
|
self.areaPositionPage.registerField(u'mainPositionY', self.mainYSpinBox)
|
||||||
self.areaPositionPage.registerField(
|
|
||||||
u'mainPositionY', self.mainYSpinBox)
|
|
||||||
self.areaPositionPage.registerField(
|
self.areaPositionPage.registerField(
|
||||||
u'mainPositionWidth', self.mainWidthSpinBox)
|
u'mainPositionWidth', self.mainWidthSpinBox)
|
||||||
self.areaPositionPage.registerField(
|
self.areaPositionPage.registerField(
|
||||||
@ -188,12 +183,10 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
|||||||
u'footerPositionHeight', self.footerHeightSpinBox)
|
u'footerPositionHeight', self.footerHeightSpinBox)
|
||||||
self.backgroundPage.registerField(
|
self.backgroundPage.registerField(
|
||||||
u'horizontal', self.horizontalComboBox)
|
u'horizontal', self.horizontalComboBox)
|
||||||
self.backgroundPage.registerField(
|
self.backgroundPage.registerField(u'vertical', self.verticalComboBox)
|
||||||
u'vertical', self.verticalComboBox)
|
|
||||||
self.backgroundPage.registerField(
|
self.backgroundPage.registerField(
|
||||||
u'slideTransition', self.transitionsCheckBox)
|
u'slideTransition', self.transitionsCheckBox)
|
||||||
self.backgroundPage.registerField(
|
self.backgroundPage.registerField(u'name', self.themeNameEdit)
|
||||||
u'name', self.themeNameEdit)
|
|
||||||
|
|
||||||
def calculateLines(self):
|
def calculateLines(self):
|
||||||
"""
|
"""
|
||||||
@ -269,10 +262,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
|||||||
Change state as Outline check box changed
|
Change state as Outline check box changed
|
||||||
"""
|
"""
|
||||||
if self.updateThemeAllowed:
|
if self.updateThemeAllowed:
|
||||||
if state == QtCore.Qt.Checked:
|
self.theme.font_main_outline = state == QtCore.Qt.Checked
|
||||||
self.theme.font_main_outline = True
|
|
||||||
else:
|
|
||||||
self.theme.font_main_outline = False
|
|
||||||
self.outlineColorButton.setEnabled(self.theme.font_main_outline)
|
self.outlineColorButton.setEnabled(self.theme.font_main_outline)
|
||||||
self.outlineSizeSpinBox.setEnabled(self.theme.font_main_outline)
|
self.outlineSizeSpinBox.setEnabled(self.theme.font_main_outline)
|
||||||
self.calculateLines()
|
self.calculateLines()
|
||||||
@ -642,8 +632,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
|||||||
"""
|
"""
|
||||||
Handle Color buttons
|
Handle Color buttons
|
||||||
"""
|
"""
|
||||||
new_color = QtGui.QColorDialog.getColor(
|
new_color = QtGui.QColorDialog.getColor(QtGui.QColor(field), self)
|
||||||
QtGui.QColor(field), self)
|
|
||||||
if new_color.isValid():
|
if new_color.isValid():
|
||||||
field = new_color.name()
|
field = new_color.name()
|
||||||
return field
|
return field
|
||||||
|
@ -53,6 +53,7 @@ class WizardStrings(object):
|
|||||||
OL = u'OpenLyrics'
|
OL = u'OpenLyrics'
|
||||||
OS = u'OpenSong'
|
OS = u'OpenSong'
|
||||||
OSIS = u'OSIS'
|
OSIS = u'OSIS'
|
||||||
|
PS = u'PowerSong 1.0'
|
||||||
SB = u'SongBeamer'
|
SB = u'SongBeamer'
|
||||||
SoF = u'Songs of Fellowship'
|
SoF = u'Songs of Fellowship'
|
||||||
SSP = u'SongShow Plus'
|
SSP = u'SongShow Plus'
|
||||||
@ -71,11 +72,14 @@ class WizardStrings(object):
|
|||||||
'importer, you will need to install the "python-sqlite" '
|
'importer, you will need to install the "python-sqlite" '
|
||||||
'module.')
|
'module.')
|
||||||
OpenTypeFile = unicode(translate('OpenLP.Ui', 'Open %s File'))
|
OpenTypeFile = unicode(translate('OpenLP.Ui', 'Open %s File'))
|
||||||
|
OpenTypeFolder = unicode(translate('OpenLP.Ui', 'Open %s Folder'))
|
||||||
PercentSymbolFormat = unicode(translate('OpenLP.Ui', '%p%'))
|
PercentSymbolFormat = unicode(translate('OpenLP.Ui', '%p%'))
|
||||||
Ready = translate('OpenLP.Ui', 'Ready.')
|
Ready = translate('OpenLP.Ui', 'Ready.')
|
||||||
StartingImport = translate('OpenLP.Ui', 'Starting import...')
|
StartingImport = translate('OpenLP.Ui', 'Starting import...')
|
||||||
YouSpecifyFile = unicode(translate('OpenLP.Ui', 'You need to specify at '
|
YouSpecifyFile = unicode(translate('OpenLP.Ui', 'You need to specify at '
|
||||||
'least one %s file to import from.', 'A file type e.g. OpenSong'))
|
'least one %s file to import from.', 'A file type e.g. OpenSong'))
|
||||||
|
YouSpecifyFolder = unicode(translate('OpenLP.Ui', 'You need to specify a '
|
||||||
|
'%s folder to import from.', 'A file type e.g. OpenSong'))
|
||||||
|
|
||||||
|
|
||||||
class OpenLPWizard(QtGui.QWizard):
|
class OpenLPWizard(QtGui.QWizard):
|
||||||
@ -253,7 +257,7 @@ class OpenLPWizard(QtGui.QWizard):
|
|||||||
The title of the dialog (unicode).
|
The title of the dialog (unicode).
|
||||||
|
|
||||||
``editbox``
|
``editbox``
|
||||||
A editbox (QLineEdit).
|
An editbox (QLineEdit).
|
||||||
|
|
||||||
``filters``
|
``filters``
|
||||||
The file extension filters. It should contain the file description
|
The file extension filters. It should contain the file description
|
||||||
@ -264,11 +268,28 @@ class OpenLPWizard(QtGui.QWizard):
|
|||||||
if filters:
|
if filters:
|
||||||
filters += u';;'
|
filters += u';;'
|
||||||
filters += u'%s (*)' % UiStrings().AllFiles
|
filters += u'%s (*)' % UiStrings().AllFiles
|
||||||
filename = QtGui.QFileDialog.getOpenFileName(self, title,
|
filename = unicode(QtGui.QFileDialog.getOpenFileName(self, title,
|
||||||
os.path.dirname(SettingsManager.get_last_dir(
|
os.path.dirname(SettingsManager.get_last_dir(
|
||||||
self.plugin.settingsSection, 1)), filters)
|
self.plugin.settingsSection, 1)), filters))
|
||||||
if filename:
|
if filename:
|
||||||
editbox.setText(filename)
|
editbox.setText(filename)
|
||||||
SettingsManager.set_last_dir(self.plugin.settingsSection,
|
SettingsManager.set_last_dir(self.plugin.settingsSection,
|
||||||
filename, 1)
|
filename, 1)
|
||||||
|
|
||||||
|
def getFolder(self, title, editbox):
|
||||||
|
"""
|
||||||
|
Opens a QFileDialog and saves the selected folder to the given editbox.
|
||||||
|
|
||||||
|
``title``
|
||||||
|
The title of the dialog (unicode).
|
||||||
|
|
||||||
|
``editbox``
|
||||||
|
An editbox (QLineEdit).
|
||||||
|
"""
|
||||||
|
folder = unicode(QtGui.QFileDialog.getExistingDirectory(self, title,
|
||||||
|
os.path.dirname(SettingsManager.get_last_dir(
|
||||||
|
self.plugin.settingsSection, 1)), QtGui.QFileDialog.ShowDirsOnly))
|
||||||
|
if folder:
|
||||||
|
editbox.setText(folder)
|
||||||
|
SettingsManager.set_last_dir(self.plugin.settingsSection,
|
||||||
|
folder, 1)
|
||||||
|
@ -330,13 +330,7 @@ class BibleManager(object):
|
|||||||
'Import Wizard to install one or more Bibles.')
|
'Import Wizard to install one or more Bibles.')
|
||||||
})
|
})
|
||||||
return None
|
return None
|
||||||
language_selection = self.get_meta_data(bible, u'book_name_language')
|
language_selection = self.get_language_selection(bible)
|
||||||
if language_selection:
|
|
||||||
language_selection = int(language_selection.value)
|
|
||||||
if language_selection is None or language_selection == -1:
|
|
||||||
language_selection = QtCore.QSettings().value(
|
|
||||||
self.settingsSection + u'/bookname language',
|
|
||||||
QtCore.QVariant(0)).toInt()[0]
|
|
||||||
reflist = parse_reference(versetext, self.db_cache[bible],
|
reflist = parse_reference(versetext, self.db_cache[bible],
|
||||||
language_selection, book_ref_id)
|
language_selection, book_ref_id)
|
||||||
if reflist:
|
if reflist:
|
||||||
@ -378,12 +372,16 @@ class BibleManager(object):
|
|||||||
"""
|
"""
|
||||||
log.debug(u'BibleManager.get_language_selection("%s")', bible)
|
log.debug(u'BibleManager.get_language_selection("%s")', bible)
|
||||||
language_selection = self.get_meta_data(bible, u'book_name_language')
|
language_selection = self.get_meta_data(bible, u'book_name_language')
|
||||||
if language_selection and language_selection.value != u'None':
|
if language_selection:
|
||||||
return int(language_selection.value)
|
try:
|
||||||
if language_selection is None or language_selection.value == u'None':
|
language_selection = int(language_selection.value)
|
||||||
return QtCore.QSettings().value(
|
except (ValueError, TypeError):
|
||||||
|
language_selection = LanguageSelection.Application
|
||||||
|
if language_selection is None or language_selection == -1:
|
||||||
|
language_selection = QtCore.QSettings().value(
|
||||||
self.settingsSection + u'/bookname language',
|
self.settingsSection + u'/bookname language',
|
||||||
QtCore.QVariant(0)).toInt()[0]
|
QtCore.QVariant(0)).toInt()[0]
|
||||||
|
return language_selection
|
||||||
|
|
||||||
def verse_search(self, bible, second_bible, text):
|
def verse_search(self, bible, second_bible, text):
|
||||||
"""
|
"""
|
||||||
|
@ -843,10 +843,11 @@ class BibleMediaItem(MediaManagerItem):
|
|||||||
items = []
|
items = []
|
||||||
language_selection = self.plugin.manager.get_language_selection(bible)
|
language_selection = self.plugin.manager.get_language_selection(bible)
|
||||||
for count, verse in enumerate(search_results):
|
for count, verse in enumerate(search_results):
|
||||||
|
book = None
|
||||||
if language_selection == LanguageSelection.Bible:
|
if language_selection == LanguageSelection.Bible:
|
||||||
book = verse.book.name
|
book = verse.book.name
|
||||||
elif language_selection == LanguageSelection.Application:
|
elif language_selection == LanguageSelection.Application:
|
||||||
book_names = BibleStrings().Booknames
|
book_names = BibleStrings().BookNames
|
||||||
data = BiblesResourcesDB.get_book_by_id(
|
data = BiblesResourcesDB.get_book_by_id(
|
||||||
verse.book.book_reference_id)
|
verse.book.book_reference_id)
|
||||||
book = unicode(book_names[data[u'abbreviation']])
|
book = unicode(book_names[data[u'abbreviation']])
|
||||||
|
@ -363,11 +363,5 @@ class SongExportForm(OpenLPWizard):
|
|||||||
Called when the *directoryButton* was clicked. Opens a dialog and writes
|
Called when the *directoryButton* was clicked. Opens a dialog and writes
|
||||||
the path to *directoryLineEdit*.
|
the path to *directoryLineEdit*.
|
||||||
"""
|
"""
|
||||||
path = unicode(QtGui.QFileDialog.getExistingDirectory(self,
|
self.getFolder(translate('SongsPlugin.ExportWizardForm',
|
||||||
translate('SongsPlugin.ExportWizardForm',
|
'Select Destination Folder'), self.directoryLineEdit)
|
||||||
'Select Destination Folder'),
|
|
||||||
SettingsManager.get_last_dir(self.plugin.settingsSection, 1),
|
|
||||||
options=QtGui.QFileDialog.ShowDirsOnly))
|
|
||||||
SettingsManager.set_last_dir(self.plugin.settingsSection, path, 1)
|
|
||||||
self.directoryLineEdit.setText(path)
|
|
||||||
|
|
||||||
|
@ -105,6 +105,9 @@ class SongImportForm(OpenLPWizard):
|
|||||||
QtCore.QObject.connect(self.openLP1BrowseButton,
|
QtCore.QObject.connect(self.openLP1BrowseButton,
|
||||||
QtCore.SIGNAL(u'clicked()'),
|
QtCore.SIGNAL(u'clicked()'),
|
||||||
self.onOpenLP1BrowseButtonClicked)
|
self.onOpenLP1BrowseButtonClicked)
|
||||||
|
QtCore.QObject.connect(self.powerSongBrowseButton,
|
||||||
|
QtCore.SIGNAL(u'clicked()'),
|
||||||
|
self.onPowerSongBrowseButtonClicked)
|
||||||
QtCore.QObject.connect(self.openLyricsAddButton,
|
QtCore.QObject.connect(self.openLyricsAddButton,
|
||||||
QtCore.SIGNAL(u'clicked()'),
|
QtCore.SIGNAL(u'clicked()'),
|
||||||
self.onOpenLyricsAddButtonClicked)
|
self.onOpenLyricsAddButtonClicked)
|
||||||
@ -217,6 +220,8 @@ class SongImportForm(OpenLPWizard):
|
|||||||
self.addFileSelectItem(u'foilPresenter')
|
self.addFileSelectItem(u'foilPresenter')
|
||||||
# Open Song
|
# Open Song
|
||||||
self.addFileSelectItem(u'openSong', u'OpenSong')
|
self.addFileSelectItem(u'openSong', u'OpenSong')
|
||||||
|
# PowerSong
|
||||||
|
self.addFileSelectItem(u'powerSong', single_select=True)
|
||||||
# SongBeamer
|
# SongBeamer
|
||||||
self.addFileSelectItem(u'songBeamer')
|
self.addFileSelectItem(u'songBeamer')
|
||||||
# Song Show Plus
|
# Song Show Plus
|
||||||
@ -264,6 +269,8 @@ class SongImportForm(OpenLPWizard):
|
|||||||
self.formatComboBox.setItemText(
|
self.formatComboBox.setItemText(
|
||||||
SongFormat.FoilPresenter, WizardStrings.FP)
|
SongFormat.FoilPresenter, WizardStrings.FP)
|
||||||
self.formatComboBox.setItemText(SongFormat.OpenSong, WizardStrings.OS)
|
self.formatComboBox.setItemText(SongFormat.OpenSong, WizardStrings.OS)
|
||||||
|
self.formatComboBox.setItemText(
|
||||||
|
SongFormat.PowerSong, WizardStrings.PS)
|
||||||
self.formatComboBox.setItemText(
|
self.formatComboBox.setItemText(
|
||||||
SongFormat.SongBeamer, WizardStrings.SB)
|
SongFormat.SongBeamer, WizardStrings.SB)
|
||||||
self.formatComboBox.setItemText(
|
self.formatComboBox.setItemText(
|
||||||
@ -280,6 +287,9 @@ class SongImportForm(OpenLPWizard):
|
|||||||
translate('SongsPlugin.ImportWizardForm', 'Filename:'))
|
translate('SongsPlugin.ImportWizardForm', 'Filename:'))
|
||||||
self.openLP1BrowseButton.setText(UiStrings().Browse)
|
self.openLP1BrowseButton.setText(UiStrings().Browse)
|
||||||
self.openLP1DisabledLabel.setText(WizardStrings.NoSqlite)
|
self.openLP1DisabledLabel.setText(WizardStrings.NoSqlite)
|
||||||
|
self.powerSongFilenameLabel.setText(
|
||||||
|
translate('SongsPlugin.ImportWizardForm', 'Folder:'))
|
||||||
|
self.powerSongBrowseButton.setText(UiStrings().Browse)
|
||||||
self.openLyricsAddButton.setText(
|
self.openLyricsAddButton.setText(
|
||||||
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
|
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
|
||||||
self.openLyricsRemoveButton.setText(
|
self.openLyricsRemoveButton.setText(
|
||||||
@ -375,6 +385,7 @@ class SongImportForm(OpenLPWizard):
|
|||||||
source_format = self.formatComboBox.currentIndex()
|
source_format = self.formatComboBox.currentIndex()
|
||||||
QtCore.QSettings().setValue(u'songs/last import type',
|
QtCore.QSettings().setValue(u'songs/last import type',
|
||||||
source_format)
|
source_format)
|
||||||
|
import_class = SongFormat.get_class(source_format)
|
||||||
if source_format == SongFormat.OpenLP2:
|
if source_format == SongFormat.OpenLP2:
|
||||||
if self.openLP2FilenameEdit.text().isEmpty():
|
if self.openLP2FilenameEdit.text().isEmpty():
|
||||||
critical_error_message_box(UiStrings().NFSs,
|
critical_error_message_box(UiStrings().NFSs,
|
||||||
@ -387,6 +398,14 @@ class SongImportForm(OpenLPWizard):
|
|||||||
WizardStrings.YouSpecifyFile % UiStrings().OLPV1)
|
WizardStrings.YouSpecifyFile % UiStrings().OLPV1)
|
||||||
self.openLP1BrowseButton.setFocus()
|
self.openLP1BrowseButton.setFocus()
|
||||||
return False
|
return False
|
||||||
|
elif source_format == SongFormat.PowerSong:
|
||||||
|
if self.powerSongFilenameEdit.text().isEmpty() or \
|
||||||
|
not import_class.isValidSource(
|
||||||
|
folder=self.powerSongFilenameEdit.text()):
|
||||||
|
critical_error_message_box(UiStrings().NFdSs,
|
||||||
|
WizardStrings.YouSpecifyFolder % WizardStrings.PS)
|
||||||
|
self.powerSongBrowseButton.setFocus()
|
||||||
|
return False
|
||||||
elif source_format == SongFormat.OpenLyrics:
|
elif source_format == SongFormat.OpenLyrics:
|
||||||
if self.openLyricsFileListWidget.count() == 0:
|
if self.openLyricsFileListWidget.count() == 0:
|
||||||
critical_error_message_box(UiStrings().NFSp,
|
critical_error_message_box(UiStrings().NFSp,
|
||||||
@ -526,6 +545,13 @@ class SongImportForm(OpenLPWizard):
|
|||||||
'openlp.org v1.x Databases')
|
'openlp.org v1.x Databases')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def onPowerSongBrowseButtonClicked(self):
|
||||||
|
"""
|
||||||
|
Get PowerSong song database folder
|
||||||
|
"""
|
||||||
|
self.getFolder(WizardStrings.OpenTypeFolder % WizardStrings.PS,
|
||||||
|
self.powerSongFilenameEdit)
|
||||||
|
|
||||||
def onOpenLyricsAddButtonClicked(self):
|
def onOpenLyricsAddButtonClicked(self):
|
||||||
"""
|
"""
|
||||||
Get OpenLyrics song database files
|
Get OpenLyrics song database files
|
||||||
@ -712,6 +738,7 @@ class SongImportForm(OpenLPWizard):
|
|||||||
self.formatComboBox.setCurrentIndex(last_import_type)
|
self.formatComboBox.setCurrentIndex(last_import_type)
|
||||||
self.openLP2FilenameEdit.setText(u'')
|
self.openLP2FilenameEdit.setText(u'')
|
||||||
self.openLP1FilenameEdit.setText(u'')
|
self.openLP1FilenameEdit.setText(u'')
|
||||||
|
self.powerSongFilenameEdit.setText(u'')
|
||||||
self.openLyricsFileListWidget.clear()
|
self.openLyricsFileListWidget.clear()
|
||||||
self.openSongFileListWidget.clear()
|
self.openSongFileListWidget.clear()
|
||||||
self.wordsOfWorshipFileListWidget.clear()
|
self.wordsOfWorshipFileListWidget.clear()
|
||||||
@ -757,6 +784,11 @@ class SongImportForm(OpenLPWizard):
|
|||||||
filename=unicode(self.openLP1FilenameEdit.text()),
|
filename=unicode(self.openLP1FilenameEdit.text()),
|
||||||
plugin=self.plugin
|
plugin=self.plugin
|
||||||
)
|
)
|
||||||
|
elif source_format == SongFormat.PowerSong:
|
||||||
|
# Import PowerSong folder
|
||||||
|
importer = self.plugin.importSongs(SongFormat.PowerSong,
|
||||||
|
folder=unicode(self.powerSongFilenameEdit.text())
|
||||||
|
)
|
||||||
elif source_format == SongFormat.OpenLyrics:
|
elif source_format == SongFormat.OpenLyrics:
|
||||||
# Import OpenLyrics songs
|
# Import OpenLyrics songs
|
||||||
importer = self.plugin.importSongs(SongFormat.OpenLyrics,
|
importer = self.plugin.importSongs(SongFormat.OpenLyrics,
|
||||||
@ -821,10 +853,6 @@ class SongImportForm(OpenLPWizard):
|
|||||||
filenames=self.getListOfFiles(self.foilPresenterFileListWidget)
|
filenames=self.getListOfFiles(self.foilPresenterFileListWidget)
|
||||||
)
|
)
|
||||||
importer.doImport()
|
importer.doImport()
|
||||||
if importer.errorLog:
|
|
||||||
self.progressLabel.setText(translate(
|
|
||||||
'SongsPlugin.SongImportForm', 'Your song import failed.'))
|
|
||||||
else:
|
|
||||||
self.progressLabel.setText(WizardStrings.FinishedImport)
|
self.progressLabel.setText(WizardStrings.FinishedImport)
|
||||||
|
|
||||||
def onErrorCopyToButtonClicked(self):
|
def onErrorCopyToButtonClicked(self):
|
||||||
|
@ -36,6 +36,7 @@ from openlyricsimport import OpenLyricsImport
|
|||||||
from wowimport import WowImport
|
from wowimport import WowImport
|
||||||
from cclifileimport import CCLIFileImport
|
from cclifileimport import CCLIFileImport
|
||||||
from dreambeamimport import DreamBeamImport
|
from dreambeamimport import DreamBeamImport
|
||||||
|
from powersongimport import PowerSongImport
|
||||||
from ewimport import EasyWorshipSongImport
|
from ewimport import EasyWorshipSongImport
|
||||||
from songbeamerimport import SongBeamerImport
|
from songbeamerimport import SongBeamerImport
|
||||||
from songshowplusimport import SongShowPlusImport
|
from songshowplusimport import SongShowPlusImport
|
||||||
@ -79,16 +80,17 @@ class SongFormat(object):
|
|||||||
EasyWorship = 7
|
EasyWorship = 7
|
||||||
FoilPresenter = 8
|
FoilPresenter = 8
|
||||||
OpenSong = 9
|
OpenSong = 9
|
||||||
SongBeamer = 10
|
PowerSong = 10
|
||||||
SongShowPlus = 11
|
SongBeamer = 11
|
||||||
SongsOfFellowship = 12
|
SongShowPlus = 12
|
||||||
WordsOfWorship = 13
|
SongsOfFellowship = 13
|
||||||
#CSV = 14
|
WordsOfWorship = 14
|
||||||
|
#CSV = 15
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_class(format):
|
def get_class(format):
|
||||||
"""
|
"""
|
||||||
Return the appropriate imeplementation class.
|
Return the appropriate implementation class.
|
||||||
|
|
||||||
``format``
|
``format``
|
||||||
The song format.
|
The song format.
|
||||||
@ -111,6 +113,8 @@ class SongFormat(object):
|
|||||||
return CCLIFileImport
|
return CCLIFileImport
|
||||||
elif format == SongFormat.DreamBeam:
|
elif format == SongFormat.DreamBeam:
|
||||||
return DreamBeamImport
|
return DreamBeamImport
|
||||||
|
elif format == SongFormat.PowerSong:
|
||||||
|
return PowerSongImport
|
||||||
elif format == SongFormat.EasySlides:
|
elif format == SongFormat.EasySlides:
|
||||||
return EasySlidesImport
|
return EasySlidesImport
|
||||||
elif format == SongFormat.EasyWorship:
|
elif format == SongFormat.EasyWorship:
|
||||||
@ -139,6 +143,7 @@ class SongFormat(object):
|
|||||||
SongFormat.EasyWorship,
|
SongFormat.EasyWorship,
|
||||||
SongFormat.FoilPresenter,
|
SongFormat.FoilPresenter,
|
||||||
SongFormat.OpenSong,
|
SongFormat.OpenSong,
|
||||||
|
SongFormat.PowerSong,
|
||||||
SongFormat.SongBeamer,
|
SongFormat.SongBeamer,
|
||||||
SongFormat.SongShowPlus,
|
SongFormat.SongShowPlus,
|
||||||
SongFormat.SongsOfFellowship,
|
SongFormat.SongsOfFellowship,
|
||||||
|
225
openlp/plugins/songs/lib/powersongimport.py
Normal file
225
openlp/plugins/songs/lib/powersongimport.py
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2012 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
|
||||||
|
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
|
||||||
|
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# This program is free software; you can redistribute it and/or modify it #
|
||||||
|
# under the terms of the GNU General Public License as published by the Free #
|
||||||
|
# Software Foundation; version 2 of the License. #
|
||||||
|
# #
|
||||||
|
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||||
|
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||||
|
# more details. #
|
||||||
|
# #
|
||||||
|
# You should have received a copy of the GNU General Public License along #
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||||
|
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||||
|
###############################################################################
|
||||||
|
"""
|
||||||
|
The :mod:`powersongimport` module provides the functionality for importing
|
||||||
|
PowerSong songs into the OpenLP database.
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import fnmatch
|
||||||
|
import os
|
||||||
|
|
||||||
|
from openlp.core.lib import translate
|
||||||
|
from openlp.core.ui.wizard import WizardStrings
|
||||||
|
from openlp.plugins.songs.lib.songimport import SongImport
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class PowerSongImport(SongImport):
|
||||||
|
"""
|
||||||
|
The :class:`PowerSongImport` class provides the ability to import song files
|
||||||
|
from PowerSong.
|
||||||
|
|
||||||
|
**PowerSong 1.0 Song File Format:**
|
||||||
|
|
||||||
|
The file has a number of label-field (think key-value) pairs.
|
||||||
|
|
||||||
|
Label and Field strings:
|
||||||
|
|
||||||
|
* Every label and field is a variable length string preceded by an
|
||||||
|
integer specifying it's byte length.
|
||||||
|
* Integer is 32-bit but is encoded in 7-bit format to save space. Thus
|
||||||
|
if length will fit in 7 bits (ie <= 127) it takes up only one byte.
|
||||||
|
|
||||||
|
Metadata fields:
|
||||||
|
|
||||||
|
* Every PowerSong file has a TITLE field.
|
||||||
|
* There is zero or more AUTHOR fields.
|
||||||
|
* There is always a COPYRIGHTLINE label, but its field may be empty.
|
||||||
|
This field may also contain a CCLI number: e.g. "CCLI 176263".
|
||||||
|
|
||||||
|
Lyrics fields:
|
||||||
|
|
||||||
|
* Each verse is contained in a PART field.
|
||||||
|
* Lines have Windows line endings ``CRLF`` (0x0d, 0x0a).
|
||||||
|
* There is no concept of verse types.
|
||||||
|
|
||||||
|
Valid extensions for a PowerSong song file are:
|
||||||
|
|
||||||
|
* .song
|
||||||
|
"""
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def isValidSource(**kwargs):
|
||||||
|
"""
|
||||||
|
Checks if source is a PowerSong 1.0 folder:
|
||||||
|
* is a directory
|
||||||
|
* contains at least one *.song file
|
||||||
|
"""
|
||||||
|
if u'folder' in kwargs:
|
||||||
|
dir = kwargs[u'folder']
|
||||||
|
if os.path.isdir(dir):
|
||||||
|
for file in os.listdir(dir):
|
||||||
|
if fnmatch.fnmatch(file, u'*.song'):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def doImport(self):
|
||||||
|
"""
|
||||||
|
Receive either a list of files or a folder (unicode) to import.
|
||||||
|
"""
|
||||||
|
if isinstance(self.importSource, unicode):
|
||||||
|
if os.path.isdir(self.importSource):
|
||||||
|
dir = self.importSource
|
||||||
|
self.importSource = []
|
||||||
|
for file in os.listdir(dir):
|
||||||
|
if fnmatch.fnmatch(file, u'*.song'):
|
||||||
|
self.importSource.append(os.path.join(dir, file))
|
||||||
|
else:
|
||||||
|
self.importSource = u''
|
||||||
|
if not self.importSource or not isinstance(self.importSource, list):
|
||||||
|
self.logError(unicode(translate('SongsPlugin.PowerSongImport',
|
||||||
|
'No songs to import.')),
|
||||||
|
unicode(translate('SongsPlugin.PowerSongImport',
|
||||||
|
'No %s files found.' % WizardStrings.PS)))
|
||||||
|
return
|
||||||
|
self.importWizard.progressBar.setMaximum(len(self.importSource))
|
||||||
|
for file in self.importSource:
|
||||||
|
if self.stopImportFlag:
|
||||||
|
return
|
||||||
|
self.setDefaults()
|
||||||
|
parse_error = False
|
||||||
|
with open(file, 'rb') as song_data:
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
label = self._readString(song_data)
|
||||||
|
if not label:
|
||||||
|
break
|
||||||
|
field = self._readString(song_data)
|
||||||
|
except ValueError:
|
||||||
|
parse_error = True
|
||||||
|
self.logError(os.path.basename(file), unicode(
|
||||||
|
translate('SongsPlugin.PowerSongImport',
|
||||||
|
'Invalid %s file. Unexpected byte value.'
|
||||||
|
% WizardStrings.PS)))
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if label == u'TITLE':
|
||||||
|
self.title = field.replace(u'\n', u' ')
|
||||||
|
elif label == u'AUTHOR':
|
||||||
|
self.parseAuthor(field)
|
||||||
|
elif label == u'COPYRIGHTLINE':
|
||||||
|
found_copyright = True
|
||||||
|
self._parseCopyrightCCLI(field)
|
||||||
|
elif label == u'PART':
|
||||||
|
self.addVerse(field)
|
||||||
|
if parse_error:
|
||||||
|
continue
|
||||||
|
# Check that file had TITLE field
|
||||||
|
if not self.title:
|
||||||
|
self.logError(os.path.basename(file), unicode(
|
||||||
|
translate('SongsPlugin.PowerSongImport',
|
||||||
|
'Invalid %s file. Missing "TITLE" header.'
|
||||||
|
% WizardStrings.PS)))
|
||||||
|
continue
|
||||||
|
# Check that file had COPYRIGHTLINE label
|
||||||
|
if not found_copyright:
|
||||||
|
self.logError(self.title, unicode(
|
||||||
|
translate('SongsPlugin.PowerSongImport',
|
||||||
|
'Invalid %s file. Missing "COPYRIGHTLINE" '
|
||||||
|
'header.' % WizardStrings.PS)))
|
||||||
|
continue
|
||||||
|
# Check that file had at least one verse
|
||||||
|
if not self.verses:
|
||||||
|
self.logError(self.title, unicode(
|
||||||
|
translate('SongsPlugin.PowerSongImport',
|
||||||
|
'Verses not found. Missing "PART" header.')))
|
||||||
|
continue
|
||||||
|
if not self.finish():
|
||||||
|
self.logError(self.title)
|
||||||
|
|
||||||
|
def _readString(self, file_object):
|
||||||
|
"""
|
||||||
|
Reads in next variable-length string.
|
||||||
|
"""
|
||||||
|
string_len = self._read7BitEncodedInteger(file_object)
|
||||||
|
return unicode(file_object.read(string_len), u'utf-8', u'ignore')
|
||||||
|
|
||||||
|
def _read7BitEncodedInteger(self, file_object):
|
||||||
|
"""
|
||||||
|
Reads in a 32-bit integer in compressed 7-bit format.
|
||||||
|
|
||||||
|
Accomplished by reading the integer 7 bits at a time. The high bit
|
||||||
|
of the byte when set means to continue reading more bytes.
|
||||||
|
If the integer will fit in 7 bits (ie <= 127), it only takes up one
|
||||||
|
byte. Otherwise, it may take up to 5 bytes.
|
||||||
|
|
||||||
|
Reference: .NET method System.IO.BinaryReader.Read7BitEncodedInt
|
||||||
|
"""
|
||||||
|
val = 0
|
||||||
|
shift = 0
|
||||||
|
i = 0
|
||||||
|
while True:
|
||||||
|
# Check for corrupted stream (since max 5 bytes per 32-bit integer)
|
||||||
|
if i == 5:
|
||||||
|
raise ValueError
|
||||||
|
byte = self._readByte(file_object)
|
||||||
|
# Strip high bit and shift left
|
||||||
|
val += (byte & 0x7f) << shift
|
||||||
|
shift += 7
|
||||||
|
high_bit_set = byte & 0x80
|
||||||
|
if not high_bit_set:
|
||||||
|
break
|
||||||
|
i += 1
|
||||||
|
return val
|
||||||
|
|
||||||
|
def _readByte(self, file_object):
|
||||||
|
"""
|
||||||
|
Reads in next byte as an unsigned integer
|
||||||
|
|
||||||
|
Note: returns 0 at end of file.
|
||||||
|
"""
|
||||||
|
byte_str = file_object.read(1)
|
||||||
|
# If read result is empty, then reached end of file
|
||||||
|
if not byte_str:
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return ord(byte_str)
|
||||||
|
|
||||||
|
def _parseCopyrightCCLI(self, field):
|
||||||
|
"""
|
||||||
|
Look for CCLI song number, and get copyright
|
||||||
|
"""
|
||||||
|
copyright, sep, ccli_no = field.rpartition(u'CCLI')
|
||||||
|
if not sep:
|
||||||
|
copyright = ccli_no
|
||||||
|
ccli_no = u''
|
||||||
|
if copyright:
|
||||||
|
self.addCopyright(copyright.rstrip(u'\n').replace(u'\n', u' '))
|
||||||
|
if ccli_no:
|
||||||
|
ccli_no = ccli_no.strip(u' :')
|
||||||
|
if ccli_no.isdigit():
|
||||||
|
self.ccliNumber = ccli_no
|
@ -50,6 +50,13 @@ class SongImport(QtCore.QObject):
|
|||||||
whether the authors etc already exist and add them or refer to them
|
whether the authors etc already exist and add them or refer to them
|
||||||
as necessary
|
as necessary
|
||||||
"""
|
"""
|
||||||
|
@staticmethod
|
||||||
|
def isValidSource(**kwargs):
|
||||||
|
"""
|
||||||
|
Override this method to validate the source prior to import.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
def __init__(self, manager, **kwargs):
|
def __init__(self, manager, **kwargs):
|
||||||
"""
|
"""
|
||||||
Initialise and create defaults for properties
|
Initialise and create defaults for properties
|
||||||
@ -65,14 +72,16 @@ class SongImport(QtCore.QObject):
|
|||||||
self.importSource = kwargs[u'filename']
|
self.importSource = kwargs[u'filename']
|
||||||
elif u'filenames' in kwargs:
|
elif u'filenames' in kwargs:
|
||||||
self.importSource = kwargs[u'filenames']
|
self.importSource = kwargs[u'filenames']
|
||||||
|
elif u'folder' in kwargs:
|
||||||
|
self.importSource = kwargs[u'folder']
|
||||||
else:
|
else:
|
||||||
raise KeyError(u'Keyword arguments "filename[s]" not supplied.')
|
raise KeyError(
|
||||||
|
u'Keyword arguments "filename[s]" or "folder" not supplied.')
|
||||||
log.debug(self.importSource)
|
log.debug(self.importSource)
|
||||||
self.importWizard = None
|
self.importWizard = None
|
||||||
self.song = None
|
self.song = None
|
||||||
self.stopImportFlag = False
|
self.stopImportFlag = False
|
||||||
self.setDefaults()
|
self.setDefaults()
|
||||||
self.errorLog = []
|
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'openlp_stop_wizard'), self.stopImport)
|
QtCore.SIGNAL(u'openlp_stop_wizard'), self.stopImport)
|
||||||
|
|
||||||
@ -111,7 +120,7 @@ class SongImport(QtCore.QObject):
|
|||||||
instance a database), then this should be the song's title.
|
instance a database), then this should be the song's title.
|
||||||
|
|
||||||
``reason``
|
``reason``
|
||||||
The reason, why the import failed. The string should be as
|
The reason why the import failed. The string should be as
|
||||||
informative as possible.
|
informative as possible.
|
||||||
"""
|
"""
|
||||||
self.setDefaults()
|
self.setDefaults()
|
||||||
|
@ -71,7 +71,7 @@ class WowImport(SongImport):
|
|||||||
* ``SOH`` (0x01) - Chorus
|
* ``SOH`` (0x01) - Chorus
|
||||||
* ``STX`` (0x02) - Bridge
|
* ``STX`` (0x02) - Bridge
|
||||||
|
|
||||||
Blocks are seperated by two bytes. The first byte is 0x01, and the
|
Blocks are separated by two bytes. The first byte is 0x01, and the
|
||||||
second byte is 0x80.
|
second byte is 0x80.
|
||||||
|
|
||||||
Lines:
|
Lines:
|
||||||
@ -126,7 +126,7 @@ class WowImport(SongImport):
|
|||||||
('Invalid Words of Worship song file. Missing '
|
('Invalid Words of Worship song file. Missing '
|
||||||
'"CSongDoc::CBlock" string.'))))
|
'"CSongDoc::CBlock" string.'))))
|
||||||
continue
|
continue
|
||||||
# Seek to the beging of the first block
|
# Seek to the beginning of the first block
|
||||||
song_data.seek(82)
|
song_data.seek(82)
|
||||||
for block in range(no_of_blocks):
|
for block in range(no_of_blocks):
|
||||||
self.linesToRead = ord(song_data.read(4)[:1])
|
self.linesToRead = ord(song_data.read(4)[:1])
|
||||||
@ -140,7 +140,7 @@ class WowImport(SongImport):
|
|||||||
block_text += self.lineText
|
block_text += self.lineText
|
||||||
self.linesToRead -= 1
|
self.linesToRead -= 1
|
||||||
block_type = BLOCK_TYPES[ord(song_data.read(4)[:1])]
|
block_type = BLOCK_TYPES[ord(song_data.read(4)[:1])]
|
||||||
# Blocks are seperated by 2 bytes, skip them, but not if
|
# Blocks are separated by 2 bytes, skip them, but not if
|
||||||
# this is the last block!
|
# this is the last block!
|
||||||
if block + 1 < no_of_blocks:
|
if block + 1 < no_of_blocks:
|
||||||
song_data.seek(2, os.SEEK_CUR)
|
song_data.seek(2, os.SEEK_CUR)
|
||||||
|
@ -33,7 +33,7 @@ The basic XML for storing the lyrics in the song database looks like this::
|
|||||||
<song version="1.0">
|
<song version="1.0">
|
||||||
<lyrics>
|
<lyrics>
|
||||||
<verse type="c" label="1" lang="en">
|
<verse type="c" label="1" lang="en">
|
||||||
<![CDATA[Chorus virtual slide 1[---]Chorus virtual slide 2]]>
|
<![CDATA[Chorus optional split 1[---]Chorus optional split 2]]>
|
||||||
</verse>
|
</verse>
|
||||||
</lyrics>
|
</lyrics>
|
||||||
</song>
|
</song>
|
||||||
@ -135,7 +135,7 @@ class SongXML(object):
|
|||||||
The returned list has the following format::
|
The returned list has the following format::
|
||||||
|
|
||||||
[[{'type': 'v', 'label': '1'},
|
[[{'type': 'v', 'label': '1'},
|
||||||
u"virtual slide 1[---]virtual slide 2"],
|
u"optional slide split 1[---]optional slide split 2"],
|
||||||
[{'lang': 'en', 'type': 'c', 'label': '1'}, u"English chorus"]]
|
[{'lang': 'en', 'type': 'c', 'label': '1'}, u"English chorus"]]
|
||||||
"""
|
"""
|
||||||
self.song_xml = None
|
self.song_xml = None
|
||||||
@ -317,9 +317,7 @@ class OpenLyrics(object):
|
|||||||
tags_element = None
|
tags_element = None
|
||||||
match = re.search(u'\{/?\w+\}', song.lyrics, re.UNICODE)
|
match = re.search(u'\{/?\w+\}', song.lyrics, re.UNICODE)
|
||||||
if match:
|
if match:
|
||||||
# Reset available tags.
|
# Named 'format_' - 'format' is built-in fuction in Python.
|
||||||
FormattingTags.reset_html_tags()
|
|
||||||
# Named 'formatting' - 'format' is built-in fuction in Python.
|
|
||||||
format_ = etree.SubElement(song_xml, u'format')
|
format_ = etree.SubElement(song_xml, u'format')
|
||||||
tags_element = etree.SubElement(format_, u'tags')
|
tags_element = etree.SubElement(format_, u'tags')
|
||||||
tags_element.set(u'application', u'OpenLP')
|
tags_element.set(u'application', u'OpenLP')
|
||||||
@ -334,18 +332,59 @@ class OpenLyrics(object):
|
|||||||
self._add_text_to_element(u'verse', lyrics, None, verse_def)
|
self._add_text_to_element(u'verse', lyrics, None, verse_def)
|
||||||
if u'lang' in verse[0]:
|
if u'lang' in verse[0]:
|
||||||
verse_element.set(u'lang', verse[0][u'lang'])
|
verse_element.set(u'lang', verse[0][u'lang'])
|
||||||
# Create a list with all "virtual" verses.
|
# Create a list with all "optional" verses.
|
||||||
virtual_verses = cgi.escape(verse[1])
|
optional_verses = cgi.escape(verse[1])
|
||||||
virtual_verses = virtual_verses.split(u'[---]')
|
optional_verses = optional_verses.split(u'\n[---]\n')
|
||||||
for index, virtual_verse in enumerate(virtual_verses):
|
start_tags = u''
|
||||||
|
end_tags = u''
|
||||||
|
for index, optional_verse in enumerate(optional_verses):
|
||||||
|
# Fix up missing end and start tags such as {r} or {/r}.
|
||||||
|
optional_verse = start_tags + optional_verse
|
||||||
|
start_tags, end_tags = self._get_missing_tags(optional_verse)
|
||||||
|
optional_verse += end_tags
|
||||||
# Add formatting tags to text
|
# Add formatting tags to text
|
||||||
lines_element = self._add_text_with_tags_to_lines(verse_element,
|
lines_element = self._add_text_with_tags_to_lines(verse_element,
|
||||||
virtual_verse, tags_element)
|
optional_verse, tags_element)
|
||||||
# Do not add the break attribute to the last lines element.
|
# Do not add the break attribute to the last lines element.
|
||||||
if index < len(virtual_verses) - 1:
|
if index < len(optional_verses) - 1:
|
||||||
lines_element.set(u'break', u'optional')
|
lines_element.set(u'break', u'optional')
|
||||||
return self._extract_xml(song_xml)
|
return self._extract_xml(song_xml)
|
||||||
|
|
||||||
|
def _get_missing_tags(self, text):
|
||||||
|
"""
|
||||||
|
Tests the given text for not closed formatting tags and returns a tuple
|
||||||
|
consisting of two unicode strings::
|
||||||
|
|
||||||
|
(u'{st}{r}', u'{/r}{/st}')
|
||||||
|
|
||||||
|
The first unicode string are the start tags (for the next slide). The
|
||||||
|
second unicode string are the end tags.
|
||||||
|
|
||||||
|
``text``
|
||||||
|
The text to test. The text must **not** contain html tags, only
|
||||||
|
OpenLP formatting tags are allowed::
|
||||||
|
|
||||||
|
{st}{r}Text text text
|
||||||
|
"""
|
||||||
|
tags = []
|
||||||
|
for tag in FormattingTags.get_html_tags():
|
||||||
|
if tag[u'start tag'] == u'{br}':
|
||||||
|
continue
|
||||||
|
if text.count(tag[u'start tag']) != text.count(tag[u'end tag']):
|
||||||
|
tags.append((text.find(tag[u'start tag']),
|
||||||
|
tag[u'start tag'], tag[u'end tag']))
|
||||||
|
# Sort the lists, so that the tags which were opened first on the first
|
||||||
|
# slide (the text we are checking) will be opened first on the next
|
||||||
|
# slide as well.
|
||||||
|
tags.sort(key=lambda tag: tag[0])
|
||||||
|
end_tags = []
|
||||||
|
start_tags = []
|
||||||
|
for tag in tags:
|
||||||
|
start_tags.append(tag[1])
|
||||||
|
end_tags.append(tag[2])
|
||||||
|
end_tags.reverse()
|
||||||
|
return u''.join(start_tags), u''.join(end_tags)
|
||||||
|
|
||||||
def xml_to_song(self, xml, parse_and_temporary_save=False):
|
def xml_to_song(self, xml, parse_and_temporary_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
|
||||||
@ -572,7 +611,8 @@ class OpenLyrics(object):
|
|||||||
for tag in FormattingTags.get_html_tags()]
|
for tag in FormattingTags.get_html_tags()]
|
||||||
new_tags = [tag for tag in found_tags
|
new_tags = [tag for tag in found_tags
|
||||||
if tag[u'start tag'] not in existing_tag_ids]
|
if tag[u'start tag'] not in existing_tag_ids]
|
||||||
FormattingTags.add_html_tags(new_tags, True)
|
FormattingTags.add_html_tags(new_tags)
|
||||||
|
FormattingTags.save_html_tags()
|
||||||
|
|
||||||
def _process_lines_mixed_content(self, element, newlines=True):
|
def _process_lines_mixed_content(self, element, newlines=True):
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user