Head
@ -14,6 +14,8 @@ Contents:
|
|||||||
introduction
|
introduction
|
||||||
glossary
|
glossary
|
||||||
dualmonitors
|
dualmonitors
|
||||||
|
mediamanager
|
||||||
|
songs
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
||||||
==================
|
==================
|
||||||
|
26
documentation/manual/source/mediamanager.rst
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
=============
|
||||||
|
Media Manager
|
||||||
|
=============
|
||||||
|
|
||||||
|
Once you get your system set up for OpenLP you will be ready to add content to
|
||||||
|
your setup. This will all happen through the **Media Manager**. The
|
||||||
|
`Media Manager` contains all the bibles, songs, presentations, media, and
|
||||||
|
everything else that you will project through OpenLP.
|
||||||
|
|
||||||
|
Enabling the Plugins
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
You may need to enable the plugins that came with OpenLP. As you can see below
|
||||||
|
this is what the `Media Manager` looks like with all the plugins enabled.
|
||||||
|
|
||||||
|
.. image:: pics/mediamanager.png
|
||||||
|
|
||||||
|
To enable the plugins navigate to :menuselection:`Settings --> Plugins` or
|
||||||
|
press `F7`. You will then want to click on the plugin to the left that you want
|
||||||
|
to enable and select **active** from the drop down box to the right.
|
||||||
|
|
||||||
|
.. image:: pics/plugins.png
|
||||||
|
|
||||||
|
|
||||||
|
Now you should be ready to add content to OpenLP check out the section of this
|
||||||
|
guide on the individual plugins.
|
BIN
documentation/manual/source/pics/finishedimport.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
documentation/manual/source/pics/plugins.png
Normal file
After Width: | Height: | Size: 29 KiB |
BIN
documentation/manual/source/pics/selectsongs.png
Normal file
After Width: | Height: | Size: 63 KiB |
BIN
documentation/manual/source/pics/songimporter.png
Normal file
After Width: | Height: | Size: 138 KiB |
BIN
documentation/manual/source/pics/songimporterchoices.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
documentation/manual/source/pics/songselectlyrics.png
Normal file
After Width: | Height: | Size: 206 KiB |
BIN
documentation/manual/source/pics/songselectsongsearch.png
Normal file
After Width: | Height: | Size: 160 KiB |
BIN
documentation/manual/source/pics/songusage.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
documentation/manual/source/pics/songusagedelete.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
documentation/manual/source/pics/songusagereport.png
Normal file
After Width: | Height: | Size: 55 KiB |
0
documentation/manual/source/pics/vistadisplaysettings.png
Executable file → Normal file
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
0
documentation/manual/source/pics/vistapersonalize.png
Executable file → Normal file
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 139 KiB |
0
documentation/manual/source/pics/winsevendisplay.png
Executable file → Normal file
Before Width: | Height: | Size: 155 KiB After Width: | Height: | Size: 155 KiB |
0
documentation/manual/source/pics/winsevenresolution.png
Executable file → Normal file
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 141 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 24 KiB |
100
documentation/manual/source/songs.rst
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
=====
|
||||||
|
Songs
|
||||||
|
=====
|
||||||
|
|
||||||
|
Managing your songs in OpenLP is a relatively simple process. There are also
|
||||||
|
converters provided to get data from other formats into OpenLP.
|
||||||
|
|
||||||
|
Song Importer
|
||||||
|
=============
|
||||||
|
|
||||||
|
If you are using an earlier version of OpenLP or come from another software
|
||||||
|
package, you may be able to convert your existing database to work in OpenLP
|
||||||
|
2.0. To access the Song Importer :menuselection:`File --> Import --> Song`.
|
||||||
|
You will then see the Song Importer window, then click :guilabel:`Next`.
|
||||||
|
|
||||||
|
.. image:: pics/songimporter.png
|
||||||
|
|
||||||
|
After choosing :guilabel:`Next` you can then select from the various types of
|
||||||
|
software that OpenLP will convert songs from.
|
||||||
|
|
||||||
|
.. image:: pics/songimporterchoices.png
|
||||||
|
|
||||||
|
Then click on the file folder icon to choose the file of the song database you
|
||||||
|
want to import. See the following sections for information on the different
|
||||||
|
formats that OpenLP will import.
|
||||||
|
|
||||||
|
Importing from OpenLP Version 1
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Converting from OpenLP Version 1 is a pretty simple process. You will first
|
||||||
|
need to locate your version 1 database file.
|
||||||
|
|
||||||
|
Windows XP::
|
||||||
|
|
||||||
|
C:\Documents and Settings\All Users\Application Data\openlp.org\Data\songs.olp
|
||||||
|
|
||||||
|
Windows Vista / Windows 7::
|
||||||
|
|
||||||
|
C:\ProgramData\openlp.org\Data\songs.olp
|
||||||
|
|
||||||
|
After clicking :guilabel:`Next` your conversion should be complete.
|
||||||
|
|
||||||
|
.. image:: pics/finishedimport.png
|
||||||
|
|
||||||
|
Then press :guilabel:`Finish` and you should now be ready to use your OpenLP
|
||||||
|
version one songs.
|
||||||
|
|
||||||
|
Importing from OpenSong
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Converting from OpenSong you will need to locate your songs database. In the
|
||||||
|
later versions of OpenSong you are asked to define the location of this. The
|
||||||
|
songs will be located in a folder named :guilabel:`Songs`. This folder should
|
||||||
|
contain files with all your songs in them without a file extension. (file.xxx).
|
||||||
|
When you have located this folder you will then need to select the songs from
|
||||||
|
the folder.
|
||||||
|
|
||||||
|
.. image:: pics/selectsongs.png
|
||||||
|
|
||||||
|
On most operating systems to select all the songs, first select the first song
|
||||||
|
in the lest then press shift and select the last song in the list. After this
|
||||||
|
press :guilabel:`Next` and you should see that your import has been successful.
|
||||||
|
|
||||||
|
.. image:: pics/finishedimport.png
|
||||||
|
|
||||||
|
Press :guilabel:`Finish` and you will now be ready to use your songs imported
|
||||||
|
from OpenSong.
|
||||||
|
|
||||||
|
Importing from CCLI Song Select
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
To import from CCLI Song Select you must be a CCLI Subscriber and also a
|
||||||
|
subscriber of the Song Select service. For info on that go to:
|
||||||
|
http://www.ccli.com
|
||||||
|
|
||||||
|
The first step for importing from CCLI Song Select is to log into your account.
|
||||||
|
Then search for your desired song. For this example we will be adding the song
|
||||||
|
"Amazing Grace".
|
||||||
|
|
||||||
|
.. image:: pics/songselectsongsearch.png
|
||||||
|
|
||||||
|
For the song you are searching for select `lyrics` This should take you to a
|
||||||
|
page displaying the lyrics and copyright info for your song.
|
||||||
|
|
||||||
|
.. image:: pics/songselectlyrics.png
|
||||||
|
|
||||||
|
Next, hover over the :guilabel:`Lyrics` menu from the upper right corner. Then
|
||||||
|
choose either the .txt or .usr file. You will then be asked to chose a download
|
||||||
|
location if your browser does not automatically select that for you. Select
|
||||||
|
this file from the OpenLP import window and then click :guilabel:`Next` You can
|
||||||
|
also select multiple songs for import at once on most operating systems by
|
||||||
|
selecting the first item in the list then holding shift select the last item in
|
||||||
|
the list. When finished you should see that your import has completed.
|
||||||
|
|
||||||
|
.. image:: pics/finishedimport.png
|
||||||
|
|
||||||
|
Press :guilabel:`Finish` and you will now be ready to use your songs imported
|
||||||
|
from CCLI SongSelect.
|
||||||
|
|
||||||
|
|
@ -294,4 +294,5 @@ class Manager(object):
|
|||||||
"""
|
"""
|
||||||
if self.is_dirty:
|
if self.is_dirty:
|
||||||
engine = create_engine(self.db_url)
|
engine = create_engine(self.db_url)
|
||||||
|
if self.db_url.startswith(u'sqlite'):
|
||||||
engine.execute("vacuum")
|
engine.execute("vacuum")
|
||||||
|
@ -320,15 +320,9 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
translate('OpenLP.MediaManagerItem',
|
translate('OpenLP.MediaManagerItem',
|
||||||
'&Add to selected Service Item'),
|
'&Add to selected Service Item'),
|
||||||
self.onAddEditClick))
|
self.onAddEditClick))
|
||||||
if QtCore.QSettings().value(u'advanced/double click live',
|
|
||||||
QtCore.QVariant(False)).toBool():
|
|
||||||
QtCore.QObject.connect(self.listView,
|
QtCore.QObject.connect(self.listView,
|
||||||
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
|
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
|
||||||
self.onLiveClick)
|
self.onClickPressed)
|
||||||
else:
|
|
||||||
QtCore.QObject.connect(self.listView,
|
|
||||||
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
|
|
||||||
self.onPreviewClick)
|
|
||||||
|
|
||||||
def initialise(self):
|
def initialise(self):
|
||||||
"""
|
"""
|
||||||
@ -426,10 +420,20 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
raise NotImplementedError(u'MediaManagerItem.onDeleteClick needs to '
|
raise NotImplementedError(u'MediaManagerItem.onDeleteClick needs to '
|
||||||
u'be defined by the plugin')
|
u'be defined by the plugin')
|
||||||
|
|
||||||
def generateSlideData(self, service_item, item=None):
|
def generateSlideData(self, serviceItem, item=None, xmlVersion=False):
|
||||||
raise NotImplementedError(u'MediaManagerItem.generateSlideData needs '
|
raise NotImplementedError(u'MediaManagerItem.generateSlideData needs '
|
||||||
u'to be defined by the plugin')
|
u'to be defined by the plugin')
|
||||||
|
|
||||||
|
def onClickPressed(self):
|
||||||
|
"""
|
||||||
|
Allows the list click action to be determined dynamically
|
||||||
|
"""
|
||||||
|
if QtCore.QSettings().value(u'advanced/double click live',
|
||||||
|
QtCore.QVariant(False)).toBool():
|
||||||
|
self.onLiveClick()
|
||||||
|
else:
|
||||||
|
self.onPreviewClick()
|
||||||
|
|
||||||
def onPreviewClick(self):
|
def onPreviewClick(self):
|
||||||
"""
|
"""
|
||||||
Preview an item by building a service item then adding that service
|
Preview an item by building a service item then adding that service
|
||||||
@ -442,10 +446,10 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
'You must select one or more items to preview.'))
|
'You must select one or more items to preview.'))
|
||||||
else:
|
else:
|
||||||
log.debug(self.plugin.name + u' Preview requested')
|
log.debug(self.plugin.name + u' Preview requested')
|
||||||
service_item = self.buildServiceItem()
|
serviceItem = self.buildServiceItem()
|
||||||
if service_item:
|
if serviceItem:
|
||||||
service_item.from_plugin = True
|
serviceItem.from_plugin = True
|
||||||
self.parent.previewController.addServiceItem(service_item)
|
self.parent.previewController.addServiceItem(serviceItem)
|
||||||
|
|
||||||
def onLiveClick(self):
|
def onLiveClick(self):
|
||||||
"""
|
"""
|
||||||
@ -459,10 +463,10 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
'You must select one or more items to send live.'))
|
'You must select one or more items to send live.'))
|
||||||
else:
|
else:
|
||||||
log.debug(self.plugin.name + u' Live requested')
|
log.debug(self.plugin.name + u' Live requested')
|
||||||
service_item = self.buildServiceItem()
|
serviceItem = self.buildServiceItem()
|
||||||
if service_item:
|
if serviceItem:
|
||||||
service_item.from_plugin = True
|
serviceItem.from_plugin = True
|
||||||
self.parent.liveController.addServiceItem(service_item)
|
self.parent.liveController.addServiceItem(serviceItem)
|
||||||
|
|
||||||
def onAddClick(self):
|
def onAddClick(self):
|
||||||
"""
|
"""
|
||||||
@ -474,22 +478,22 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
translate('OpenLP.MediaManagerItem',
|
translate('OpenLP.MediaManagerItem',
|
||||||
'You must select one or more items.'))
|
'You must select one or more items.'))
|
||||||
else:
|
else:
|
||||||
# Is it posssible to process multiple list items to generate multiple
|
# Is it posssible to process multiple list items to generate
|
||||||
# service items?
|
# multiple service items?
|
||||||
if self.singleServiceItem or self.remoteTriggered:
|
if self.singleServiceItem or self.remoteTriggered:
|
||||||
log.debug(self.plugin.name + u' Add requested')
|
log.debug(self.plugin.name + u' Add requested')
|
||||||
service_item = self.buildServiceItem()
|
serviceItem = self.buildServiceItem(None, True)
|
||||||
if service_item:
|
if serviceItem:
|
||||||
service_item.from_plugin = False
|
serviceItem.from_plugin = False
|
||||||
self.parent.serviceManager.addServiceItem(service_item,
|
self.parent.serviceManager.addServiceItem(serviceItem,
|
||||||
replace=self.remoteTriggered)
|
replace=self.remoteTriggered)
|
||||||
else:
|
else:
|
||||||
items = self.listView.selectedIndexes()
|
items = self.listView.selectedIndexes()
|
||||||
for item in items:
|
for item in items:
|
||||||
service_item = self.buildServiceItem(item)
|
serviceItem = self.buildServiceItem(item, True)
|
||||||
if service_item:
|
if serviceItem:
|
||||||
service_item.from_plugin = False
|
serviceItem.from_plugin = False
|
||||||
self.parent.serviceManager.addServiceItem(service_item)
|
self.parent.serviceManager.addServiceItem(serviceItem)
|
||||||
|
|
||||||
def onAddEditClick(self):
|
def onAddEditClick(self):
|
||||||
"""
|
"""
|
||||||
@ -502,16 +506,16 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
'You must select one or more items'))
|
'You must select one or more items'))
|
||||||
else:
|
else:
|
||||||
log.debug(self.plugin.name + u' Add requested')
|
log.debug(self.plugin.name + u' Add requested')
|
||||||
service_item = self.parent.serviceManager.getServiceItem()
|
serviceItem = self.parent.serviceManager.getServiceItem()
|
||||||
if not service_item:
|
if not serviceItem:
|
||||||
QtGui.QMessageBox.information(self,
|
QtGui.QMessageBox.information(self,
|
||||||
translate('OpenLP.MediaManagerItem',
|
translate('OpenLP.MediaManagerItem',
|
||||||
'No Service Item Selected'),
|
'No Service Item Selected'),
|
||||||
translate('OpenLP.MediaManagerItem',
|
translate('OpenLP.MediaManagerItem',
|
||||||
'You must select an existing service item to add to.'))
|
'You must select an existing service item to add to.'))
|
||||||
elif self.title.lower() == service_item.name.lower():
|
elif self.title.lower() == serviceItem.name.lower():
|
||||||
self.generateSlideData(service_item)
|
self.generateSlideData(serviceItem)
|
||||||
self.parent.serviceManager.addServiceItem(service_item,
|
self.parent.serviceManager.addServiceItem(serviceItem,
|
||||||
replace=True)
|
replace=True)
|
||||||
else:
|
else:
|
||||||
# Turn off the remote edit update message indicator
|
# Turn off the remote edit update message indicator
|
||||||
@ -521,17 +525,17 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
unicode(translate('OpenLP.MediaManagerItem',
|
unicode(translate('OpenLP.MediaManagerItem',
|
||||||
'You must select a %s service item.')) % self.title)
|
'You must select a %s service item.')) % self.title)
|
||||||
|
|
||||||
def buildServiceItem(self, item=None):
|
def buildServiceItem(self, item=None, xmlVersion=False):
|
||||||
"""
|
"""
|
||||||
Common method for generating a service item
|
Common method for generating a service item
|
||||||
"""
|
"""
|
||||||
service_item = ServiceItem(self.parent)
|
serviceItem = ServiceItem(self.parent)
|
||||||
if self.serviceItemIconName:
|
if self.serviceItemIconName:
|
||||||
service_item.add_icon(self.serviceItemIconName)
|
serviceItem.add_icon(self.serviceItemIconName)
|
||||||
else:
|
else:
|
||||||
service_item.add_icon(self.parent.icon_path)
|
serviceItem.add_icon(self.parent.icon_path)
|
||||||
if self.generateSlideData(service_item, item):
|
if self.generateSlideData(serviceItem, item, xmlVersion):
|
||||||
return service_item
|
return serviceItem
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -101,6 +101,7 @@ class ServiceItem(object):
|
|||||||
self.search_string = u''
|
self.search_string = u''
|
||||||
self.data_string = u''
|
self.data_string = u''
|
||||||
self.edit_id = None
|
self.edit_id = None
|
||||||
|
self.xml_version = None
|
||||||
self._new_item()
|
self._new_item()
|
||||||
|
|
||||||
def _new_item(self):
|
def _new_item(self):
|
||||||
@ -252,7 +253,8 @@ class ServiceItem(object):
|
|||||||
u'from_plugin': self.from_plugin,
|
u'from_plugin': self.from_plugin,
|
||||||
u'capabilities': self.capabilities,
|
u'capabilities': self.capabilities,
|
||||||
u'search': self.search_string,
|
u'search': self.search_string,
|
||||||
u'data': self.data_string
|
u'data': self.data_string,
|
||||||
|
u'xml_version': self.xml_version
|
||||||
}
|
}
|
||||||
service_data = []
|
service_data = []
|
||||||
if self.service_item_type == ServiceItemType.Text:
|
if self.service_item_type == ServiceItemType.Text:
|
||||||
@ -294,6 +296,8 @@ class ServiceItem(object):
|
|||||||
if u'search' in header:
|
if u'search' in header:
|
||||||
self.search_string = header[u'search']
|
self.search_string = header[u'search']
|
||||||
self.data_string = header[u'data']
|
self.data_string = header[u'data']
|
||||||
|
if u'xml_version' in header:
|
||||||
|
self.xml_version = header[u'xml_version']
|
||||||
if self.service_item_type == ServiceItemType.Text:
|
if self.service_item_type == ServiceItemType.Text:
|
||||||
for slide in serviceitem[u'serviceitem'][u'data']:
|
for slide in serviceitem[u'serviceitem'][u'data']:
|
||||||
self._raw_frames.append(slide)
|
self._raw_frames.append(slide)
|
||||||
|
@ -146,7 +146,7 @@ class AdvancedTab(SettingsTab):
|
|||||||
self.mediaPluginCheckBox.setText(translate('OpenLP.AdvancedTab',
|
self.mediaPluginCheckBox.setText(translate('OpenLP.AdvancedTab',
|
||||||
'Remember active media manager tab on startup'))
|
'Remember active media manager tab on startup'))
|
||||||
self.doubleClickLiveCheckBox.setText(translate('OpenLP.AdvancedTab',
|
self.doubleClickLiveCheckBox.setText(translate('OpenLP.AdvancedTab',
|
||||||
'Double-click to send items straight to live (requires restart)'))
|
'Double-click to send items straight to live'))
|
||||||
self.expandServiceItemCheckBox.setText(translate('OpenLP.AdvancedTab',
|
self.expandServiceItemCheckBox.setText(translate('OpenLP.AdvancedTab',
|
||||||
'Expand new service items on creation'))
|
'Expand new service items on creation'))
|
||||||
# self.sharedDirGroupBox.setTitle(
|
# self.sharedDirGroupBox.setTitle(
|
||||||
|
@ -100,7 +100,7 @@ class MainDisplay(DisplayWidget):
|
|||||||
self.screens = screens
|
self.screens = screens
|
||||||
self.isLive = live
|
self.isLive = live
|
||||||
self.alertTab = None
|
self.alertTab = None
|
||||||
self.hide_mode = None
|
self.hideMode = None
|
||||||
self.setWindowTitle(u'OpenLP Display')
|
self.setWindowTitle(u'OpenLP Display')
|
||||||
self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;')
|
self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;')
|
||||||
self.setWindowFlags(QtCore.Qt.FramelessWindowHint |
|
self.setWindowFlags(QtCore.Qt.FramelessWindowHint |
|
||||||
@ -381,8 +381,8 @@ class MainDisplay(DisplayWidget):
|
|||||||
if self.isLive:
|
if self.isLive:
|
||||||
self.setVisible(True)
|
self.setVisible(True)
|
||||||
# if was hidden keep it hidden
|
# if was hidden keep it hidden
|
||||||
if self.hide_mode and self.isLive:
|
if self.hideMode and self.isLive:
|
||||||
self.hideDisplay(self.hide_mode)
|
self.hideDisplay(self.hideMode)
|
||||||
preview = QtGui.QImage(self.screen[u'size'].width(),
|
preview = QtGui.QImage(self.screen[u'size'].width(),
|
||||||
self.screen[u'size'].height(),
|
self.screen[u'size'].height(),
|
||||||
QtGui.QImage.Format_ARGB32_Premultiplied)
|
QtGui.QImage.Format_ARGB32_Premultiplied)
|
||||||
@ -412,8 +412,8 @@ class MainDisplay(DisplayWidget):
|
|||||||
if serviceItem.foot_text and serviceItem.foot_text:
|
if serviceItem.foot_text and serviceItem.foot_text:
|
||||||
self.footer(serviceItem.foot_text)
|
self.footer(serviceItem.foot_text)
|
||||||
# if was hidden keep it hidden
|
# if was hidden keep it hidden
|
||||||
if self.hide_mode and self.isLive:
|
if self.hideMode and self.isLive:
|
||||||
self.hideDisplay(self.hide_mode)
|
self.hideDisplay(self.hideMode)
|
||||||
|
|
||||||
def footer(self, text):
|
def footer(self, text):
|
||||||
"""
|
"""
|
||||||
@ -444,7 +444,7 @@ class MainDisplay(DisplayWidget):
|
|||||||
self.setVisible(True)
|
self.setVisible(True)
|
||||||
if self.phononActive:
|
if self.phononActive:
|
||||||
self.webView.setVisible(True)
|
self.webView.setVisible(True)
|
||||||
self.hide_mode = mode
|
self.hideMode = mode
|
||||||
|
|
||||||
def showDisplay(self):
|
def showDisplay(self):
|
||||||
"""
|
"""
|
||||||
@ -459,9 +459,9 @@ class MainDisplay(DisplayWidget):
|
|||||||
if self.phononActive:
|
if self.phononActive:
|
||||||
self.webView.setVisible(False)
|
self.webView.setVisible(False)
|
||||||
self.videoPlay()
|
self.videoPlay()
|
||||||
|
self.hideMode = None
|
||||||
# Trigger actions when display is active again
|
# Trigger actions when display is active again
|
||||||
Receiver.send_message(u'maindisplay_active')
|
Receiver.send_message(u'maindisplay_active')
|
||||||
self.hide_mode = None
|
|
||||||
|
|
||||||
class AudioPlayer(QtCore.QObject):
|
class AudioPlayer(QtCore.QObject):
|
||||||
"""
|
"""
|
||||||
|
@ -789,6 +789,8 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
self.serviceName = name[len(name) - 1]
|
self.serviceName = name[len(name) - 1]
|
||||||
self.parent.addRecentFile(filename)
|
self.parent.addRecentFile(filename)
|
||||||
self.parent.serviceChanged(True, self.serviceName)
|
self.parent.serviceChanged(True, self.serviceName)
|
||||||
|
# Refresh Plugin lists
|
||||||
|
Receiver.send_message(u'plugin_list_refresh')
|
||||||
|
|
||||||
def validateItem(self, serviceItem):
|
def validateItem(self, serviceItem):
|
||||||
"""
|
"""
|
||||||
@ -1028,6 +1030,9 @@ class ServiceManager(QtGui.QWidget):
|
|||||||
# ServiceManager started the drag and drop
|
# ServiceManager started the drag and drop
|
||||||
if plugin == u'ServiceManager':
|
if plugin == u'ServiceManager':
|
||||||
startpos, startCount = self.findServiceItem()
|
startpos, startCount = self.findServiceItem()
|
||||||
|
# If no items selected
|
||||||
|
if startpos == -1:
|
||||||
|
return
|
||||||
if item is None:
|
if item is None:
|
||||||
endpos = len(self.serviceItems)
|
endpos = len(self.serviceItems)
|
||||||
else:
|
else:
|
||||||
|
@ -331,10 +331,8 @@ class SlideController(QtGui.QWidget):
|
|||||||
QtCore.QObject.connect(self.PreviewListWidget,
|
QtCore.QObject.connect(self.PreviewListWidget,
|
||||||
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected)
|
QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected)
|
||||||
if not self.isLive:
|
if not self.isLive:
|
||||||
if QtCore.QSettings().value(u'advanced/double click live',
|
|
||||||
QtCore.QVariant(False)).toBool():
|
|
||||||
QtCore.QObject.connect(self.PreviewListWidget,
|
QtCore.QObject.connect(self.PreviewListWidget,
|
||||||
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.onGoLive)
|
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'), self.onGoLiveClick)
|
||||||
if isLive:
|
if isLive:
|
||||||
QtCore.QObject.connect(Receiver.get_receiver(),
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
QtCore.SIGNAL(u'slidecontroller_live_spin_delay'),
|
QtCore.SIGNAL(u'slidecontroller_live_spin_delay'),
|
||||||
@ -391,6 +389,8 @@ class SlideController(QtGui.QWidget):
|
|||||||
if self.isLive:
|
if self.isLive:
|
||||||
QtCore.QObject.connect(self.volumeSlider,
|
QtCore.QObject.connect(self.volumeSlider,
|
||||||
QtCore.SIGNAL(u'sliderReleased()'), self.mediaVolume)
|
QtCore.SIGNAL(u'sliderReleased()'), self.mediaVolume)
|
||||||
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
|
QtCore.SIGNAL(u'maindisplay_active'), self.updatePreview)
|
||||||
|
|
||||||
def screenSizeChanged(self):
|
def screenSizeChanged(self):
|
||||||
"""
|
"""
|
||||||
@ -823,16 +823,15 @@ class SlideController(QtGui.QWidget):
|
|||||||
row)
|
row)
|
||||||
|
|
||||||
def updatePreview(self):
|
def updatePreview(self):
|
||||||
|
log.debug(u'updatePreview %s ' %self.screens.current[u'primary'])
|
||||||
if not self.screens.current[u'primary']:
|
if not self.screens.current[u'primary']:
|
||||||
# Grab now, but try again in a couple of seconds if slide change
|
# Grab now, but try again in a couple of seconds if slide change
|
||||||
# is slow
|
# is slow
|
||||||
QtCore.QTimer.singleShot(0.5, self.grabMainDisplay)
|
QtCore.QTimer.singleShot(0.5, self.grabMainDisplay)
|
||||||
QtCore.QTimer.singleShot(2.5, self.grabMainDisplay)
|
QtCore.QTimer.singleShot(2.5, self.grabMainDisplay)
|
||||||
else:
|
else:
|
||||||
label = self.PreviewListWidget.cellWidget(
|
self.SlidePreview.setPixmap(
|
||||||
self.PreviewListWidget.currentRow(), 1)
|
QtGui.QPixmap.fromImage(self.display.preview()))
|
||||||
if label:
|
|
||||||
self.SlidePreview.setPixmap(label.pixmap())
|
|
||||||
|
|
||||||
def grabMainDisplay(self):
|
def grabMainDisplay(self):
|
||||||
winid = QtGui.QApplication.desktop().winId()
|
winid = QtGui.QApplication.desktop().winId()
|
||||||
@ -944,6 +943,14 @@ class SlideController(QtGui.QWidget):
|
|||||||
Receiver.send_message(u'%s_edit' % self.serviceItem.name.lower(),
|
Receiver.send_message(u'%s_edit' % self.serviceItem.name.lower(),
|
||||||
u'P:%s' % self.serviceItem.edit_id)
|
u'P:%s' % self.serviceItem.edit_id)
|
||||||
|
|
||||||
|
def onGoLiveClick(self):
|
||||||
|
"""
|
||||||
|
triggered by clicking the Preview slide items
|
||||||
|
"""
|
||||||
|
if QtCore.QSettings().value(u'advanced/double click live',
|
||||||
|
QtCore.QVariant(False)).toBool():
|
||||||
|
self.onGoLive()
|
||||||
|
|
||||||
def onGoLive(self):
|
def onGoLive(self):
|
||||||
"""
|
"""
|
||||||
If preview copy slide item to live
|
If preview copy slide item to live
|
||||||
|
@ -651,7 +651,7 @@ class BibleMediaItem(MediaManagerItem):
|
|||||||
obj = obj.toPyObject()
|
obj = obj.toPyObject()
|
||||||
return unicode(obj)
|
return unicode(obj)
|
||||||
|
|
||||||
def generateSlideData(self, service_item, item=None):
|
def generateSlideData(self, service_item, item=None, xmlVersion=False):
|
||||||
"""
|
"""
|
||||||
Generates and formats the slides for the service item as well as the
|
Generates and formats the slides for the service item as well as the
|
||||||
service item's title.
|
service item's title.
|
||||||
|
@ -46,7 +46,6 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
|||||||
Constructor
|
Constructor
|
||||||
"""
|
"""
|
||||||
QtGui.QDialog.__init__(self, parent)
|
QtGui.QDialog.__init__(self, parent)
|
||||||
#self.parent = parent
|
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
# Connecting signals and slots
|
# Connecting signals and slots
|
||||||
self.previewButton = QtGui.QPushButton()
|
self.previewButton = QtGui.QPushButton()
|
||||||
@ -124,8 +123,9 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
|||||||
self.slideListView.addItem(slide[1])
|
self.slideListView.addItem(slide[1])
|
||||||
theme = self.customSlide.theme_name
|
theme = self.customSlide.theme_name
|
||||||
id = self.themeComboBox.findText(theme, QtCore.Qt.MatchExactly)
|
id = self.themeComboBox.findText(theme, QtCore.Qt.MatchExactly)
|
||||||
|
# No theme match
|
||||||
if id == -1:
|
if id == -1:
|
||||||
id = 0 # Not Found
|
id = 0
|
||||||
self.themeComboBox.setCurrentIndex(id)
|
self.themeComboBox.setCurrentIndex(id)
|
||||||
else:
|
else:
|
||||||
self.themeComboBox.setCurrentIndex(0)
|
self.themeComboBox.setCurrentIndex(0)
|
||||||
@ -264,7 +264,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
|
|||||||
self.titleEdit.setFocus()
|
self.titleEdit.setFocus()
|
||||||
return False, translate('CustomPlugin.EditCustomForm',
|
return False, translate('CustomPlugin.EditCustomForm',
|
||||||
'You need to type in a title.')
|
'You need to type in a title.')
|
||||||
# We must have one slide.
|
# We must have at least one slide.
|
||||||
if self.slideListView.count() == 0:
|
if self.slideListView.count() == 0:
|
||||||
return False, translate('CustomPlugin.EditCustomForm',
|
return False, translate('CustomPlugin.EditCustomForm',
|
||||||
'You need to add at least one slide')
|
'You need to add at least one slide')
|
||||||
|
@ -67,7 +67,7 @@ class EditCustomSlideForm(QtGui.QDialog, Ui_CustomSlideEditDialog):
|
|||||||
|
|
||||||
def onSplitButtonPressed(self):
|
def onSplitButtonPressed(self):
|
||||||
"""
|
"""
|
||||||
Splits a slide in two slides.
|
Adds a slide split at the cursor.
|
||||||
"""
|
"""
|
||||||
if self.slideTextEdit.textCursor().columnNumber() != 0:
|
if self.slideTextEdit.textCursor().columnNumber() != 0:
|
||||||
self.slideTextEdit.insertPlainText(u'\n')
|
self.slideTextEdit.insertPlainText(u'\n')
|
||||||
|
@ -43,6 +43,7 @@ import logging
|
|||||||
|
|
||||||
from xml.dom.minidom import Document
|
from xml.dom.minidom import Document
|
||||||
from xml.etree.ElementTree import ElementTree, XML, dump
|
from xml.etree.ElementTree import ElementTree, XML, dump
|
||||||
|
from lxml import etree, objectify
|
||||||
from xml.parsers.expat import ExpatError
|
from xml.parsers.expat import ExpatError
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -55,14 +56,14 @@ class CustomXMLBuilder(object):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""
|
"""
|
||||||
Set up the song builder.
|
Set up the custom builder.
|
||||||
"""
|
"""
|
||||||
# Create the minidom document
|
# Create the minidom document
|
||||||
self.custom_xml = Document()
|
self.custom_xml = Document()
|
||||||
|
|
||||||
def new_document(self):
|
def new_document(self):
|
||||||
"""
|
"""
|
||||||
Create a new song XML document.
|
Create a new custom XML document.
|
||||||
"""
|
"""
|
||||||
# Create the <song> base element
|
# Create the <song> base element
|
||||||
self.song = self.custom_xml.createElement(u'song')
|
self.song = self.custom_xml.createElement(u'song')
|
||||||
@ -72,7 +73,7 @@ class CustomXMLBuilder(object):
|
|||||||
def add_lyrics_to_song(self):
|
def add_lyrics_to_song(self):
|
||||||
"""
|
"""
|
||||||
Set up and add a ``<lyrics>`` tag which contains the lyrics of the
|
Set up and add a ``<lyrics>`` tag which contains the lyrics of the
|
||||||
song.
|
custom item.
|
||||||
"""
|
"""
|
||||||
# Create the main <lyrics> element
|
# Create the main <lyrics> element
|
||||||
self.lyrics = self.custom_xml.createElement(u'lyrics')
|
self.lyrics = self.custom_xml.createElement(u'lyrics')
|
||||||
@ -93,7 +94,6 @@ class CustomXMLBuilder(object):
|
|||||||
``content``
|
``content``
|
||||||
The actual text of the verse to be stored.
|
The actual text of the verse to be stored.
|
||||||
"""
|
"""
|
||||||
#log.debug(u'add_verse_to_lyrics %s, %s\n%s' % (type, number, content))
|
|
||||||
verse = self.custom_xml.createElement(u'verse')
|
verse = self.custom_xml.createElement(u'verse')
|
||||||
verse.setAttribute(u'type', type)
|
verse.setAttribute(u'type', type)
|
||||||
verse.setAttribute(u'label', number)
|
verse.setAttribute(u'label', number)
|
||||||
@ -102,7 +102,7 @@ class CustomXMLBuilder(object):
|
|||||||
cds = self.custom_xml.createCDATASection(content)
|
cds = self.custom_xml.createCDATASection(content)
|
||||||
verse.appendChild(cds)
|
verse.appendChild(cds)
|
||||||
|
|
||||||
def dump_xml(self):
|
def _dump_xml(self):
|
||||||
"""
|
"""
|
||||||
Debugging aid to dump XML so that we can see what we have.
|
Debugging aid to dump XML so that we can see what we have.
|
||||||
"""
|
"""
|
||||||
@ -110,29 +110,30 @@ class CustomXMLBuilder(object):
|
|||||||
|
|
||||||
def extract_xml(self):
|
def extract_xml(self):
|
||||||
"""
|
"""
|
||||||
Extract our newly created XML song.
|
Extract our newly created XML custom.
|
||||||
"""
|
"""
|
||||||
return self.custom_xml.toxml(u'utf-8')
|
return self.custom_xml.toxml(u'utf-8')
|
||||||
|
|
||||||
|
|
||||||
class CustomXMLParser(object):
|
class CustomXMLParser(object):
|
||||||
"""
|
"""
|
||||||
A class to read in and parse a song's XML.
|
A class to read in and parse a custom's XML.
|
||||||
"""
|
"""
|
||||||
log.info(u'CustomXMLParser Loaded')
|
log.info(u'CustomXMLParser Loaded')
|
||||||
|
|
||||||
def __init__(self, xml):
|
def __init__(self, xml):
|
||||||
"""
|
"""
|
||||||
Set up our song XML parser.
|
Set up our custom XML parser.
|
||||||
|
|
||||||
``xml``
|
``xml``
|
||||||
The XML of the song to be parsed.
|
The XML of the custom to be parsed.
|
||||||
"""
|
"""
|
||||||
self.custom_xml = None
|
self.custom_xml = None
|
||||||
|
if xml[:5] == u'<?xml':
|
||||||
|
xml = xml[38:]
|
||||||
try:
|
try:
|
||||||
self.custom_xml = ElementTree(
|
self.custom_xml = objectify.fromstring(xml)
|
||||||
element=XML(unicode(xml).encode('unicode-escape')))
|
except etree.XMLSyntaxError:
|
||||||
except ExpatError:
|
|
||||||
log.exception(u'Invalid xml %s', xml)
|
log.exception(u'Invalid xml %s', xml)
|
||||||
|
|
||||||
def get_verses(self):
|
def get_verses(self):
|
||||||
@ -146,11 +147,10 @@ class CustomXMLParser(object):
|
|||||||
if element.tag == u'verse':
|
if element.tag == u'verse':
|
||||||
if element.text is None:
|
if element.text is None:
|
||||||
element.text = u''
|
element.text = u''
|
||||||
verse_list.append([element.attrib,
|
verse_list.append([element.attrib, unicode(element.text)])
|
||||||
unicode(element.text).decode('unicode-escape')])
|
|
||||||
return verse_list
|
return verse_list
|
||||||
|
|
||||||
def dump_xml(self):
|
def _dump_xml(self):
|
||||||
"""
|
"""
|
||||||
Debugging aid to dump XML so that we can see what we have.
|
Debugging aid to dump XML so that we can see what we have.
|
||||||
"""
|
"""
|
||||||
|
@ -74,9 +74,9 @@ class CustomMediaItem(MediaManagerItem):
|
|||||||
def initialise(self):
|
def initialise(self):
|
||||||
self.loadCustomListView(self.manager.get_all_objects(
|
self.loadCustomListView(self.manager.get_all_objects(
|
||||||
CustomSlide, order_by_ref=CustomSlide.title))
|
CustomSlide, order_by_ref=CustomSlide.title))
|
||||||
#Called to redisplay the song list screen edith from a search
|
# Called to redisplay the custom list screen edith from a search
|
||||||
#or from the exit of the Song edit dialog. If remote editing is active
|
# or from the exit of the Custom edit dialog. If remote editing is
|
||||||
#Trigger it and clean up so it will not update again.
|
# active trigger it and clean up so it will not update again.
|
||||||
if self.remoteTriggered == u'L':
|
if self.remoteTriggered == u'L':
|
||||||
self.onAddClick()
|
self.onAddClick()
|
||||||
if self.remoteTriggered == u'P':
|
if self.remoteTriggered == u'P':
|
||||||
@ -144,7 +144,7 @@ class CustomMediaItem(MediaManagerItem):
|
|||||||
for row in row_list:
|
for row in row_list:
|
||||||
self.listView.takeItem(row)
|
self.listView.takeItem(row)
|
||||||
|
|
||||||
def generateSlideData(self, service_item, item=None):
|
def generateSlideData(self, service_item, item=None, xmlVersion=False):
|
||||||
raw_slides = []
|
raw_slides = []
|
||||||
raw_footer = []
|
raw_footer = []
|
||||||
slide = None
|
slide = None
|
||||||
|
@ -154,7 +154,7 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
|
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
|
||||||
self.listView.addItem(item_name)
|
self.listView.addItem(item_name)
|
||||||
|
|
||||||
def generateSlideData(self, service_item, item=None):
|
def generateSlideData(self, service_item, item=None, xmlVersion=False):
|
||||||
items = self.listView.selectedIndexes()
|
items = self.listView.selectedIndexes()
|
||||||
if items:
|
if items:
|
||||||
service_item.title = unicode(
|
service_item.title = unicode(
|
||||||
@ -163,6 +163,8 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
service_item.add_capability(ItemCapabilities.AllowsPreview)
|
service_item.add_capability(ItemCapabilities.AllowsPreview)
|
||||||
service_item.add_capability(ItemCapabilities.AllowsLoop)
|
service_item.add_capability(ItemCapabilities.AllowsLoop)
|
||||||
service_item.add_capability(ItemCapabilities.AllowsAdditions)
|
service_item.add_capability(ItemCapabilities.AllowsAdditions)
|
||||||
|
# force a nonexistent theme
|
||||||
|
service_item.theme = -1
|
||||||
for item in items:
|
for item in items:
|
||||||
bitem = self.listView.item(item.row())
|
bitem = self.listView.item(item.row())
|
||||||
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
|
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
|
||||||
|
@ -116,7 +116,7 @@ class MediaMediaItem(MediaManagerItem):
|
|||||||
self.parent.liveController.display.video(filename, 0, True)
|
self.parent.liveController.display.video(filename, 0, True)
|
||||||
self.resetButton.setVisible(True)
|
self.resetButton.setVisible(True)
|
||||||
|
|
||||||
def generateSlideData(self, service_item, item=None):
|
def generateSlideData(self, service_item, item=None, xmlVersion=False):
|
||||||
if item is None:
|
if item is None:
|
||||||
item = self.listView.currentItem()
|
item = self.listView.currentItem()
|
||||||
if item is None:
|
if item is None:
|
||||||
|
@ -238,7 +238,7 @@ class PresentationMediaItem(MediaManagerItem):
|
|||||||
SettingsManager.set_list(self.settingsSection,
|
SettingsManager.set_list(self.settingsSection,
|
||||||
self.settingsSection, self.getFileList())
|
self.settingsSection, self.getFileList())
|
||||||
|
|
||||||
def generateSlideData(self, service_item, item=None):
|
def generateSlideData(self, service_item, item=None, xmlVersion=False):
|
||||||
"""
|
"""
|
||||||
Load the relevant information for displaying the presentation
|
Load the relevant information for displaying the presentation
|
||||||
in the slidecontroller. In the case of powerpoints, an image
|
in the slidecontroller. In the case of powerpoints, an image
|
||||||
|
@ -152,6 +152,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
|
|
||||||
def newSong(self):
|
def newSong(self):
|
||||||
log.debug(u'New Song')
|
log.debug(u'New Song')
|
||||||
|
self.initialise()
|
||||||
self.SongTabWidget.setCurrentIndex(0)
|
self.SongTabWidget.setCurrentIndex(0)
|
||||||
self.TitleEditItem.setText(u'')
|
self.TitleEditItem.setText(u'')
|
||||||
self.AlternativeEdit.setText(u'')
|
self.AlternativeEdit.setText(u'')
|
||||||
@ -170,8 +171,18 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
# it's a new song to preview is not possible
|
# it's a new song to preview is not possible
|
||||||
self.previewButton.setVisible(False)
|
self.previewButton.setVisible(False)
|
||||||
|
|
||||||
def loadSong(self, id, preview):
|
def loadSong(self, id, preview=False):
|
||||||
|
"""
|
||||||
|
Loads a song.
|
||||||
|
|
||||||
|
``id``
|
||||||
|
The song id (int).
|
||||||
|
|
||||||
|
``preview``
|
||||||
|
Should be ``True`` if the song is also previewed (boolean).
|
||||||
|
"""
|
||||||
log.debug(u'Load Song')
|
log.debug(u'Load Song')
|
||||||
|
self.initialise()
|
||||||
self.SongTabWidget.setCurrentIndex(0)
|
self.SongTabWidget.setCurrentIndex(0)
|
||||||
self.loadAuthors()
|
self.loadAuthors()
|
||||||
self.loadTopics()
|
self.loadTopics()
|
||||||
@ -594,6 +605,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
"""
|
"""
|
||||||
Save and Preview button pressed.
|
Save and Preview button pressed.
|
||||||
The Song is valid so as the plugin to add it to preview to see.
|
The Song is valid so as the plugin to add it to preview to see.
|
||||||
|
|
||||||
|
``button``
|
||||||
|
A button (QPushButton).
|
||||||
"""
|
"""
|
||||||
log.debug(u'onPreview')
|
log.debug(u'onPreview')
|
||||||
if unicode(button.objectName()) == u'previewButton' and \
|
if unicode(button.objectName()) == u'previewButton' and \
|
||||||
@ -631,13 +645,16 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
database.
|
database.
|
||||||
|
|
||||||
``preview``
|
``preview``
|
||||||
Should be True if song is also previewed.
|
Should be ``True`` if the song is also previewed (boolean).
|
||||||
"""
|
"""
|
||||||
self.song.title = unicode(self.TitleEditItem.text())
|
self.song.title = unicode(self.TitleEditItem.text())
|
||||||
self.song.alternate_title = unicode(self.AlternativeEdit.text())
|
self.song.alternate_title = unicode(self.AlternativeEdit.text())
|
||||||
self.song.copyright = unicode(self.CopyrightEditItem.text())
|
self.song.copyright = unicode(self.CopyrightEditItem.text())
|
||||||
|
if self.song.alternate_title:
|
||||||
self.song.search_title = self.song.title + u'@' + \
|
self.song.search_title = self.song.title + u'@' + \
|
||||||
self.song.alternate_title
|
self.song.alternate_title
|
||||||
|
else:
|
||||||
|
self.song.search_title = self.song.title
|
||||||
self.song.comments = unicode(self.CommentsEdit.toPlainText())
|
self.song.comments = unicode(self.CommentsEdit.toPlainText())
|
||||||
self.song.verse_order = unicode(self.VerseOrderEdit.text())
|
self.song.verse_order = unicode(self.VerseOrderEdit.text())
|
||||||
self.song.ccli_number = unicode(self.CCLNumberEdit.text())
|
self.song.ccli_number = unicode(self.CCLNumberEdit.text())
|
||||||
@ -648,6 +665,11 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
Book.name == book_name)
|
Book.name == book_name)
|
||||||
else:
|
else:
|
||||||
self.song.book = None
|
self.song.book = None
|
||||||
|
theme_name = unicode(self.ThemeSelectionComboItem.currentText())
|
||||||
|
if theme_name:
|
||||||
|
self.song.theme_name = theme_name
|
||||||
|
else:
|
||||||
|
self.song.theme_name = None
|
||||||
if self._validate_song():
|
if self._validate_song():
|
||||||
self.processLyrics()
|
self.processLyrics()
|
||||||
self.processTitle()
|
self.processTitle()
|
||||||
|
@ -62,6 +62,36 @@ class VerseType(object):
|
|||||||
elif verse_type == VerseType.Other:
|
elif verse_type == VerseType.Other:
|
||||||
return translate('SongsPlugin.VerseType', 'Other')
|
return translate('SongsPlugin.VerseType', 'Other')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def expand_string(verse_type):
|
||||||
|
"""
|
||||||
|
Return the VerseType for a given string
|
||||||
|
|
||||||
|
``verse_type``
|
||||||
|
The string to return a VerseType for
|
||||||
|
"""
|
||||||
|
verse_type = verse_type.lower()
|
||||||
|
if verse_type == unicode(VerseType.to_string(VerseType.Verse)).lower()[0]:
|
||||||
|
return translate('SongsPlugin.VerseType', 'Verse')
|
||||||
|
elif verse_type == \
|
||||||
|
unicode(VerseType.to_string(VerseType.Chorus)).lower()[0]:
|
||||||
|
return translate('SongsPlugin.VerseType', 'Chorus')
|
||||||
|
elif verse_type == \
|
||||||
|
unicode(VerseType.to_string(VerseType.Bridge)).lower()[0]:
|
||||||
|
return translate('SongsPlugin.VerseType', 'Bridge')
|
||||||
|
elif verse_type == \
|
||||||
|
unicode(VerseType.to_string(VerseType.PreChorus)).lower()[0]:
|
||||||
|
return translate('SongsPlugin.VerseType', 'PreChorus')
|
||||||
|
elif verse_type == \
|
||||||
|
unicode(VerseType.to_string(VerseType.Intro)).lower()[0]:
|
||||||
|
return translate('SongsPlugin.VerseType', 'Intro')
|
||||||
|
elif verse_type == \
|
||||||
|
unicode(VerseType.to_string(VerseType.Ending)).lower()[0]:
|
||||||
|
return translate('SongsPlugin.VerseType', 'Ending')
|
||||||
|
elif verse_type == \
|
||||||
|
unicode(VerseType.to_string(VerseType.Other)).lower()[0]:
|
||||||
|
return translate('SongsPlugin.VerseType', 'Other')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_string(verse_type):
|
def from_string(verse_type):
|
||||||
"""
|
"""
|
||||||
@ -92,7 +122,6 @@ class VerseType(object):
|
|||||||
unicode(VerseType.to_string(VerseType.Other)).lower():
|
unicode(VerseType.to_string(VerseType.Other)).lower():
|
||||||
return VerseType.Other
|
return VerseType.Other
|
||||||
|
|
||||||
|
from xml import LyricsXML, SongXMLBuilder, SongXMLParser, OpenLyricsParser
|
||||||
from xml import LyricsXML, SongXMLBuilder, SongXMLParser
|
|
||||||
from songstab import SongsTab
|
from songstab import SongsTab
|
||||||
from mediaitem import SongMediaItem
|
from mediaitem import SongMediaItem
|
||||||
|
@ -32,7 +32,7 @@ from openlp.core.lib import MediaManagerItem, BaseListWithDnD, Receiver, \
|
|||||||
ItemCapabilities, translate, check_item_selected
|
ItemCapabilities, translate, check_item_selected
|
||||||
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
|
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
|
||||||
SongImportForm
|
SongImportForm
|
||||||
from openlp.plugins.songs.lib import SongXMLParser
|
from openlp.plugins.songs.lib import SongXMLParser, OpenLyricsParser
|
||||||
from openlp.plugins.songs.lib.db import Author, Song
|
from openlp.plugins.songs.lib.db import Author, Song
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -53,8 +53,8 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
self.ListViewWithDnD_class = SongListView
|
self.ListViewWithDnD_class = SongListView
|
||||||
MediaManagerItem.__init__(self, parent, self, icon)
|
MediaManagerItem.__init__(self, parent, self, icon)
|
||||||
self.edit_song_form = EditSongForm(self, self.parent.manager)
|
self.edit_song_form = EditSongForm(self, self.parent.manager)
|
||||||
|
self.openLyrics = OpenLyricsParser(self.parent.manager)
|
||||||
self.singleServiceItem = False
|
self.singleServiceItem = False
|
||||||
#self.edit_song_form = EditSongForm(self.parent.manager, self)
|
|
||||||
self.song_maintenance_form = SongMaintenanceForm(
|
self.song_maintenance_form = SongMaintenanceForm(
|
||||||
self.parent.manager, self)
|
self.parent.manager, self)
|
||||||
# Holds information about whether the edit is remotly triggered and
|
# Holds information about whether the edit is remotly triggered and
|
||||||
@ -114,6 +114,8 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
self.SearchButtonLayout.addWidget(self.ClearTextButton)
|
self.SearchButtonLayout.addWidget(self.ClearTextButton)
|
||||||
self.pageLayout.addLayout(self.SearchButtonLayout)
|
self.pageLayout.addLayout(self.SearchButtonLayout)
|
||||||
# Signals and slots
|
# Signals and slots
|
||||||
|
QtCore.QObject.connect(Receiver.get_receiver(),
|
||||||
|
QtCore.SIGNAL(u'plugin_list_refresh'), self.onSearchTextButtonClick)
|
||||||
QtCore.QObject.connect(self.SearchTextEdit,
|
QtCore.QObject.connect(self.SearchTextEdit,
|
||||||
QtCore.SIGNAL(u'returnPressed()'), self.onSearchTextButtonClick)
|
QtCore.SIGNAL(u'returnPressed()'), self.onSearchTextButtonClick)
|
||||||
QtCore.QObject.connect(self.SearchTextButton,
|
QtCore.QObject.connect(self.SearchTextButton,
|
||||||
@ -141,7 +143,7 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
self.updateServiceOnEdit = QtCore.QSettings().value(
|
self.updateServiceOnEdit = QtCore.QSettings().value(
|
||||||
self.settingsSection + u'/update service on edit',
|
self.settingsSection + u'/update service on edit',
|
||||||
QtCore.QVariant(u'False')).toBool()
|
QtCore.QVariant(u'False')).toBool()
|
||||||
self.AddSongFromServide = QtCore.QSettings().value(
|
self.addSongFromService = QtCore.QSettings().value(
|
||||||
self.settingsSection + u'/add song from service',
|
self.settingsSection + u'/add song from service',
|
||||||
QtCore.QVariant(u'True')).toBool()
|
QtCore.QVariant(u'True')).toBool()
|
||||||
|
|
||||||
@ -328,7 +330,7 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
self.parent.manager.delete_object(Song, item_id)
|
self.parent.manager.delete_object(Song, item_id)
|
||||||
self.onSearchTextButtonClick()
|
self.onSearchTextButtonClick()
|
||||||
|
|
||||||
def generateSlideData(self, service_item, item=None):
|
def generateSlideData(self, service_item, item=None, xmlVersion=False):
|
||||||
log.debug(u'generateSlideData (%s:%s)' % (service_item, item))
|
log.debug(u'generateSlideData (%s:%s)' % (service_item, item))
|
||||||
raw_footer = []
|
raw_footer = []
|
||||||
author_list = u''
|
author_list = u''
|
||||||
@ -397,6 +399,7 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
]
|
]
|
||||||
service_item.data_string = {u'title':song.search_title,
|
service_item.data_string = {u'title':song.search_title,
|
||||||
u'authors':author_list}
|
u'authors':author_list}
|
||||||
|
service_item.xml_version = self.openLyrics.song_to_xml(song)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def serviceLoad(self, item):
|
def serviceLoad(self, item):
|
||||||
@ -406,21 +409,31 @@ class SongMediaItem(MediaManagerItem):
|
|||||||
log.debug(u'serviceLoad')
|
log.debug(u'serviceLoad')
|
||||||
if item.data_string:
|
if item.data_string:
|
||||||
search_results = self.parent.manager.get_all_objects(Song,
|
search_results = self.parent.manager.get_all_objects(Song,
|
||||||
Song.search_title.like(u'%' +
|
Song.search_title ==
|
||||||
item.data_string[u'title'].split(u'@')[0] + u'%'),
|
item.data_string[u'title'].split(u'@')[0].lower() ,
|
||||||
Song.search_title.asc())
|
Song.search_title.asc())
|
||||||
author_list = item.data_string[u'authors'].split(u', ')
|
author_list = item.data_string[u'authors'].split(u', ')
|
||||||
editId = 0
|
editId = 0
|
||||||
uuid = 0
|
uuid = item._uuid
|
||||||
if search_results:
|
if search_results:
|
||||||
for song in search_results:
|
for song in search_results:
|
||||||
count = 0
|
count = 0
|
||||||
for author in song.authors:
|
for author in song.authors:
|
||||||
if author.display_name in author_list:
|
if author.display_name in author_list:
|
||||||
count += 1
|
count += 1
|
||||||
|
# All Authors the same
|
||||||
if count == len(author_list):
|
if count == len(author_list):
|
||||||
editId = song.id
|
editId = song.id
|
||||||
uuid = item._uuid
|
else:
|
||||||
|
# Authors different
|
||||||
|
if self.addSongFromService:
|
||||||
|
editId = self.openLyrics. \
|
||||||
|
xml_to_song(item.xml_version)
|
||||||
|
else:
|
||||||
|
# Title does not match
|
||||||
|
if self.addSongFromService:
|
||||||
|
editId = self.openLyrics.xml_to_song(item.xml_version)
|
||||||
|
# Update service with correct song id
|
||||||
if editId != 0:
|
if editId != 0:
|
||||||
Receiver.send_message(u'service_item_update',
|
Receiver.send_message(u'service_item_update',
|
||||||
u'%s:%s' %(editId, uuid))
|
u'%s:%s' %(editId, uuid))
|
||||||
|
@ -39,8 +39,11 @@ The basic XML is of the format::
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
|
|
||||||
from lxml import etree, objectify
|
from lxml import etree, objectify
|
||||||
|
from openlp.plugins.songs.lib import VerseType
|
||||||
|
from openlp.plugins.songs.lib.db import Author, Song
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -77,7 +80,6 @@ class SongXMLBuilder(object):
|
|||||||
``content``
|
``content``
|
||||||
The actual text of the verse to be stored.
|
The actual text of the verse to be stored.
|
||||||
"""
|
"""
|
||||||
# log.debug(u'add_verse_to_lyrics %s, %s\n%s' % (type, number, content))
|
|
||||||
verse = etree.Element(u'verse', type = unicode(type),
|
verse = etree.Element(u'verse', type = unicode(type),
|
||||||
label = unicode(number))
|
label = unicode(number))
|
||||||
verse.text = etree.CDATA(content)
|
verse.text = etree.CDATA(content)
|
||||||
@ -239,3 +241,153 @@ class LyricsXML(object):
|
|||||||
song_output = u'<?xml version="1.0" encoding="UTF-8"?>' + \
|
song_output = u'<?xml version="1.0" encoding="UTF-8"?>' + \
|
||||||
u'<song version="1.0">%s</song>' % lyrics_output
|
u'<song version="1.0">%s</song>' % lyrics_output
|
||||||
return song_output
|
return song_output
|
||||||
|
|
||||||
|
|
||||||
|
class OpenLyricsParser(object):
|
||||||
|
"""
|
||||||
|
This class represents the converter for Song to/from OpenLyrics XML.
|
||||||
|
"""
|
||||||
|
def __init__(self, manager):
|
||||||
|
self.manager = manager
|
||||||
|
|
||||||
|
def song_to_xml(self, song):
|
||||||
|
"""
|
||||||
|
Convert the song to OpenLyrics Format
|
||||||
|
"""
|
||||||
|
song_xml_parser = SongXMLParser(song.lyrics)
|
||||||
|
verse_list = song_xml_parser.get_verses()
|
||||||
|
song_xml = objectify.fromstring(
|
||||||
|
u'<song version="0.7" createdIn="OpenLP 2.0"/>')
|
||||||
|
properties = etree.SubElement(song_xml, u'properties')
|
||||||
|
titles = etree.SubElement(properties, u'titles')
|
||||||
|
self._add_text_to_element(u'title', titles, song.title)
|
||||||
|
if song.alternate_title:
|
||||||
|
self._add_text_to_element(u'title', titles, song.alternate_title)
|
||||||
|
if song.theme_name:
|
||||||
|
themes = etree.SubElement(properties, u'themes')
|
||||||
|
self._add_text_to_element(u'theme', themes, song.theme_name)
|
||||||
|
self._add_text_to_element(u'copyright', properties, song.copyright)
|
||||||
|
self._add_text_to_element(u'verseOrder', properties, song.verse_order)
|
||||||
|
if song.ccli_number:
|
||||||
|
self._add_text_to_element(u'ccliNo', properties, song.ccli_number)
|
||||||
|
authors = etree.SubElement(properties, u'authors')
|
||||||
|
for author in song.authors:
|
||||||
|
self._add_text_to_element(u'author', authors, author.display_name)
|
||||||
|
lyrics = etree.SubElement(song_xml, u'lyrics')
|
||||||
|
for verse in verse_list:
|
||||||
|
verse_tag = u'%s%s' % (
|
||||||
|
verse[0][u'type'][0].lower(), verse[0][u'label'])
|
||||||
|
element = \
|
||||||
|
self._add_text_to_element(u'verse', lyrics, None, verse_tag)
|
||||||
|
element = self._add_text_to_element(u'lines', element)
|
||||||
|
for line in unicode(verse[1]).split(u'\n'):
|
||||||
|
self._add_text_to_element(u'line', element, line)
|
||||||
|
return self._extract_xml(song_xml)
|
||||||
|
|
||||||
|
def xml_to_song(self, xml):
|
||||||
|
"""
|
||||||
|
Create a Song from OpenLyrics format xml
|
||||||
|
"""
|
||||||
|
# No xml get out of here
|
||||||
|
if not xml:
|
||||||
|
return 0
|
||||||
|
song = Song()
|
||||||
|
if xml[:5] == u'<?xml':
|
||||||
|
xml = xml[38:]
|
||||||
|
song_xml = objectify.fromstring(xml)
|
||||||
|
properties = song_xml.properties
|
||||||
|
song.copyright = unicode(properties.copyright.text)
|
||||||
|
song.verse_order = unicode(properties.verseOrder.text)
|
||||||
|
if song.verse_order == u'None':
|
||||||
|
song.verse_order = u''
|
||||||
|
song.topics = []
|
||||||
|
song.book = None
|
||||||
|
theme_name = None
|
||||||
|
try:
|
||||||
|
song.ccli_number = unicode(properties.ccliNo.text)
|
||||||
|
except:
|
||||||
|
song.ccli_number = u''
|
||||||
|
try:
|
||||||
|
theme_name = unicode(properties.themes.theme)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
if theme_name:
|
||||||
|
song.theme_name = theme_name
|
||||||
|
else:
|
||||||
|
song.theme_name = u''
|
||||||
|
# Process Titles
|
||||||
|
for title in properties.titles.title:
|
||||||
|
if not song.title:
|
||||||
|
song.title = unicode(title.text)
|
||||||
|
song.search_title = unicode(song.title)
|
||||||
|
song.alternate_title = u''
|
||||||
|
else:
|
||||||
|
song.alternate_title = unicode(title.text)
|
||||||
|
song.search_title += u'@' + song.alternate_title
|
||||||
|
song.search_title = re.sub(r'[\'"`,;:(){}?]+', u'',
|
||||||
|
unicode(song.search_title)).lower()
|
||||||
|
# Process Lyrics
|
||||||
|
sxml = SongXMLBuilder()
|
||||||
|
search_text = u''
|
||||||
|
for lyrics in song_xml.lyrics:
|
||||||
|
for verse in song_xml.lyrics.verse:
|
||||||
|
text = u''
|
||||||
|
for line in verse.lines.line:
|
||||||
|
line = unicode(line)
|
||||||
|
if not text:
|
||||||
|
text = line
|
||||||
|
else:
|
||||||
|
text += u'\n' + line
|
||||||
|
type = VerseType.expand_string(verse.attrib[u'name'][0])
|
||||||
|
sxml.add_verse_to_lyrics(type, verse.attrib[u'name'][1], text)
|
||||||
|
search_text = search_text + text
|
||||||
|
song.search_lyrics = search_text.lower()
|
||||||
|
song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
|
||||||
|
song.comments = u''
|
||||||
|
song.song_number = u''
|
||||||
|
# Process Authors
|
||||||
|
for author in properties.authors.author:
|
||||||
|
self._process_author(author.text, song)
|
||||||
|
self.manager.save_object(song)
|
||||||
|
return song.id
|
||||||
|
|
||||||
|
def _add_text_to_element(self, tag, parent, text=None, label=None):
|
||||||
|
if label:
|
||||||
|
element = etree.Element(tag, name = unicode(label))
|
||||||
|
else:
|
||||||
|
element = etree.Element(tag)
|
||||||
|
if text:
|
||||||
|
element.text = unicode(text)
|
||||||
|
parent.append(element)
|
||||||
|
return element
|
||||||
|
|
||||||
|
def _dump_xml(self, xml):
|
||||||
|
"""
|
||||||
|
Debugging aid to dump XML so that we can see what we have.
|
||||||
|
"""
|
||||||
|
return etree.tostring(xml, encoding=u'UTF-8',
|
||||||
|
xml_declaration=True, pretty_print=True)
|
||||||
|
|
||||||
|
def _extract_xml(self, xml):
|
||||||
|
"""
|
||||||
|
Extract our newly created XML song.
|
||||||
|
"""
|
||||||
|
return etree.tostring(xml, encoding=u'UTF-8',
|
||||||
|
xml_declaration=True)
|
||||||
|
|
||||||
|
def _process_author(self, name, song):
|
||||||
|
"""
|
||||||
|
Find or create an Author from display_name.
|
||||||
|
"""
|
||||||
|
name = unicode(name)
|
||||||
|
author = self.manager.get_object_filtered(Author,
|
||||||
|
Author.display_name == name)
|
||||||
|
if author:
|
||||||
|
# should only be one! so take the first
|
||||||
|
song.authors.append(author)
|
||||||
|
else:
|
||||||
|
# Need a new author
|
||||||
|
new_author = Author.populate(first_name=name.rsplit(u' ', 1)[0],
|
||||||
|
last_name=name.rsplit(u' ', 1)[1], display_name=name)
|
||||||
|
self.manager.save_object(new_author)
|
||||||
|
song.authors.append(new_author)
|
||||||
|