This commit is contained in:
Andreas Preikschat 2011-02-18 21:07:58 +01:00
commit e028947113
32 changed files with 587 additions and 429 deletions

View File

@ -239,7 +239,8 @@ def resize_image(image, width, height, background=QtCore.Qt.black):
Resize an image to fit on the current screen.
``image``
The image to resize.
The image to resize. It has to be either a ``QImage`` instance or the
path to the image.
``width``
The new image width.

View File

@ -85,8 +85,7 @@ class ImageManager(QtCore.QObject):
for key in self._cache.keys():
image = self._cache[key]
image.dirty = True
image.image = resize_image(image.path,
self.width, self.height)
image.image = resize_image(image.path, self.width, self.height)
self._cache_dirty = True
# only one thread please
if not self._thread_running:
@ -128,8 +127,7 @@ class ImageManager(QtCore.QObject):
image = Image()
image.name = name
image.path = path
image.image = resize_image(path,
self.width, self.height)
image.image = resize_image(path, self.width, self.height)
self._cache[name] = image
else:
log.debug(u'Image in cache %s:%s' % (name, path))

View File

@ -240,7 +240,7 @@ class MediaManagerItem(QtGui.QWidget):
Creates the main widget for listing items the media item is tracking
"""
# Add the List widget
self.listView = ListWidgetWithDnD(self, self.title)
self.listView = ListWidgetWithDnD(self, self.plugin.name)
self.listView.uniformItemSizes = True
self.listView.setSpacing(1)
self.listView.setSelectionMode(
@ -252,7 +252,6 @@ class MediaManagerItem(QtGui.QWidget):
self.pageLayout.addWidget(self.listView)
# define and add the context menu
self.listView.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
name_string = self.plugin.getString(StringContent.Name)
if self.hasEditIcon:
self.listView.addAction(
context_menu_action(

View File

@ -203,12 +203,12 @@ class RenderManager(object):
# set the default image size for previews
self.calculate_default(self.screens.preview[u'size'])
verse = u'The Lord said to {r}Noah{/r}: \n' \
'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n' \
'The Lord said to {g}Noah{/g}:\n' \
'There\'s gonna be a {st}floody{/st}, {it}floody{/it}\n' \
'Get those children out of the muddy, muddy \n' \
'{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}' \
'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n' \
'The Lord said to {g}Noah{/g}:\n' \
'There\'s gonna be a {st}floody{/st}, {it}floody{/it}\n' \
'Get those children out of the muddy, muddy \n' \
'{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}' \
'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
# make big page for theme edit dialog to get line count
if self.force_page:
verse = verse + verse + verse

View File

@ -436,7 +436,6 @@ class ServiceItem(object):
"""
Returns the start and finish time for a media item
"""
tooltip = None
start = None
end = None
if self.start_time != 0:

View File

@ -178,9 +178,9 @@ class HorizontalType(object):
"""
Return a string representation of a horizontal type.
"""
if horizontal_type == Horizontal.Right:
if horizontal_type == HorizontalType.Right:
return u'right'
elif horizontal_type == Horizontal.Center:
elif horizontal_type == HorizontalType.Center:
return u'center'
else:
return u'left'

View File

@ -24,7 +24,6 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import datetime
import os
from PyQt4 import QtCore, QtGui

View File

@ -36,7 +36,6 @@ class Ui_SettingsDialog(object):
settingsDialog.setWindowIcon(
build_icon(u':/system/system_settings.png'))
self.settingsLayout = QtGui.QVBoxLayout(settingsDialog)
margins = self.settingsLayout.contentsMargins()
self.settingsLayout.setObjectName(u'settingsLayout')
self.settingsTabWidget = QtGui.QTabWidget(settingsDialog)
self.settingsTabWidget.setObjectName(u'settingsTabWidget')

View File

@ -36,7 +36,7 @@ class Ui_ShortcutListDialog(object):
self.treeWidget = QtGui.QTreeWidget(shortcutListDialog)
self.treeWidget.setAlternatingRowColors(True)
self.treeWidget.setObjectName(u'treeWidget')
self.treeWidget.setColumnCount(2)
self.treeWidget.setColumnCount(3)
self.dialogLayout.addWidget(self.treeWidget)
self.defaultButton = QtGui.QRadioButton(shortcutListDialog)
self.defaultButton.setChecked(True)
@ -78,7 +78,8 @@ class Ui_ShortcutListDialog(object):
translate('OpenLP.ShortcutListDialog', 'Customize Shortcuts'))
self.treeWidget.setHeaderLabels([
translate('OpenLP.ShortcutListDialog', 'Action'),
translate('OpenLP.ShortcutListDialog', 'Shortcut')])
translate('OpenLP.ShortcutListDialog', 'Shortcut'),
translate('OpenLP.ShortcutListDialog', 'Alternate')])
self.defaultButton.setText(
translate('OpenLP.ShortcutListDialog', 'Default: %s'))
self.customButton.setText(

View File

@ -95,8 +95,13 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog):
item = QtGui.QTreeWidgetItem([category.name])
for action in category.actions:
actionText = REMOVE_AMPERSAND.sub('', unicode(action.text()))
shortcutText = action.shortcut().toString()
actionItem = QtGui.QTreeWidgetItem([actionText, shortcutText])
if (len(action.shortcuts()) == 2):
shortcutText = action.shortcuts()[0].toString()
alternateText = action.shortcuts()[1].toString()
else:
shortcutText = action.shortcut().toString()
alternateText = u''
actionItem = QtGui.QTreeWidgetItem([actionText, shortcutText, alternateText])
actionItem.setIcon(0, action.icon())
item.addChild(actionItem)
item.setExpanded(True)

View File

@ -380,7 +380,7 @@ class SlideController(QtGui.QWidget):
self.previousItem.setShortcuts([QtCore.Qt.Key_Up, QtCore.Qt.Key_PageUp])
self.previousItem.setShortcutContext(
QtCore.Qt.WidgetWithChildrenShortcut)
actionList.add_action(self.nextItem, u'Live')
actionList.add_action(self.previousItem, u'Live')
self.nextItem.setShortcuts([QtCore.Qt.Key_Down, QtCore.Qt.Key_PageDown])
self.nextItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
actionList.add_action(self.nextItem, u'Live')
@ -603,14 +603,15 @@ class SlideController(QtGui.QWidget):
slideHeight = 0
if self.serviceItem.is_text():
if frame[u'verseTag']:
bits = frame[u'verseTag'].split(u':')
tag = u'%s\n%s' % (bits[0][0], bits[1][0:] )
tag1 = u'%s%s' % (bits[0][0], bits[1][0:] )
row = tag
# These tags are already translated.
verse_def = frame[u'verseTag']
verse_def = u'%s%s' % (verse_def[0].upper(), verse_def[1:])
two_line_def = u'%s\n%s' % (verse_def[0], verse_def[1:] )
row = two_line_def
if self.isLive:
if tag1 not in self.slideList:
self.slideList[tag1] = framenumber
self.songMenu.menu().addAction(tag1,
if verse_def not in self.slideList:
self.slideList[verse_def] = framenumber
self.songMenu.menu().addAction(verse_def,
self.onSongBarHandler)
else:
row += 1

View File

@ -314,7 +314,6 @@ class ThemeManager(QtGui.QWidget):
translate('OpenLP.ThemeManager',
'You must select a theme to edit.')):
item = self.themeListWidget.currentItem()
themeName = unicode(item.text())
theme = self.getThemeData(
unicode(item.data(QtCore.Qt.UserRole).toString()))
if theme.background_type == u'image':
@ -406,8 +405,8 @@ class ThemeManager(QtGui.QWidget):
files = QtGui.QFileDialog.getOpenFileNames(self,
translate('OpenLP.ThemeManager', 'Select Theme Import File'),
SettingsManager.get_last_dir(self.settingsSection),
unicode(translate('OpenLP.ThemeManager', 'Theme v1 (*.theme);;'
'Theme v2 (*.otz);;%s (*.*)')) % UiStrings.AllFiles)
unicode(translate('OpenLP.ThemeManager',
'OpenLP Themes (*.theme *.otz)')))
log.info(u'New Themes %s', unicode(files))
if files:
for file in files:
@ -530,6 +529,18 @@ class ThemeManager(QtGui.QWidget):
else:
outfile = open(fullpath, u'wb')
outfile.write(zip.read(file))
except (IOError, NameError):
critical_error_message_box(
translate('OpenLP.ThemeManager', 'Validation Error'),
translate('OpenLP.ThemeManager', 'File is not a valid theme.'))
log.exception(u'Importing theme from zip failed %s' % filename)
finally:
# Close the files, to be able to continue creating the theme.
if zip:
zip.close()
if outfile:
outfile.close()
# As all files are closed, we can create the Theme.
if filexml:
theme = self._createThemeFromXml(filexml, self.path)
self.generateAndSaveImage(dir, themename, theme)
@ -540,17 +551,6 @@ class ThemeManager(QtGui.QWidget):
'File is not a valid theme.'))
log.exception(u'Theme file does not contain XML data %s' %
filename)
except (IOError, NameError):
critical_error_message_box(
translate('OpenLP.ThemeManager', 'Validation Error'),
translate('OpenLP.ThemeManager',
'File is not a valid theme.'))
log.exception(u'Importing theme from zip failed %s' % filename)
finally:
if zip:
zip.close()
if outfile:
outfile.close()
def checkIfThemeExists(self, themeName):
"""

