This commit is contained in:
Andreas Preikschat 2011-04-28 16:51:35 +02:00
commit 2a660a305e
41 changed files with 664 additions and 512 deletions

View File

@ -1,3 +1,5 @@
.. _dualmonitors:
================== ==================
Dual Monitor Setup Dual Monitor Setup
================== ==================
@ -32,6 +34,14 @@ projector hooked up to your computer as the second monitor. With the option of
extending your desktop across the second monitor, or your operating system's extending your desktop across the second monitor, or your operating system's
equivalent. equivalent.
**Special Note For Projectors Using USB Connections**
Users have reported experiencing difficulties when using a projector with a USB
connection, as third party software is often required to properly configure
dual monitors. If possible, it is best to use a direct output (VGA, DVI, HDMI,
S-Video) from your machine's video card. If a USB connection is your only option
please consult the manufacturer's manual for instructions on a proper setup.
Microsoft Windows Microsoft Windows
----------------- -----------------

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

View File

@ -175,3 +175,23 @@ only download the section you search for. If you do not have an internet
connection where you intend to use OpenLP you will need another scripture connection where you intend to use OpenLP you will need another scripture
source. For more information about acquiring Bibles please see :ref:`bibleimporter`. source. For more information about acquiring Bibles please see :ref:`bibleimporter`.
OpenLP is using a large amount of RAM when showing a presentation
-----------------------------------------------------------------
OpenLP uses a large amount of RAM when showing a presentation due to the way it
handles presentations. OpenLP itself is unable to show those presentations or
load the presentation files, so it interacts with the presentation through
either Microsoft PowerPoint or LibreOffice Impress. In order to show the slides
in the slide controller, OpenLP requests that the presentation application
export the slides to images, and then uses those images as slides. This results
in a large amount of RAM being used, especially in presentations with more than
about 20 slides.
OpenLP is not displaying correctly, or is not on the correct screen
-------------------------------------------------------------------
Please read the documentation on :ref:`dualmonitors`. It is very important to
have dual monitors setup properly for OpenLP to function as expected.

View File

@ -637,4 +637,4 @@ class ThemeXML(object):
self.font_footer_shadow_size) self.font_footer_shadow_size)
self.add_display(self.display_horizontal_align, self.add_display(self.display_horizontal_align,
self.display_vertical_align, self.display_vertical_align,
self.display_slide_transition) self.display_slide_transition)

View File

@ -41,11 +41,11 @@ class AdvancedTab(SettingsTab):
""" """
Initialise the settings tab Initialise the settings tab
""" """
generalTranslated = translate('AdvancedTab', 'Advanced') advancedTranslated = translate('OpenLP.AdvancedTab', 'Advanced')
SettingsTab.__init__(self, parent ,u'Advanced', generalTranslated)
self.default_image = u':/graphics/openlp-splash-screen.png' self.default_image = u':/graphics/openlp-splash-screen.png'
self.default_color = u'#ffffff' self.default_color = u'#ffffff'
self.icon_path = u':/system/system_settings.png' self.icon_path = u':/system/system_settings.png'
SettingsTab.__init__(self, parent, u'Advanced', advancedTranslated)
def setupUi(self): def setupUi(self):
""" """
@ -82,14 +82,6 @@ class AdvancedTab(SettingsTab):
u'enableAutoCloseCheckBox') u'enableAutoCloseCheckBox')
self.uiLayout.addRow(self.enableAutoCloseCheckBox) self.uiLayout.addRow(self.enableAutoCloseCheckBox)
self.leftLayout.addWidget(self.uiGroupBox) self.leftLayout.addWidget(self.uiGroupBox)
self.hideMouseGroupBox = QtGui.QGroupBox(self.leftColumn)
self.hideMouseGroupBox.setObjectName(u'hideMouseGroupBox')
self.hideMouseLayout = QtGui.QVBoxLayout(self.hideMouseGroupBox)
self.hideMouseLayout.setObjectName(u'hideMouseLayout')
self.hideMouseCheckBox = QtGui.QCheckBox(self.hideMouseGroupBox)
self.hideMouseCheckBox.setObjectName(u'hideMouseCheckBox')
self.hideMouseLayout.addWidget(self.hideMouseCheckBox)
self.leftLayout.addWidget(self.hideMouseGroupBox)
self.leftLayout.addStretch() self.leftLayout.addStretch()
self.defaultImageGroupBox = QtGui.QGroupBox(self.rightColumn) self.defaultImageGroupBox = QtGui.QGroupBox(self.rightColumn)
self.defaultImageGroupBox.setObjectName(u'defaultImageGroupBox') self.defaultImageGroupBox.setObjectName(u'defaultImageGroupBox')
@ -109,26 +101,42 @@ class AdvancedTab(SettingsTab):
self.defaultBrowseButton.setObjectName(u'defaultBrowseButton') self.defaultBrowseButton.setObjectName(u'defaultBrowseButton')
self.defaultBrowseButton.setIcon( self.defaultBrowseButton.setIcon(
build_icon(u':/general/general_open.png')) build_icon(u':/general/general_open.png'))
self.defaultRevertButton = QtGui.QToolButton(self.defaultImageGroupBox)
self.defaultRevertButton.setObjectName(u'defaultRevertButton')
self.defaultRevertButton.setIcon(
build_icon(u':/general/general_revert.png'))
self.defaultFileLayout = QtGui.QHBoxLayout() self.defaultFileLayout = QtGui.QHBoxLayout()
self.defaultFileLayout.setObjectName(u'defaultFileLayout') self.defaultFileLayout.setObjectName(u'defaultFileLayout')
self.defaultFileLayout.addWidget(self.defaultFileEdit) self.defaultFileLayout.addWidget(self.defaultFileEdit)
self.defaultFileLayout.addWidget(self.defaultBrowseButton) self.defaultFileLayout.addWidget(self.defaultBrowseButton)
self.defaultFileLayout.addWidget(self.defaultRevertButton)
self.defaultImageLayout.addRow(self.defaultFileLabel, self.defaultImageLayout.addRow(self.defaultFileLabel,
self.defaultFileLayout) self.defaultFileLayout)
self.rightLayout.addWidget(self.defaultImageGroupBox) self.rightLayout.addWidget(self.defaultImageGroupBox)
self.hideMouseGroupBox = QtGui.QGroupBox(self.leftColumn)
self.hideMouseGroupBox.setObjectName(u'hideMouseGroupBox')
self.hideMouseLayout = QtGui.QVBoxLayout(self.hideMouseGroupBox)
self.hideMouseLayout.setObjectName(u'hideMouseLayout')
self.hideMouseCheckBox = QtGui.QCheckBox(self.hideMouseGroupBox)
self.hideMouseCheckBox.setObjectName(u'hideMouseCheckBox')
self.hideMouseLayout.addWidget(self.hideMouseCheckBox)
self.rightLayout.addWidget(self.hideMouseGroupBox)
self.rightLayout.addStretch() self.rightLayout.addStretch()
QtCore.QObject.connect(self.defaultColorButton, QtCore.QObject.connect(self.defaultColorButton,
QtCore.SIGNAL(u'pressed()'), self.onDefaultColorButtonPressed) QtCore.SIGNAL(u'pressed()'), self.onDefaultColorButtonPressed)
QtCore.QObject.connect(self.defaultBrowseButton, QtCore.QObject.connect(self.defaultBrowseButton,
QtCore.SIGNAL(u'pressed()'), self.onDefaultBrowseButtonPressed) QtCore.SIGNAL(u'pressed()'), self.onDefaultBrowseButtonPressed)
QtCore.QObject.connect(self.defaultRevertButton,
QtCore.SIGNAL(u'pressed()'), self.onDefaultRevertButtonPressed)
def retranslateUi(self): def retranslateUi(self):
""" """
Setup the interface translation strings. Setup the interface translation strings.
""" """
self.tabTitleVisible = UiStrings().Advanced self.tabTitleVisible = UiStrings().Advanced
self.uiGroupBox.setTitle(translate('OpenLP.AdvancedTab', 'UI Settings')) self.uiGroupBox.setTitle(
translate('OpenLP.AdvancedTab', 'UI Settings'))
self.recentLabel.setText( self.recentLabel.setText(
translate('OpenLP.AdvancedTab', translate('OpenLP.AdvancedTab',
'Number of recent files to display:')) 'Number of recent files to display:'))
@ -150,8 +158,14 @@ class AdvancedTab(SettingsTab):
'Default Image')) 'Default Image'))
self.defaultColorLabel.setText(translate('OpenLP.AdvancedTab', self.defaultColorLabel.setText(translate('OpenLP.AdvancedTab',
'Background color:')) 'Background color:'))
self.defaultColorButton.setToolTip(translate('OpenLP.AdvancedTab',
'Click to select a color.'))
self.defaultFileLabel.setText(translate('OpenLP.AdvancedTab', self.defaultFileLabel.setText(translate('OpenLP.AdvancedTab',
'Image file:')) 'Image file:'))
self.defaultBrowseButton.setToolTip(translate('OpenLP.AdvancedTab',
'Browse for an image file to display.'))
self.defaultRevertButton.setToolTip(translate('OpenLP.AdvancedTab',
'Revert to the default OpenLP logo.'))
def load(self): def load(self):
""" """
@ -232,4 +246,8 @@ class AdvancedTab(SettingsTab):
file_filters) file_filters)
if filename: if filename:
self.defaultFileEdit.setText(filename) self.defaultFileEdit.setText(filename)
self.defaultFileEdit.setFocus() self.defaultFileEdit.setFocus()
def onDefaultRevertButtonPressed(self):
self.defaultFileEdit.setText(u':/graphics/openlp-splash-screen.png')
self.defaultFileEdit.setFocus()

View File

@ -142,7 +142,8 @@ class MainDisplay(DisplayWidget):
image_file = QtCore.QSettings().value(u'advanced/default image', image_file = QtCore.QSettings().value(u'advanced/default image',
QtCore.QVariant(u':/graphics/openlp-splash-screen.png'))\ QtCore.QVariant(u':/graphics/openlp-splash-screen.png'))\
.toString() .toString()
background_color = QtGui.QColor(QtCore.QSettings().value( background_color = QtGui.QColor()
background_color.setNamedColor(QtCore.QSettings().value(
u'advanced/default color', u'advanced/default color',
QtCore.QVariant(u'#ffffff')).toString()) QtCore.QVariant(u'#ffffff')).toString())
if not background_color.isValid(): if not background_color.isValid():

View File

