diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py
index 5247ae938..80bf4a67b 100644
--- a/openlp/core/lib/__init__.py
+++ b/openlp/core/lib/__init__.py
@@ -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.
diff --git a/openlp/core/lib/imagemanager.py b/openlp/core/lib/imagemanager.py
index fb242602a..0a76ce834 100644
--- a/openlp/core/lib/imagemanager.py
+++ b/openlp/core/lib/imagemanager.py
@@ -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))
diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py
index f74ba63a9..c2c2987c2 100644
--- a/openlp/core/lib/mediamanageritem.py
+++ b/openlp/core/lib/mediamanageritem.py
@@ -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(
diff --git a/openlp/core/lib/plugin.py b/openlp/core/lib/plugin.py
index a073d31ea..730bb1a36 100644
--- a/openlp/core/lib/plugin.py
+++ b/openlp/core/lib/plugin.py
@@ -335,37 +335,39 @@ class Plugin(QtCore.QObject):
"""
return self.textStrings[name]
- def setPluginTextStrings(self):
+ def setPluginUiTextStrings(self, tooltips):
"""
Called to define all translatable texts of the plugin
"""
## Load Action ##
- self._setSingularTextString(StringContent.Load,
- UiStrings.Load, UiStrings.LoadANew)
+ self.__setNameTextString(StringContent.Load,
+ UiStrings.Load, tooltips[u'load'])
+ ## Import Action ##
+ self.__setNameTextString(StringContent.Import,
+ UiStrings.Import, tooltips[u'import'])
## New Action ##
- self._setSingularTextString(StringContent.New,
- UiStrings.Add, UiStrings.AddANew)
+ self.__setNameTextString(StringContent.New,
+ UiStrings.Add, tooltips[u'new'])
## Edit Action ##
- self._setSingularTextString(StringContent.Edit,
- UiStrings.Edit, UiStrings.EditSelect)
+ self.__setNameTextString(StringContent.Edit,
+ UiStrings.Edit, tooltips[u'edit'])
## Delete Action ##
- self._setSingularTextString(StringContent.Delete,
- UiStrings.Delete, UiStrings.DeleteSelect)
+ self.__setNameTextString(StringContent.Delete,
+ UiStrings.Delete, tooltips[u'delete'])
## Preview Action ##
- self._setSingularTextString(StringContent.Preview,
- UiStrings.Preview, UiStrings.PreviewSelect)
+ self.__setNameTextString(StringContent.Preview,
+ UiStrings.Preview, tooltips[u'preview'])
## Send Live Action ##
- self._setSingularTextString(StringContent.Live,
- UiStrings.Live, UiStrings.SendSelectLive)
+ self.__setNameTextString(StringContent.Live,
+ UiStrings.Live, tooltips[u'live'])
## Add to Service Action ##
- self._setSingularTextString(StringContent.Service,
- UiStrings.Service, UiStrings.AddSelectService)
+ self.__setNameTextString(StringContent.Service,
+ UiStrings.Service, tooltips[u'service'])
- def _setSingularTextString(self, name, title, tooltip):
+ def __setNameTextString(self, name, title, tooltip):
"""
Utility method for creating a plugin's textStrings. This method makes
use of the singular name of the plugin object so must only be called
after this has been set.
"""
- self.textStrings[name] = { u'title': title, u'tooltip': tooltip %
- self.getString(StringContent.Name)[u'singular']}
+ self.textStrings[name] = {u'title': title, u'tooltip': tooltip}
diff --git a/openlp/core/lib/rendermanager.py b/openlp/core/lib/rendermanager.py
index 32a29915f..860a52b60 100644
--- a/openlp/core/lib/rendermanager.py
+++ b/openlp/core/lib/rendermanager.py
@@ -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
diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py
index c74b89144..f9d690ba2 100644
--- a/openlp/core/lib/serviceitem.py
+++ b/openlp/core/lib/serviceitem.py
@@ -28,11 +28,13 @@ The :mod:`serviceitem` provides the service item functionality including the
type and capability of an item.
"""
+import datetime
import logging
import os
import uuid
from openlp.core.lib import build_icon, clean_tags, expand_tags
+from openlp.core.lib.ui import UiStrings
log = logging.getLogger(__name__)
@@ -60,6 +62,7 @@ class ItemCapabilities(object):
AddIfNewItem = 9
ProvidesOwnDisplay = 10
AllowsDetailedTitleDisplay = 11
+ AllowsVarableStartTime = 12
class ServiceItem(object):
@@ -105,6 +108,8 @@ class ServiceItem(object):
self.data_string = u''
self.edit_id = None
self.xml_version = None
+ self.start_time = 0
+ self.media_length = 0
self._new_item()
def _new_item(self):
@@ -257,7 +262,9 @@ class ServiceItem(object):
u'capabilities': self.capabilities,
u'search': self.search_string,
u'data': self.data_string,
- u'xml_version': self.xml_version
+ u'xml_version': self.xml_version,
+ u'start_time': self.start_time,
+ u'media_length': self.media_length
}
service_data = []
if self.service_item_type == ServiceItemType.Text:
@@ -301,6 +308,10 @@ class ServiceItem(object):
self.data_string = header[u'data']
if u'xml_version' in header:
self.xml_version = header[u'xml_version']
+ if u'start_time' in header:
+ self.start_time = header[u'start_time']
+ if u'media_length' in header:
+ self.media_length = header[u'media_length']
if self.service_item_type == ServiceItemType.Text:
for slide in serviceitem[u'serviceitem'][u'data']:
self._raw_frames.append(slide)
@@ -420,3 +431,24 @@ class ServiceItem(object):
return self._raw_frames[row][u'path']
except IndexError:
return u''
+
+ def get_media_time(self):
+ """
+ Returns the start and finish time for a media item
+ """
+ start = None
+ end = None
+ if self.start_time != 0:
+ start = UiStrings.StartTimeCode % \
+ unicode(datetime.timedelta(seconds=self.start_time))
+ if self.media_length != 0:
+ end = UiStrings.LengthTime % \
+ unicode(datetime.timedelta(seconds=self.media_length))
+ if not start and not end:
+ return None
+ elif start and not end:
+ return start
+ elif not start and end:
+ return end
+ else:
+ return u'%s : %s' % (start, end)
diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py
index 06340c2eb..225e1335c 100644
--- a/openlp/core/lib/theme.py
+++ b/openlp/core/lib/theme.py
@@ -170,17 +170,17 @@ class HorizontalType(object):
Type enumeration for horizontal alignment.
"""
Left = 0
- Center = 1
- Right = 2
+ Center = 2
+ Right = 1
@staticmethod
def to_string(horizontal_type):
"""
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'
diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py
index a3b442801..a98e2fb7f 100644
--- a/openlp/core/lib/ui.py
+++ b/openlp/core/lib/ui.py
@@ -41,44 +41,32 @@ class UiStrings(object):
# These strings should need a good reason to be retranslated elsewhere.
# Should some/more/less of these have an & attached?
Add = translate('OpenLP.Ui', '&Add')
- AddANew = unicode(translate('OpenLP.Ui', 'Add a new %s.'))
- AddSelectService = unicode(translate('OpenLP.Ui',
- 'Add the selected %s to the service.'))
Advanced = translate('OpenLP.Ui', 'Advanced')
AllFiles = translate('OpenLP.Ui', 'All Files')
Authors = translate('OpenLP.Ui', 'Authors')
- CreateANew = unicode(translate('OpenLP.Ui', 'Create a new %s.'))
+ CreateService = translate('OpenLP.Ui', 'Create a new service.')
Delete = translate('OpenLP.Ui', '&Delete')
- DeleteSelect = unicode(translate('OpenLP.Ui', 'Delete the selected %s.'))
- DeleteType = unicode(translate('OpenLP.Ui', 'Delete %s'))
Edit = translate('OpenLP.Ui', '&Edit')
- EditSelect = unicode(translate('OpenLP.Ui', 'Edit the selected %s.'))
- EditType = unicode(translate('OpenLP.Ui', 'Edit %s'))
Error = translate('OpenLP.Ui', 'Error')
- ExportType = unicode(translate('OpenLP.Ui', 'Export %s'))
Import = translate('OpenLP.Ui', 'Import')
- ImportType = unicode(translate('OpenLP.Ui', 'Import %s'))
+ LengthTime = unicode(translate('OpenLP.Ui', 'Length %s'))
Live = translate('OpenLP.Ui', 'Live')
Load = translate('OpenLP.Ui', 'Load')
- LoadANew = unicode(translate('OpenLP.Ui', 'Load a new %s.'))
New = translate('OpenLP.Ui', 'New')
- NewType = unicode(translate('OpenLP.Ui', 'New %s'))
+ NewService = translate('OpenLP.Ui', 'New Service')
OLPV2 = translate('OpenLP.Ui', 'OpenLP 2.0')
- OpenType = unicode(translate('OpenLP.Ui', 'Open %s'))
+ OpenService = translate('OpenLP.Ui', 'Open Service')
Preview = translate('OpenLP.Ui', 'Preview')
- PreviewSelect = unicode(translate('OpenLP.Ui', 'Preview the selected %s.'))
ReplaceBG = translate('OpenLP.Ui', 'Replace Background')
ReplaceLiveBG = translate('OpenLP.Ui', 'Replace Live Background')
ResetBG = translate('OpenLP.Ui', 'Reset Background')
ResetLiveBG = translate('OpenLP.Ui', 'Reset Live Background')
- SaveType = unicode(translate('OpenLP.Ui', 'Save %s'))
- SendSelectLive = unicode(translate('OpenLP.Ui',
- 'Send the selected %s live.'))
+ SaveService = translate('OpenLP.Ui', 'Save Service')
Service = translate('OpenLP.Ui', 'Service')
+ StartTimeCode = unicode(translate('OpenLP.Ui', 'Start %s'))
Theme = translate('OpenLP.Ui', 'Theme')
Themes = translate('OpenLP.Ui', 'Themes')
-
def add_welcome_page(parent, image):
"""
Generate an opening welcome page for a wizard using a provided image.
diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py
index d820c9a5b..45218802e 100644
--- a/openlp/core/ui/__init__.py
+++ b/openlp/core/ui/__init__.py
@@ -53,6 +53,7 @@ class HideMode(object):
from themeform import ThemeForm
from filerenameform import FileRenameForm
+from starttimeform import StartTimeForm
from maindisplay import MainDisplay
from servicenoteform import ServiceNoteForm
from serviceitemeditform import ServiceItemEditForm
diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py
index 05a301bd7..90042f80b 100644
--- a/openlp/core/ui/maindisplay.py
+++ b/openlp/core/ui/maindisplay.py
@@ -65,6 +65,7 @@ class MainDisplay(DisplayWidget):
self.parent = parent
self.screens = screens
self.isLive = live
+ self.alertTab = None
self.hideMode = None
self.override = {}
mainIcon = build_icon(u':/icon/openlp-logo-16x16.png')
@@ -105,6 +106,9 @@ class MainDisplay(DisplayWidget):
self.audio = Phonon.AudioOutput(Phonon.VideoCategory, self.mediaObject)
Phonon.createPath(self.mediaObject, self.videoWidget)
Phonon.createPath(self.mediaObject, self.audio)
+ QtCore.QObject.connect(self.mediaObject,
+ QtCore.SIGNAL(u'stateChanged(Phonon::State, Phonon::State)'),
+ self.videoStart)
self.webView = QtWebKit.QWebView(self)
self.webView.setGeometry(0, 0,
self.screen[u'size'].width(), self.screen[u'size'].height())
@@ -144,7 +148,7 @@ class MainDisplay(DisplayWidget):
serviceItem = ServiceItem()
serviceItem.bg_image_bytes = image_to_byte(initialFrame)
self.webView.setHtml(build_html(serviceItem, self.screen,
- self.parent.alertTab, self.isLive, None))
+ self.alertTab, self.isLive, None))
self.initialFrame = True
# To display or not to display?
if not self.screen[u'primary']:
@@ -340,6 +344,13 @@ class MainDisplay(DisplayWidget):
Receiver.send_message(u'maindisplay_active')
return self.preview()
+ def videoStart(self, newState, oldState):
+ """
+ Start the video at a predetermined point.
+ """
+ if newState == Phonon.PlayingState:
+ self.mediaObject.seek(self.serviceItem.start_time * 1000)
+
def isWebLoaded(self):
"""
Called by webView event to show display is fully loaded
@@ -405,7 +416,7 @@ class MainDisplay(DisplayWidget):
if self.serviceItem.themedata.background_filename:
self.serviceItem.bg_image_bytes = self.imageManager. \
get_image_bytes(self.serviceItem.themedata.theme_name)
- html = build_html(self.serviceItem, self.screen, self.parent.alertTab,
+ html = build_html(self.serviceItem, self.screen, self.alertTab,
self.isLive, background)
log.debug(u'buildHtml - pre setHtml')
self.webView.setHtml(html)
diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py
index 4840ef5da..c691c006e 100644
--- a/openlp/core/ui/mainwindow.py
+++ b/openlp/core/ui/mainwindow.py
@@ -319,17 +319,16 @@ class Ui_MainWindow(object):
self.themeManagerDock.setWindowTitle(
translate('OpenLP.MainWindow', 'Theme Manager'))
self.FileNewItem.setText(translate('OpenLP.MainWindow', '&New'))
- self.FileNewItem.setToolTip(UiStrings.NewType % UiStrings.Service)
- self.FileNewItem.setStatusTip(
- UiStrings.CreateANew % UiStrings.Service.toLower())
+ self.FileNewItem.setToolTip(UiStrings.NewService)
+ self.FileNewItem.setStatusTip(UiStrings.CreateService)
self.FileNewItem.setShortcut(translate('OpenLP.MainWindow', 'Ctrl+N'))
self.FileOpenItem.setText(translate('OpenLP.MainWindow', '&Open'))
- self.FileOpenItem.setToolTip(UiStrings.OpenType % UiStrings.Service)
+ self.FileOpenItem.setToolTip(UiStrings.OpenService)
self.FileOpenItem.setStatusTip(
translate('OpenLP.MainWindow', 'Open an existing service.'))
self.FileOpenItem.setShortcut(translate('OpenLP.MainWindow', 'Ctrl+O'))
self.FileSaveItem.setText(translate('OpenLP.MainWindow', '&Save'))
- self.FileSaveItem.setToolTip(UiStrings.SaveType % UiStrings.Service)
+ self.FileSaveItem.setToolTip(UiStrings.SaveService)
self.FileSaveItem.setStatusTip(
translate('OpenLP.MainWindow', 'Save the current service to disk.'))
self.FileSaveItem.setShortcut(translate('OpenLP.MainWindow', 'Ctrl+S'))
diff --git a/openlp/core/ui/printserviceorderform.py b/openlp/core/ui/printserviceorderform.py
index 3b01f9ac7..b5160e61c 100644
--- a/openlp/core/ui/printserviceorderform.py
+++ b/openlp/core/ui/printserviceorderform.py
@@ -24,8 +24,6 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import datetime
-import mutagen
-import os
from PyQt4 import QtCore, QtGui
@@ -113,16 +111,9 @@ class PrintServiceOrderForm(QtGui.QDialog, Ui_PrintServiceOrderDialog):
item.notes.replace(u'\n', u'
'))
# Add play length of media files.
if item.is_media() and self.printMetaDataCheckBox.isChecked():
- path = os.path.join(item.get_frames()[0][u'path'],
- item.get_frames()[0][u'title'])
- if not os.path.isfile(path):
- continue
- file = mutagen.File(path)
- if file is not None:
- length = int(file.info.length)
- text += u'
%s %s
' % (translate(
- 'OpenLP.ServiceManager', u'Playing time:'),
- unicode(datetime.timedelta(seconds=length)))
+ text += u'%s %s
' % (translate(
+ 'OpenLP.ServiceManager', u'Playing time:'),
+ unicode(datetime.timedelta(seconds=item.media_length)))
if self.customNoteEdit.toPlainText():
text += u'%s
%s' % (translate('OpenLP.ServiceManager',
u'Custom Service Notes:'), self.customNoteEdit.toPlainText())
diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py
index 744da7b46..623c2d641 100644
--- a/openlp/core/ui/servicemanager.py
+++ b/openlp/core/ui/servicemanager.py
@@ -36,7 +36,7 @@ from openlp.core.lib import OpenLPToolbar, ServiceItem, context_menu_action, \
Receiver, build_icon, ItemCapabilities, SettingsManager, translate, \
ThemeLevel
from openlp.core.lib.ui import UiStrings, critical_error_message_box
-from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm
+from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm
from openlp.core.ui.printserviceorderform import PrintServiceOrderForm
from openlp.core.utils import AppLocation, delete_file, file_is_unicode, \
split_filename
@@ -88,6 +88,7 @@ class ServiceManager(QtGui.QWidget):
self._fileName = u''
self.serviceNoteForm = ServiceNoteForm(self.mainwindow)
self.serviceItemEditForm = ServiceItemEditForm(self.mainwindow)
+ self.startTimeForm = StartTimeForm(self.mainwindow)
# start with the layout
self.layout = QtGui.QVBoxLayout(self)
self.layout.setSpacing(0)
@@ -95,18 +96,14 @@ class ServiceManager(QtGui.QWidget):
# Create the top toolbar
self.toolbar = OpenLPToolbar(self)
self.toolbar.addToolbarButton(
- UiStrings.NewType % UiStrings.Service,
- u':/general/general_new.png',
- UiStrings.CreateANew % UiStrings.Service.toLower(),
- self.onNewServiceClicked)
+ UiStrings.NewService, u':/general/general_new.png',
+ UiStrings.CreateService, self.onNewServiceClicked)
self.toolbar.addToolbarButton(
- UiStrings.OpenType % UiStrings.Service,
- u':/general/general_open.png',
+ UiStrings.OpenService, u':/general/general_open.png',
translate('OpenLP.ServiceManager', 'Load an existing service'),
self.onLoadServiceClicked)
self.toolbar.addToolbarButton(
- UiStrings.SaveType % UiStrings.Service,
- u':/general/general_save.png',
+ UiStrings.SaveService, u':/general/general_save.png',
translate('OpenLP.ServiceManager', 'Save this service'),
self.saveFile)
self.toolbar.addSeparator()
@@ -270,16 +267,19 @@ class ServiceManager(QtGui.QWidget):
self.notesAction = self.menu.addAction(
translate('OpenLP.ServiceManager', '&Notes'))
self.notesAction.setIcon(build_icon(u':/services/service_notes.png'))
+ self.timeAction = self.menu.addAction(
+ translate('OpenLP.ServiceManager', '&Start Time'))
+ self.timeAction.setIcon(build_icon(u':/media/media_time.png'))
self.deleteAction = self.menu.addAction(
translate('OpenLP.ServiceManager', '&Delete From Service'))
self.deleteAction.setIcon(build_icon(u':/general/general_delete.png'))
self.sep1 = self.menu.addAction(u'')
self.sep1.setSeparator(True)
self.previewAction = self.menu.addAction(
- translate('OpenLP.ServiceManager', '&Preview Verse'))
+ translate('OpenLP.ServiceManager', 'Show &Preview'))
self.previewAction.setIcon(build_icon(u':/general/general_preview.png'))
self.liveAction = self.menu.addAction(
- translate('OpenLP.ServiceManager', '&Live Verse'))
+ translate('OpenLP.ServiceManager', 'Show &Live'))
self.liveAction.setIcon(build_icon(u':/general/general_live.png'))
self.sep2 = self.menu.addAction(u'')
self.sep2.setSeparator(True)
@@ -465,7 +465,7 @@ class ServiceManager(QtGui.QWidget):
save the file.
"""
fileName = unicode(QtGui.QFileDialog.getSaveFileName(self.mainwindow,
- UiStrings.SaveType % UiStrings.Service,
+ UiStrings.SaveService,
SettingsManager.get_last_dir(
self.mainwindow.serviceSettingsSection),
translate('OpenLP.ServiceManager', 'OpenLP Service Files (*.osz)')))
@@ -563,6 +563,7 @@ class ServiceManager(QtGui.QWidget):
self.editAction.setVisible(False)
self.maintainAction.setVisible(False)
self.notesAction.setVisible(False)
+ self.timeAction.setVisible(False)
if serviceItem[u'service_item'].is_capable(ItemCapabilities.AllowsEdit)\
and serviceItem[u'service_item'].edit_id:
self.editAction.setVisible(True)
@@ -571,6 +572,9 @@ class ServiceManager(QtGui.QWidget):
self.maintainAction.setVisible(True)
if item.parent() is None:
self.notesAction.setVisible(True)
+ if serviceItem[u'service_item']\
+ .is_capable(ItemCapabilities.AllowsVarableStartTime):
+ self.timeAction.setVisible(True)
self.themeMenu.menuAction().setVisible(False)
if serviceItem[u'service_item'].is_text():
self.themeMenu.menuAction().setVisible(True)
@@ -583,6 +587,8 @@ class ServiceManager(QtGui.QWidget):
self.onDeleteFromService()
if action == self.notesAction:
self.onServiceItemNoteForm()
+ if action == self.timeAction:
+ self.onStartTimeForm()
if action == self.previewAction:
self.makePreview()
if action == self.liveAction:
@@ -597,6 +603,16 @@ class ServiceManager(QtGui.QWidget):
self.serviceNoteForm.textEdit.toPlainText()
self.repaintServiceList(item, -1)
+ def onStartTimeForm(self):
+ item = self.findServiceItem()[0]
+ self.startTimeForm.item = self.serviceItems[item]
+ if self.startTimeForm.exec_():
+ self.serviceItems[item][u'service_item'].start_time = \
+ self.startTimeForm.hourSpinBox.value() * 3600 + \
+ self.startTimeForm.minuteSpinBox.value() * 60 + \
+ self.startTimeForm.secondSpinBox.value()
+ self.repaintServiceList(item, -1)
+
def onServiceItemEditForm(self):
item = self.findServiceItem()[0]
self.serviceItemEditForm.setServiceItem(
@@ -843,6 +859,11 @@ class ServiceManager(QtGui.QWidget):
text = frame[u'title'].replace(u'\n', u' ')
child.setText(0, text[:40])
child.setData(0, QtCore.Qt.UserRole, QtCore.QVariant(count))
+ if item[u'service_item'] \
+ .is_capable(ItemCapabilities.AllowsVarableStartTime):
+ tip = item[u'service_item'].get_media_time()
+ if tip:
+ child.setToolTip(0, tip)
if serviceItem == itemcount:
if item[u'expanded'] and serviceItemChild == count:
self.serviceManagerList.setCurrentItem(child)
diff --git a/openlp/core/ui/settingsdialog.py b/openlp/core/ui/settingsdialog.py
index 99acadc14..41b6baccb 100644
--- a/openlp/core/ui/settingsdialog.py
+++ b/openlp/core/ui/settingsdialog.py
@@ -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')
diff --git a/openlp/core/ui/shortcutlistdialog.py b/openlp/core/ui/shortcutlistdialog.py
index 3f41d377a..4e20671c5 100644
--- a/openlp/core/ui/shortcutlistdialog.py
+++ b/openlp/core/ui/shortcutlistdialog.py
@@ -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(
diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py
index 9d2b31853..0de4bea7f 100644
--- a/openlp/core/ui/shortcutlistform.py
+++ b/openlp/core/ui/shortcutlistform.py
@@ -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)
diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py
index cd43cddb4..9177e313f 100644
--- a/openlp/core/ui/slidecontroller.py
+++ b/openlp/core/ui/slidecontroller.py
@@ -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')
@@ -415,6 +415,7 @@ class SlideController(QtGui.QWidget):
# rebuild display as screen size changed
self.display = MainDisplay(self, self.screens, self.isLive)
self.display.imageManager = self.parent.renderManager.image_manager
+ self.display.alertTab = self.alertTab
self.display.setup()
if self.isLive:
self.__addActionsToWidget(self.display)
@@ -602,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
diff --git a/openlp/core/ui/starttimedialog.py b/openlp/core/ui/starttimedialog.py
new file mode 100644
index 000000000..8dcc2c9ee
--- /dev/null
+++ b/openlp/core/ui/starttimedialog.py
@@ -0,0 +1,70 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2011 Raoul Snyman #
+# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
+# Carsten Tinggaard, Frode Woldsund #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it #
+# under the terms of the GNU General Public License as published by the Free #
+# Software Foundation; version 2 of the License. #
+# #
+# This program is distributed in the hope that it will be useful, but WITHOUT #
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
+# more details. #
+# #
+# You should have received a copy of the GNU General Public License along #
+# with this program; if not, write to the Free Software Foundation, Inc., 59 #
+# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
+###############################################################################
+
+from PyQt4 import QtCore, QtGui
+
+from openlp.core.lib import translate
+from openlp.core.lib.ui import create_accept_reject_button_box
+
+class Ui_StartTimeDialog(object):
+ def setupUi(self, StartTimeDialog):
+ StartTimeDialog.setObjectName(u'StartTimeDialog')
+ StartTimeDialog.resize(300, 10)
+ self.dialogLayout = QtGui.QGridLayout(StartTimeDialog)
+ self.dialogLayout.setObjectName(u'dialogLayout')
+ self.hourLabel = QtGui.QLabel(StartTimeDialog)
+ self.hourLabel.setObjectName("hourLabel")
+ self.dialogLayout.addWidget(self.hourLabel, 0, 0, 1, 1)
+ self.hourSpinBox = QtGui.QSpinBox(StartTimeDialog)
+ self.hourSpinBox.setObjectName("hourSpinBox")
+ self.dialogLayout.addWidget(self.hourSpinBox, 0, 1, 1, 1)
+ self.minuteLabel = QtGui.QLabel(StartTimeDialog)
+ self.minuteLabel.setObjectName("minuteLabel")
+ self.dialogLayout.addWidget(self.minuteLabel, 1, 0, 1, 1)
+ self.minuteSpinBox = QtGui.QSpinBox(StartTimeDialog)
+ self.minuteSpinBox.setObjectName("minuteSpinBox")
+ self.dialogLayout.addWidget(self.minuteSpinBox, 1, 1, 1, 1)
+ self.secondLabel = QtGui.QLabel(StartTimeDialog)
+ self.secondLabel.setObjectName("secondLabel")
+ self.dialogLayout.addWidget(self.secondLabel, 2, 0, 1, 1)
+ self.secondSpinBox = QtGui.QSpinBox(StartTimeDialog)
+ self.secondSpinBox.setObjectName("secondSpinBox")
+ self.dialogLayout.addWidget(self.secondSpinBox, 2, 1, 1, 1)
+ self.buttonBox = create_accept_reject_button_box(StartTimeDialog, True)
+ self.dialogLayout.addWidget(self.buttonBox, 4, 0, 1, 2)
+ self.retranslateUi(StartTimeDialog)
+ self.setMaximumHeight(self.sizeHint().height())
+ QtCore.QMetaObject.connectSlotsByName(StartTimeDialog)
+
+ def retranslateUi(self, StartTimeDialog):
+ self.setWindowTitle(translate('OpenLP.StartTimeForm',
+ 'Item Start Time'))
+ self.hourLabel.setText(translate('OpenLP.StartTimeForm', 'Hours:'))
+ self.hourSpinBox.setSuffix(translate('OpenLP.StartTimeForm', 'h'))
+ self.minuteSpinBox.setSuffix(translate('OpenLP.StartTimeForm', 'm'))
+ self.secondSpinBox.setSuffix(translate('OpenLP.StartTimeForm', 's'))
+ self.minuteLabel.setText(translate('OpenLP.StartTimeForm', 'Minutes:'))
+ self.secondLabel.setText(translate('OpenLP.StartTimeForm', 'Seconds:'))
diff --git a/openlp/core/ui/starttimeform.py b/openlp/core/ui/starttimeform.py
new file mode 100644
index 000000000..1280931d5
--- /dev/null
+++ b/openlp/core/ui/starttimeform.py
@@ -0,0 +1,52 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2011 Raoul Snyman #
+# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
+# Carsten Tinggaard, Frode Woldsund #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it #
+# under the terms of the GNU General Public License as published by the Free #
+# Software Foundation; version 2 of the License. #
+# #
+# This program is distributed in the hope that it will be useful, but WITHOUT #
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
+# more details. #
+# #
+# You should have received a copy of the GNU General Public License along #
+# with this program; if not, write to the Free Software Foundation, Inc., 59 #
+# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
+###############################################################################
+
+from PyQt4 import QtGui
+
+from starttimedialog import Ui_StartTimeDialog
+
+class StartTimeForm(QtGui.QDialog, Ui_StartTimeDialog):
+ """
+ The exception dialog
+ """
+ def __init__(self, parent):
+ QtGui.QDialog.__init__(self, parent)
+ self.setupUi(self)
+
+ def exec_(self):
+ """
+ Run the Dialog with correct heading.
+ """
+ seconds = self.item[u'service_item'].start_time
+ hours = seconds / 3600
+ seconds -= 3600 * hours
+ minutes = seconds / 60
+ seconds -= 60 * minutes
+ self.hourSpinBox.setValue(hours)
+ self.minuteSpinBox.setValue(minutes)
+ self.secondSpinBox.setValue(seconds)
+ return QtGui.QDialog.exec_(self)
+
diff --git a/openlp/core/ui/themeform.py b/openlp/core/ui/themeform.py
index f86fa0143..ad9e80d66 100644
--- a/openlp/core/ui/themeform.py
+++ b/openlp/core/ui/themeform.py
@@ -483,7 +483,8 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
Background Image button pushed.
"""
images_filter = get_images_filter()
- images_filter = '%s;;%s (*.*) (*)' % (images_filter, UiStrings.AllFiles)
+ images_filter = u'%s;;%s (*.*) (*)' % (
+ images_filter, UiStrings.AllFiles)
filename = QtGui.QFileDialog.getOpenFileName(self,
translate('OpenLP.ThemeForm', 'Select Image'), u'',
images_filter)
diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py
index 36abb19c1..78c4596e3 100644
--- a/openlp/core/ui/thememanager.py
+++ b/openlp/core/ui/thememanager.py
@@ -63,28 +63,28 @@ class ThemeManager(QtGui.QWidget):
self.layout.setObjectName(u'layout')
self.toolbar = OpenLPToolbar(self)
self.toolbar.addToolbarButton(
- UiStrings.NewType % UiStrings.Theme,
+ translate('OpenLP.ThemeManager', 'New Theme'),
u':/themes/theme_new.png',
- UiStrings.CreateANew % UiStrings.Theme.toLower(),
+ translate('OpenLP.ThemeManager', 'Create a new theme.'),
self.onAddTheme)
self.toolbar.addToolbarButton(
- UiStrings.EditType % UiStrings.Theme,
+ translate('OpenLP.ThemeManager', 'Edit Theme'),
u':/themes/theme_edit.png',
translate('OpenLP.ThemeManager', 'Edit a theme.'),
self.onEditTheme)
self.deleteToolbarAction = self.toolbar.addToolbarButton(
- UiStrings.DeleteType % UiStrings.Theme,
+ translate('OpenLP.ThemeManager', 'Delete Theme'),
u':/general/general_delete.png',
translate('OpenLP.ThemeManager', 'Delete a theme.'),
self.onDeleteTheme)
self.toolbar.addSeparator()
self.toolbar.addToolbarButton(
- UiStrings.ImportType % UiStrings.Theme,
+ translate('OpenLP.ThemeManager', 'Import Theme'),
u':/general/general_import.png',
translate('OpenLP.ThemeManager', 'Import a theme.'),
self.onImportTheme)
self.toolbar.addToolbarButton(
- UiStrings.ExportType % UiStrings.Theme,
+ translate('OpenLP.ThemeManager', 'Export Theme'),
u':/general/general_export.png',
translate('OpenLP.ThemeManager', 'Export a theme.'),
self.onExportTheme)
@@ -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),
- 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):
"""
diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py
index 2862afef4..3f5ee90c2 100644
--- a/openlp/core/utils/__init__.py
+++ b/openlp/core/utils/__init__.py
@@ -164,24 +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':
- return os.path.join(os.getenv(u'APPDATA'), u'openlp')
+ if dir_type == AppLocation.DataDir:
+ 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')
- else:
- return os.path.join(os.getenv(u'HOME'), u'.openlp')
+ return os.path.join(unicode(BaseDirectory.xdg_cache_home,
+ encoding), u'openlp')
+ if dir_type == AppLocation.DataDir:
+ 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):
"""
@@ -189,8 +199,7 @@ def _get_frozen_path(frozen_option, non_frozen_option):
"""
if hasattr(sys, u'frozen') and sys.frozen == 1:
return frozen_option
- else:
- return non_frozen_option
+ return non_frozen_option
def check_latest_version(current_version):
"""
@@ -373,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):
"""
diff --git a/openlp/plugins/bibles/bibleplugin.py b/openlp/plugins/bibles/bibleplugin.py
index e3447cfdd..b992552f1 100644
--- a/openlp/plugins/bibles/bibleplugin.py
+++ b/openlp/plugins/bibles/bibleplugin.py
@@ -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__)
@@ -129,9 +128,15 @@ class BiblePlugin(Plugin):
u'title': translate('BiblesPlugin', 'Bibles', 'container title')
}
# Middle Header Bar
- ## Import Action ##
- self.textStrings[StringContent.Import] = {
- u'title': UiStrings.Import,
- u'tooltip': translate('BiblesPlugin', 'Import a Bible')
+ tooltips = {
+ u'load': u'',
+ u'import': translate('BiblesPlugin', 'Import a Bible'),
+ u'new': translate('BiblesPlugin', 'Add a new Bible'),
+ u'edit': translate('BiblesPlugin', 'Edit the selected Bible'),
+ u'delete': translate('BiblesPlugin', 'Delete the selected Bible'),
+ u'preview': translate('BiblesPlugin', 'Preview the selected Bible'),
+ u'live': translate('BiblesPlugin', 'Send the selected Bible live'),
+ u'service': translate('BiblesPlugin',
+ 'Add the selected Bible to the service')
}
- Plugin.setPluginTextStrings(self)
+ self.setPluginUiTextStrings(tooltips)
diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py
index 58dc11a6a..a6ceba6ac 100644
--- a/openlp/plugins/bibles/lib/mediaitem.py
+++ b/openlp/plugins/bibles/lib/mediaitem.py
@@ -30,6 +30,7 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \
translate
+from openlp.core.lib.searchedit import SearchEdit
from openlp.core.lib.ui import UiStrings, add_widget_completer, \
media_item_combo_box, critical_error_message_box
from openlp.plugins.bibles.forms import BibleImportForm
@@ -37,6 +38,14 @@ from openlp.plugins.bibles.lib import get_reference_match
log = logging.getLogger(__name__)
+class BibleSearch(object):
+ """
+ Enumeration class for the different search methods for the "quick search".
+ """
+ Reference = 1
+ Text = 2
+
+
class BibleMediaItem(MediaManagerItem):
"""
This is the custom media manager item for Bibles.
@@ -83,18 +92,17 @@ class BibleMediaItem(MediaManagerItem):
u'quickSecondComboBox')
self.quickSecondLabel.setBuddy(self.quickSecondComboBox)
self.quickLayout.addRow(self.quickSecondLabel, self.quickSecondComboBox)
- self.quickSearchTypeLabel = QtGui.QLabel(self.quickTab)
- self.quickSearchTypeLabel.setObjectName(u'quickSearchTypeLabel')
- self.quickSearchComboBox = media_item_combo_box(self.quickTab,
- u'quickSearchComboBox')
- self.quickSearchTypeLabel.setBuddy(self.quickSearchComboBox)
- self.quickLayout.addRow(self.quickSearchTypeLabel,
- self.quickSearchComboBox)
self.quickSearchLabel = QtGui.QLabel(self.quickTab)
self.quickSearchLabel.setObjectName(u'quickSearchLabel')
- self.quickSearchEdit = QtGui.QLineEdit(self.quickTab)
+ self.quickSearchEdit = SearchEdit(self.quickTab)
self.quickSearchEdit.setObjectName(u'quickSearchEdit')
self.quickSearchLabel.setBuddy(self.quickSearchEdit)
+ self.quickSearchEdit.setSearchTypes([
+ (BibleSearch.Reference, u':/bibles/bibles_search_reference.png',
+ translate('BiblesPlugin.MediaItem', 'Scripture Reference')),
+ (BibleSearch.Text, u':/bibles/bibles_search_text.png',
+ translate('BiblesPlugin.MediaItem', 'Text Search'))
+ ])
self.quickLayout.addRow(self.quickSearchLabel, self.quickSearchEdit)
self.quickClearLabel = QtGui.QLabel(self.quickTab)
self.quickClearLabel.setObjectName(u'quickClearLabel')
@@ -196,8 +204,8 @@ class BibleMediaItem(MediaManagerItem):
QtCore.SIGNAL(u'activated(int)'), self.onAdvancedFromVerse)
QtCore.QObject.connect(self.advancedToChapter,
QtCore.SIGNAL(u'activated(int)'), self.onAdvancedToChapter)
- QtCore.QObject.connect(self.quickSearchComboBox,
- QtCore.SIGNAL(u'activated(int)'), self.updateAutoCompleter)
+ QtCore.QObject.connect(self.quickSearchEdit,
+ QtCore.SIGNAL(u'searchTypeChanged(int)'), self.updateAutoCompleter)
QtCore.QObject.connect(self.quickVersionComboBox,
QtCore.SIGNAL(u'activated(int)'), self.updateAutoCompleter)
# Buttons
@@ -231,8 +239,6 @@ class BibleMediaItem(MediaManagerItem):
translate('BiblesPlugin.MediaItem', 'Version:'))
self.quickSecondLabel.setText(
translate('BiblesPlugin.MediaItem', 'Second:'))
- self.quickSearchTypeLabel.setText(
- translate('BiblesPlugin.MediaItem', 'Search type:'))
self.quickSearchLabel.setText(
translate('BiblesPlugin.MediaItem', 'Find:'))
self.quickSearchButton.setText(
@@ -257,10 +263,6 @@ class BibleMediaItem(MediaManagerItem):
translate('BiblesPlugin.MediaItem', 'Results:'))
self.advancedSearchButton.setText(
translate('BiblesPlugin.MediaItem', 'Search'))
- self.quickSearchComboBox.addItem(
- translate('BiblesPlugin.MediaItem', 'Verse Search'))
- self.quickSearchComboBox.addItem(
- translate('BiblesPlugin.MediaItem', 'Text Search'))
self.quickClearComboBox.addItem(
translate('BiblesPlugin.MediaItem', 'Clear'))
self.quickClearComboBox.addItem(
@@ -358,11 +360,11 @@ class BibleMediaItem(MediaManagerItem):
"""
This updates the bible book completion list for the search field. The
completion depends on the bible. It is only updated when we are doing a
- verse search, otherwise the auto completion list is removed.
+ reference search, otherwise the auto completion list is removed.
"""
books = []
- # We have to do a 'Verse Search'.
- if self.quickSearchComboBox.currentIndex() == 0:
+ # We have to do a 'Reference Search'.
+ if self.quickSearchEdit.currentSearchType() == BibleSearch.Reference:
bibles = self.parent.manager.get_bibles()
bible = unicode(self.quickVersionComboBox.currentText())
if bible:
@@ -491,7 +493,7 @@ class BibleMediaItem(MediaManagerItem):
def onQuickSearchButton(self):
"""
Does a quick search and saves the search results. Quick search can
- either be "Verse Search" or "Text Search".
+ either be "Reference Search" or "Text Search".
"""
log.debug(u'Quick Search Button pressed')
self.quickSearchButton.setEnabled(False)
@@ -499,8 +501,8 @@ class BibleMediaItem(MediaManagerItem):
bible = unicode(self.quickVersionComboBox.currentText())
second_bible = unicode(self.quickSecondComboBox.currentText())
text = unicode(self.quickSearchEdit.text())
- if self.quickSearchComboBox.currentIndex() == 0:
- # We are doing a 'Verse Search'.
+ if self.quickSearchEdit.currentSearchType() == BibleSearch.Reference:
+ # We are doing a 'Reference Search'.
self.search_results = self.parent.manager.get_verses(bible, text)
if second_bible and self.search_results:
self.second_search_results = self.parent.manager.get_verses(
diff --git a/openlp/plugins/custom/customplugin.py b/openlp/plugins/custom/customplugin.py
index 92546cd4f..65245fc8a 100644
--- a/openlp/plugins/custom/customplugin.py
+++ b/openlp/plugins/custom/customplugin.py
@@ -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
@@ -106,13 +105,20 @@ class CustomPlugin(Plugin):
u'title': translate('CustomsPlugin', 'Custom', 'container title')
}
# Middle Header Bar
- ## Import Action ##
- self.textStrings[StringContent.Import] = {
- u'title': UiStrings.Import,
- u'tooltip': translate('CustomsPlugin',
- 'Import a Custom')
+ tooltips = {
+ u'load': translate('CustomsPlugin', 'Load a new Custom'),
+ u'import': translate('CustomsPlugin', 'Import a Custom'),
+ u'new': translate('CustomsPlugin', 'Add a new Custom'),
+ u'edit': translate('CustomsPlugin', 'Edit the selected Custom'),
+ u'delete': translate('CustomsPlugin', 'Delete the selected Custom'),
+ u'preview': translate('CustomsPlugin',
+ 'Preview the selected Custom'),
+ u'live': translate('CustomsPlugin',
+ 'Send the selected Custom live'),
+ u'service': translate('CustomsPlugin',
+ 'Add the selected Custom to the service')
}
- Plugin.setPluginTextStrings(self)
+ self.setPluginUiTextStrings(tooltips)
def finalise(self):
"""
diff --git a/openlp/plugins/images/imageplugin.py b/openlp/plugins/images/imageplugin.py
index 6b64598fc..2cc0f1d93 100644
--- a/openlp/plugins/images/imageplugin.py
+++ b/openlp/plugins/images/imageplugin.py
@@ -69,4 +69,15 @@ class ImagePlugin(Plugin):
u'title': translate('ImagePlugin', 'Images', 'container title')
}
# Middle Header Bar
- Plugin.setPluginTextStrings(self)
+ tooltips = {
+ u'load': translate('ImagePlugin', 'Load a new Image'),
+ u'import': u'',
+ u'new': translate('ImagePlugin', 'Add a new Image'),
+ u'edit': translate('ImagePlugin', 'Edit the selected Image'),
+ u'delete': translate('ImagePlugin', 'Delete the selected Image'),
+ u'preview': translate('ImagePlugin', 'Preview the selected Image'),
+ u'live': translate('ImagePlugin', 'Send the selected Image live'),
+ u'service': translate('ImagePlugin',
+ 'Add the selected Image to the service')
+ }
+ self.setPluginUiTextStrings(tooltips)
diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py
index 7fb0ed0c1..cc126bbef 100644
--- a/openlp/plugins/media/lib/mediaitem.py
+++ b/openlp/plugins/media/lib/mediaitem.py
@@ -32,6 +32,7 @@ from PyQt4 import QtCore, QtGui
from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \
SettingsManager, translate, check_item_selected, Receiver
from openlp.core.lib.ui import UiStrings, critical_error_message_box
+from PyQt4.phonon import Phonon
log = logging.getLogger(__name__)
@@ -48,9 +49,13 @@ class MediaMediaItem(MediaManagerItem):
u':/media/media_video.png').toImage()
MediaManagerItem.__init__(self, parent, self, icon)
self.singleServiceItem = False
+ self.mediaObject = Phonon.MediaObject(self)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'video_background_replaced'),
self.videobackgroundReplaced)
+ QtCore.QObject.connect(self.mediaObject,
+ QtCore.SIGNAL(u'stateChanged(Phonon::State, Phonon::State)'),
+ self.videoStart)
def retranslateUi(self):
self.OnNewPrompt = translate('MediaPlugin.MediaItem', 'Select Media')
@@ -120,13 +125,22 @@ class MediaMediaItem(MediaManagerItem):
return False
filename = unicode(item.data(QtCore.Qt.UserRole).toString())
if os.path.exists(filename):
+ self.MediaState = None
+ self.mediaObject.stop()
+ self.mediaObject.clearQueue()
+ self.mediaObject.setCurrentSource(Phonon.MediaSource(filename))
+ self.mediaObject.play()
service_item.title = unicode(
translate('MediaPlugin.MediaItem', 'Media'))
service_item.add_capability(ItemCapabilities.RequiresMedia)
+ service_item.add_capability(ItemCapabilities.AllowsVarableStartTime)
# force a nonexistent theme
service_item.theme = -1
frame = u':/media/image_clapperboard.png'
(path, name) = os.path.split(filename)
+ while not self.MediaState:
+ Receiver.send_message(u'openlp_process_events')
+ service_item.media_length = self.mediaLength
service_item.add_from_command(path, name, frame)
return True
else:
@@ -164,3 +178,12 @@ class MediaMediaItem(MediaManagerItem):
item_name.setIcon(build_icon(img))
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
self.listView.addItem(item_name)
+
+ def videoStart(self, newState, oldState):
+ """
+ Start the video at a predetermined point.
+ """
+ if newState == Phonon.PlayingState:
+ self.MediaState = newState
+ self.mediaLength = self.mediaObject.totalTime()/1000
+ self.mediaObject.stop()
diff --git a/openlp/plugins/media/mediaplugin.py b/openlp/plugins/media/mediaplugin.py
index b9db9b8c1..ee413aa8c 100644
--- a/openlp/plugins/media/mediaplugin.py
+++ b/openlp/plugins/media/mediaplugin.py
@@ -95,4 +95,15 @@ class MediaPlugin(Plugin):
u'title': translate('MediaPlugin', 'Media', 'container title')
}
# Middle Header Bar
- Plugin.setPluginTextStrings(self)
+ tooltips = {
+ u'load': translate('MediaPlugin', 'Load a new Media'),
+ u'import': u'',
+ u'new': translate('MediaPlugin', 'Add a new Media'),
+ u'edit': translate('MediaPlugin', 'Edit the selected Media'),
+ u'delete': translate('MediaPlugin', 'Delete the selected Media'),
+ u'preview': translate('MediaPlugin', 'Preview the selected Media'),
+ u'live': translate('MediaPlugin', 'Send the selected Media live'),
+ u'service': translate('MediaPlugin',
+ 'Add the selected Media to the service')
+ }
+ self.setPluginUiTextStrings(tooltips)
diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py
index 65e9f35ff..eb00da255 100644
--- a/openlp/plugins/presentations/lib/powerpointcontroller.py
+++ b/openlp/plugins/presentations/lib/powerpointcontroller.py
@@ -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):
"""
diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py
index a64cd31dd..fc839195c 100644
--- a/openlp/plugins/presentations/lib/pptviewcontroller.py
+++ b/openlp/plugins/presentations/lib/pptviewcontroller.py
@@ -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):
diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py
index c81cdc028..ece25e363 100644
--- a/openlp/plugins/presentations/presentationplugin.py
+++ b/openlp/plugins/presentations/presentationplugin.py
@@ -167,4 +167,18 @@ class PresentationPlugin(Plugin):
'container title')
}
# Middle Header Bar
- Plugin.setPluginTextStrings(self)
+ tooltips = {
+ u'load': translate('PresentationPlugin', 'Load a new Presentation'),
+ u'import': u'',
+ u'new': u'',
+ u'edit': u'',
+ u'delete': translate('PresentationPlugin',
+ 'Delete the selected Presentation'),
+ u'preview': translate('PresentationPlugin',
+ 'Preview the selected Presentation'),
+ u'live': translate('PresentationPlugin',
+ 'Send the selected Presentation live'),
+ u'service': translate('PresentationPlugin',
+ 'Add the selected Presentation to the service')
+ }
+ self.setPluginUiTextStrings(tooltips)
diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py
index 8536d38b8..77caf012e 100644
--- a/openlp/plugins/songs/forms/editsongform.py
+++ b/openlp/plugins/songs/forms/editsongform.py
@@ -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' 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:
diff --git a/openlp/plugins/songs/forms/editversedialog.py b/openlp/plugins/songs/forms/editversedialog.py
index 7caf782e6..64da3e89e 100644
--- a/openlp/plugins/songs/forms/editversedialog.py
+++ b/openlp/plugins/songs/forms/editversedialog.py
@@ -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'))
diff --git a/openlp/plugins/songs/forms/editverseform.py b/openlp/plugins/songs/forms/editverseform.py
index e67e0733a..6156e6821 100644
--- a/openlp/plugins/songs/forms/editverseform.py
+++ b/openlp/plugins/songs/forms/editverseform.py
@@ -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):
diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py
index 849a1ad1e..f331fbcb9 100644
--- a/openlp/plugins/songs/forms/songexportform.py
+++ b/openlp/plugins/songs/forms/songexportform.py
@@ -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):
"""
diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py
index b84af8dde..eda3d6750 100644
--- a/openlp/plugins/songs/forms/songimportform.py
+++ b/openlp/plugins/songs/forms/songimportform.py
@@ -140,6 +140,12 @@ class SongImportForm(OpenLPWizard):
QtCore.QObject.connect(self.songBeamerRemoveButton,
QtCore.SIGNAL(u'clicked()'),
self.onSongBeamerRemoveButtonClicked)
+ QtCore.QObject.connect(self.songShowPlusAddButton,
+ QtCore.SIGNAL(u'clicked()'),
+ self.onSongShowPlusAddButtonClicked)
+ QtCore.QObject.connect(self.songShowPlusRemoveButton,
+ QtCore.SIGNAL(u'clicked()'),
+ self.onSongShowPlusRemoveButtonClicked)
def addCustomPages(self):
"""
@@ -188,6 +194,8 @@ class SongImportForm(OpenLPWizard):
self.addFileSelectItem(u'ew', single_select=True)
# Words of Worship
self.addFileSelectItem(u'songBeamer')
+ # Song Show Plus
+ self.addFileSelectItem(u'songShowPlus')
# Commented out for future use.
# self.addFileSelectItem(u'csv', u'CSV', single_select=True)
self.sourceLayout.addLayout(self.formatStack)
@@ -237,6 +245,8 @@ class SongImportForm(OpenLPWizard):
translate('SongsPlugin.ImportWizardForm', 'EasyWorship'))
self.formatComboBox.setItemText(10,
translate('SongsPlugin.ImportWizardForm', 'SongBeamer'))
+ self.formatComboBox.setItemText(11,
+ translate('SongsPlugin.ImportWizardForm', 'SongShow Plus'))
# self.formatComboBox.setItemText(11,
# translate('SongsPlugin.ImportWizardForm', 'CSV'))
self.openLP2FilenameLabel.setText(
@@ -301,6 +311,10 @@ class SongImportForm(OpenLPWizard):
translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
self.songBeamerRemoveButton.setText(
translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
+ self.songShowPlusAddButton.setText(
+ translate('SongsPlugin.ImportWizardForm', 'Add Files...'))
+ self.songShowPlusRemoveButton.setText(
+ translate('SongsPlugin.ImportWizardForm', 'Remove File(s)'))
# self.csvFilenameLabel.setText(
# translate('SongsPlugin.ImportWizardForm', 'Filename:'))
# self.csvBrowseButton.setText(
@@ -438,6 +452,16 @@ class SongImportForm(OpenLPWizard):
'file to import from.'))
self.songBeamerAddButton.setFocus()
return False
+ elif source_format == SongFormat.SongShowPlus:
+ if self.songShowPlusFileListWidget.count() == 0:
+ critical_error_message_box(
+ translate('SongsPlugin.ImportWizardForm',
+ 'No SongShow Plus Files Selected'),
+ translate('SongsPlugin.ImportWizardForm',
+ 'You need to add at least one SongShow Plus '
+ 'file to import from.'))
+ self.wordsOfWorshipAddButton.setFocus()
+ return False
return True
elif self.currentPage() == self.progressPage:
return True
@@ -456,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';;'
@@ -585,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):
@@ -635,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):
@@ -643,6 +667,24 @@ class SongImportForm(OpenLPWizard):
Remove selected SongBeamer files from the import list
"""
self.removeSelectedItems(self.songBeamerFileListWidget)
+
+ def onSongShowPlusAddButtonClicked(self):
+ """
+ Get SongShow Plus song database files
+ """
+ self.getFiles(
+ translate('SongsPlugin.ImportWizardForm',
+ 'Select SongShow Plus Files'),
+ self.songShowPlusFileListWidget, u'%s (*.sbsong)'
+ % translate('SongsPlugin.ImportWizardForm',
+ 'SongShow Plus Song Files')
+ )
+
+ def onSongShowPlusRemoveButtonClicked(self):
+ """
+ Remove selected SongShow Plus files from the import list
+ """
+ self.removeSelectedItems(self.songShowPlusFileListWidget)
def setDefaults(self):
"""
@@ -663,6 +705,7 @@ class SongImportForm(OpenLPWizard):
self.easiSlidesFilenameEdit.setText(u'')
self.ewFilenameEdit.setText(u'')
self.songBeamerFileListWidget.clear()
+ self.songShowPlusFileListWidget.clear()
#self.csvFilenameEdit.setText(u'')
def preWizard(self):
@@ -739,6 +782,12 @@ class SongImportForm(OpenLPWizard):
importer = self.plugin.importSongs(SongFormat.SongBeamer,
filenames=self.getListOfFiles(self.songBeamerFileListWidget)
)
+ elif source_format == SongFormat.SongShowPlus:
+ # Import ShongShow Plus songs
+ importer = self.plugin.importSongs(SongFormat.SongShowPlus,
+ filenames=self.getListOfFiles(
+ self.songShowPlusFileListWidget)
+ )
if importer.do_import():
self.progressLabel.setText(
translate('SongsPlugin.SongImportForm', 'Finished import.'))
diff --git a/openlp/plugins/songs/forms/songmaintenanceform.py b/openlp/plugins/songs/forms/songmaintenanceform.py
index 1eb63fbf4..1f693223c 100644
--- a/openlp/plugins/songs/forms/songmaintenanceform.py
+++ b/openlp/plugins/songs/forms/songmaintenanceform.py
@@ -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'),
diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py
index c763d70b9..95fae92a8 100644
--- a/openlp/plugins/songs/lib/__init__.py
+++ b/openlp/plugins/songs/lib/__init__.py
@@ -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):
"""
diff --git a/openlp/plugins/songs/lib/easislidesimport.py b/openlp/plugins/songs/lib/easislidesimport.py
index 5d56af8ce..b31e50862 100644
--- a/openlp/plugins/songs/lib/easislidesimport.py
+++ b/openlp/plugins/songs/lib/easislidesimport.py
@@ -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',
diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py
index 6f566ff4f..230dcd8d0 100644
--- a/openlp/plugins/songs/lib/importer.py
+++ b/openlp/plugins/songs/lib/importer.py
@@ -34,6 +34,7 @@ from wowimport import WowImport
from cclifileimport import CCLIFileImport
from ewimport import EasyWorshipSongImport
from songbeamerimport import SongBeamerImport
+from songshowplusimport import SongShowPlusImport
# Imports that might fail
try:
from olp1import import OpenLP1SongImport
@@ -67,10 +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):
@@ -102,6 +104,8 @@ class SongFormat(object):
return EasyWorshipSongImport
elif format == SongFormat.SongBeamer:
return SongBeamerImport
+ elif format == SongFormat.SongShowPlus:
+ return SongShowPlusImport
return None
@staticmethod
@@ -120,7 +124,8 @@ class SongFormat(object):
SongFormat.Generic,
SongFormat.EasiSlides,
SongFormat.EasyWorship,
- SongFormat.SongBeamer
+ SongFormat.SongBeamer,
+ SongFormat.SongShowPlus
]
@staticmethod
diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py
index 283aa6c03..c7e9bae3b 100644
--- a/openlp/plugins/songs/lib/mediaitem.py
+++ b/openlp/plugins/songs/lib/mediaitem.py
@@ -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' 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:
diff --git a/openlp/plugins/songs/lib/opensongimport.py b/openlp/plugins/songs/lib/opensongimport.py
index 8c6d283e3..00fdbf044 100644
--- a/openlp/plugins/songs/lib/opensongimport.py
+++ b/openlp/plugins/songs/lib/opensongimport.py
@@ -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
diff --git a/openlp/plugins/songs/lib/songimport.py b/openlp/plugins/songs/lib/songimport.py
index da017d4f5..e0e078c43 100644
--- a/openlp/plugins/songs/lib/songimport.py
+++ b/openlp/plugins/songs/lib/songimport.py
@@ -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:
diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py
new file mode 100644
index 000000000..5df36a5b1
--- /dev/null
+++ b/openlp/plugins/songs/lib/songshowplusimport.py
@@ -0,0 +1,210 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2011 Raoul Snyman #
+# Portions copyright (c) 2008-2011 Tim Bentley, Jonathan Corwin, Michael #
+# Gorven, Scott Guerrieri, Meinert Jordan, Andreas Preikschat, Christian #
+# Richter, Philip Ridout, Maikel Stuivenberg, Martin Thompson, Jon Tibble, #
+# Carsten Tinggaard, Frode Woldsund #
+# --------------------------------------------------------------------------- #
+# This program is free software; you can redistribute it and/or modify it #
+# under the terms of the GNU General Public License as published by the Free #
+# Software Foundation; version 2 of the License. #
+# #
+# This program is distributed in the hope that it will be useful, but WITHOUT #
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
+# more details. #
+# #
+# You should have received a copy of the GNU General Public License along #
+# with this program; if not, write to the Free Software Foundation, Inc., 59 #
+# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
+###############################################################################
+"""
+The :mod:`wowimport` module provides the functionality for importing Words of
+Worship songs into the OpenLP database.
+"""
+import os
+import logging
+import struct
+
+from openlp.plugins.songs.lib.songimport import SongImport
+
+TITLE = 1
+AUTHOR = 2
+COPYRIGHT = 3
+CCLI_NO = 5
+VERSE = 12
+CHORUS = 20
+TOPIC = 29
+COMMENTS = 30
+VERSE_ORDER = 31
+SONG_BOOK = 35
+SONG_NUMBER = 36
+CUSTOM_VERSE = 37
+
+log = logging.getLogger(__name__)
+
+class SongShowPlusImport(SongImport):
+ """
+ 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
+ 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
+ the data is (see blockKey below)
+ 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
+ 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
+ second is the verse content.
+
+ The file is ended with four null bytes.
+
+ Valid extensions for a SongShow Plus song file are:
+
+ * .sbsong
+ """
+ otherList = {}
+ otherCount = 0
+
+ def __init__(self, master_manager, **kwargs):
+ """
+ Initialise the import.
+
+ ``master_manager``
+ The song manager for the running OpenLP installation.
+ """
+ SongImport.__init__(self, master_manager)
+ if kwargs.has_key(u'filename'):
+ self.import_source = kwargs[u'filename']
+ 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.
+ """
+ if isinstance(self.import_source, list):
+ self.import_wizard.progressBar.setMaximum(len(self.import_source))
+ for file in self.import_source:
+ author = u''
+ self.sspVerseOrderList = []
+ otherCount = 0
+ otherList = {}
+ file_name = os.path.split(file)[1]
+ self.import_wizard.incrementProgressBar(
+ u'Importing %s' % (file_name), 0)
+ songData = open(file, 'rb')
+ while (1):
+ 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))
+ if blockKey == VERSE or blockKey == CHORUS:
+ null, verseNo, = struct.unpack("BB", songData.read(2))
+ elif blockKey == CUSTOM_VERSE:
+ null, verseNameLength, = struct.unpack("BB",
+ songData.read(2))
+ verseName = songData.read(verseNameLength)
+ 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))
+ elif lengthDescriptorSize == 2:
+ lengthDescriptor = 1
+ elif lengthDescriptorSize == 9:
+ lengthDescriptor = 0
+ else:
+ lengthDescriptor, = struct.unpack("B", songData.read(1))
+ data = songData.read(lengthDescriptor)
+ if blockKey == TITLE:
+ self.title = unicode(data, u'cp1252')
+ elif blockKey == AUTHOR:
+ authors = data.split(" / ")
+ for author in authors:
+ if author.find(",") !=-1:
+ authorParts = author.split(", ")
+ 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'),
+ "V%s" % verseNo)
+ elif blockKey == CHORUS:
+ self.add_verse(unicode(data, u'cp1252'),
+ "C%s" % verseNo)
+ elif blockKey == TOPIC:
+ self.topics.append(unicode(data, u'cp1252'))
+ elif blockKey == COMMENTS:
+ self.comments = unicode(data, u'cp1252')
+ elif blockKey == VERSE_ORDER:
+ verseTag = self.toOpenLPVerseTag(data)
+ self.sspVerseOrderList.append(unicode(verseTag,
+ u'cp1252'))
+ elif blockKey == SONG_BOOK:
+ self.song_book_name = unicode(data, u'cp1252')
+ elif blockKey == SONG_NUMBER:
+ self.song_number = ord(data)
+ elif blockKey == CUSTOM_VERSE:
+ verseTag = self.toOpenLPVerseTag(verseName)
+ self.add_verse(unicode(data, u'cp1252'), verseTag)
+ else:
+ log.debug("Unrecognised blockKey: %s, data: %s"
+ %(blockKey, data))
+ self.verse_order_list = self.sspVerseOrderList
+ songData.close()
+ self.finish()
+ self.import_wizard.incrementProgressBar(
+ u'Importing %s' % (file_name))
+ return True
+
+ def toOpenLPVerseTag(self, verseName):
+ if verseName.find(" ") !=-1:
+ verseParts = verseName.split(" ")
+ verseType = verseParts[0]
+ verseNumber = verseParts[1]
+ else:
+ verseType = verseName
+ verseNumber = "1"
+ verseType = verseType.lower()
+ if verseType == "verse":
+ verseTag = "V"
+ elif verseType == "chorus":
+ verseTag = "C"
+ elif verseType == "bridge":
+ verseTag = "B"
+ elif verseType == "pre-chorus":
+ verseTag = "P"
+ elif verseType == "bridge":
+ verseTag = "B"
+ else:
+ if not self.otherList.has_key(verseName):
+ self.otherCount = self.otherCount + 1
+ self.otherList[verseName] = str(self.otherCount)
+ verseTag = "O"
+ verseNumber = self.otherList[verseName]
+ verseTag = verseTag + verseNumber
+ return verseTag
diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py
index b96e79961..e5c3963c0 100644
--- a/openlp/plugins/songs/lib/xml.py
+++ b/openlp/plugins/songs/lib/xml.py
@@ -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".
diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py
index 646e8e86e..887ddb7b2 100644
--- a/openlp/plugins/songs/songsplugin.py
+++ b/openlp/plugins/songs/songsplugin.py
@@ -228,7 +228,18 @@ class SongsPlugin(Plugin):
u'title': translate('SongsPlugin', 'Songs', 'container title')
}
# Middle Header Bar
- Plugin.setPluginTextStrings(self)
+ tooltips = {
+ u'load': u'',
+ u'import': u'',
+ u'new': translate('SongsPlugin', 'Add a new Song'),
+ u'edit': translate('SongsPlugin', 'Edit the selected Song'),
+ u'delete': translate('SongsPlugin', 'Delete the selected Song'),
+ u'preview': translate('SongsPlugin', 'Preview the selected Song'),
+ u'live': translate('SongsPlugin', 'Send the selected Song live'),
+ u'service': translate('SongsPlugin',
+ 'Add the selected Song to the service')
+ }
+ self.setPluginUiTextStrings(tooltips)
def finalise(self):
"""
diff --git a/resources/images/bibles_search_reference.png b/resources/images/bibles_search_reference.png
new file mode 100644
index 000000000..f64c0ad78
Binary files /dev/null and b/resources/images/bibles_search_reference.png differ
diff --git a/resources/images/bibles_search_text.png b/resources/images/bibles_search_text.png
new file mode 100644
index 000000000..1ab7145c6
Binary files /dev/null and b/resources/images/bibles_search_text.png differ
diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc
index dd5abd861..9e6ff6543 100644
--- a/resources/images/openlp-2.qrc
+++ b/resources/images/openlp-2.qrc
@@ -21,6 +21,10 @@
song_topic_edit.png
song_book_edit.png
+
+ bibles_search_text.png
+ bibles_search_reference.png
+
plugin_alerts.png
plugin_bibles.png
diff --git a/resources/windows/OpenLP-2.0.iss b/resources/windows/OpenLP-2.0.iss
index a85b41187..c64e2b488 100644
--- a/resources/windows/OpenLP-2.0.iss
+++ b/resources/windows/OpenLP-2.0.iss
@@ -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