View File

@ -164,27 +164,34 @@ def _get_os_dir_path(dir_type):
"""
Return a path based on which OS and environment we are running in.
"""
encoding = sys.getfilesystemencoding()
if sys.platform == u'win32':
if dir_type == AppLocation.DataDir:
return os.path.join(os.getenv(u'APPDATA'), u'openlp', u'data')
return os.path.join(os.getenv(u'APPDATA'), u'openlp')
return os.path.join(unicode(os.getenv(u'APPDATA'), encoding),
u'openlp', u'data')
return os.path.join(unicode(os.getenv(u'APPDATA'), encoding),
u'openlp')
elif sys.platform == u'darwin':
if dir_type == AppLocation.DataDir:
return os.path.join(os.getenv(u'HOME'), u'Library',
u'Application Support', u'openlp', u'Data')
return os.path.join(os.getenv(u'HOME'), u'Library',
u'Application Support', u'openlp')
return os.path.join(unicode(os.getenv(u'HOME'), encoding),
u'Library', u'Application Support', u'openlp', u'Data')
return os.path.join(unicode(os.getenv(u'HOME'), encoding),
u'Library', u'Application Support', u'openlp')
else:
if XDG_BASE_AVAILABLE:
if dir_type == AppLocation.ConfigDir:
return os.path.join(BaseDirectory.xdg_config_home, u'openlp')
return os.path.join(unicode(BaseDirectory.xdg_config_home,
encoding), u'openlp')
elif dir_type == AppLocation.DataDir:
return os.path.join(BaseDirectory.xdg_data_home, u'openlp')
return os.path.join(
unicode(BaseDirectory.xdg_data_home, encoding), u'openlp')
elif dir_type == AppLocation.CacheDir:
return os.path.join(BaseDirectory.xdg_cache_home, u'openlp')
return os.path.join(unicode(BaseDirectory.xdg_cache_home,
encoding), u'openlp')
if dir_type == AppLocation.DataDir:
return os.path.join(os.getenv(u'HOME'), u'.openlp', u'data')
return os.path.join(os.getenv(u'HOME'), u'.openlp')
return os.path.join(unicode(os.getenv(u'HOME'), encoding),
u'.openlp', u'data')
return os.path.join(unicode(os.getenv(u'HOME'), encoding), u'.openlp')
def _get_frozen_path(frozen_option, non_frozen_option):
"""
@ -375,13 +382,13 @@ def get_uno_command():
"""
Returns the UNO command to launch an openoffice.org instance.
"""
COMMAND = u'soffice'
OPTIONS = u'-nologo -norestore -minimized -invisible -nofirststartwizard'
if UNO_CONNECTION_TYPE == u'pipe':
return u'openoffice.org -nologo -norestore -minimized -invisible ' \
+ u'-nofirststartwizard -accept=pipe,name=openlp_pipe;urp;'
CONNECTION = u'"-accept=pipe,name=openlp_pipe;urp;"'
else:
return u'openoffice.org -nologo -norestore -minimized ' \
+ u'-invisible -nofirststartwizard ' \
+ u'-accept=socket,host=localhost,port=2002;urp;'
CONNECTION = u'"-accept=socket,host=localhost,port=2002;urp;"'
return u'%s %s %s' % (COMMAND, OPTIONS, CONNECTION)
def get_uno_instance(resolver):
"""

View File

@ -29,7 +29,6 @@ import logging
from PyQt4 import QtCore, QtGui
from openlp.core.lib import Plugin, StringContent, build_icon, translate
from openlp.core.lib.ui import UiStrings
from openlp.plugins.bibles.lib import BibleManager, BiblesTab, BibleMediaItem
log = logging.getLogger(__name__)

View File

@ -30,7 +30,6 @@ from forms import EditCustomForm
from openlp.core.lib import Plugin, StringContent, build_icon, translate
from openlp.core.lib.db import Manager
from openlp.core.lib.ui import UiStrings
from openlp.plugins.custom.lib import CustomMediaItem, CustomTab
from openlp.plugins.custom.lib.db import CustomSlide, init_schema

View File

@ -147,8 +147,10 @@ class PowerpointDocument(PresentationDocument):
"""
if self.check_thumbnails():
return
self.presentation.Export(os.path.join(self.get_thumbnail_folder(), ''),
'png', 320, 240)
for num in range(0, self.presentation.Slides.Count):
self.presentation.Slides(num + 1).Export(os.path.join(
self.get_thumbnail_folder(), 'slide%d.png' % (num + 1)),
'png', 320, 240)
def close_presentation(self):
"""

View File