@ -46,41 +46,58 @@ http://doc.trolltech.com/4.7/richtext-html-subset.html#css-properties
color:black; color:black;
} }
.item {
color:black;
}
.itemTitle { .itemTitle {
font-weight:600; font-weight:600;
font-size:large; font-size:large;
color:black;
} }
.itemText { .itemText {}
color:black;
}
.itemFooter { .itemFooter {
font-size:8px; font-size:8px;
color:black;
} }
.itemNotes {}
.itemNotesTitle { .itemNotesTitle {
font-weight:bold; font-weight:bold;
font-size:12px; font-size:12px;
color:black;
} }
.itemNotesText { .itemNotesText {
font-size:11px; font-size:11px;
color:black; }
.media {}
.mediaTitle {
font-weight:bold;
font-size:11px;
}
.mediaText {}
.imageList {}
.customNotes {
margin-top: 10px;
} }
.customNotesTitle { .customNotesTitle {
font-weight:bold; font-weight:bold;
font-size:11px; font-size:11px;
color:black;
} }
.customNotesText { .customNotesText {
font-size:11px; font-size:11px;
color:black; }
.newPage {
page-break-before:always;
} }
""" """
@ -153,86 +170,90 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
""" """
Creates the html text and updates the html of *self.document*. Creates the html text and updates the html of *self.document*.
""" """
html_data = html.fromstring( html_data = self._addElement(u'html')
u'<title>%s</title>' % unicode(self.titleLineEdit.text())) self._addElement(u'head', parent=html_data)
self._addElement(u'title', unicode(self.titleLineEdit.text()),
html_data.head)
css_path = os.path.join( css_path = os.path.join(
AppLocation.get_data_path(), u'servicePrint.css') AppLocation.get_data_path(), u'service_print.css')
if not os.path.isfile(css_path):
# Create default css file.
css_file = open(css_path, u'w')
css_file.write(DEFAULT_CSS)
css_file.close()
custom_css = get_text_file_string(css_path) custom_css = get_text_file_string(css_path)
self._addChildToParent( if not custom_css:
u'style', custom_css, html_data.head, u'type', u'text/css') custom_css = DEFAULT_CSS
self._addChildToParent(u'body', parent=html_data) self._addElement(u'style', custom_css, html_data.head,
self._addChildToParent(u'span', unicode(self.titleLineEdit.text()), attribute=(u'type', u'text/css'))
html_data.body, u'class', u'serviceTitle') self._addElement(u'body', parent=html_data)
self._addElement(u'h1', unicode(self.titleLineEdit.text()),
html_data.body, classId=u'serviceTitle')
for index, item in enumerate(self.serviceManager.serviceItems): for index, item in enumerate(self.serviceManager.serviceItems):
item = item[u'service_item'] self._addPreviewItem(html_data.body, item[u'service_item'], index)
div = self._addChildToParent(u'div', parent=html_data.body)
# Add the title of the service item.
item_title = self._addChildToParent(
u'h2', parent=div, attribute=u'class', value=u'itemTitle')
self._addChildToParent(
u'img', parent=item_title, attribute=u'src', value=item.icon)
self._fromstring(
u'<span> %s</span>' % item.get_display_title(), item_title)
if self.slideTextCheckBox.isChecked():
# Add the text of the service item.
if item.is_text():
verse_def = None
for slide in item.get_frames():
if not verse_def or verse_def != slide[u'verseTag']:
p = self._addChildToParent(u'p', parent=div,
attribute=u'class', value=u'itemText')
else:
self._addChildToParent(u'br', parent=p)
self._fromstring(u'<span>%s</span>' % slide[u'html'], p)
verse_def = slide[u'verseTag']
# Break the page before the div element.
if index != 0 and self.pageBreakAfterText.isChecked():
div.set(u'style', u'page-break-before:always')
# Add the image names of the service item.
elif item.is_image():
ol = self._addChildToParent(u'ol', parent=div)
for slide in range(len(item.get_frames())):
self._addChildToParent(u'li', item.get_frame_title(slide), ol)
# add footer
if item.foot_text:
self._fromstring(
item.foot_text, div, u'class', u'itemFooter')
# Add service items' notes.
if self.notesCheckBox.isChecked():
if item.notes:
p = self._addChildToParent(u'p', parent=div)
self._addChildToParent(u'span', unicode(
translate('OpenLP.ServiceManager', 'Notes:')), p,
u'class', u'itemNotesTitle')
self._fromstring(u'<span> %s</span>' % item.notes.replace(
u'\n', u'<br />'), p, u'class', u'itemNotesText')
# Add play length of media files.
if item.is_media() and self.metaDataCheckBox.isChecked():
tme = item.media_length
if item.end_time > 0:
tme = item.end_time - item.start_time
title = self._fromstring(u'<p><strong>%s</strong> </p>' %
translate('OpenLP.ServiceManager', 'Playing time:'), div)
self._fromstring(u'<span>%s</span>' %
unicode(datetime.timedelta(seconds=tme)), title)
# Add the custom service notes: # Add the custom service notes:
if self.footerTextEdit.toPlainText(): if self.footerTextEdit.toPlainText():
div = self._addChildToParent(u'div', parent=html_data.body) div = self._addElement(u'div', parent=html_data.body,
self._addChildToParent(u'span', translate('OpenLP.ServiceManager', classId=u'customNotes')
u'Custom Service Notes:'), div, u'class', u'customNotesTitle') self._addElement(u'span', translate('OpenLP.ServiceManager',
self._addChildToParent( 'Custom Service Notes: '), div, classId=u'customNotesTitle')
u'span', u' %s' % self.footerTextEdit.toPlainText(), div, self._addElement(u'span', self.footerTextEdit.toPlainText(), div,
u'class', u'customNotesText') classId=u'customNotesText')
self.document.setHtml(html.tostring(html_data)) self.document.setHtml(html.tostring(html_data))
self.previewWidget.updatePreview() self.previewWidget.updatePreview()
def _addChildToParent(self, tag, text=None, parent=None, attribute=None, def _addPreviewItem(self, body, item, index):
value=None): div = self._addElement(u'div', classId=u'item', parent=body)
# Add the title of the service item.
item_title = self._addElement(u'h2', parent=div, classId=u'itemTitle')
self._addElement(u'img', parent=item_title,
attribute=(u'src', item.icon))
self._addElement(u'span', u'&nbsp;' + item.get_display_title(),
item_title)
if self.slideTextCheckBox.isChecked():
# Add the text of the service item.
if item.is_text():
verse_def = None
for slide in item.get_frames():
if not verse_def or verse_def != slide[u'verseTag']:
p = self._addElement(u'div', parent=div,
classId=u'itemText')
else:
self._addElement(u'br', parent=p)
self._addElement(u'p', slide[u'html'], p)
verse_def = slide[u'verseTag']
# Break the page before the div element.
if index != 0 and self.pageBreakAfterText.isChecked():
div.set(u'class', u'item newPage')
# Add the image names of the service item.
elif item.is_image():
ol = self._addElement(u'ol', parent=div, classId=u'imageList')
for slide in range(len(item.get_frames())):
self._addElement(u'li', item.get_frame_title(slide), ol)
# add footer
foot_text = item.foot_text
foot_text = foot_text.partition(u'<br>')[2]
if foot_text:
foot = self._addElement(u'div', foot_text, parent=div,
classId=u'itemFooter')
# Add service items' notes.
if self.notesCheckBox.isChecked():
if item.notes:
p = self._addElement(u'div', classId=u'itemNotes', parent=div)
self._addElement(u'span',
translate('OpenLP.ServiceManager', 'Notes: '), p,
classId=u'itemNotesTitle')
notes = self._addElement(u'span',
item.notes.replace(u'\n', u'<br />'), p,
classId=u'itemNotesText')
# Add play length of media files.
if item.is_media() and self.metaDataCheckBox.isChecked():
tme = item.media_length
if item.end_time > 0:
tme = item.end_time - item.start_time
title = self._addElement(u'div', classId=u'media', parent=div)
self._addElement(u'span', translate('OpenLP.ServiceManager',
'Playing time: '), title, classId=u'mediaTitle')
self._addElement(u'span', unicode(datetime.timedelta(seconds=tme)),
title, classId=u'mediaText')
def _addElement(self, tag, text=None, parent=None, classId=None,
attribute=None):
""" """
Creates a html element. If ``text`` is given, the element's text will Creates a html element. If ``text`` is given, the element's text will
set and if a ``parent`` is given, the element is appended. set and if a ``parent`` is given, the element is appended.
@ -246,30 +267,22 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
``parent`` ``parent``
The parent element. Defaults to ``None``. The parent element. Defaults to ``None``.
``attribute`` ``classId``
An optional attribute, for instance ``u'class``. Value for the class attribute
``value`` ``attribute``
The value for the given ``attribute``. It does not have a meaning, Tuple name/value pair to add as an optional attribute
if the attribute is left to its default.
""" """
element = html.Element(tag)
if text is not None: if text is not None:
element.text = unicode(text) element = html.fragment_fromstring(unicode(text), create_parent=tag)
else:
element = html.Element(tag)
if parent is not None: if parent is not None:
parent.append(element) parent.append(element)
if classId is not None:
element.set(u'class', classId)
if attribute is not None: if attribute is not None:
element.set(attribute, value if value is not None else u'') element.set(attribute[0], attribute[1])
return element
def _fromstring(self, string, parent, attribute=None, value=None):
"""
This is used to create a child html element from a string.
"""
element = html.fromstring(string)
if attribute is not None:
element.set(attribute, value if value is not None else u'')
parent.append(element)
return element return element
def paintRequested(self, printer): def paintRequested(self, printer):
@ -380,4 +393,4 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog):
QtCore.QVariant(self.metaDataCheckBox.isChecked())) QtCore.QVariant(self.metaDataCheckBox.isChecked()))
settings.setValue(u'print notes', settings.setValue(u'print notes',
QtCore.QVariant(self.notesCheckBox.isChecked())) QtCore.QVariant(self.notesCheckBox.isChecked()))
settings.endGroup() settings.endGroup()

View File

@ -686,6 +686,7 @@ class ServiceManager(QtGui.QWidget):
self.serviceItems[item][u'service_item'].notes = \ self.serviceItems[item][u'service_item'].notes = \
self.serviceNoteForm.textEdit.toPlainText() self.serviceNoteForm.textEdit.toPlainText()
self.repaintServiceList(item, -1) self.repaintServiceList(item, -1)
self.setModified()
def onStartTimeForm(self): def onStartTimeForm(self):
item = self.findServiceItem()[0] item = self.findServiceItem()[0]
@ -789,7 +790,7 @@ class ServiceManager(QtGui.QWidget):
# Top Item was selected so set the last one # Top Item was selected so set the last one
if setLastItem: if setLastItem:
lastItem.setSelected(True) lastItem.setSelected(True)
self.setModified(True) self.setModified()
def onMoveSelectionDown(self): def onMoveSelectionDown(self):
""" """
@ -811,7 +812,7 @@ class ServiceManager(QtGui.QWidget):
serviceIterator += 1 serviceIterator += 1
if setSelected: if setSelected:
firstItem.setSelected(True) firstItem.setSelected(True)
self.setModified(True) self.setModified()
def onCollapseAll(self): def onCollapseAll(self):
""" """
@ -855,7 +856,7 @@ class ServiceManager(QtGui.QWidget):
self.serviceItems.remove(self.serviceItems[item]) self.serviceItems.remove(self.serviceItems[item])
self.serviceItems.insert(0, temp) self.serviceItems.insert(0, temp)
self.repaintServiceList(0, child) self.repaintServiceList(0, child)
self.setModified(True) self.setModified()
def onServiceUp(self): def onServiceUp(self):
""" """
@ -867,7 +868,7 @@ class ServiceManager(QtGui.QWidget):
self.serviceItems.remove(self.serviceItems[item]) self.serviceItems.remove(self.serviceItems[item])
self.serviceItems.insert(item - 1, temp) self.serviceItems.insert(item - 1, temp)
self.repaintServiceList(item - 1, child) self.repaintServiceList(item - 1, child)
self.setModified(True) self.setModified()
def onServiceDown(self): def onServiceDown(self):
""" """
@ -879,7 +880,7 @@ class ServiceManager(QtGui.QWidget):
self.serviceItems.remove(self.serviceItems[item]) self.serviceItems.remove(self.serviceItems[item])
self.serviceItems.insert(item + 1, temp) self.serviceItems.insert(item + 1, temp)
self.repaintServiceList(item + 1, child) self.repaintServiceList(item + 1, child)
self.setModified(True) self.setModified()
def onServiceEnd(self): def onServiceEnd(self):
""" """
@ -891,7 +892,7 @@ class ServiceManager(QtGui.QWidget):
self.serviceItems.remove(self.serviceItems[item]) self.serviceItems.remove(self.serviceItems[item])
self.serviceItems.insert(len(self.serviceItems), temp) self.serviceItems.insert(len(self.serviceItems), temp)
self.repaintServiceList(len(self.serviceItems) - 1, child) self.repaintServiceList(len(self.serviceItems) - 1, child)
self.setModified(True) self.setModified()
def onDeleteFromService(self): def onDeleteFromService(self):
""" """
@ -901,7 +902,7 @@ class ServiceManager(QtGui.QWidget):
if item != -1: if item != -1:
self.serviceItems.remove(self.serviceItems[item]) self.serviceItems.remove(self.serviceItems[item])
self.repaintServiceList(item - 1, -1) self.repaintServiceList(item - 1, -1)
self.setModified(True) self.setModified()
def repaintServiceList(self, serviceItem, serviceItemChild): def repaintServiceList(self, serviceItem, serviceItemChild):
""" """
@ -1026,7 +1027,7 @@ class ServiceManager(QtGui.QWidget):
item[u'service_item'], False, expand=item[u'expanded']) item[u'service_item'], False, expand=item[u'expanded'])
# Set to False as items may have changed rendering # Set to False as items may have changed rendering
# does not impact the saved song so True may also be valid # does not impact the saved song so True may also be valid
self.setModified(True) self.setModified()
Receiver.send_message(u'cursor_normal') Receiver.send_message(u'cursor_normal')
def serviceItemUpdate(self, message): def serviceItemUpdate(self, message):
@ -1037,7 +1038,7 @@ class ServiceManager(QtGui.QWidget):
for item in self.serviceItems: for item in self.serviceItems:
if item[u'service_item']._uuid == uuid: if item[u'service_item']._uuid == uuid:
item[u'service_item'].edit_id = int(editId) item[u'service_item'].edit_id = int(editId)
self.setModified(True) self.setModified()
def replaceServiceItem(self, newItem): def replaceServiceItem(self, newItem):
""" """
@ -1053,7 +1054,7 @@ class ServiceManager(QtGui.QWidget):
self.repaintServiceList(itemcount + 1, 0) self.repaintServiceList(itemcount + 1, 0)
self.mainwindow.liveController.replaceServiceManagerItem( self.mainwindow.liveController.replaceServiceManagerItem(
newItem) newItem)
self.setModified(True) self.setModified()
def addServiceItem(self, item, rebuild=False, expand=None, replace=False): def addServiceItem(self, item, rebuild=False, expand=None, replace=False):
""" """
@ -1098,7 +1099,7 @@ class ServiceManager(QtGui.QWidget):
if rebuild: if rebuild:
self.mainwindow.liveController.replaceServiceManagerItem(item) self.mainwindow.liveController.replaceServiceManagerItem(item)
self.dropPosition = 0 self.dropPosition = 0
self.setModified(True) self.setModified()
def makePreview(self): def makePreview(self):
""" """
@ -1235,7 +1236,7 @@ class ServiceManager(QtGui.QWidget):
self.serviceItems.remove(serviceItem) self.serviceItems.remove(serviceItem)
self.serviceItems.insert(endpos, serviceItem) self.serviceItems.insert(endpos, serviceItem)
self.repaintServiceList(endpos, child) self.repaintServiceList(endpos, child)
self.setModified(True) self.setModified()
else: else:
# we are not over anything so drop # we are not over anything so drop
replace = False replace = False

View File

@ -380,18 +380,21 @@ class SlideController(QtGui.QWidget):
action_list.add_action(self.previousItem) action_list.add_action(self.previousItem)
action_list.add_action(self.nextItem) action_list.add_action(self.nextItem)
self.previousService = shortcut_action(parent, u'previousService', self.previousService = shortcut_action(parent, u'previousService',
[QtCore.Qt.Key_Left], self.servicePrevious, UiStrings().LiveToolbar) [QtCore.Qt.Key_Left], self.servicePrevious,
self.previousService.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) category=UiStrings().LiveToolbar,
context=QtCore.Qt.WidgetWithChildrenShortcut)
self.previousService.setText( self.previousService.setText(
translate('OpenLP.SlideController', 'Previous Service')) translate('OpenLP.SlideController', 'Previous Service'))
self.nextService = shortcut_action(parent, 'nextService', self.nextService = shortcut_action(parent, 'nextService',
[QtCore.Qt.Key_Right], self.serviceNext, UiStrings().LiveToolbar) [QtCore.Qt.Key_Right], self.serviceNext,
self.nextService.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) category=UiStrings().LiveToolbar,
context=QtCore.Qt.WidgetWithChildrenShortcut)
self.nextService.setText( self.nextService.setText(
translate('OpenLP.SlideController', 'Next Service')) translate('OpenLP.SlideController', 'Next Service'))
self.escapeItem = shortcut_action(parent, 'escapeItem', self.escapeItem = shortcut_action(parent, 'escapeItem',
[QtCore.Qt.Key_Escape], self.liveEscape, UiStrings().LiveToolbar) [QtCore.Qt.Key_Escape], self.liveEscape,
self.escapeItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut) category=UiStrings().LiveToolbar,
context=QtCore.Qt.WidgetWithChildrenShortcut)
self.escapeItem.setText( self.escapeItem.setText(
translate('OpenLP.SlideController', 'Escape Item')) translate('OpenLP.SlideController', 'Escape Item'))
@ -1150,4 +1153,5 @@ class SlideController(QtGui.QWidget):
elif self.desktopScreen.isChecked(): elif self.desktopScreen.isChecked():
return HideMode.Screen return HideMode.Screen
else: else:
return None return None

View File

@ -56,6 +56,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
self.setupUi(self) self.setupUi(self)
self.registerFields() self.registerFields()
self.updateThemeAllowed = True self.updateThemeAllowed = True
self.temp_background_filename = u''
QtCore.QObject.connect(self.backgroundComboBox, QtCore.QObject.connect(self.backgroundComboBox,
QtCore.SIGNAL(u'currentIndexChanged(int)'), QtCore.SIGNAL(u'currentIndexChanged(int)'),
self.onBackgroundComboBoxCurrentIndexChanged) self.onBackgroundComboBoxCurrentIndexChanged)
@ -279,6 +280,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
Run the wizard. Run the wizard.
""" """
log.debug(u'Editing theme %s' % self.theme.theme_name) log.debug(u'Editing theme %s' % self.theme.theme_name)
self.temp_background_filename = u''
self.updateThemeAllowed = False self.updateThemeAllowed = False
self.setDefaults() self.setDefaults()
self.updateThemeAllowed = True self.updateThemeAllowed = True
@ -432,6 +434,16 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
# do not allow updates when screen is building for the first time. # do not allow updates when screen is building for the first time.
if self.updateThemeAllowed: if self.updateThemeAllowed:
self.theme.background_type = BackgroundType.to_string(index) self.theme.background_type = BackgroundType.to_string(index)
if self.theme.background_type != \
BackgroundType.to_string(BackgroundType.Image) and \
self.temp_background_filename == u'':
self.temp_background_filename = self.theme.background_filename
self.theme.background_filename = u''
if self.theme.background_type == \
BackgroundType.to_string(BackgroundType.Image) and \
self.temp_background_filename != u'':
self.theme.background_filename = self.temp_background_filename
self.temp_background_filename = u''
self.setBackgroundPageValues() self.setBackgroundPageValues()
def onGradientComboBoxCurrentIndexChanged(self, index): def onGradientComboBoxCurrentIndexChanged(self, index):
@ -589,4 +601,4 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
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

View File

@ -95,6 +95,10 @@ class OpenLPWizard(QtGui.QWizard):
self.customSignals() self.customSignals()
QtCore.QObject.connect(self, QtCore.SIGNAL(u'currentIdChanged(int)'), QtCore.QObject.connect(self, QtCore.SIGNAL(u'currentIdChanged(int)'),
self.onCurrentIdChanged) self.onCurrentIdChanged)
QtCore.QObject.connect(self.errorCopyToButton,
QtCore.SIGNAL(u'clicked()'), self.onErrorCopyToButtonClicked)
QtCore.QObject.connect(self.errorSaveToButton,
QtCore.SIGNAL(u'clicked()'), self.onErrorSaveToButtonClicked)
def setupUi(self, image): def setupUi(self, image):
""" """
@ -129,10 +133,36 @@ class OpenLPWizard(QtGui.QWizard):
self.progressLayout.setObjectName(u'progressLayout') self.progressLayout.setObjectName(u'progressLayout')
self.progressLabel = QtGui.QLabel(self.progressPage) self.progressLabel = QtGui.QLabel(self.progressPage)
self.progressLabel.setObjectName(u'progressLabel') self.progressLabel.setObjectName(u'progressLabel')
self.progressLabel.setWordWrap(True)
self.progressLayout.addWidget(self.progressLabel) self.progressLayout.addWidget(self.progressLabel)
self.progressBar = QtGui.QProgressBar(self.progressPage) self.progressBar = QtGui.QProgressBar(self.progressPage)
self.progressBar.setObjectName(u'progressBar') self.progressBar.setObjectName(u'progressBar')
self.progressLayout.addWidget(self.progressBar) self.progressLayout.addWidget(self.progressBar)
# Add a QTextEdit and a copy to file and copy to clipboard button to be
# able to provide feedback to the user. Hidden by default.
self.errorReportTextEdit = QtGui.QTextEdit(self.progressPage)
self.errorReportTextEdit.setObjectName(u'progresserrorReportTextEdit')
self.errorReportTextEdit.setHidden(True)
self.errorReportTextEdit.setReadOnly(True)
self.progressLayout.addWidget(self.errorReportTextEdit)
self.errorButtonLayout = QtGui.QHBoxLayout()
self.errorButtonLayout.setObjectName(u'errorButtonLayout')
spacer = QtGui.QSpacerItem(40, 20,
QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.errorButtonLayout.addItem(spacer)
self.errorCopyToButton = QtGui.QPushButton(self.progressPage)
self.errorCopyToButton.setObjectName(u'errorCopyToButton')
self.errorCopyToButton.setHidden(True)
self.errorCopyToButton.setIcon(
build_icon(u':/system/system_edit_copy.png'))
self.errorButtonLayout.addWidget(self.errorCopyToButton)
self.errorSaveToButton = QtGui.QPushButton(self.progressPage)
self.errorSaveToButton.setObjectName(u'errorSaveToButton')
self.errorSaveToButton.setHidden(True)
self.errorSaveToButton.setIcon(
build_icon(u':/general/general_save.png'))
self.errorButtonLayout.addWidget(self.errorSaveToButton)
self.progressLayout.addLayout(self.errorButtonLayout)
self.addPage(self.progressPage) self.addPage(self.progressPage)
def exec_(self): def exec_(self):
@ -160,6 +190,18 @@ class OpenLPWizard(QtGui.QWizard):
self.performWizard() self.performWizard()
self.postWizard() self.postWizard()
def onErrorCopyToButtonClicked(self):
"""
Called when the ``onErrorCopyToButtonClicked`` has been clicked.
"""
pass
def onErrorSaveToButtonClicked(self):
"""
Called when the ``onErrorSaveToButtonClicked`` has been clicked.
"""
pass
def incrementProgressBar(self, status_text, increment=1): def incrementProgressBar(self, status_text, increment=1):
""" """
Update the wizard progress page. Update the wizard progress page.
@ -219,4 +261,5 @@ class OpenLPWizard(QtGui.QWizard):
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)

View File

@ -37,6 +37,9 @@ from openlp.plugins.bibles.lib.db import BibleDB
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def replacement(match):
return match.group(2).upper()
class OSISBible(BibleDB): class OSISBible(BibleDB):
""" """
`OSIS <http://www.bibletechnologies.net/>`_ Bible format importer class. `OSIS <http://www.bibletechnologies.net/>`_ Bible format importer class.
@ -60,6 +63,7 @@ class OSISBible(BibleDB):
self.lg_regex = re.compile(r'<lg(.*?)>') self.lg_regex = re.compile(r'<lg(.*?)>')
self.l_regex = re.compile(r'<l (.*?)>') self.l_regex = re.compile(r'<l (.*?)>')
self.w_regex = re.compile(r'<w (.*?)>') self.w_regex = re.compile(r'<w (.*?)>')
self.q_regex = re.compile(r'<q(.*?)>')
self.q1_regex = re.compile(r'<q(.*?)level="1"(.*?)>') self.q1_regex = re.compile(r'<q(.*?)level="1"(.*?)>')
self.q2_regex = re.compile(r'<q(.*?)level="2"(.*?)>') self.q2_regex = re.compile(r'<q(.*?)level="2"(.*?)>')
self.trans_regex = re.compile(r'<transChange(.*?)>(.*?)</transChange>') self.trans_regex = re.compile(r'<transChange(.*?)>(.*?)</transChange>')
@ -106,6 +110,7 @@ class OSISBible(BibleDB):
detect_file.close() detect_file.close()
try: try:
osis = codecs.open(self.filename, u'r', details['encoding']) osis = codecs.open(self.filename, u'r', details['encoding'])
repl = replacement
for file_record in osis: for file_record in osis:
if self.stop_import_flag: if self.stop_import_flag:
break break
@ -148,12 +153,13 @@ class OSISBible(BibleDB):
verse_text = self.rf_regex.sub(u'', verse_text) verse_text = self.rf_regex.sub(u'', verse_text)
verse_text = self.lb_regex.sub(u' ', verse_text) verse_text = self.lb_regex.sub(u' ', verse_text)
verse_text = self.lg_regex.sub(u'', verse_text) verse_text = self.lg_regex.sub(u'', verse_text)
verse_text = self.l_regex.sub(u'', verse_text) verse_text = self.l_regex.sub(u' ', verse_text)
verse_text = self.w_regex.sub(u'', verse_text) verse_text = self.w_regex.sub(u'', verse_text)
verse_text = self.q1_regex.sub(u'"', verse_text) verse_text = self.q1_regex.sub(u'"', verse_text)
verse_text = self.q2_regex.sub(u'\'', verse_text) verse_text = self.q2_regex.sub(u'\'', verse_text)
verse_text = self.q_regex.sub(u'', verse_text)
verse_text = self.divine_name_regex.sub(repl, verse_text)
verse_text = self.trans_regex.sub(u'', verse_text) verse_text = self.trans_regex.sub(u'', verse_text)
verse_text = self.divine_name_regex.sub(u'', verse_text)
verse_text = verse_text.replace(u'</lb>', u'')\ verse_text = verse_text.replace(u'</lb>', u'')\
.replace(u'</l>', u'').replace(u'<lg>', u'')\ .replace(u'</l>', u'').replace(u'<lg>', u'')\
.replace(u'</lg>', u'').replace(u'</q>', u'')\ .replace(u'</lg>', u'').replace(u'</q>', u'')\

View File

@ -26,6 +26,7 @@
""" """
The song import functions for OpenLP. The song import functions for OpenLP.
""" """
import codecs
import logging import logging
import os import os
@ -55,6 +56,7 @@ class SongImportForm(OpenLPWizard):
``plugin`` ``plugin``
The songs plugin. The songs plugin.
""" """
self.clipboard = plugin.formparent.clipboard
OpenLPWizard.__init__(self, parent, plugin, u'songImportWizard', OpenLPWizard.__init__(self, parent, plugin, u'songImportWizard',
u':/wizards/wizard_importsong.bmp') u':/wizards/wizard_importsong.bmp')
@ -330,6 +332,10 @@ class SongImportForm(OpenLPWizard):
'Please wait while your songs are imported.')) 'Please wait while your songs are imported.'))
self.progressLabel.setText(WizardStrings.Ready) self.progressLabel.setText(WizardStrings.Ready)
self.progressBar.setFormat(WizardStrings.PercentSymbolFormat) self.progressBar.setFormat(WizardStrings.PercentSymbolFormat)
self.errorCopyToButton.setText(translate('SongsPlugin.ImportWizardForm',
'Copy'))
self.errorSaveToButton.setText(translate('SongsPlugin.ImportWizardForm',
'Save to File'))
# Align all QFormLayouts towards each other. # Align all QFormLayouts towards each other.
width = max(self.formatLabel.minimumSizeHint().width(), width = max(self.formatLabel.minimumSizeHint().width(),
self.openLP2FilenameLabel.minimumSizeHint().width()) self.openLP2FilenameLabel.minimumSizeHint().width())
@ -459,10 +465,7 @@ class SongImportForm(OpenLPWizard):
""" """
Return a list of file from the listbox Return a list of file from the listbox
""" """
files = [] return [unicode(listbox.item(i).text()) for i in range(listbox.count())]
for row in range(0, listbox.count()):
files.append(unicode(listbox.item(row).text()))
return files
def removeSelectedItems(self, listbox): def removeSelectedItems(self, listbox):
""" """
@ -659,6 +662,10 @@ class SongImportForm(OpenLPWizard):
self.songShowPlusFileListWidget.clear() self.songShowPlusFileListWidget.clear()
self.foilPresenterFileListWidget.clear() self.foilPresenterFileListWidget.clear()
#self.csvFilenameEdit.setText(u'') #self.csvFilenameEdit.setText(u'')
self.errorReportTextEdit.clear()
self.errorReportTextEdit.setHidden(True)
self.errorCopyToButton.setHidden(True)
self.errorSaveToButton.setHidden(True)
def preWizard(self): def preWizard(self):
""" """
@ -743,12 +750,30 @@ class SongImportForm(OpenLPWizard):
importer = self.plugin.importSongs(SongFormat.FoilPresenter, importer = self.plugin.importSongs(SongFormat.FoilPresenter,
filenames=self.getListOfFiles(self.foilPresenterFileListWidget) filenames=self.getListOfFiles(self.foilPresenterFileListWidget)
) )
if importer.do_import(): importer.do_import()
self.progressLabel.setText(WizardStrings.FinishedImport) if importer.error_log:
self.progressLabel.setText(translate(
'SongsPlugin.SongImportForm', 'Your song import failed.'))
else: else:
self.progressLabel.setText( self.progressLabel.setText(WizardStrings.FinishedImport)
translate('SongsPlugin.SongImportForm',
'Your song import failed.')) def onErrorCopyToButtonClicked(self):
"""
Copy the error report to the clipboard.
"""
self.clipboard.setText(self.errorReportTextEdit.toPlainText())
def onErrorSaveToButtonClicked(self):
"""
Save the error report to a file.
"""
filename = QtGui.QFileDialog.getSaveFileName(self,
SettingsManager.get_last_dir(self.plugin.settingsSection, 1))
if not filename:
return
file = codecs.open(filename, u'w', u'utf-8')
file.write(self.errorReportTextEdit.toPlainText())
file.close()
def addFileSelectItem(self, prefix, obj_prefix=None, can_disable=False, def addFileSelectItem(self, prefix, obj_prefix=None, can_disable=False,
single_select=False): single_select=False):
@ -836,4 +861,4 @@ class SongImportForm(OpenLPWizard):
setattr(self, prefix + u'DisabledLayout', disabledLayout) setattr(self, prefix + u'DisabledLayout', disabledLayout)
setattr(self, prefix + u'DisabledLabel', disabledLabel) setattr(self, prefix + u'DisabledLabel', disabledLabel)
setattr(self, prefix + u'ImportWidget', importWidget) setattr(self, prefix + u'ImportWidget', importWidget)
return importWidget return importWidget