@ -154,8 +154,9 @@ class PptviewDocument(PresentationDocument):
being shut down
"""
log.debug(u'ClosePresentation')
self.controller.process.ClosePPT(self.pptid)
self.pptid = -1
if self.controller.process:
self.controller.process.ClosePPT(self.pptid)
self.pptid = -1
self.controller.remove_doc(self)
def is_loaded(self):

View File

@ -161,6 +161,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
def newSong(self):
log.debug(u'New Song')
self.song = None
self.initialise()
self.songTabWidget.setCurrentIndex(0)
self.titleEdit.setText(u'')
@ -226,10 +227,6 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.copyrightEdit.setText(u'')
self.verseListWidget.clear()
self.verseListWidget.setRowCount(0)
if self.song.verse_order:
self.verseOrderEdit.setText(self.song.verse_order)
else:
self.verseOrderEdit.setText(u'')
if self.song.comments:
self.commentsEdit.setPlainText(self.song.comments)
else:
@ -249,15 +246,31 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
# This is just because occasionally the lyrics come back as a "buffer"
if isinstance(self.song.lyrics, buffer):
self.song.lyrics = unicode(self.song.lyrics)
verse_tags_translated = False
if self.song.lyrics.startswith(u'<?xml version='):
songXML = SongXML()
verseList = songXML.get_verses(self.song.lyrics)
for count, verse in enumerate(verseList):
self.verseListWidget.setRowCount(
self.verseListWidget.rowCount() + 1)
variant = u'%s:%s' % (verse[0][u'type'], verse[0][u'label'])
# This silently migrates from localized verse type markup.
# If we trusted the database, this would be unnecessary.
verse_tag = verse[0][u'type']
index = None
if len(verse_tag) > 1:
index = VerseType.from_translated_string(verse_tag)
if index is None:
index = VerseType.from_string(verse_tag)
else:
verse_tags_translated = True
if index is None:
index = VerseType.from_tag(verse_tag)
if index is None:
index = VerseType.Other
verse[0][u'type'] = VerseType.Tags[index]
verse_def = u'%s%s' % (verse[0][u'type'], verse[0][u'label'])
item = QtGui.QTableWidgetItem(verse[1])
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(variant))
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def))
self.verseListWidget.setItem(count, 0, item)
else:
verses = self.song.lyrics.split(u'\n\n')
@ -265,10 +278,24 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.verseListWidget.setRowCount(
self.verseListWidget.rowCount() + 1)
item = QtGui.QTableWidgetItem(verse)
variant = u'%s:%s' % \
(VerseType.to_string(VerseType.Verse), unicode(count + 1))
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(variant))
verse_def = u'%s%s' % \
(VerseType.Tags[VerseType.Verse], unicode(count + 1))
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def))
self.verseListWidget.setItem(count, 0, item)
if self.song.verse_order:
# we translate verse order
translated = []
for verse_def in self.song.verse_order.split():
verse_index = None
if verse_tags_translated:
verse_index = VerseType.from_translated_tag(verse_def[0])
if verse_index is None:
verse_index = VerseType.from_tag(verse_def[0])
verse_tag = VerseType.TranslatedTags[verse_index].upper()
translated.append(u'%s%s' % (verse_tag, verse_def[1:]))
self.verseOrderEdit.setText(u' '.join(translated))
else:
self.verseOrderEdit.setText(u'')
self.verseListWidget.resizeRowsToContents()
self.tagRows()
# clear the results
@ -293,14 +320,14 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
"""
Tag the Song List rows based on the verse list
"""
rowLabel = []
row_label = []
for row in range(0, self.verseListWidget.rowCount()):
item = self.verseListWidget.item(row, 0)
data = unicode(item.data(QtCore.Qt.UserRole).toString())
bit = data.split(u':')
rowTag = u'%s%s' % (bit[0][0:1], bit[1])
rowLabel.append(rowTag)
self.verseListWidget.setVerticalHeaderLabels(rowLabel)
verse_def = unicode(item.data(QtCore.Qt.UserRole).toString())
verse_tag = VerseType.translated_tag(verse_def[0])
row_def = u'%s%s' % (verse_tag, verse_def[1:])
row_label.append(row_def)
self.verseListWidget.setVerticalHeaderLabels(row_label)
def onAuthorAddButtonClicked(self):
item = int(self.authorsComboBox.currentIndex())
@ -419,11 +446,11 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
def onVerseAddButtonClicked(self):
self.verse_form.setVerse(u'', True)
if self.verse_form.exec_():
afterText, verse, subVerse = self.verse_form.getVerse()
data = u'%s:%s' % (verse, subVerse)
item = QtGui.QTableWidgetItem(afterText)
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(data))
item.setText(afterText)
after_text, verse_tag, verse_num = self.verse_form.getVerse()
verse_def = u'%s%s' % (verse_tag, verse_num)
item = QtGui.QTableWidgetItem(after_text)
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def))
item.setText(after_text)
self.verseListWidget.setRowCount(
self.verseListWidget.rowCount() + 1)
self.verseListWidget.setItem(
@ -439,12 +466,12 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
verseId = unicode(item.data(QtCore.Qt.UserRole).toString())
self.verse_form.setVerse(tempText, True, verseId)
if self.verse_form.exec_():
afterText, verse, subVerse = self.verse_form.getVerse()
data = u'%s:%s' % (verse, subVerse)
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(data))
item.setText(afterText)
after_text, verse_tag, verse_num = self.verse_form.getVerse()
verse_def = u'%s%s' % (verse_tag, verse_num)
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def))
item.setText(after_text)
# number of lines has change so repaint the list moving the data
if len(tempText.split(u'\n')) != len(afterText.split(u'\n')):
if len(tempText.split(u'\n')) != len(after_text.split(u'\n')):
tempList = {}
tempId = {}
for row in range(0, self.verseListWidget.rowCount()):
@ -466,7 +493,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
for row in range(0, self.verseListWidget.rowCount()):
item = self.verseListWidget.item(row, 0)
field = unicode(item.data(QtCore.Qt.UserRole).toString())
verse_list += u'---[%s]---\n' % field
verse_tag = VerseType.translated_name(field[0])
verse_num = field[1:]
verse_list += u'---[%s:%s]---\n' % (verse_tag, verse_num)
verse_list += item.text()
verse_list += u'\n'
self.verse_form.setVerse(verse_list)
@ -482,15 +511,32 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
for count, parts in enumerate(match.split(u']---\n')):
if len(parts) > 1:
if count == 0:
# make sure the tag is correctly cased
variant = u'%s%s' % \
(parts[0:1].upper(), parts[1:].lower())
# handling carefully user inputted versetags
separator = parts.find(u':')
if separator >= 0:
verse_name = parts[0:separator].strip()
verse_num = parts[separator+1:].strip()
else:
verse_name = parts
verse_num = u'1'
verse_index = \
VerseType.from_loose_input(verse_name)
verse_tag = VerseType.Tags[verse_index]
# Later we need to handle v1a as well.
#regex = re.compile(r'(\d+\w.)')
regex = re.compile(r'\D*(\d+)\D*')
match = regex.match(verse_num)
if match:
verse_num = match.group(1)
else:
verse_num = u'1'
verse_def = u'%s%s' % (verse_tag, verse_num)
else:
if parts.endswith(u'\n'):
parts = parts.rstrip(u'\n')
item = QtGui.QTableWidgetItem(parts)
item.setData(QtCore.Qt.UserRole,
QtCore.QVariant(variant))
QtCore.QVariant(verse_def))
self.verseListWidget.setRowCount(
self.verseListWidget.rowCount() + 1)
self.verseListWidget.setItem(
@ -542,25 +588,31 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
order_names = unicode(self.verseOrderEdit.text()).split()
for item in order_names:
if len(item) == 1:
order.append(item.lower() + u'1')
verse_index = VerseType.from_translated_tag(item)
if verse_index is not None:
order.append(VerseType.Tags[verse_index] + u'1')
else:
order.append(u'') # it matches no verses anyway
else:
order.append(item.lower())
verse_index = VerseType.from_translated_tag(item[0])
if verse_index is None:
order.append(u'') # same as above
else:
verse_tag = VerseType.Tags[verse_index]
verse_num = item[1:].lower()
order.append(verse_tag + verse_num)
verses = []
verse_names = []
for index in range (0, self.verseListWidget.rowCount()):
for index in range(0, self.verseListWidget.rowCount()):
verse = self.verseListWidget.item(index, 0)
verse = unicode(verse.data(QtCore.Qt.UserRole).toString())
if verse not in verse_names:
verses.append(
re.sub(r'(.)[^:]*:(.*)', r'\1\2', verse.lower()))
verse_names.append(verse)
verses.append(verse)
verse_names.append(u'%s%s' % (
VerseType.translated_tag(verse[0]), verse[1:]))
for count, item in enumerate(order):
if item not in verses:
self.songTabWidget.setCurrentIndex(0)
self.verseOrderEdit.setFocus()
valid = verses.pop(0)
for verse in verses:
valid = valid + u', ' + verse
valid = u', '.join(verse_names)
critical_error_message_box(
message=unicode(translate('SongsPlugin.EditSongForm',
'The verse order is invalid. There is no verse '
@ -576,7 +628,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
unicode(translate('SongsPlugin.EditSongForm',
'You have not used %s anywhere in the verse '
'order. Are you sure you want to save the song '
'like this?')) % verse_names[count].replace(u':', u' '),
'like this?')) % verse_names[count],
QtGui.QMessageBox.Yes | QtGui.QMessageBox.No)
if answer == QtGui.QMessageBox.No:
return False
@ -683,7 +735,14 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
else:
self.song.search_title = self.song.title
self.song.comments = unicode(self.commentsEdit.toPlainText())
self.song.verse_order = unicode(self.verseOrderEdit.text())
ordertext = unicode(self.verseOrderEdit.text())
order = []
for item in ordertext.split():
verse_tag = VerseType.Tags[
VerseType.from_translated_tag(item[0])]
verse_num = item[1:].lower()
order.append(u'%s%s' % (verse_tag, verse_num))
self.song.verse_order = u' '.join(order)
self.song.ccli_number = unicode(self.CCLNumberEdit.text())
self.song.song_number = unicode(self.songBookNumberEdit.text())
book_name = unicode(self.songBookComboBox.currentText())
@ -726,12 +785,14 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
for i in range(0, self.verseListWidget.rowCount()):
item = self.verseListWidget.item(i, 0)
verseId = unicode(item.data(QtCore.Qt.UserRole).toString())
bits = verseId.split(u':')
sxml.add_verse_to_lyrics(bits[0], bits[1], unicode(item.text()))
verse_tag = verseId[0]
verse_num = verseId[1:]
sxml.add_verse_to_lyrics(verse_tag, verse_num,
unicode(item.text()))
text = text + self.whitespace.sub(u' ',
unicode(self.verseListWidget.item(i, 0).text())) + u' '
if (bits[1] > u'1') and (bits[0][0] not in multiple):
multiple.append(bits[0][0])
if (verse_num > u'1') and (verse_tag not in multiple):
multiple.append(verse_tag)
self.song.search_lyrics = text.lower()
self.song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
for verse in multiple:

View File

@ -70,19 +70,19 @@ class Ui_EditVerseDialog(object):
translate('SongsPlugin.EditVerseForm', 'Edit Verse'))
self.verseTypeLabel.setText(
translate('SongsPlugin.EditVerseForm', '&Verse type:'))
self.verseTypeComboBox.setItemText(0,
VerseType.to_string(VerseType.Verse))
self.verseTypeComboBox.setItemText(1,
VerseType.to_string(VerseType.Chorus))
self.verseTypeComboBox.setItemText(2,
VerseType.to_string(VerseType.Bridge))
self.verseTypeComboBox.setItemText(3,
VerseType.to_string(VerseType.PreChorus))
self.verseTypeComboBox.setItemText(4,
VerseType.to_string(VerseType.Intro))
self.verseTypeComboBox.setItemText(5,
VerseType.to_string(VerseType.Ending))
self.verseTypeComboBox.setItemText(6,
VerseType.to_string(VerseType.Other))
self.verseTypeComboBox.setItemText(VerseType.Verse,
VerseType.TranslatedNames[VerseType.Verse])
self.verseTypeComboBox.setItemText(VerseType.Chorus,
VerseType.TranslatedNames[VerseType.Chorus])
self.verseTypeComboBox.setItemText(VerseType.Bridge,
VerseType.TranslatedNames[VerseType.Bridge])
self.verseTypeComboBox.setItemText(VerseType.PreChorus,
VerseType.TranslatedNames[VerseType.PreChorus])
self.verseTypeComboBox.setItemText(VerseType.Intro,
VerseType.TranslatedNames[VerseType.Intro])
self.verseTypeComboBox.setItemText(VerseType.Ending,
VerseType.TranslatedNames[VerseType.Ending])
self.verseTypeComboBox.setItemText(VerseType.Other,
VerseType.TranslatedNames[VerseType.Other])
self.insertButton.setText(
translate('SongsPlugin.EditVerseForm', '&Insert'))

View File

@ -57,22 +57,23 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
QtCore.QObject.connect(self.verseTypeComboBox,
QtCore.SIGNAL(u'currentIndexChanged(int)'),
self.onVerseTypeComboBoxChanged)
self.verse_regex = re.compile(r'---\[([-\w]+):([\d]+)\]---')
self.verse_regex = re.compile(r'---\[(.+):\D*(\d*)\D*.*\]---')
def contextMenu(self, point):
item = self.serviceManagerList.itemAt(point)
def insertVerse(self, title, num=1):
def insertVerse(self, verse_tag, verse_num=1):
if self.verseTextEdit.textCursor().columnNumber() != 0:
self.verseTextEdit.insertPlainText(u'\n')
self.verseTextEdit.insertPlainText(u'---[%s:%s]---\n' % (title, num))
verse_tag = VerseType.translated_name(verse_tag)
self.verseTextEdit.insertPlainText(u'---[%s:%s]---\n' %
(verse_tag, verse_num))
self.verseTextEdit.setFocus()
def onInsertButtonClicked(self):
verse_type = self.verseTypeComboBox.currentIndex()
if VerseType.to_string(verse_type) is not None:
self.insertVerse(VerseType.to_string(verse_type),
self.verseNumberBox.value())
verse_type_index = self.verseTypeComboBox.currentIndex()
self.insertVerse(VerseType.Tags[verse_type_index],
self.verseNumberBox.value())
def onVerseTypeComboBoxChanged(self):
"""
@ -81,10 +82,11 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
"""
position = self.verseTextEdit.textCursor().position()
text = unicode(self.verseTextEdit.toPlainText())
verse_type = VerseType.to_string(self.verseTypeComboBox.currentIndex())
verse_name = VerseType.TranslatedNames[
self.verseTypeComboBox.currentIndex()]
if not text:
return
position = text.rfind(u'---[%s' % verse_type, 0, position)
position = text.rfind(u'---[%s' % verse_name, 0, position)
if position == -1:
self.verseNumberBox.setValue(1)
return
@ -95,11 +97,11 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
text = text[:position + 4]
match = self.verse_regex.match(text)
if match:
verse_type = match.group(1)
verse_number = int(match.group(2))
verse_type_index = VerseType.from_string(verse_type)
verse_tag = match.group(1)
verse_num = int(match.group(2))
verse_type_index = VerseType.from_loose_input(verse_tag)
if verse_type_index is not None:
self.verseNumberBox.setValue(verse_number)
self.verseNumberBox.setValue(verse_num)
def onCursorPositionChanged(self):
"""
@ -124,25 +126,26 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
match = self.verse_regex.match(text)
if match:
verse_type = match.group(1)
verse_type_index = VerseType.from_loose_input(verse_type)
verse_number = int(match.group(2))
verse_type_index = VerseType.from_string(verse_type)
if verse_type_index is not None:
self.verseTypeComboBox.setCurrentIndex(verse_type_index)
self.verseNumberBox.setValue(verse_number)
def setVerse(self, text, single=False,
tag=u'%s:1' % VerseType.to_string(VerseType.Verse)):
tag=u'%s1' % VerseType.Tags[VerseType.Verse]):
self.hasSingleVerse = single
if single:
verse_type, verse_number = tag.split(u':')
verse_type_index = VerseType.from_string(verse_type)
verse_type_index = VerseType.from_tag(tag[0])
verse_number = tag[1:]
if verse_type_index is not None:
self.verseTypeComboBox.setCurrentIndex(verse_type_index)
self.verseNumberBox.setValue(int(verse_number))
self.insertButton.setVisible(False)
else:
if not text:
text = u'---[%s:1]---\n' % VerseType.to_string(VerseType.Verse)
text = u'---[%s:1]---\n' % \
VerseType.TranslatedNames[VerseType.Verse]
self.verseTypeComboBox.setCurrentIndex(0)
self.verseNumberBox.setValue(1)
self.insertButton.setVisible(True)
@ -152,14 +155,14 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
def getVerse(self):
return self.verseTextEdit.toPlainText(), \
VerseType.to_string(self.verseTypeComboBox.currentIndex()), \
VerseType.Tags[self.verseTypeComboBox.currentIndex()], \
unicode(self.verseNumberBox.value())
def getVerseAll(self):
text = self.verseTextEdit.toPlainText()
if not text.startsWith(u'---['):
text = u'---[%s:1]---\n%s' % (VerseType.to_string(VerseType.Verse),
text)
text = u'---[%s:1]---\n%s' % \
(VerseType.TranslatedNames[VerseType.Verse], text)
return text
def accept(self):

View File

@ -252,6 +252,7 @@ class SongExportForm(OpenLPWizard):
self.availableListWidget.clear()
self.selectedListWidget.clear()
self.directoryLineEdit.clear()
self.searchLineEdit.clear()
# Load the list of songs.
Receiver.send_message(u'cursor_busy')
songs = self.plugin.manager.get_all_objects(Song)
@ -340,19 +341,21 @@ class SongExportForm(OpenLPWizard):
def onUncheckButtonClicked(self):
"""
The *uncheckButton* has been clicked. Set all songs unchecked.
The *uncheckButton* has been clicked. Set all visible songs unchecked.
"""
for row in range(self.availableListWidget.count()):
item = self.availableListWidget.item(row)
item.setCheckState(QtCore.Qt.Unchecked)
if not item.isHidden():
item.setCheckState(QtCore.Qt.Unchecked)
def onCheckButtonClicked(self):
"""
The *checkButton* has been clicked. Set all songs checked.
The *checkButton* has been clicked. Set all visible songs checked.
"""
for row in range(self.availableListWidget.count()):
item = self.availableListWidget.item(row)
item.setCheckState(QtCore.Qt.Checked)
if not item.isHidden():
item.setCheckState(QtCore.Qt.Checked)
def onDirectoryButtonClicked(self):
"""

View File

@ -480,7 +480,7 @@ class SongImportForm(OpenLPWizard):
The file extension filters. It should contain the file descriptions
as well as the file extensions. For example::
u'SongBeamer files (*.sng)'
u'SongBeamer Files (*.sng)'
"""
if filters:
filters += u';;'
@ -609,7 +609,7 @@ class SongImportForm(OpenLPWizard):
'Select Songs of Fellowship Files'),
self.songsOfFellowshipFileListWidget, u'%s (*.rtf)'
% translate('SongsPlugin.ImportWizardForm',
'Songs Of Felloship Song Files')
'Songs Of Fellowship Song Files')
)
def onSongsOfFellowshipRemoveButtonClicked(self):
@ -659,7 +659,7 @@ class SongImportForm(OpenLPWizard):
translate('SongsPlugin.ImportWizardForm',
'Select SongBeamer Files'),
self.songBeamerFileListWidget, u'%s (*.sng)' %
translate('SongsPlugin.ImportWizardForm', 'SongBeamer files')
translate('SongsPlugin.ImportWizardForm', 'SongBeamer Files')
)
def onSongBeamerRemoveButtonClicked(self):

View File