View File

@ -386,7 +386,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
existing_author = self.manager.get_object_filtered(Author, existing_author = self.manager.get_object_filtered(Author,
and_(Author.first_name == old_author.first_name, and_(Author.first_name == old_author.first_name,
Author.last_name == old_author.last_name, Author.last_name == old_author.last_name,
Author.display_name == old_author.display_name)) Author.display_name == old_author.display_name,
Author.id != old_author.id))
# Find the songs, which have the old_author as author. # Find the songs, which have the old_author as author.
songs = self.manager.get_all_objects(Song, songs = self.manager.get_all_objects(Song,
Song.authors.contains(old_author)) Song.authors.contains(old_author))
@ -408,7 +409,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
""" """
# Find the duplicate. # Find the duplicate.
existing_topic = self.manager.get_object_filtered(Topic, existing_topic = self.manager.get_object_filtered(Topic,
Topic.name == old_topic.name) and_(Topic.name == old_topic.name, Topic.id != old_topic.id))
# Find the songs, which have the old_topic as topic. # Find the songs, which have the old_topic as topic.
songs = self.manager.get_all_objects(Song, songs = self.manager.get_all_objects(Song,
Song.topics.contains(old_topic)) Song.topics.contains(old_topic))
@ -431,7 +432,8 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
# Find the duplicate. # Find the duplicate.
existing_book = self.manager.get_object_filtered(Book, existing_book = self.manager.get_object_filtered(Book,
and_(Book.name == old_book.name, and_(Book.name == old_book.name,
Book.publisher == old_book.publisher)) Book.publisher == old_book.publisher,
Book.id != old_book.id))
# Find the songs, which have the old_book as book. # Find the songs, which have the old_book as book.
songs = self.manager.get_all_objects(Song, songs = self.manager.get_all_objects(Song,
Song.song_book_id == old_book.id) Song.song_book_id == old_book.id)
@ -503,4 +505,5 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
editButton.setEnabled(False) editButton.setEnabled(False)
else: else:
deleteButton.setEnabled(True) deleteButton.setEnabled(True)
editButton.setEnabled(True) editButton.setEnabled(True)

View File

@ -278,24 +278,29 @@ def clean_song(manager, song):
# List for later comparison. # List for later comparison.
compare_order = [] compare_order = []
for verse in verses: for verse in verses:
type = VerseType.Tags[VerseType.from_loose_input(verse[0][u'type'])] verse_type = VerseType.Tags[VerseType.from_loose_input(
verse[0][u'type'])]
sxml.add_verse_to_lyrics( sxml.add_verse_to_lyrics(
type, verse_type,
verse[0][u'label'], verse[0][u'label'],
verse[1], verse[1],
verse[0][u'lang'] if verse[0].has_key(u'lang') else None verse[0][u'lang'] if verse[0].has_key(u'lang') else None
) )
compare_order.append((u'%s%s' % (type, verse[0][u'label'])).upper()) compare_order.append((u'%s%s' % (verse_type, verse[0][u'label'])
).upper())
if verse[0][u'label'] == u'1':
compare_order.append(verse_type.upper())
song.lyrics = unicode(sxml.extract_xml(), u'utf-8') song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
# Rebuild the verse order, to convert translated verse tags, which might # Rebuild the verse order, to convert translated verse tags, which might
# have been added prior to 1.9.5. # have been added prior to 1.9.5.
order = song.verse_order.strip().split() order = song.verse_order.strip().split()
new_order = [] new_order = []
for verse_def in order: for verse_def in order:
new_order.append((u'%s%s' % ( verse_type = VerseType.Tags[VerseType.from_loose_input(verse_def[0])]
VerseType.Tags[VerseType.from_loose_input(verse_def[0])], if len(verse_def) > 1:
verse_def[1:])).upper() new_order.append((u'%s%s' % (verse_type, verse_def[1:])).upper())
) else:
new_order.append(verse_type.upper())
song.verse_order = u' '.join(new_order) song.verse_order = u' '.join(new_order)
# Check if the verse order contains tags for verses which do not exist. # Check if the verse order contains tags for verses which do not exist.
for order in new_order: for order in new_order:

View File

@ -59,16 +59,10 @@ class CCLIFileImport(SongImport):
Import either a ``.usr`` or a ``.txt`` SongSelect file. Import either a ``.usr`` or a ``.txt`` SongSelect file.
""" """
log.debug(u'Starting CCLI File Import') log.debug(u'Starting CCLI File Import')
song_total = len(self.import_source) self.import_wizard.progressBar.setMaximum(len(self.import_source))
self.import_wizard.progressBar.setMaximum(song_total)
song_count = 1
for filename in self.import_source: for filename in self.import_source:
self.import_wizard.incrementProgressBar(unicode(translate(
'SongsPlugin.CCLIFileImport', 'Importing song %d of %d')) %
(song_count, song_total))
filename = unicode(filename) filename = unicode(filename)
log.debug(u'Importing CCLI File: %s', filename) log.debug(u'Importing CCLI File: %s', filename)
self.set_defaults()
lines = [] lines = []
if os.path.isfile(filename): if os.path.isfile(filename):
detect_file = open(filename, u'r') detect_file = open(filename, u'r')
@ -81,19 +75,23 @@ class CCLIFileImport(SongImport):
detect_file.close() detect_file.close()
infile = codecs.open(filename, u'r', details['encoding']) infile = codecs.open(filename, u'r', details['encoding'])
lines = infile.readlines() lines = infile.readlines()
infile.close()
ext = os.path.splitext(filename)[1] ext = os.path.splitext(filename)[1]
if ext.lower() == u'.usr': if ext.lower() == u'.usr':
log.info(u'SongSelect .usr format file found: %s', filename) log.info(u'SongSelect .usr format file found: %s', filename)
self.do_import_usr_file(lines) if not self.do_import_usr_file(lines):
self.log_error(filename)
elif ext.lower() == u'.txt': elif ext.lower() == u'.txt':
log.info(u'SongSelect .txt format file found: %s', filename) log.info(u'SongSelect .txt format file found: %s', filename)
self.do_import_txt_file(lines) if not self.do_import_txt_file(lines):
self.log_error(filename)
else: else:
self.log_error(filename,
translate('SongsPlugin.CCLIFileImport',
'The file does not have a valid extension.'))
log.info(u'Extension %s is not valid', filename) log.info(u'Extension %s is not valid', filename)
song_count += 1
if self.stop_import_flag: if self.stop_import_flag:
return False return
return True
def do_import_usr_file(self, textList): def do_import_usr_file(self, textList):
""" """
@ -218,7 +216,7 @@ class CCLIFileImport(SongImport):
else: else:
self.add_author(author) self.add_author(author)
self.topics = [topic.strip() for topic in song_topics.split(u'/t')] self.topics = [topic.strip() for topic in song_topics.split(u'/t')]
self.finish() return self.finish()
def do_import_txt_file(self, textList): def do_import_txt_file(self, textList):
""" """
@ -334,6 +332,5 @@ class CCLIFileImport(SongImport):
if len(author_list) < 2: if len(author_list) < 2:
author_list = song_author.split(u'|') author_list = song_author.split(u'|')
# Clean spaces before and after author names. # Clean spaces before and after author names.
for author_name in author_list: [self.add_author(author_name.strip()) for author_name in author_list]
self.add_author(author_name.strip()) return self.finish()
self.finish()

View File

@ -26,11 +26,13 @@
import logging import logging
import os import os
from lxml import etree, objectify
import re import re
from lxml import etree, objectify
from openlp.core.lib import translate from openlp.core.lib import translate
from openlp.core.ui.wizard import WizardStrings from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.songimport import SongImport from openlp.plugins.songs.lib.songimport import SongImport
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -56,26 +58,16 @@ class EasiSlidesImport(SongImport):
multiple opensong files. If `self.commit` is set False, the multiple opensong files. If `self.commit` is set False, the
import will not be committed to the database (useful for test scripts). import will not be committed to the database (useful for test scripts).
""" """
self.import_wizard.progressBar.setMaximum(1)
log.info(u'Importing EasiSlides XML file %s', self.import_source) log.info(u'Importing EasiSlides XML file %s', self.import_source)
parser = etree.XMLParser(remove_blank_text=True) parser = etree.XMLParser(remove_blank_text=True)
file = etree.parse(self.import_source, parser) file = etree.parse(self.import_source, parser)
xml = unicode(etree.tostring(file)) xml = unicode(etree.tostring(file))
song_xml = objectify.fromstring(xml) song_xml = objectify.fromstring(xml)
self.import_wizard.incrementProgressBar(
WizardStrings.ImportingType % os.path.split(self.import_source)[-1])
self.import_wizard.progressBar.setMaximum(len(song_xml.Item)) self.import_wizard.progressBar.setMaximum(len(song_xml.Item))
for song in song_xml.Item: for song in song_xml.Item:
self.import_wizard.incrementProgressBar( if self.stop_import_flag:
unicode(translate('SongsPlugin.ImportWizardForm', return
u'Importing %s, song %s...')) % self._parse_song(song)
(os.path.split(self.import_source)[-1], song.Title1))
success = self._parse_song(song)
if not success or self.stop_import_flag:
return False
elif self.commit:
self.finish()
return True
def _parse_song(self, song): def _parse_song(self, song):
self._success = True self._success = True
@ -90,7 +82,11 @@ class EasiSlidesImport(SongImport):
self._add_copyright(song.LicenceAdmin2) self._add_copyright(song.LicenceAdmin2)
self._add_unicode_attribute(u'song_book_name', song.BookReference) self._add_unicode_attribute(u'song_book_name', song.BookReference)
self._parse_and_add_lyrics(song) self._parse_and_add_lyrics(song)
return self._success if self._success:
if not self.finish():
self.log_error(song.Title1 if song.Title1 else u'')
else:
self.set_defaults()
def _add_unicode_attribute(self, self_attribute, import_attribute, def _add_unicode_attribute(self, self_attribute, import_attribute,
mandatory=False): mandatory=False):
@ -122,10 +118,8 @@ class EasiSlidesImport(SongImport):
def _add_authors(self, song): def _add_authors(self, song):
try: try:
authors = unicode(song.Writer).split(u',') authors = unicode(song.Writer).split(u',')
for author in authors: self.authors = \
author = author.strip() [author.strip() for author in authors if author.strip()]
if len(author):
self.authors.append(author)
except UnicodeDecodeError: except UnicodeDecodeError:
log.exception(u'Unicode decode error while decoding Writer') log.exception(u'Unicode decode error while decoding Writer')
self._success = False self._success = False
@ -188,12 +182,13 @@ class EasiSlidesImport(SongImport):
# if the regions are inside verses # if the regions are inside verses
regionsInVerses = (regions and regionlines[regionlines.keys()[0]] > 1) regionsInVerses = (regions and regionlines[regionlines.keys()[0]] > 1)
MarkTypes = { MarkTypes = {
u'CHORUS': u'C', u'CHORUS': VerseType.Tags[VerseType.Chorus],
u'VERSE': u'V', u'VERSE': VerseType.Tags[VerseType.Verse],
u'INTRO': u'I', u'INTRO': VerseType.Tags[VerseType.Intro],
u'ENDING': u'E', u'ENDING': VerseType.Tags[VerseType.Ending],
u'BRIDGE': u'B', u'BRIDGE': VerseType.Tags[VerseType.Bridge],
u'PRECHORUS': u'P'} u'PRECHORUS': VerseType.Tags[VerseType.PreChorus]
}
verses = {} verses = {}
# list as [region, versetype, versenum, instance] # list as [region, versetype, versenum, instance]
our_verse_order = [] our_verse_order = []

View File

@ -33,6 +33,7 @@ import struct
from openlp.core.lib import translate from openlp.core.lib import translate
from openlp.core.ui.wizard import WizardStrings from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib import retrieve_windows_encoding from openlp.plugins.songs.lib import retrieve_windows_encoding
from songimport import SongImport from songimport import SongImport
@ -142,12 +143,12 @@ class EasyWorshipSongImport(SongImport):
# Open the DB and MB files if they exist # Open the DB and MB files if they exist
import_source_mb = self.import_source.replace('.DB', '.MB') import_source_mb = self.import_source.replace('.DB', '.MB')
if not os.path.isfile(self.import_source): if not os.path.isfile(self.import_source):
return False return
if not os.path.isfile(import_source_mb): if not os.path.isfile(import_source_mb):
return False return
db_size = os.path.getsize(self.import_source) db_size = os.path.getsize(self.import_source)
if db_size < 0x800: if db_size < 0x800:
return False return
db_file = open(self.import_source, 'rb') db_file = open(self.import_source, 'rb')
self.memo_file = open(import_source_mb, 'rb') self.memo_file = open(import_source_mb, 'rb')
# Don't accept files that are clearly not paradox files # Don't accept files that are clearly not paradox files
@ -156,7 +157,7 @@ class EasyWorshipSongImport(SongImport):
if header_size != 0x800 or block_size < 1 or block_size > 4: if header_size != 0x800 or block_size < 1 or block_size > 4:
db_file.close() db_file.close()
self.memo_file.close() self.memo_file.close()
return False return
# Take a stab at how text is encoded # Take a stab at how text is encoded
self.encoding = u'cp1252' self.encoding = u'cp1252'
db_file.seek(106) db_file.seek(106)
@ -183,7 +184,7 @@ class EasyWorshipSongImport(SongImport):
self.encoding = u'cp874' self.encoding = u'cp874'
self.encoding = retrieve_windows_encoding(self.encoding) self.encoding = retrieve_windows_encoding(self.encoding)
if not self.encoding: if not self.encoding:
return False return
# There does not appear to be a _reliable_ way of getting the number # There does not appear to be a _reliable_ way of getting the number
# of songs/records, so let's use file blocks for measuring progress. # of songs/records, so let's use file blocks for measuring progress.
total_blocks = (db_size - header_size) / (block_size * 1024) total_blocks = (db_size - header_size) / (block_size * 1024)
@ -203,8 +204,8 @@ class EasyWorshipSongImport(SongImport):
field_size)) field_size))
self.set_record_struct(field_descs) self.set_record_struct(field_descs)
# Pick out the field description indexes we will need # Pick out the field description indexes we will need
success = True
try: try:
success = True
fi_title = self.find_field(u'Title') fi_title = self.find_field(u'Title')
fi_author = self.find_field(u'Author') fi_author = self.find_field(u'Author')
fi_copy = self.find_field(u'Copyright') fi_copy = self.find_field(u'Copyright')
@ -223,31 +224,25 @@ class EasyWorshipSongImport(SongImport):
# Loop through each record within the current block # Loop through each record within the current block
for i in range(rec_count): for i in range(rec_count):
if self.stop_import_flag: if self.stop_import_flag:
success = False
break break
raw_record = db_file.read(record_size) raw_record = db_file.read(record_size)
self.fields = self.record_struct.unpack(raw_record) self.fields = self.record_struct.unpack(raw_record)
self.set_defaults() self.set_defaults()
# Get title and update progress bar message self.title = self.get_field(fi_title)
title = self.get_field(fi_title) # Get remaining fields.
if title:
self.import_wizard.incrementProgressBar(
WizardStrings.ImportingType % title, 0)
self.title = title
# Get remaining fields
copy = self.get_field(fi_copy) copy = self.get_field(fi_copy)
admin = self.get_field(fi_admin) admin = self.get_field(fi_admin)
ccli = self.get_field(fi_ccli) ccli = self.get_field(fi_ccli)
authors = self.get_field(fi_author) authors = self.get_field(fi_author)
words = self.get_field(fi_words) words = self.get_field(fi_words)
# Set the SongImport object members # Set the SongImport object members.
if copy: if copy:
self.copyright = copy self.copyright = copy
if admin: if admin:
if copy: if copy:
self.copyright += u', ' self.copyright += u', '
self.copyright += \ self.copyright += \
unicode(translate('SongsPlugin.ImportWizardForm', unicode(translate('SongsPlugin.EasyWorshipSongImport',
'Administered by %s')) % admin 'Administered by %s')) % admin
if ccli: if ccli:
self.ccli_number = ccli self.ccli_number = ccli
@ -264,19 +259,17 @@ class EasyWorshipSongImport(SongImport):
# Format the lyrics # Format the lyrics
words = strip_rtf(words, self.encoding) words = strip_rtf(words, self.encoding)
for verse in words.split(u'\n\n'): for verse in words.split(u'\n\n'):
self.add_verse(verse.strip(), u'V') self.add_verse(
verse.strip(), VerseType.Tags[VerseType.Verse])
if self.stop_import_flag: if self.stop_import_flag:
success = False
break break
self.finish() if not self.finish():
if not self.stop_import_flag: self.log_error(self.import_source)
self.import_wizard.incrementProgressBar(u'')
db_file.close() db_file.close()
self.memo_file.close() self.memo_file.close()
return success
def find_field(self, field_name): def find_field(self, field_name):
return [i for i, x in enumerate(self.field_descs) \ return [i for i, x in enumerate(self.field_descs)
if x.name == field_name][0] if x.name == field_name][0]
def set_record_struct(self, field_descs): def set_record_struct(self, field_descs):

View File