@ -457,7 +457,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
def onTopicDeleteButtonClick(self):
"""
Delete the Book is the Book is not attached to any songs.
Delete the Book if the Book is not attached to any songs.
"""
self._deleteItem(Topic, self.topicsListWidget, self.resetTopics,
translate('SongsPlugin.SongMaintenanceForm', 'Delete Topic'),
@ -470,7 +470,7 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
def onBookDeleteButtonClick(self):
"""
Delete the Book is the Book is not attached to any songs.
Delete the Book if the Book is not attached to any songs.
"""
self._deleteItem(Book, self.booksListWidget, self.resetBooks,
translate('SongsPlugin.SongMaintenanceForm', 'Delete Book'),

View File

@ -40,68 +40,147 @@ class VerseType(object):
Ending = 5
Other = 6
@staticmethod
def to_string(verse_type):
"""
Return a string for a given VerseType
Names = [
u'Verse',
u'Chorus',
u'Bridge',
u'Pre-Chorus',
u'Intro',
u'Ending',
u'Other']
Tags = [name[0].lower() for name in Names]
``verse_type``
The type to return a string for
"""
if not isinstance(verse_type, int):
verse_type = verse_type.lower()
if verse_type == VerseType.Verse or verse_type == \
unicode(VerseType.to_string(VerseType.Verse)).lower()[0]:
return translate('SongsPlugin.VerseType', 'Verse')
elif verse_type == VerseType.Chorus or verse_type == \
unicode(VerseType.to_string(VerseType.Chorus)).lower()[0]:
return translate('SongsPlugin.VerseType', 'Chorus')
elif verse_type == VerseType.Bridge or verse_type == \
unicode(VerseType.to_string(VerseType.Bridge)).lower()[0]:
return translate('SongsPlugin.VerseType', 'Bridge')
elif verse_type == VerseType.PreChorus or verse_type == \
unicode(VerseType.to_string(VerseType.PreChorus)).lower()[0]:
return translate('SongsPlugin.VerseType', 'Pre-Chorus')
elif verse_type == VerseType.Intro or verse_type == \
unicode(VerseType.to_string(VerseType.Intro)).lower()[0]:
return translate('SongsPlugin.VerseType', 'Intro')
elif verse_type == VerseType.Ending or verse_type == \
unicode(VerseType.to_string(VerseType.Ending)).lower()[0]:
return translate('SongsPlugin.VerseType', 'Ending')
elif verse_type == VerseType.Other or verse_type == \
unicode(VerseType.to_string(VerseType.Other)).lower()[0]:
return translate('SongsPlugin.VerseType', 'Other')
TranslatedNames = [
unicode(translate('SongsPlugin.VerseType', 'Verse')),
unicode(translate('SongsPlugin.VerseType', 'Chorus')),
unicode(translate('SongsPlugin.VerseType', 'Bridge')),
unicode(translate('SongsPlugin.VerseType', 'Pre-Chorus')),
unicode(translate('SongsPlugin.VerseType', 'Intro')),
unicode(translate('SongsPlugin.VerseType', 'Ending')),
unicode(translate('SongsPlugin.VerseType', 'Other'))]
TranslatedTags = [name[0].lower() for name in TranslatedNames]
@staticmethod
def from_string(verse_type):
def translated_tag(verse_tag, strict=False):
"""
Return the translated UPPERCASE tag for a given tag,
used to show translated verse tags in UI
``verse_tag``
The string to return a VerseType for
``strict``
Determines if the default Other or None should be returned
"""
if strict:
not_found_value = None
else:
not_found_value = VerseType.TranslatedTags[VerseType.Other].upper()
verse_tag = verse_tag[0].lower()
for num, tag in enumerate(VerseType.Tags):
if verse_tag == tag:
return VerseType.TranslatedTags[num].upper()
return not_found_value
@staticmethod
def translated_name(verse_tag, strict=False):
"""
Return the translated name for a given tag
``verse_tag``
The string to return a VerseType for
``strict``
Determines if the default Other or None should be returned
"""
if strict:
not_found_value = None
else:
not_found_value = VerseType.TranslatedNames[VerseType.Other]
verse_tag = verse_tag[0].lower()
for num, tag in enumerate(VerseType.Tags):
if verse_tag == tag:
return VerseType.TranslatedNames[num]
return not_found_value
@staticmethod
def from_tag(verse_tag, strict=False):
"""
Return the VerseType for a given tag
``verse_tag``
The string to return a VerseType for
``strict``
Determines if the default Other or None should be returned
"""
if strict:
no_return_value = None
else:
no_return_value = VerseType.Other
verse_tag = verse_tag[0].lower()
for num, tag in enumerate(VerseType.Tags):
if verse_tag == tag:
return num
return no_return_value
@staticmethod
def from_translated_tag(verse_tag):
"""
Return the VerseType for a given tag
``verse_tag``
The string to return a VerseType for
"""
verse_tag = verse_tag[0].lower()
for num, tag in enumerate(VerseType.TranslatedTags):
if verse_tag == tag:
return num
@staticmethod
def from_string(verse_name):
"""
Return the VerseType for a given string
``verse_type``
``verse_name``
The string to return a VerseType for
"""
verse_type = verse_type.lower()
if verse_type == unicode(VerseType.to_string(VerseType.Verse)).lower():
return VerseType.Verse
elif verse_type == \
unicode(VerseType.to_string(VerseType.Chorus)).lower():
return VerseType.Chorus
elif verse_type == \
unicode(VerseType.to_string(VerseType.Bridge)).lower():
return VerseType.Bridge
elif verse_type == \
unicode(VerseType.to_string(VerseType.PreChorus)).lower():
return VerseType.PreChorus
elif verse_type == \
unicode(VerseType.to_string(VerseType.Intro)).lower():
return VerseType.Intro
elif verse_type == \
unicode(VerseType.to_string(VerseType.Ending)).lower():
return VerseType.Ending
elif verse_type == \
unicode(VerseType.to_string(VerseType.Other)).lower():
return VerseType.Other
verse_name = verse_name.lower()
for num, name in enumerate(VerseType.Names):
if verse_name == name.lower():
return num
@staticmethod
def from_translated_string(verse_name):
"""
Return the VerseType for a given string
``verse_name``
The string to return a VerseType for
"""
verse_name = verse_name.lower()
for num, translation in enumerate(VerseType.TranslatedNames):
if verse_name == translation.lower():
return num
@staticmethod
def from_loose_input(verse_name):
"""
Return the VerseType for a given string, Other if not found
``verse_name``
The string to return a VerseType for
"""
verse_index = None
if len(verse_name) > 1:
verse_index = VerseType.from_translated_string(verse_name)
if verse_index is None:
verse_index = VerseType.from_string(verse_name)
if verse_index is None:
verse_index = VerseType.from_translated_tag(verse_name)
if verse_index is None:
verse_index = VerseType.from_tag(verse_name)
return verse_index
def retrieve_windows_encoding(recommendation=None):
"""

View File

@ -81,14 +81,16 @@ class EasiSlidesImport(SongImport):
def _parse_song(self, song):
self._success = True
self._add_unicode_attribute(self.title, song.Title1, True)
self._add_unicode_attribute(self.alternate_title, song.Title2)
self._add_unicode_attribute(self.song_number, song.SongNumber)
self._add_unicode_attribute(u'title', song.Title1, True)
self._add_unicode_attribute(u'alternate_title', song.Title2)
self._add_unicode_attribute(u'song_number', song.SongNumber)
if self.song_number == u'0':
self.song_number = u''
self._add_authors(song)
self._add_copyright(song)
self._add_unicode_attribute(self.song_book_name, song.BookReference)
self._add_copyright(song.Copyright)
self._add_copyright(song.LicenceAdmin1)
self._add_copyright(song.LicenceAdmin2)
self._add_unicode_attribute(u'song_book_name', song.BookReference)
self._parse_and_add_lyrics(song)
return self._success
@ -110,7 +112,7 @@ class EasiSlidesImport(SongImport):
Signals that this attribute must exist in a valid song.
"""
try:
self_attribute = unicode(import_attribute).strip()
setattr(self, self_attribute, unicode(import_attribute).strip())
except UnicodeDecodeError:
log.exception(u'UnicodeDecodeError decoding %s' % import_attribute)
self._success = False
@ -124,7 +126,7 @@ class EasiSlidesImport(SongImport):
authors = unicode(song.Writer).split(u',')
for author in authors:
author = author.strip()
if len(author) > 0:
if len(author):
self.authors.append(author)
except UnicodeDecodeError:
log.exception(u'Unicode decode error while decoding Writer')
@ -132,35 +134,18 @@ class EasiSlidesImport(SongImport):
except AttributeError:
pass
def _add_copyright(self, song):
"""
Assign the copyright information from the import to the song being
created.
``song``
The current song being imported.
"""
copyright_list = []
self.__add_copyright_element(copyright_list, song.Copyright)
self.__add_copyright_element(copyright_list, song.LicenceAdmin1)
self.__add_copyright_element(copyright_list, song.LicenceAdmin2)
self.add_copyright(u' '.join(copyright_list))
def __add_copyright_element(self, copyright_list, element):
def _add_copyright(self, element):
"""
Add a piece of copyright to the total copyright information for the
song.
``copyright_list``
The array to add the information to.
``element``
The imported variable to get the data from.
"""
try:
copyright_list.append(unicode(element).strip())
self.add_copyright(unicode(element).strip())
except UnicodeDecodeError:
log.exception(u'Unicode error decoding %s' % element)
log.exception(u'Unicode error on decoding copyright: %s' % element)
self._success = False
except AttributeError:
pass
@ -285,10 +270,12 @@ class EasiSlidesImport(SongImport):
# as these appeared originally in the file
for [reg, vt, vn, inst] in our_verse_order:
if self._listHas(verses, [reg, vt, vn, inst]):
# this is false, but needs user input
lang = None
versetag = u'%s%s' % (vt, vn)
versetags.append(versetag)
lines = u'\n'.join(verses[reg][vt][vn][inst])
self.verses.append([versetag, lines])
self.verses.append([versetag, lines, lang])
SeqTypes = {
u'p': u'P1',

View File

@ -68,11 +68,11 @@ class SongFormat(object):
CCLI = 5
SongsOfFellowship = 6
Generic = 7
#CSV = 8
EasiSlides = 8
EasyWorship = 9
SongBeamer = 10
SongShowPlus = 11
#CSV = 12
@staticmethod
def get_class(format):

View File

@ -36,7 +36,7 @@ from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \
from openlp.core.lib.ui import UiStrings
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
SongImportForm, SongExportForm
from openlp.plugins.songs.lib import OpenLyrics, SongXML
from openlp.plugins.songs.lib import OpenLyrics, SongXML, VerseType
from openlp.plugins.songs.lib.db import Author, Song
from openlp.core.lib.searchedit import SearchEdit
@ -344,24 +344,49 @@ class SongMediaItem(MediaManagerItem):
if song.lyrics.startswith(u'<?xml version='):
verseList = SongXML().get_verses(song.lyrics)
# no verse list or only 1 space (in error)
if not song.verse_order or not song.verse_order.strip():
verse_tags_translated = False
if VerseType.from_translated_string(unicode(
verseList[0][0][u'type'])) is not None:
verse_tags_translated = True
if not song.verse_order.strip():
for verse in verseList:
verseTag = u'%s:%s' % (
verse[0][u'type'], verse[0][u'label'])
# We cannot use from_loose_input() here, because database
# is supposed to contain English lowercase singlechar tags.
verse_tag = verse[0][u'type']
verse_index = None
if len(verse_tag) > 1:
verse_index = \
VerseType.from_translated_string(verse_tag)
if verse_index is None:
verse_index = VerseType.from_string(verse_tag)
if verse_index is None:
verse_index = VerseType.from_tag(verse_tag)
verse_tag = VerseType.TranslatedTags[verse_index].upper()
verse_def = u'%s%s' % (verse_tag, verse[0][u'label'])
service_item.add_from_text(
verse[1][:30], unicode(verse[1]), verseTag)
verse[1][:30], unicode(verse[1]), verse_def)
else:
# Loop through the verse list and expand the song accordingly.
for order in song.verse_order.upper().split():
for order in song.verse_order.lower().split():
if len(order) == 0:
break
for verse in verseList:
if verse[0][u'type'][0] == order[0] and \
(verse[0][u'label'] == order[1:] or not order[1:]):
verseTag = u'%s:%s' % \
(verse[0][u'type'], verse[0][u'label'])
if verse[0][u'type'][0].lower() == order[0] and \
(verse[0][u'label'].lower() == order[1:] or \
not order[1:]):
if verse_tags_translated:
verse_index = VerseType.from_translated_tag(
verse[0][u'type'])
else:
verse_index = VerseType.from_tag(
verse[0][u'type'])
if verse_index is None:
verse_index = VerseType.Other
verse_tag = VerseType.TranslatedTags[verse_index]
verse_def = u'%s%s' % (verse_tag,
verse[0][u'label'])
service_item.add_from_text(
verse[1][:30], verse[1], verseTag)
verse[1][:30], verse[1], verse_def)
else:
verses = song.lyrics.split(u'\n\n')
for slide in verses:

View File

@ -149,23 +149,25 @@ class OpenSongImport(SongImport):
unicode(translate('SongsPlugin.ImportWizardForm',
'Importing %s...')) % parts[-1])
songfile = z.open(song)
self.do_import_file(songfile)
if self.commit:
if self.do_import_file(songfile) and self.commit and \
not self.stop_import_flag:
self.finish()
if self.stop_import_flag:
success = False
break
else:
success = False
break
else:
# not a zipfile
log.info(u'Direct import %s', filename)
self.import_wizard.incrementProgressBar(
unicode(translate('SongsPlugin.ImportWizardForm',
'Importing %s...')) % os.path.split(filename)[-1])
file = open(filename)
self.do_import_file(file)
if self.commit:
song_file = open(filename)
if self.do_import_file(song_file) and self.commit and \
not self.stop_import_flag:
self.finish()
else:
success = False
break
return success
def do_import_file(self, file):
@ -178,7 +180,7 @@ class OpenSongImport(SongImport):
tree = objectify.parse(file)
except (Error, LxmlError):
log.exception(u'Error parsing XML')
return
return False
root = tree.getroot()
fields = dir(root)
decode = {
@ -196,114 +198,103 @@ class OpenSongImport(SongImport):
setattr(self, fn_or_string, ustring)
else:
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:
self.topics.append(unicode(root.theme))
if u'alttheme' in fields and unicode(root.alttheme) not in self.topics:
self.topics.append(unicode(root.alttheme))
# data storage while importing
verses = {}
# keep track of a "default" verse order, in case none is specified
# keep track of verses appearance order
our_verse_order = []
verses_seen = {}
# in the absence of any other indication, verses are the default,
# erm, versetype!
versetype = u'V'
versenum = None
# default verse
verse_tag = u'v'
verse_num = u'1'
# for the case where song has several sections with same marker
inst = 1
lyrics = unicode(root.lyrics)
for thisline in lyrics.split(u'\n'):
for this_line in lyrics.split(u'\n'):
# remove comments
semicolon = thisline.find(u';')
semicolon = this_line.find(u';')
if semicolon >= 0:
thisline = thisline[:semicolon]
thisline = thisline.strip()
if len(thisline) == 0:
this_line = this_line[:semicolon]
this_line = this_line.strip()
if not len(this_line):
continue
# skip inthisline guitar chords and page and column breaks
if thisline[0] == u'.' or thisline.startswith(u'---') \
or thisline.startswith(u'-!!'):
# skip guitar chords and page and column breaks
if this_line.startswith(u'.') or this_line.startswith(u'---') \
or this_line.startswith(u'-!!'):
continue
# verse/chorus/etc. marker
if thisline[0] == u'[':
if this_line.startswith(u'['):
# drop the square brackets
right_bracket = thisline.find(u']')
content = thisline[1:right_bracket].upper()
right_bracket = this_line.find(u']')
content = this_line[1:right_bracket].lower()
# have we got any digits?
# If so, versenumber 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)
match = re.match(u'(.*)(\d+.*)', content)
if match is not None:
versetype = match.group(1)
versenum = match.group(2)
verse_tag = match.group(1)
verse_num = match.group(2)
else:
# otherwise we assume number 1 and take the whole prefix as
# the versetype
versetype = content
versenum = u'1'
# the verse tag
verse_tag = content
verse_num = u'1'
inst = 1
if [verse_tag, verse_num, inst] in our_verse_order \
and verses.has_key(verse_tag) \
and verses[verse_tag].has_key(verse_num):
inst = len(verses[verse_tag][verse_num])+1
our_verse_order.append([verse_tag, verse_num, inst])
continue
words = None
# number at start of line.. it's verse number
if thisline[0].isdigit():
versenum = thisline[0]
words = thisline[1:].strip()
if words is None:
words = thisline
if not versenum:
versenum = u'1'
if versenum is not None:
versetag = u'%s%s' % (versetype, versenum)
if not verses.has_key(versetype):
verses[versetype] = {}
if not verses[versetype].has_key(versenum):
# storage for lines in this verse
verses[versetype][versenum] = []
if not verses_seen.has_key(versetag):
verses_seen[versetag] = 1
our_verse_order.append(versetag)
if words:
# Tidy text and remove the ____s from extended words
words = self.tidy_text(words)
words = words.replace('_', '')
verses[versetype][versenum].append(words)
if this_line[0].isdigit():
verse_num = this_line[0]
this_line = this_line[1:].strip()
our_verse_order.append([verse_tag, verse_num, inst])
if not verses.has_key(verse_tag):
verses[verse_tag] = {}
if not verses[verse_tag].has_key(verse_num):
verses[verse_tag][verse_num] = {}
if not verses[verse_tag][verse_num].has_key(inst):
verses[verse_tag][verse_num][inst] = []
# Tidy text and remove the ____s from extended words
this_line = self.tidy_text(this_line)
this_line = this_line.replace(u'_', u'')
this_line = this_line.replace(u'|', u'\n')
verses[verse_tag][verse_num][inst].append(this_line)
# done parsing
versetypes = verses.keys()
versetypes.sort()
versetags = {}
for versetype in versetypes:
our_verse_type = versetype
if our_verse_type == u'':
our_verse_type = u'V'
versenums = verses[versetype].keys()
versenums.sort()
for num in versenums:
versetag = u'%s%s' % (our_verse_type, num)
lines = u'\n'.join(verses[versetype][num])
self.add_verse(lines, versetag)
# Keep track of what we have for error checking later
versetags[versetag] = 1
# now figure out the presentation order
order = []
# add verses in original order
for (verse_tag, verse_num, inst) in our_verse_order:
verse_def = u'%s%s' % (verse_tag, verse_num)
lines = u'\n'.join(verses[verse_tag][verse_num][inst])
self.add_verse(lines, verse_def)
# figure out the presentation order, if present
if u'presentation' in fields and root.presentation != u'':
order = unicode(root.presentation)
# We make all the tags in the lyrics upper 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
order = order.upper().split()
else:
if len(our_verse_order) > 0:
order = our_verse_order
else:
log.warn(u'No verse order available for %s, skipping.',
self.title)
# TODO: make sure that the default order list will be overwritten, if
# the songs provides its own order list.
for tag in order:
if tag[0].isdigit():
# Assume it's a verse if it has no prefix
tag = u'V' + tag
elif not re.search('\d+', tag):
# Assume it's no.1 if there's no digits
tag = tag + u'1'
if not versetags.has_key(tag):
log.info(u'Got order %s but not in versetags, dropping this'
u'item from presentation order', tag)
else:
self.verse_order_list.append(tag)
order = order.lower().split()
for verse_def in order:
match = re.match(u'(.*)(\d+.*)', verse_def)
if match is not None:
verse_tag = match.group(1)
verse_num = match.group(2)
if not len(verse_tag):
verse_tag = u'v'
else:
# Assume it's no.1 if there are no digits
verse_tag = verse_def
verse_num = u'1'
verse_def = u'%s%s' % (verse_tag, verse_num)
if verses.has_key(verse_tag) \
and verses[verse_tag].has_key(verse_num):
self.verse_order_list.append(verse_def)
else:
log.info(u'Got order %s but not in verse tags, dropping'
u'this item from presentation order', verse_def)
return True

View File

@ -75,9 +75,11 @@ class SongImport(QtCore.QObject):
self.media_files = []
self.song_book_name = u''
self.song_book_pub = u''
self.verse_order_list_generated_useful = False
self.verse_order_list_generated = []
self.verse_order_list = []
self.verses = []
self.versecounts = {}
self.verse_counts = {}
self.copyright_string = unicode(translate(
'SongsPlugin.SongImport', 'copyright'))
self.copyright_symbol = unicode(translate(
@ -128,20 +130,20 @@ class SongImport(QtCore.QObject):
return text
def process_song_text(self, text):
versetexts = text.split(u'\n\n')
for versetext in versetexts:
if versetext.strip() != u'':
self.process_verse_text(versetext.strip())
verse_texts = text.split(u'\n\n')
for verse_text in verse_texts:
if verse_text.strip() != u'':
self.process_verse_text(verse_text.strip())
def process_verse_text(self, text):
lines = text.split(u'\n')
if text.lower().find(self.copyright_string) >= 0 \
or text.lower().find(self.copyright_symbol) >= 0:
or text.find(self.copyright_symbol) >= 0:
copyright_found = False
for line in lines:
if (copyright_found or
line.lower().find(self.copyright_string) >= 0 or
line.lower().find(self.copyright_symbol) >= 0):
line.find(self.copyright_symbol) >= 0):
copyright_found = True
self.add_copyright(line)
else:
@ -198,45 +200,46 @@ class SongImport(QtCore.QObject):
return
self.media_files.append(filename)
def add_verse(self, versetext, versetag=u'V', lang=None):
def add_verse(self, verse_text, verse_def=u'v', lang=None):
"""
Add a verse. This is the whole verse, lines split by \\n. It will also
attempt to detect duplicates. In this case it will just add to the verse
order.
``versetext``
``verse_text``
The text of the verse.
``versetag``
The verse tag can be V1/C1/B etc, or 'V' and 'C' (will count the
``verse_def``
The verse tag can be v1/c1/b etc, or 'v' and 'c' (will count the
verses/choruses itself) or None, where it will assume verse.
``lang``
The language code (ISO-639) of the verse, for example *en* or *de*.
"""
for (oldversetag, oldverse, oldlang) in self.verses:
if oldverse.strip() == versetext.strip():
self.verse_order_list.append(oldversetag)
for (old_verse_def, old_verse, old_lang) in self.verses:
if old_verse.strip() == verse_text.strip():
self.verse_order_list_generated.append(old_verse_def)
self.verse_order_list_generated_useful = True
return
if versetag[0] in self.versecounts:
self.versecounts[versetag[0]] += 1
if verse_def[0] in self.verse_counts:
self.verse_counts[verse_def[0]] += 1
else:
self.versecounts[versetag[0]] = 1
if len(versetag) == 1:
versetag += unicode(self.versecounts[versetag[0]])
elif int(versetag[1:]) > self.versecounts[versetag[0]]:
self.versecounts[versetag[0]] = int(versetag[1:])
self.verses.append([versetag, versetext.rstrip(), lang])
self.verse_order_list.append(versetag)
if versetag.startswith(u'V') and u'C1' in self.verse_order_list:
self.verse_order_list.append(u'C1')
self.verse_counts[verse_def[0]] = 1
if len(verse_def) == 1:
verse_def += unicode(self.verse_counts[verse_def[0]])
elif int(verse_def[1:]) > self.verse_counts[verse_def[0]]:
self.verse_counts[verse_def[0]] = int(verse_def[1:])
self.verses.append([verse_def, verse_text.rstrip(), lang])
self.verse_order_list_generated.append(verse_def)
def repeat_verse(self):
"""
Repeat the previous verse in the verse order
"""
self.verse_order_list.append(self.verse_order_list[-1])
self.verse_order_list_generated.append(
self.verse_order_list_generated[-1])
self.verse_order_list_generated_useful = True
def check_complete(self):
"""
@ -273,34 +276,29 @@ class SongImport(QtCore.QObject):
verses_changed_to_other = {}
sxml = SongXML()
other_count = 1
for (versetag, versetext, lang) in self.verses:
if versetag[0] == u'C':
versetype = VerseType.to_string(VerseType.Chorus)
elif versetag[0] == u'V':
versetype = VerseType.to_string(VerseType.Verse)
elif versetag[0] == u'B':
versetype = VerseType.to_string(VerseType.Bridge)
elif versetag[0] == u'I':
versetype = VerseType.to_string(VerseType.Intro)
elif versetag[0] == u'P':
versetype = VerseType.to_string(VerseType.PreChorus)
elif versetag[0] == u'E':
versetype = VerseType.to_string(VerseType.Ending)
for (verse_def, verse_text, lang) in self.verses:
if verse_def[0].lower() in VerseType.Tags:
verse_tag = verse_def[0].lower()
else:
newversetag = u'O%d' % other_count
verses_changed_to_other[versetag] = newversetag
new_verse_def = u'%s%d' % (VerseType.Tags[VerseType.Other],
other_count)
verses_changed_to_other[verse_def] = new_verse_def
other_count += 1
versetype = VerseType.to_string(VerseType.Other)
log.info(u'Versetype %s changing to %s' , versetag, newversetag)
versetag = newversetag
sxml.add_verse_to_lyrics(versetype, versetag[1:], versetext, lang)
song.search_lyrics += u' ' + self.remove_punctuation(versetext)
verse_tag = VerseType.Tags[VerseType.Other]
log.info(u'Versetype %s changing to %s' , verse_def,
new_verse_def)
verse_def = new_verse_def
sxml.add_verse_to_lyrics(verse_tag, verse_def[1:], verse_text, lang)
song.search_lyrics += u' ' + self.remove_punctuation(verse_text)
song.search_lyrics = song.search_lyrics.lower()
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
for i, current_verse_tag in enumerate(self.verse_order_list):
if verses_changed_to_other.has_key(current_verse_tag):
if not len(self.verse_order_list) and \
self.verse_order_list_generated_useful:
self.verse_order_list = self.verse_order_list_generated
for i, current_verse_def in enumerate(self.verse_order_list):
if verses_changed_to_other.has_key(current_verse_def):
self.verse_order_list[i] = \
verses_changed_to_other[current_verse_tag]
verses_changed_to_other[current_verse_def]
song.verse_order = u' '.join(self.verse_order_list)
song.copyright = self.copyright
song.comments = self.comments
@ -345,9 +343,10 @@ class SongImport(QtCore.QObject):
+ u'========================================'
print u'TITLE: ' + self.title
print u'ALT TITLE: ' + self.alternate_title
for (versetag, versetext, lang) in self.verses:
print u'VERSE ' + versetag + u': ' + versetext
for (verse_def, verse_text, lang) in self.verses:
print u'VERSE ' + verse_def + u': ' + verse_text
print u'ORDER: ' + u' '.join(self.verse_order_list)
print u'GENERATED ORDER: ' + u' '.join(self.verse_order_list_generated)
for author in self.authors:
print u'AUTHOR: ' + author
if self.copyright:

View File

@ -42,7 +42,7 @@ CHORUS = 20
TOPIC = 29
COMMENTS = 30
VERSE_ORDER = 31
SONG_BOOK = 35
SONG_BOOK = 35
SONG_NUMBER = 36
CUSTOM_VERSE = 37
@ -50,32 +50,32 @@ log = logging.getLogger(__name__)
class SongShowPlusImport(SongImport):
"""
The :class:`SongShowPlusImport` class provides the ability to import song
The :class:`SongShowPlusImport` class provides the ability to import song
files from SongShow Plus.
**SongShow Plus Song File Format:**
The SongShow Plus song file format is as follows:
* Each piece of data in the song file has some information that precedes
* Each piece of data in the song file has some information that precedes
it.
* The general format of this data is as follows:
4 Bytes, forming a 32 bit number, a key if you will, this describes what
4 Bytes, forming a 32 bit number, a key if you will, this describes what
the data is (see blockKey below)
4 Bytes, forming a 32 bit number, which is the number of bytes until the
4 Bytes, forming a 32 bit number, which is the number of bytes until the
next block starts
1 Byte, which tells how namy bytes follows
1 or 4 Bytes, describes how long the string is, if its 1 byte, the string
1 or 4 Bytes, describes how long the string is, if its 1 byte, the string
is less than 255
The next bytes are the actuall data.
The next block of data follows on.
This description does differ for verses. Which includes extra bytes
stating the verse type or number. In some cases a "custom" verse is used,
in that case, this block will in include 2 strings, with the associated
string length descriptors. The first string is the name of the verse, the
This description does differ for verses. Which includes extra bytes
stating the verse type or number. In some cases a "custom" verse is used,
in that case, this block will in include 2 strings, with the associated
string length descriptors. The first string is the name of the verse, the
second is the verse content.
The file is ended with four null bytes.
Valid extensions for a SongShow Plus song file are:
@ -98,7 +98,7 @@ class SongShowPlusImport(SongImport):
if kwargs.has_key(u'filenames'):
self.import_source = kwargs[u'filenames']
log.debug(self.import_source)
def do_import(self):
"""
Receive a single file or a list of files to import.
@ -107,38 +107,36 @@ class SongShowPlusImport(SongImport):
self.import_wizard.progressBar.setMaximum(len(self.import_source))
for file in self.import_source:
author = u''
copyright = u''
self.sspVerseOrderList = []
otherCount = 0
otherList = {}
file_name = os.path.split(file)[1]
self.import_wizard.incrementProgressBar(
u'Importing %s' % (file_name), 0)
u'Importing %s' % (file_name), 0)
songData = open(file, 'rb')
while (1):
blockKey, = struct.unpack("I",songData.read(4))
blockKey, = struct.unpack("I", songData.read(4))
# The file ends with 4 NUL's
if blockKey == 0:
break
nextBlockStarts, = struct.unpack("I",songData.read(4))
nextBlockStarts, = struct.unpack("I", songData.read(4))
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:
null, verseNameLength, = struct.unpack("BB",
null, verseNameLength, = struct.unpack("BB",
songData.read(2))
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
if lengthDescriptorSize == 12:
lengthDescriptor, = struct.unpack("I",songData.read(4))
lengthDescriptor, = struct.unpack("I", songData.read(4))
elif lengthDescriptorSize == 2:
lengthDescriptor = 1
elif lengthDescriptorSize == 9:
lengthDescriptor = 0
else:
lengthDescriptor, = struct.unpack("B",songData.read(1))
else:
lengthDescriptor, = struct.unpack("B", songData.read(1))
data = songData.read(lengthDescriptor)
if blockKey == TITLE:
self.title = unicode(data, u'cp1252')
elif blockKey == AUTHOR:
@ -146,17 +144,17 @@ class SongShowPlusImport(SongImport):
for author in authors:
if author.find(",") !=-1:
authorParts = author.split(", ")
author = authorParts[1] + " " + authorParts[0]
self.parse_author(unicode(author, u'cp1252'))
author = authorParts[1] + " " + authorParts[0]
self.parse_author(unicode(author, u'cp1252'))
elif blockKey == COPYRIGHT:
self.add_copyright(unicode(data, u'cp1252'))
elif blockKey == CCLI_NO:
self.ccli_number = int(data)
elif blockKey == VERSE:
self.add_verse(unicode(data, u'cp1252'),
self.add_verse(unicode(data, u'cp1252'),
"V%s" % verseNo)
elif blockKey == CHORUS:
self.add_verse(unicode(data, u'cp1252'),
self.add_verse(unicode(data, u'cp1252'),
"C%s" % verseNo)
elif blockKey == TOPIC:
self.topics.append(unicode(data, u'cp1252'))
@ -182,9 +180,9 @@ class SongShowPlusImport(SongImport):
self.import_wizard.incrementProgressBar(
u'Importing %s' % (file_name))
return True
def toOpenLPVerseTag(self, verseName):
if verseName.find(" ")!=-1:
if verseName.find(" ") !=-1:
verseParts = verseName.split(" ")
verseType = verseParts[0]
verseNumber = verseParts[1]
@ -203,7 +201,7 @@ class SongShowPlusImport(SongImport):
elif verseType == "bridge":
verseTag = "B"
else:
if not self.otherList.has_key(verseName):
if not self.otherList.has_key(verseName):
self.otherCount = self.otherCount + 1
self.otherList[verseName] = str(self.otherCount)
verseTag = "O"

View File

@ -464,7 +464,8 @@ class OpenLyrics(object):
text += u'\n'
text += u'\n'.join([unicode(line) for line in lines.line])
verse_name = self._get(verse, u'name')
verse_type = unicode(VerseType.to_string(verse_name[0]))
verse_type_index = VerseType.from_tag(verse_name[0])
verse_type = VerseType.Names[verse_type_index]
verse_number = re.compile(u'[a-zA-Z]*').sub(u'', verse_name)
verse_part = re.compile(u'[0-9]*').sub(u'', verse_name[1:])
# OpenLyrics allows e. g. "c", but we need "c1".

View File

@ -68,6 +68,7 @@ Source: ..\..\dist\OpenLP\*; DestDir: {app}; Flags: ignoreversion recursesubdirs
[Icons]
Name: {group}\{#AppName}; Filename: {app}\{#AppExeName}
Name: {group}\{#AppName} (Debug); Filename: {app}\{#AppExeName}; Parameters: -l debug
Name: {group}\{cm:ProgramOnTheWeb,{#AppName}}; Filename: {#AppURL}
Name: {group}\{cm:UninstallProgram,{#AppName}}; Filename: {uninstallexe}
Name: {commondesktop}\{#AppName}; Filename: {app}\{#AppExeName}; Tasks: desktopicon