@ -97,6 +97,7 @@ from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib import clean_song, VerseType from openlp.plugins.songs.lib import clean_song, VerseType
from openlp.plugins.songs.lib.songimport import SongImport from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic from openlp.plugins.songs.lib.db import Author, Book, Song, Topic
from openlp.plugins.songs.lib.ui import SongStrings
from openlp.plugins.songs.lib.xml import SongXML from openlp.plugins.songs.lib.xml import SongXML
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -121,17 +122,16 @@ class FoilPresenterImport(SongImport):
parser = etree.XMLParser(remove_blank_text=True) parser = etree.XMLParser(remove_blank_text=True)
for file_path in self.import_source: for file_path in self.import_source:
if self.stop_import_flag: if self.stop_import_flag:
return False return
self.import_wizard.incrementProgressBar( self.import_wizard.incrementProgressBar(
WizardStrings.ImportingType % os.path.basename(file_path)) WizardStrings.ImportingType % os.path.basename(file_path))
try: try:
parsed_file = etree.parse(file_path, parser) parsed_file = etree.parse(file_path, parser)
xml = unicode(etree.tostring(parsed_file)) xml = unicode(etree.tostring(parsed_file))
if self.FoilPresenter.xml_to_song(xml) is None: self.FoilPresenter.xml_to_song(xml)
log.debug(u'File could not be imported: %s' % file_path)
except etree.XMLSyntaxError: except etree.XMLSyntaxError:
self.log_error(file_path, SongStrings.XMLSyntaxError)
log.exception(u'XML syntax error in file %s' % file_path) log.exception(u'XML syntax error in file %s' % file_path)
return True
class FoilPresenter(object): class FoilPresenter(object):
@ -211,7 +211,7 @@ class FoilPresenter(object):
""" """
# No xml get out of here. # No xml get out of here.
if not xml: if not xml:
return None return
if xml[:5] == u'<?xml': if xml[:5] == u'<?xml':
xml = xml[38:] xml = xml[38:]
song = Song() song = Song()
@ -235,7 +235,6 @@ class FoilPresenter(object):
self._process_topics(foilpresenterfolie, song) self._process_topics(foilpresenterfolie, song)
clean_song(self.manager, song) clean_song(self.manager, song)
self.manager.save_object(song) self.manager.save_object(song)
return song.id
def _child(self, element): def _child(self, element):
""" """
@ -305,9 +304,8 @@ class FoilPresenter(object):
for marker in markers: for marker in markers:
copyright = re.compile(marker).sub(u'<marker>', copyright, re.U) copyright = re.compile(marker).sub(u'<marker>', copyright, re.U)
copyright = re.compile(u'(?<=<marker>) *:').sub(u'', copyright) copyright = re.compile(u'(?<=<marker>) *:').sub(u'', copyright)
i = 0
x = 0 x = 0
while i != 1: while True:
if copyright.find(u'<marker>') != -1: if copyright.find(u'<marker>') != -1:
temp = copyright.partition(u'<marker>') temp = copyright.partition(u'<marker>')
if temp[0].strip() and x > 0: if temp[0].strip() and x > 0:
@ -316,9 +314,9 @@ class FoilPresenter(object):
x += 1 x += 1
elif x > 0: elif x > 0:
strings.append(copyright) strings.append(copyright)
i = 1 break
else: else:
i = 1 break
author_temp = [] author_temp = []
for author in strings: for author in strings:
temp = re.split(u',(?=\D{2})|(?<=\D),|\/(?=\D{3,})|(?<=\D);', temp = re.split(u',(?=\D{2})|(?<=\D),|\/(?=\D{3,})|(?<=\D);',
@ -349,8 +347,8 @@ class FoilPresenter(object):
if author is None: if author is None:
# We need to create a new author, as the author does not exist. # We need to create a new author, as the author does not exist.
author = Author.populate(display_name=display_name, author = Author.populate(display_name=display_name,
last_name = display_name.split(u' ')[-1], last_name=display_name.split(u' ')[-1],
first_name = u' '.join(display_name.split(u' ')[:-1])) first_name=u' '.join(display_name.split(u' ')[:-1]))
self.manager.save_object(author) self.manager.save_object(author)
song.authors.append(author) song.authors.append(author)
@ -414,8 +412,15 @@ class FoilPresenter(object):
temp_verse_order_backup = [] temp_verse_order_backup = []
temp_sortnr_backup = 1 temp_sortnr_backup = 1
temp_sortnr_liste = [] temp_sortnr_liste = []
versenumber = {u'V': 1, u'C': 1, u'B': 1, u'E': 1, u'O': 1, u'I': 1, versenumber = {
u'P': 1} VerseType.Tags[VerseType.Verse]: 1,
VerseType.Tags[VerseType.Chorus]: 1,
VerseType.Tags[VerseType.Bridge]: 1,
VerseType.Tags[VerseType.Ending]: 1,
VerseType.Tags[VerseType.Other]: 1,
VerseType.Tags[VerseType.Intro]: 1,
VerseType.Tags[VerseType.PreChorus]: 1
}
for strophe in foilpresenterfolie.strophen.strophe: for strophe in foilpresenterfolie.strophen.strophe:
text = self._child(strophe.text_) text = self._child(strophe.text_)
verse_name = self._child(strophe.key) verse_name = self._child(strophe.key)
@ -434,25 +439,25 @@ class FoilPresenter(object):
temp_verse_name = re.compile(u'[0-9].*').sub(u'', verse_name) temp_verse_name = re.compile(u'[0-9].*').sub(u'', verse_name)
temp_verse_name = temp_verse_name[:3].lower() temp_verse_name = temp_verse_name[:3].lower()
if temp_verse_name == u'ref': if temp_verse_name == u'ref':
verse_type = u'C' verse_type = VerseType.Tags[VerseType.Chorus]
elif temp_verse_name == u'r': elif temp_verse_name == u'r':
verse_type = u'C' verse_type = VerseType.Tags[VerseType.Chorus]
elif temp_verse_name == u'': elif temp_verse_name == u'':
verse_type = u'V' verse_type = VerseType.Tags[VerseType.Verse]
elif temp_verse_name == u'v': elif temp_verse_name == u'v':
verse_type = u'V' verse_type = VerseType.Tags[VerseType.Verse]
elif temp_verse_name == u'bri': elif temp_verse_name == u'bri':
verse_type = u'B' verse_type = VerseType.Tags[VerseType.Bridge]
elif temp_verse_name == u'cod': elif temp_verse_name == u'cod':
verse_type = u'E' verse_type = VerseType.Tags[VerseType.Ending]
elif temp_verse_name == u'sch': elif temp_verse_name == u'sch':
verse_type = u'E' verse_type = VerseType.Tags[VerseType.Ending]
elif temp_verse_name == u'pre': elif temp_verse_name == u'pre':
verse_type = u'P' verse_type = VerseType.Tags[VerseType.PreChorus]
elif temp_verse_name == u'int': elif temp_verse_name == u'int':
verse_type = u'I' verse_type = VerseType.Tags[VerseType.Intro]
else: else:
verse_type = u'O' verse_type = VerseType.Tags[VerseType.Other]
verse_number = re.compile(u'[a-zA-Z.+-_ ]*').sub(u'', verse_name) verse_number = re.compile(u'[a-zA-Z.+-_ ]*').sub(u'', verse_name)
# Foilpresenter allows e. g. "C", but we need "C1". # Foilpresenter allows e. g. "C", but we need "C1".
if not verse_number: if not verse_number:
@ -466,8 +471,8 @@ class FoilPresenter(object):
verse_number = unicode(int(verse_number) + 1) verse_number = unicode(int(verse_number) + 1)
verse_type_index = VerseType.from_tag(verse_type[0]) verse_type_index = VerseType.from_tag(verse_type[0])
verse_type = VerseType.Names[verse_type_index] verse_type = VerseType.Names[verse_type_index]
temp_verse_order[verse_sortnr] = (u''.join((verse_type[0], temp_verse_order[verse_sortnr] = u''.join((verse_type[0],
verse_number))) verse_number))
temp_verse_order_backup.append(u''.join((verse_type[0], temp_verse_order_backup.append(u''.join((verse_type[0],
verse_number))) verse_number)))
sxml.add_verse_to_lyrics(verse_type, verse_number, text) sxml.add_verse_to_lyrics(verse_type, verse_number, text)

View File

@ -32,7 +32,7 @@ import logging
from chardet.universaldetector import UniversalDetector from chardet.universaldetector import UniversalDetector
import sqlite import sqlite
from openlp.core.ui.wizard import WizardStrings from openlp.core.lib import translate
from openlp.plugins.songs.lib import retrieve_windows_encoding from openlp.plugins.songs.lib import retrieve_windows_encoding
from songimport import SongImport from songimport import SongImport
@ -61,10 +61,15 @@ class OpenLP1SongImport(SongImport):
""" """
Run the import for an openlp.org 1.x song database. Run the import for an openlp.org 1.x song database.
""" """
# Connect to the database if not self.import_source.endswith(u'.olp'):
self.log_error(self.import_source,
translate('SongsPlugin.OpenLP1SongImport',
'Not a valid openlp.org 1.x song database.'))
return
encoding = self.get_encoding() encoding = self.get_encoding()
if not encoding: if not encoding:
return False return
# Connect to the database
connection = sqlite.connect(self.import_source, mode=0444, connection = sqlite.connect(self.import_source, mode=0444,
encoding=(encoding, 'replace')) encoding=(encoding, 'replace'))
cursor = connection.cursor() cursor = connection.cursor()
@ -72,12 +77,6 @@ class OpenLP1SongImport(SongImport):
cursor.execute(u'SELECT name FROM sqlite_master ' cursor.execute(u'SELECT name FROM sqlite_master '
u'WHERE type = \'table\' AND name = \'tracks\'') u'WHERE type = \'table\' AND name = \'tracks\'')
new_db = len(cursor.fetchall()) > 0 new_db = len(cursor.fetchall()) > 0
# Count the number of records we need to import, for the progress bar
cursor.execute(u'-- types int')
cursor.execute(u'SELECT COUNT(songid) FROM songs')
count = cursor.fetchone()[0]
success = True
self.import_wizard.progressBar.setMaximum(count)
# "cache" our list of authors # "cache" our list of authors
cursor.execute(u'-- types int, unicode') cursor.execute(u'-- types int, unicode')
cursor.execute(u'SELECT authorid, authorname FROM authors') cursor.execute(u'SELECT authorid, authorname FROM authors')
@ -92,37 +91,29 @@ class OpenLP1SongImport(SongImport):
cursor.execute(u'SELECT songid, songtitle, lyrics || \'\' AS lyrics, ' cursor.execute(u'SELECT songid, songtitle, lyrics || \'\' AS lyrics, '
u'copyrightinfo FROM songs') u'copyrightinfo FROM songs')
songs = cursor.fetchall() songs = cursor.fetchall()
self.import_wizard.progressBar.setMaximum(len(songs))
for song in songs: for song in songs:
self.set_defaults() self.set_defaults()
if self.stop_import_flag: if self.stop_import_flag:
success = False
break break
song_id = song[0] song_id = song[0]
title = song[1] self.title = song[1]
lyrics = song[2].replace(u'\r\n', u'\n') lyrics = song[2].replace(u'\r\n', u'\n')
copyright = song[3] self.add_copyright(song[3])
self.import_wizard.incrementProgressBar(
WizardStrings.ImportingType % title)
self.title = title
verses = lyrics.split(u'\n\n') verses = lyrics.split(u'\n\n')
for verse in verses: [self.add_verse(verse.strip()) for verse in verses if verse.strip()]
if verse.strip() != u'':
self.add_verse(verse.strip())
self.add_copyright(copyright)
cursor.execute(u'-- types int') cursor.execute(u'-- types int')
cursor.execute(u'SELECT authorid FROM songauthors ' cursor.execute(u'SELECT authorid FROM songauthors '
u'WHERE songid = %s' % song_id) u'WHERE songid = %s' % song_id)
author_ids = cursor.fetchall() author_ids = cursor.fetchall()
for author_id in author_ids: for author_id in author_ids:
if self.stop_import_flag: if self.stop_import_flag:
success = False
break break
for author in authors: for author in authors:
if author[0] == author_id[0]: if author[0] == author_id[0]:
self.parse_author(author[1]) self.parse_author(author[1])
break break
if self.stop_import_flag: if self.stop_import_flag:
success = False
break break
if new_db: if new_db:
cursor.execute(u'-- types int') cursor.execute(u'-- types int')
@ -131,17 +122,15 @@ class OpenLP1SongImport(SongImport):
track_ids = cursor.fetchall() track_ids = cursor.fetchall()
for track_id in track_ids: for track_id in track_ids:
if self.stop_import_flag: if self.stop_import_flag:
success = False
break break
for track in tracks: for track in tracks:
if track[0] == track_id[0]: if track[0] == track_id[0]:
self.add_media_file(track[1]) self.add_media_file(track[1])
break break
if self.stop_import_flag: if self.stop_import_flag:
success = False
break break
self.finish() if not self.finish():
return success self.log_error(self.import_source)
def get_encoding(self): def get_encoding(self):
""" """

View File

@ -36,6 +36,7 @@ from sqlalchemy.orm.exc import UnmappedClassError
from openlp.core.lib import translate from openlp.core.lib import translate
from openlp.core.lib.db import BaseModel from openlp.core.lib.db import BaseModel
from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib import clean_song from openlp.plugins.songs.lib import clean_song
from openlp.plugins.songs.lib.db import Author, Book, Song, Topic #, MediaFile from openlp.plugins.songs.lib.db import Author, Book, Song, Topic #, MediaFile
from songimport import SongImport from songimport import SongImport
@ -93,13 +94,18 @@ class OpenLPSongImport(SongImport):
The database providing the data to import. The database providing the data to import.
""" """
SongImport.__init__(self, manager, **kwargs) SongImport.__init__(self, manager, **kwargs)
self.import_source = u'sqlite:///%s' % self.import_source
self.source_session = None self.source_session = None
def do_import(self): def do_import(self):
""" """
Run the import for an OpenLP version 2 song database. Run the import for an OpenLP version 2 song database.
""" """
if not self.import_source.endswith(u'.sqlite'):
self.log_error(self.import_source,
translate('SongsPlugin.OpenLPSongImport',
'Not a valid OpenLP 2.0 song database.'))
return
self.import_source = u'sqlite:///%s' % self.import_source
engine = create_engine(self.import_source) engine = create_engine(self.import_source)
source_meta = MetaData() source_meta = MetaData()
source_meta.reflect(engine) source_meta.reflect(engine)
@ -124,10 +130,10 @@ class OpenLPSongImport(SongImport):
mapper(OldMediaFile, source_media_files_table) mapper(OldMediaFile, source_media_files_table)
song_props = { song_props = {
'authors': relation(OldAuthor, backref='songs', 'authors': relation(OldAuthor, backref='songs',
secondary=source_authors_songs_table), secondary=source_authors_songs_table),
'book': relation(OldBook, backref='songs'), 'book': relation(OldBook, backref='songs'),
'topics': relation(OldTopic, backref='songs', 'topics': relation(OldTopic, backref='songs',
secondary=source_songs_topics_table) secondary=source_songs_topics_table)
} }
if has_media_files: if has_media_files:
song_props['media_files'] = relation(OldMediaFile, backref='songs', song_props['media_files'] = relation(OldMediaFile, backref='songs',
@ -150,15 +156,9 @@ class OpenLPSongImport(SongImport):
mapper(OldTopic, source_topics_table) mapper(OldTopic, source_topics_table)
source_songs = self.source_session.query(OldSong).all() source_songs = self.source_session.query(OldSong).all()
song_total = len(source_songs)
if self.import_wizard: if self.import_wizard:
self.import_wizard.progressBar.setMaximum(song_total) self.import_wizard.progressBar.setMaximum(len(source_songs))
song_count = 1
for song in source_songs: for song in source_songs:
if self.import_wizard:
self.import_wizard.incrementProgressBar(
unicode(translate('SongsPlugin.OpenLPSongImport',
'Importing song %d of %d.')) % (song_count, song_total))
new_song = Song() new_song = Song()
new_song.title = song.title new_song.title = song.title
if has_media_files and hasattr(song, 'alternate_title'): if has_media_files and hasattr(song, 'alternate_title'):
@ -213,8 +213,9 @@ class OpenLPSongImport(SongImport):
# file_name=media_file.file_name)) # file_name=media_file.file_name))
clean_song(self.manager, new_song) clean_song(self.manager, new_song)
self.manager.save_object(new_song) self.manager.save_object(new_song)
song_count += 1 if self.import_wizard:
self.import_wizard.incrementProgressBar(
WizardStrings.ImportingType % new_song.title)
if self.stop_import_flag: if self.stop_import_flag:
return False break
engine.dispose() engine.dispose()
return True

View File

@ -56,13 +56,11 @@ class OooImport(SongImport):
self.process_started = False self.process_started = False
def do_import(self): def do_import(self):
self.stop_import_flag = False
self.import_wizard.progressBar.setMaximum(0)
self.start_ooo() self.start_ooo()
self.import_wizard.progressBar.setMaximum(len(self.import_source))
for filename in self.import_source: for filename in self.import_source:
if self.stop_import_flag: if self.stop_import_flag:
self.import_wizard.incrementProgressBar(u'Import cancelled', 0) break
return
filename = unicode(filename) filename = unicode(filename)
if os.path.isfile(filename): if os.path.isfile(filename):
self.open_ooo_file(filename) self.open_ooo_file(filename)
@ -70,9 +68,6 @@ class OooImport(SongImport):
self.process_ooo_document() self.process_ooo_document()
self.close_ooo_file() self.close_ooo_file()
self.close_ooo() self.close_ooo()
self.import_wizard.progressBar.setMaximum(1)
self.import_wizard.incrementProgressBar(u'', 1)
return True
def process_ooo_document(self): def process_ooo_document(self):
""" """

View File

@ -35,6 +35,7 @@ from lxml import etree
from openlp.core.ui.wizard import WizardStrings from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib.songimport import SongImport from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.ui import SongStrings
from openlp.plugins.songs.lib import OpenLyrics from openlp.plugins.songs.lib import OpenLyrics
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -59,7 +60,7 @@ class OpenLyricsImport(SongImport):
parser = etree.XMLParser(remove_blank_text=True) parser = etree.XMLParser(remove_blank_text=True)
for file_path in self.import_source: for file_path in self.import_source:
if self.stop_import_flag: if self.stop_import_flag:
return False return
self.import_wizard.incrementProgressBar( self.import_wizard.incrementProgressBar(
WizardStrings.ImportingType % os.path.basename(file_path)) WizardStrings.ImportingType % os.path.basename(file_path))
try: try:
@ -67,8 +68,7 @@ class OpenLyricsImport(SongImport):
# special characters in the path (see lp:757673 and lp:744337). # special characters in the path (see lp:757673 and lp:744337).
parsed_file = etree.parse(open(file_path, u'r'), parser) parsed_file = etree.parse(open(file_path, u'r'), parser)
xml = unicode(etree.tostring(parsed_file)) xml = unicode(etree.tostring(parsed_file))
if self.openLyrics.xml_to_song(xml) is None: self.openLyrics.xml_to_song(xml)
log.debug(u'File could not be imported: %s' % file_path)
except etree.XMLSyntaxError: except etree.XMLSyntaxError:
log.exception(u'XML syntax error in file %s' % file_path) log.exception(u'XML syntax error in file %s' % file_path)
return True self.log_error(file_path, SongStrings.XMLSyntaxError)

View File

@ -26,13 +26,15 @@
import logging import logging
import os import os
import re
from zipfile import ZipFile from zipfile import ZipFile
from lxml import objectify from lxml import objectify
from lxml.etree import Error, LxmlError from lxml.etree import Error, LxmlError
import re
from openlp.core.ui.wizard import WizardStrings from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.songimport import SongImport from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.ui import SongStrings
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -105,77 +107,62 @@ class OpenSongImport(SongImport):
Initialise the class. Initialise the class.
""" """
SongImport.__init__(self, manager, **kwargs) SongImport.__init__(self, manager, **kwargs)
self.commit = True
def do_import(self): def do_import(self):
""" """
Import either each of the files in self.import_source - each element of Import either each of the files in self.import_source - each element of
which can be either a single opensong file, or a zipfile containing which can be either a single opensong file, or a zipfile containing
multiple opensong files. If `self.commit` is set False, the multiple opensong files.
import will not be committed to the database (useful for test scripts).
""" """
success = True
numfiles = 0 numfiles = 0
for filename in self.import_source: for filename in self.import_source:
ext = os.path.splitext(filename)[1] ext = os.path.splitext(filename)[1]
if ext.lower() == u'.zip': if ext.lower() == u'.zip':
z = ZipFile(filename, u'r') z = ZipFile(filename, u'r')
numfiles += len(z.infolist()) numfiles += len(z.infolist())
z.close()
else: else:
numfiles += 1 numfiles += 1
log.debug(u'Total number of files: %d', numfiles) log.debug(u'Total number of files: %d', numfiles)
self.import_wizard.progressBar.setMaximum(numfiles) self.import_wizard.progressBar.setMaximum(numfiles)
for filename in self.import_source: for filename in self.import_source:
if self.stop_import_flag: if self.stop_import_flag:
success = False return
break
ext = os.path.splitext(filename)[1] ext = os.path.splitext(filename)[1]
if ext.lower() == u'.zip': if ext.lower() == u'.zip':
log.debug(u'Zipfile found %s', filename) log.debug(u'Zipfile found %s', filename)
z = ZipFile(filename, u'r') z = ZipFile(filename, u'r')
for song in z.infolist(): for song in z.infolist():
if self.stop_import_flag: if self.stop_import_flag:
success = False z.close()
break return
parts = os.path.split(song.filename) parts = os.path.split(song.filename)
if parts[-1] == u'': if parts[-1] == u'':
#No final part => directory # No final part => directory
continue continue
log.info(u'Zip importing %s', parts[-1]) log.info(u'Zip importing %s', parts[-1])
self.import_wizard.incrementProgressBar( song_file = z.open(song)
WizardStrings.ImportingType % parts[-1]) self.do_import_file(song_file)
songfile = z.open(song) song_file.close()
if self.do_import_file(songfile) and self.commit and \ z.close()
not self.stop_import_flag:
self.finish()
else:
success = False
break
else: else:
# not a zipfile # not a zipfile
log.info(u'Direct import %s', filename) log.info(u'Direct import %s', filename)
self.import_wizard.incrementProgressBar(
WizardStrings.ImportingType % os.path.split(filename)[-1])
song_file = open(filename) song_file = open(filename)
if self.do_import_file(song_file) and self.commit and \ self.do_import_file(song_file)
not self.stop_import_flag: song_file.close()
self.finish()
else:
success = False
break
return success
def do_import_file(self, file): def do_import_file(self, file):
""" """
Process the OpenSong file - pass in a file-like object, Process the OpenSong file - pass in a file-like object, not a file path.
not a filename
""" """
self.set_defaults() self.set_defaults()
try: try:
tree = objectify.parse(file) tree = objectify.parse(file)
except (Error, LxmlError): except (Error, LxmlError):
self.log_error(file.name, SongStrings.XMLSyntaxError)
log.exception(u'Error parsing XML') log.exception(u'Error parsing XML')
return False return
root = tree.getroot() root = tree.getroot()
fields = dir(root) fields = dir(root)
decode = { decode = {
@ -193,9 +180,6 @@ class OpenSongImport(SongImport):
setattr(self, fn_or_string, ustring) setattr(self, fn_or_string, ustring)
else: else:
fn_or_string(ustring) fn_or_string(ustring)
if not len(self.title):
# to prevent creation of empty songs from wrong files
return False
if u'theme' in fields and unicode(root.theme) not in self.topics: if u'theme' in fields and unicode(root.theme) not in self.topics:
self.topics.append(unicode(root.theme)) self.topics.append(unicode(root.theme))
if u'alttheme' in fields and unicode(root.alttheme) not in self.topics: if u'alttheme' in fields and unicode(root.alttheme) not in self.topics:
@ -205,11 +189,14 @@ class OpenSongImport(SongImport):
# keep track of verses appearance order # keep track of verses appearance order
our_verse_order = [] our_verse_order = []
# default verse # default verse
verse_tag = u'v' verse_tag = VerseType.Tags[VerseType.Verse]
verse_num = u'1' verse_num = u'1'
# for the case where song has several sections with same marker # for the case where song has several sections with same marker
inst = 1 inst = 1
lyrics = unicode(root.lyrics) if u'lyrics' in fields:
lyrics = unicode(root.lyrics)
else:
lyrics = u''
for this_line in lyrics.split(u'\n'): for this_line in lyrics.split(u'\n'):
# remove comments # remove comments
semicolon = this_line.find(u';') semicolon = this_line.find(u';')
@ -230,7 +217,7 @@ class OpenSongImport(SongImport):
# have we got any digits? # have we got any digits?
# If so, verse number is everything from the digits # If so, verse number is everything from the digits
# to the end (even if there are some alpha chars on the end) # to the end (even if there are some alpha chars on the end)
match = re.match(u'(.*)(\d+.*)', content) match = re.match(u'(\D*)(\d+.*)', content)
if match is not None: if match is not None:
verse_tag = match.group(1) verse_tag = match.group(1)
verse_num = match.group(2) verse_num = match.group(2)
@ -239,12 +226,13 @@ class OpenSongImport(SongImport):
# the verse tag # the verse tag
verse_tag = content verse_tag = content
verse_num = u'1' verse_num = u'1'
verse_index = VerseType.from_loose_input(verse_tag)
verse_tag = VerseType.Tags[verse_index]
inst = 1 inst = 1
if [verse_tag, verse_num, inst] in our_verse_order \ if [verse_tag, verse_num, inst] in our_verse_order \
and verses.has_key(verse_tag) \ and verses.has_key(verse_tag) \
and verses[verse_tag].has_key(verse_num): and verses[verse_tag].has_key(verse_num):
inst = len(verses[verse_tag][verse_num])+1 inst = len(verses[verse_tag][verse_num]) + 1
our_verse_order.append([verse_tag, verse_num, inst])
continue continue
# number at start of line.. it's verse number # number at start of line.. it's verse number
if this_line[0].isdigit(): if this_line[0].isdigit():
@ -257,6 +245,7 @@ class OpenSongImport(SongImport):
verses[verse_tag][verse_num] = {} verses[verse_tag][verse_num] = {}
if not verses[verse_tag][verse_num].has_key(inst): if not verses[verse_tag][verse_num].has_key(inst):
verses[verse_tag][verse_num][inst] = [] verses[verse_tag][verse_num][inst] = []
our_verse_order.append([verse_tag, verse_num, inst])
# Tidy text and remove the ____s from extended words # Tidy text and remove the ____s from extended words
this_line = self.tidy_text(this_line) this_line = self.tidy_text(this_line)
this_line = this_line.replace(u'_', u'') this_line = this_line.replace(u'_', u'')
@ -268,28 +257,31 @@ class OpenSongImport(SongImport):
verse_def = u'%s%s' % (verse_tag, verse_num) verse_def = u'%s%s' % (verse_tag, verse_num)
lines = u'\n'.join(verses[verse_tag][verse_num][inst]) lines = u'\n'.join(verses[verse_tag][verse_num][inst])
self.add_verse(lines, verse_def) self.add_verse(lines, verse_def)
if not self.verses:
self.add_verse('')
# figure out the presentation order, if present # figure out the presentation order, if present
if u'presentation' in fields and root.presentation != u'': if u'presentation' in fields and root.presentation:
order = unicode(root.presentation) order = unicode(root.presentation)
# We make all the tags in the lyrics lower case, so match that here # We make all the tags in the lyrics lower case, so match that here
# and then split into a list on the whitespace # and then split into a list on the whitespace
order = order.lower().split() order = order.lower().split()
for verse_def in order: for verse_def in order:
match = re.match(u'(.*)(\d+.*)', verse_def) match = re.match(u'(\D*)(\d+.*)', verse_def)
if match is not None: if match is not None:
verse_tag = match.group(1) verse_tag = match.group(1)
verse_num = match.group(2) verse_num = match.group(2)
if not len(verse_tag): if not len(verse_tag):
verse_tag = u'v' verse_tag = VerseType.Tags[VerseType.Verse]
else: else:
# Assume it's no.1 if there are no digits # Assume it's no.1 if there are no digits
verse_tag = verse_def verse_tag = verse_def
verse_num = u'1' verse_num = u'1'
verse_def = u'%s%s' % (verse_tag, verse_num) verse_def = u'%s%s' % (verse_tag, verse_num)
if verses.has_key(verse_tag) \ if verses.has_key(verse_tag) and \
and verses[verse_tag].has_key(verse_num): verses[verse_tag].has_key(verse_num):
self.verse_order_list.append(verse_def) self.verse_order_list.append(verse_def)
else: else:
log.info(u'Got order %s but not in verse tags, dropping' log.info(u'Got order %s but not in verse tags, dropping'
u'this item from presentation order', verse_def) u'this item from presentation order', verse_def)
return True if not self.finish():
self.log_error(file.name)

View File

@ -88,7 +88,6 @@ class SofImport(OooImport):
paragraphs = self.document.getText().createEnumeration() paragraphs = self.document.getText().createEnumeration()
while paragraphs.hasMoreElements(): while paragraphs.hasMoreElements():
if self.stop_import_flag: if self.stop_import_flag:
self.import_wizard.incrementProgressBar(u'Import cancelled', 0)
return return
paragraph = paragraphs.nextElement() paragraph = paragraphs.nextElement()
if paragraph.supportsService("com.sun.star.text.Paragraph"): if paragraph.supportsService("com.sun.star.text.Paragraph"):

View File

@ -33,9 +33,9 @@ import logging
import os import os
import re import re
from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.songimport import SongImport from openlp.plugins.songs.lib.songimport import SongImport
from openlp.plugins.songs.lib.ui import SongStrings
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -78,58 +78,55 @@ class SongBeamerImport(SongImport):
""" """
Receive a single file or a list of files to import. Receive a single file or a list of files to import.
""" """
if isinstance(self.import_source, list): self.import_wizard.progressBar.setMaximum(len(self.import_source))
self.import_wizard.progressBar.setMaximum( if not isinstance(self.import_source, list):
len(self.import_source)) return
for file in self.import_source: for file in self.import_source:
# TODO: check that it is a valid SongBeamer file # TODO: check that it is a valid SongBeamer file
self.set_defaults() if self.stop_import_flag:
self.current_verse = u'' return
self.current_verse_type = VerseType.Tags[VerseType.Verse] self.set_defaults()
read_verses = False self.current_verse = u''
file_name = os.path.split(file)[1] self.current_verse_type = VerseType.Tags[VerseType.Verse]
self.import_wizard.incrementProgressBar( read_verses = False
WizardStrings.ImportingType % file_name, 0) file_name = os.path.split(file)[1]
if os.path.isfile(file): if os.path.isfile(file):
detect_file = open(file, u'r') detect_file = open(file, u'r')
details = chardet.detect(detect_file.read(2048)) details = chardet.detect(detect_file.read(2048))
detect_file.close() detect_file.close()
infile = codecs.open(file, u'r', details['encoding']) infile = codecs.open(file, u'r', details['encoding'])
songData = infile.readlines() songData = infile.readlines()
infile.close() infile.close()
else: else:
return False continue
self.title = file_name.split('.sng')[0] self.title = file_name.split('.sng')[0]
read_verses = False read_verses = False
for line in songData: for line in songData:
# Just make sure that the line is of the type 'Unicode'. # Just make sure that the line is of the type 'Unicode'.
line = unicode(line).strip() line = unicode(line).strip()
if line.startswith(u'#') and not read_verses: if line.startswith(u'#') and not read_verses:
self.parse_tags(line) self.parse_tags(line)
elif line.startswith(u'---'): elif line.startswith(u'---'):
if self.current_verse: if self.current_verse:
self.replace_html_tags() self.replace_html_tags()
self.add_verse(self.current_verse, self.add_verse(self.current_verse,
self.current_verse_type) self.current_verse_type)
self.current_verse = u'' self.current_verse = u''
self.current_verse_type = VerseType.Tags[VerseType.Verse] self.current_verse_type = VerseType.Tags[VerseType.Verse]
read_verses = True read_verses = True
verse_start = True verse_start = True
elif read_verses: elif read_verses:
if verse_start: if verse_start:
verse_start = False verse_start = False
if not self.check_verse_marks(line): if not self.check_verse_marks(line):
self.current_verse = line + u'\n' self.current_verse = line + u'\n'
else: else:
self.current_verse += line + u'\n' self.current_verse += line + u'\n'
if self.current_verse: if self.current_verse:
self.replace_html_tags() self.replace_html_tags()
self.add_verse(self.current_verse, self.current_verse_type) self.add_verse(self.current_verse, self.current_verse_type)
if self.check_complete(): if not self.finish():
self.finish() self.log_error(file)
self.import_wizard.incrementProgressBar(
WizardStrings.ImportingType % file_name)
return True
def replace_html_tags(self): def replace_html_tags(self):
""" """
@ -189,7 +186,7 @@ class SongBeamerImport(SongImport):
elif tag_val[0] == u'#Bible': elif tag_val[0] == u'#Bible':
pass pass
elif tag_val[0] == u'#Categories': elif tag_val[0] == u'#Categories':
self.topics = line.split(',') self.topics = tag_val[1].split(',')
elif tag_val[0] == u'#CCLI': elif tag_val[0] == u'#CCLI':
self.ccli_number = tag_val[1] self.ccli_number = tag_val[1]
elif tag_val[0] == u'#Chords': elif tag_val[0] == u'#Chords':
@ -236,11 +233,12 @@ class SongBeamerImport(SongImport):
pass pass
elif tag_val[0] == u'#Rights': elif tag_val[0] == u'#Rights':
song_book_pub = tag_val[1] song_book_pub = tag_val[1]
elif tag_val[0] == u'#Songbook': elif tag_val[0] == u'#Songbook' or tag_val[0] == u'#SongBook':
book_num = tag_val[1].split(' / ') book_data = tag_val[1].split(u'/')
self.song_book_name = book_num[0] self.song_book_name = book_data[0].strip()
if len(book_num) == book_num[1]: if len(book_data) == 2:
self.song_number = u'' number = book_data[1].strip()
self.song_number = number if number.isdigit() else u''
elif tag_val[0] == u'#Speed': elif tag_val[0] == u'#Speed':
pass pass
elif tag_val[0] == u'Tempo': elif tag_val[0] == u'Tempo':
@ -287,5 +285,4 @@ class SongBeamerImport(SongImport):
if marks[1].isdigit(): if marks[1].isdigit():
self.current_verse_type += marks[1] self.current_verse_type += marks[1]
return True return True
else: return False
return False

View File

@ -23,12 +23,14 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
import logging import logging
import re import re
from PyQt4 import QtCore from PyQt4 import QtCore
from openlp.core.lib import Receiver, translate from openlp.core.lib import Receiver, translate, check_directory_exists
from openlp.core.ui.wizard import WizardStrings
from openlp.core.utils import AppLocation
from openlp.plugins.songs.lib import clean_song, VerseType from openlp.plugins.songs.lib import clean_song, VerseType
from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile from openlp.plugins.songs.lib.db import Song, Author, Topic, Book, MediaFile
from openlp.plugins.songs.lib.ui import SongStrings from openlp.plugins.songs.lib.ui import SongStrings
@ -66,6 +68,7 @@ class SongImport(QtCore.QObject):
self.song = None self.song = None
self.stop_import_flag = False self.stop_import_flag = False
self.set_defaults() self.set_defaults()
self.error_log = []
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'openlp_stop_wizard'), self.stop_import) QtCore.SIGNAL(u'openlp_stop_wizard'), self.stop_import)
@ -94,6 +97,32 @@ class SongImport(QtCore.QObject):
self.copyright_string = unicode(translate( self.copyright_string = unicode(translate(
'SongsPlugin.SongImport', 'copyright')) 'SongsPlugin.SongImport', 'copyright'))
def log_error(self, filepath, reason=SongStrings.SongIncomplete):
"""
This should be called, when a song could not be imported.
``filepath``
This should be the file path if ``self.import_source`` is a list
with different files. If it is not a list, but a single file (for
instance a database), then this should be the song's title.
``reason``
The reason, why the import failed. The string should be as
informative as possible.
"""
self.set_defaults()
if self.import_wizard is None:
return
if self.import_wizard.errorReportTextEdit.isHidden():
self.import_wizard.errorReportTextEdit.setText(
translate('SongsPlugin.SongImport',
'The following songs could not be imported:'))
self.import_wizard.errorReportTextEdit.setVisible(True)
self.import_wizard.errorCopyToButton.setVisible(True)
self.import_wizard.errorSaveToButton.setVisible(True)
self.import_wizard.errorReportTextEdit.append(
u'- %s (%s)' % (filepath, reason))
def stop_import(self): def stop_import(self):
""" """
Sets the flag for importers to stop their import Sets the flag for importers to stop their import
@ -240,7 +269,7 @@ class SongImport(QtCore.QObject):
Author not checked here, if no author then "Author unknown" is Author not checked here, if no author then "Author unknown" is
automatically added automatically added
""" """
if self.title == u'' or len(self.verses) == 0: if not self.title or not len(self.verses):
return False return False
else: else:
return True return True
@ -249,9 +278,15 @@ class SongImport(QtCore.QObject):
""" """
All fields have been set to this song. Write the song to disk. All fields have been set to this song. Write the song to disk.
""" """
if not self.check_complete():
self.set_defaults()
return False
log.info(u'committing song %s to database', self.title) log.info(u'committing song %s to database', self.title)
song = Song() song = Song()
song.title = self.title song.title = self.title
if self.import_wizard is not None:
self.import_wizard.incrementProgressBar(
WizardStrings.ImportingType % song.title)
song.alternate_title = self.alternate_title song.alternate_title = self.alternate_title
# Values will be set when cleaning the song. # Values will be set when cleaning the song.
song.search_title = u'' song.search_title = u''
@ -308,7 +343,7 @@ class SongImport(QtCore.QObject):
publisher=self.song_book_pub) publisher=self.song_book_pub)
song.book = song_book song.book = song_book
for topictext in self.topics: for topictext in self.topics:
if len(topictext) == 0: if not topictext:
continue continue
topic = self.manager.get_object_filtered(Topic, topic = self.manager.get_object_filtered(Topic,
Topic.name == topictext) Topic.name == topictext)
@ -318,6 +353,7 @@ class SongImport(QtCore.QObject):
clean_song(self.manager, song) clean_song(self.manager, song)
self.manager.save_object(song) self.manager.save_object(song)
self.set_defaults() self.set_defaults()
return True
def print_song(self): def print_song(self):
""" """

View File

@ -32,6 +32,7 @@ import logging
import struct import struct
from openlp.core.ui.wizard import WizardStrings from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.songimport import SongImport from openlp.plugins.songs.lib.songimport import SongImport
TITLE = 1 TITLE = 1
@ -97,83 +98,81 @@ class SongShowPlusImport(SongImport):
Receive a single file or a list of files to import. Receive a single file or a list of files to import.
""" """
if isinstance(self.import_source, list): if isinstance(self.import_source, list):
self.import_wizard.progressBar.setMaximum(len(self.import_source)) return
for file in self.import_source: self.import_wizard.progressBar.setMaximum(len(self.import_source))
author = u'' for file in self.import_source:
self.sspVerseOrderList = [] self.sspVerseOrderList = []
otherCount = 0 otherCount = 0
otherList = {} otherList = {}
file_name = os.path.split(file)[1] file_name = os.path.split(file)[1]
self.import_wizard.incrementProgressBar( self.import_wizard.incrementProgressBar(
WizardStrings.ImportingType % file_name, 0) WizardStrings.ImportingType % file_name, 0)
songData = open(file, 'rb') songData = open(file, 'rb')
while (1): while True:
blockKey, = struct.unpack("I", songData.read(4)) blockKey, = struct.unpack("I", songData.read(4))
# The file ends with 4 NUL's # The file ends with 4 NUL's
if blockKey == 0: if blockKey == 0:
break break
nextBlockStarts, = struct.unpack("I", songData.read(4)) nextBlockStarts, = struct.unpack("I", songData.read(4))
if blockKey == VERSE or blockKey == CHORUS: if blockKey == VERSE or blockKey == CHORUS:
null, verseNo, = struct.unpack("BB", songData.read(2)) null, verseNo, = struct.unpack("BB", songData.read(2))
elif blockKey == CUSTOM_VERSE: elif blockKey == CUSTOM_VERSE:
null, verseNameLength, = struct.unpack("BB", null, verseNameLength, = struct.unpack("BB",
songData.read(2)) songData.read(2))
verseName = songData.read(verseNameLength) verseName = songData.read(verseNameLength)
lengthDescriptorSize, = struct.unpack("B", songData.read(1)) lengthDescriptorSize, = struct.unpack("B", songData.read(1))
# Detect if/how long the length descriptor is # Detect if/how long the length descriptor is
if lengthDescriptorSize == 12: if lengthDescriptorSize == 12:
lengthDescriptor, = struct.unpack("I", songData.read(4)) lengthDescriptor, = struct.unpack("I", songData.read(4))
elif lengthDescriptorSize == 2: elif lengthDescriptorSize == 2:
lengthDescriptor = 1 lengthDescriptor = 1
elif lengthDescriptorSize == 9: elif lengthDescriptorSize == 9:
lengthDescriptor = 0 lengthDescriptor = 0
else: else:
lengthDescriptor, = struct.unpack("B", songData.read(1)) lengthDescriptor, = struct.unpack("B", songData.read(1))
data = songData.read(lengthDescriptor) data = songData.read(lengthDescriptor)
if blockKey == TITLE: if blockKey == TITLE:
self.title = unicode(data, u'cp1252') self.title = unicode(data, u'cp1252')
elif blockKey == AUTHOR: elif blockKey == AUTHOR:
authors = data.split(" / ") authors = data.split(" / ")
for author in authors: for author in authors:
if author.find(",") !=-1: if author.find(",") !=-1:
authorParts = author.split(", ") authorParts = author.split(", ")
author = authorParts[1] + " " + authorParts[0] author = authorParts[1] + " " + authorParts[0]
self.parse_author(unicode(author, u'cp1252')) self.parse_author(unicode(author, u'cp1252'))
elif blockKey == COPYRIGHT: elif blockKey == COPYRIGHT:
self.add_copyright(unicode(data, u'cp1252')) self.add_copyright(unicode(data, u'cp1252'))
elif blockKey == CCLI_NO: elif blockKey == CCLI_NO:
self.ccli_number = int(data) self.ccli_number = int(data)
elif blockKey == VERSE: elif blockKey == VERSE:
self.add_verse(unicode(data, u'cp1252'), self.add_verse(unicode(data, u'cp1252'),
"V%s" % verseNo) "V%s" % verseNo)
elif blockKey == CHORUS: elif blockKey == CHORUS:
self.add_verse(unicode(data, u'cp1252'), self.add_verse(unicode(data, u'cp1252'),
"C%s" % verseNo) "C%s" % verseNo)
elif blockKey == TOPIC: elif blockKey == TOPIC:
self.topics.append(unicode(data, u'cp1252')) self.topics.append(unicode(data, u'cp1252'))
elif blockKey == COMMENTS: elif blockKey == COMMENTS:
self.comments = unicode(data, u'cp1252') self.comments = unicode(data, u'cp1252')
elif blockKey == VERSE_ORDER: elif blockKey == VERSE_ORDER:
verseTag = self.toOpenLPVerseTag(data, True) verseTag = self.toOpenLPVerseTag(data, True)
if verseTag: if verseTag:
self.sspVerseOrderList.append(unicode(verseTag, self.sspVerseOrderList.append(unicode(verseTag,
u'cp1252')) u'cp1252'))
elif blockKey == SONG_BOOK: elif blockKey == SONG_BOOK:
self.song_book_name = unicode(data, u'cp1252') self.song_book_name = unicode(data, u'cp1252')
elif blockKey == SONG_NUMBER: elif blockKey == SONG_NUMBER:
self.song_number = ord(data) self.song_number = ord(data)
elif blockKey == CUSTOM_VERSE: elif blockKey == CUSTOM_VERSE:
verseTag = self.toOpenLPVerseTag(verseName) verseTag = self.toOpenLPVerseTag(verseName)
self.add_verse(unicode(data, u'cp1252'), verseTag) self.add_verse(unicode(data, u'cp1252'), verseTag)
else: else:
log.debug("Unrecognised blockKey: %s, data: %s" log.debug("Unrecognised blockKey: %s, data: %s"
%(blockKey, data)) % (blockKey, data))
self.verse_order_list = self.sspVerseOrderList self.verse_order_list = self.sspVerseOrderList
songData.close() songData.close()
self.finish() if not self.finish():
self.import_wizard.incrementProgressBar( self.log_error(file)
WizardStrings.ImportingType % file_name)
return True
def toOpenLPVerseTag(self, verseName, ignoreUnique=False): def toOpenLPVerseTag(self, verseName, ignoreUnique=False):
if verseName.find(" ") != -1: if verseName.find(" ") != -1:
@ -185,22 +184,19 @@ class SongShowPlusImport(SongImport):
verseNumber = "1" verseNumber = "1"
verseType = verseType.lower() verseType = verseType.lower()
if verseType == "verse": if verseType == "verse":
verseTag = "V" verseTag = VerseType.Tags[VerseType.Verse]
elif verseType == "chorus": elif verseType == "chorus":
verseTag = "C" verseTag = VerseType.Tags[VerseType.Chorus]
elif verseType == "bridge": elif verseType == "bridge":
verseTag = "B" verseTag = VerseType.Tags[VerseType.Bridge]
elif verseType == "pre-chorus": elif verseType == "pre-chorus":
verseTag = "P" verseTag = VerseType.Tags[VerseType.PreChorus]
elif verseType == "bridge":
verseTag = "B"
else: else:
if not self.otherList.has_key(verseName): if not self.otherList.has_key(verseName):
if ignoreUnique: if ignoreUnique:
return None return None
self.otherCount = self.otherCount + 1 self.otherCount = self.otherCount + 1
self.otherList[verseName] = str(self.otherCount) self.otherList[verseName] = str(self.otherCount)
verseTag = "O" verseTag = VerseType.Tags[VerseType.Other]
verseNumber = self.otherList[verseName] verseNumber = self.otherList[verseName]
verseTag = verseTag + verseNumber return verseTag + verseNumber
return verseTag

View File

@ -40,6 +40,8 @@ class SongStrings(object):
CopyrightSymbol = translate('OpenLP.Ui', '\xa9', 'Copyright symbol.') CopyrightSymbol = translate('OpenLP.Ui', '\xa9', 'Copyright symbol.')
SongBook = translate('OpenLP.Ui', 'Song Book', 'Singular') SongBook = translate('OpenLP.Ui', 'Song Book', 'Singular')
SongBooks = translate('OpenLP.Ui', 'Song Books', 'Plural') SongBooks = translate('OpenLP.Ui', 'Song Books', 'Plural')
SongIncomplete = translate('OpenLP.Ui','Title and/or verses not found')
SongMaintenance = translate('OpenLP.Ui', 'Song Maintenance') SongMaintenance = translate('OpenLP.Ui', 'Song Maintenance')
Topic = translate('OpenLP.Ui', 'Topic', 'Singular') Topic = translate('OpenLP.Ui', 'Topic', 'Singular')
Topics = translate('OpenLP.Ui', 'Topics', 'Plural') Topics = translate('OpenLP.Ui', 'Topics', 'Plural')
XMLSyntaxError = translate('OpenLP.Ui', 'XML syntax error')

View File

@ -105,11 +105,7 @@ class WowImport(SongImport):
if isinstance(self.import_source, list): if isinstance(self.import_source, list):
self.import_wizard.progressBar.setMaximum(len(self.import_source)) self.import_wizard.progressBar.setMaximum(len(self.import_source))
for file in self.import_source: for file in self.import_source:
author = u''
copyright = u''
file_name = os.path.split(file)[1] file_name = os.path.split(file)[1]
self.import_wizard.incrementProgressBar(
WizardStrings.ImportingType % file_name, 0)
# Get the song title # Get the song title
self.title = file_name.rpartition(u'.')[0] self.title = file_name.rpartition(u'.')[0]
songData = open(file, 'rb') songData = open(file, 'rb')
@ -129,7 +125,7 @@ class WowImport(SongImport):
self.line_text = unicode( self.line_text = unicode(
songData.read(ord(songData.read(1))), u'cp1252') songData.read(ord(songData.read(1))), u'cp1252')
songData.seek(1, os.SEEK_CUR) songData.seek(1, os.SEEK_CUR)
if block_text != u'': if block_text:
block_text += u'\n' block_text += u'\n'
block_text += self.line_text block_text += self.line_text
self.lines_to_read -= 1 self.lines_to_read -= 1
@ -138,22 +134,19 @@ class WowImport(SongImport):
songData.seek(3, os.SEEK_CUR) songData.seek(3, os.SEEK_CUR)
# Blocks are seperated by 2 bytes, skip them, but not if # Blocks are seperated 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:
songData.seek(2, os.SEEK_CUR) songData.seek(2, os.SEEK_CUR)
self.add_verse(block_text, block_type) self.add_verse(block_text, block_type)
# Now to extract the author # Now to extract the author
author_length = ord(songData.read(1)) author_length = ord(songData.read(1))
if author_length != 0: if author_length:
author = unicode(songData.read(author_length), u'cp1252') self.parse_author(
unicode(songData.read(author_length), u'cp1252'))
# Finally the copyright # Finally the copyright
copyright_length = ord(songData.read(1)) copyright_length = ord(songData.read(1))
if copyright_length != 0: if copyright_length:
copyright = unicode( self.add_copyright(unicode(
songData.read(copyright_length), u'cp1252') songData.read(copyright_length), u'cp1252'))
self.parse_author(author)
self.add_copyright(copyright)
songData.close() songData.close()
self.finish() if not self.finish():
self.import_wizard.incrementProgressBar( self.log_error(file)
WizardStrings.ImportingType % file_name)
return True

Binary file not shown.

After

Width:  |  Height:  |  Size: 737 B

View File

@ -52,6 +52,7 @@
<file>general_open.png</file> <file>general_open.png</file>
<file>general_save.png</file> <file>general_save.png</file>
<file>general_email.png</file> <file>general_email.png</file>
<file>general_revert.png</file>
</qresource> </qresource>
<qresource prefix="slides"> <qresource prefix="slides">
<file>slide_close.png</file> <file>slide_close.png</file>