[merge] merge with trunk r1739

This commit is contained in:
Martin Zibricky 2011-09-05 12:48:46 +02:00
commit bd503f5c7d
46 changed files with 8458 additions and 6481 deletions

View File

@ -27,3 +27,9 @@
""" """
The :mod:`openlp` module contains all the project produced OpenLP functionality The :mod:`openlp` module contains all the project produced OpenLP functionality
""" """
import core
import plugins
__all__ = [u'core', u'plugins']

View File

@ -25,7 +25,12 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
__all__ = ('OpenLP', 'main') """
The :mod:`core` module provides all core application functions
All the core functions of the OpenLP application including the GUI, settings,
logging and a plugin framework are contained within the openlp.core module.
"""
import os import os
import sys import sys
@ -46,16 +51,11 @@ from openlp.core.ui import SplashScreen, ScreenList
from openlp.core.utils import AppLocation, LanguageManager, VersionThread, \ from openlp.core.utils import AppLocation, LanguageManager, VersionThread, \
get_application_version, DelayStartThread get_application_version, DelayStartThread
__all__ = [u'OpenLP', u'main']
log = logging.getLogger() log = logging.getLogger()
"""
The :mod:`core` module provides all core application functions
All the core functions of the OpenLP application including the GUI, settings,
logging and a plugin framework are contained within the openlp.core module.
"""
application_stylesheet = u""" application_stylesheet = u"""
QMainWindow::separator QMainWindow::separator
{ {

View File

@ -36,6 +36,13 @@ from PyQt4 import QtCore, QtGui
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class MediaType(object):
"""
An enumeration class for types of media.
"""
Audio = 1
Video = 2
def translate(context, text, comment=None, def translate(context, text, comment=None,
encoding=QtCore.QCoreApplication.CodecForTr, n=-1, encoding=QtCore.QCoreApplication.CodecForTr, n=-1,
translate=QtCore.QCoreApplication.translate): translate=QtCore.QCoreApplication.translate):
@ -241,9 +248,7 @@ from settingsmanager import SettingsManager
from plugin import PluginStatus, StringContent, Plugin from plugin import PluginStatus, StringContent, Plugin
from pluginmanager import PluginManager from pluginmanager import PluginManager
from settingstab import SettingsTab from settingstab import SettingsTab
from serviceitem import ServiceItem from serviceitem import ServiceItem, ServiceItemType, ItemCapabilities
from serviceitem import ServiceItemType
from serviceitem import ItemCapabilities
from htmlbuilder import build_html, build_lyrics_format_css, \ from htmlbuilder import build_html, build_lyrics_format_css, \
build_lyrics_outline_css build_lyrics_outline_css
from toolbar import OpenLPToolbar from toolbar import OpenLPToolbar

View File

@ -82,7 +82,7 @@ def upgrade_db(url, upgrade):
load_changes = True load_changes = True
try: try:
tables = upgrade.upgrade_setup(metadata) tables = upgrade.upgrade_setup(metadata)
except SQLAlchemyError, DBAPIError: except (SQLAlchemyError, DBAPIError):
load_changes = False load_changes = False
metadata_table = Table(u'metadata', metadata, metadata_table = Table(u'metadata', metadata,
Column(u'key', types.Unicode(64), primary_key=True), Column(u'key', types.Unicode(64), primary_key=True),
@ -106,7 +106,7 @@ def upgrade_db(url, upgrade):
getattr(upgrade, u'upgrade_%d' % version) \ getattr(upgrade, u'upgrade_%d' % version) \
(session, metadata, tables) (session, metadata, tables)
version_meta.value = unicode(version) version_meta.value = unicode(version)
except SQLAlchemyError, DBAPIError: except (SQLAlchemyError, DBAPIError):
log.exception(u'Could not run database upgrade script ' log.exception(u'Could not run database upgrade script '
'"upgrade_%s", upgrade process has been halted.', version) '"upgrade_%s", upgrade process has been halted.', version)
break break
@ -221,7 +221,8 @@ class Manager(object):
return return
try: try:
self.session = init_schema(self.db_url) self.session = init_schema(self.db_url)
except: except (SQLAlchemyError, DBAPIError):
log.exception(u'Error loading database: %s', self.db_url)
critical_error_message_box( critical_error_message_box(
translate('OpenLP.Manager', 'Database Error'), translate('OpenLP.Manager', 'Database Error'),
unicode(translate('OpenLP.Manager', 'OpenLP cannot load your ' unicode(translate('OpenLP.Manager', 'OpenLP cannot load your '

View File

@ -111,7 +111,7 @@ class MediaManagerItem(QtGui.QWidget):
self.requiredIcons() self.requiredIcons()
self.setupUi() self.setupUi()
self.retranslateUi() self.retranslateUi()
self.auto_select_id = -1 self.autoSelectId = -1
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_service_load' % self.plugin.name), QtCore.SIGNAL(u'%s_service_load' % self.plugin.name),
self.serviceLoad) self.serviceLoad)
@ -506,7 +506,7 @@ class MediaManagerItem(QtGui.QWidget):
if QtCore.QSettings().value(u'advanced/single click preview', if QtCore.QSettings().value(u'advanced/single click preview',
QtCore.QVariant(False)).toBool() and self.quickPreviewAllowed \ QtCore.QVariant(False)).toBool() and self.quickPreviewAllowed \
and self.listView.selectedIndexes() \ and self.listView.selectedIndexes() \
and self.auto_select_id == -1: and self.autoSelectId == -1:
self.onPreviewClick(True) self.onPreviewClick(True)
def onPreviewClick(self, keepFocus=False): def onPreviewClick(self, keepFocus=False):
@ -626,7 +626,7 @@ class MediaManagerItem(QtGui.QWidget):
""" """
pass pass
def check_search_result(self): def checkSearchResult(self):
""" """
Checks if the listView is empty and adds a "No Search Results" item. Checks if the listView is empty and adds a "No Search Results" item.
""" """
@ -662,15 +662,15 @@ class MediaManagerItem(QtGui.QWidget):
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0] item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
return item_id return item_id
def save_auto_select_id(self): def saveAutoSelectId(self):
""" """
Sorts out, what item to select after loading a list. Sorts out, what item to select after loading a list.
""" """
# The item to select has not been set. # The item to select has not been set.
if self.auto_select_id == -1: if self.autoSelectId == -1:
item = self.listView.currentItem() item = self.listView.currentItem()
if item: if item:
self.auto_select_id = item.data(QtCore.Qt.UserRole).toInt()[0] self.autoSelectId = item.data(QtCore.Qt.UserRole).toInt()[0]
def search(self, string): def search(self, string):
""" """

View File

@ -368,3 +368,4 @@ class Plugin(QtCore.QObject):
after this has been set. after this has been set.
""" """
self.textStrings[name] = {u'title': title, u'tooltip': tooltip} self.textStrings[name] = {u'title': title, u'tooltip': tooltip}

View File

@ -222,14 +222,14 @@ class Renderer(object):
if item.is_capable(ItemCapabilities.NoLineBreaks): if item.is_capable(ItemCapabilities.NoLineBreaks):
line_end = u' ' line_end = u' '
# Bibles # Bibles
if item.is_capable(ItemCapabilities.AllowsWordSplit): if item.is_capable(ItemCapabilities.CanWordSplit):
pages = self._paginate_slide_words(text.split(u'\n'), line_end) pages = self._paginate_slide_words(text.split(u'\n'), line_end)
else: else:
# Clean up line endings. # Clean up line endings.
lines = self._lines_split(text) lines = self._lines_split(text)
pages = self._paginate_slide(lines, line_end) pages = self._paginate_slide(lines, line_end)
# Songs and Custom # Songs and Custom
if item.is_capable(ItemCapabilities.AllowsVirtualSplit) and \ if item.is_capable(ItemCapabilities.CanSoftBreak) and \
len(pages) > 1 and u'[---]' in text: len(pages) > 1 and u'[---]' in text:
pages = [] pages = []
while True: while True:

View File

@ -52,20 +52,21 @@ class ItemCapabilities(object):
""" """
Provides an enumeration of a serviceitem's capabilities Provides an enumeration of a serviceitem's capabilities
""" """
AllowsPreview = 1 CanPreview = 1
AllowsEdit = 2 CanEdit = 2
AllowsMaintain = 3 CanMaintain = 3
RequiresMedia = 4 RequiresMedia = 4
AllowsLoop = 5 CanLoop = 5
AllowsAdditions = 6 CanAppend = 6
NoLineBreaks = 7 NoLineBreaks = 7
OnLoadUpdate = 8 OnLoadUpdate = 8
AddIfNewItem = 9 AddIfNewItem = 9
ProvidesOwnDisplay = 10 ProvidesOwnDisplay = 10
AllowsDetailedTitleDisplay = 11 HasDetailedTitleDisplay = 11
AllowsVariableStartTime = 12 HasVariableStartTime = 12
AllowsVirtualSplit = 13 CanSoftBreak = 13
AllowsWordSplit = 14 CanWordSplit = 14
HasBackgroundAudio = 15
class ServiceItem(object): class ServiceItem(object):
@ -116,6 +117,7 @@ class ServiceItem(object):
self.media_length = 0 self.media_length = 0
self.from_service = False self.from_service = False
self.image_border = u'#000000' self.image_border = u'#000000'
self.background_audio = []
self._new_item() self._new_item()
def _new_item(self): def _new_item(self):
@ -159,7 +161,7 @@ class ServiceItem(object):
""" """
The render method is what generates the frames for the screen and The render method is what generates the frames for the screen and
obtains the display information from the renderemanager. obtains the display information from the renderemanager.
At this point all the slides are build for the given At this point all the slides are built for the given
display size. display size.
""" """
log.debug(u'Render called') log.debug(u'Render called')
@ -272,7 +274,8 @@ class ServiceItem(object):
u'xml_version': self.xml_version, u'xml_version': self.xml_version,
u'start_time': self.start_time, u'start_time': self.start_time,
u'end_time': self.end_time, u'end_time': self.end_time,
u'media_length': self.media_length u'media_length': self.media_length,
u'background_audio': self.background_audio
} }
service_data = [] service_data = []
if self.service_item_type == ServiceItemType.Text: if self.service_item_type == ServiceItemType.Text:
@ -320,6 +323,8 @@ class ServiceItem(object):
self.end_time = header[u'end_time'] self.end_time = header[u'end_time']
if u'media_length' in header: if u'media_length' in header:
self.media_length = header[u'media_length'] self.media_length = header[u'media_length']
if u'background_audio' in header:
self.background_audio = header[u'background_audio']
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)
@ -341,7 +346,7 @@ class ServiceItem(object):
if self.is_text(): if self.is_text():
return self.title return self.title
else: else:
if ItemCapabilities.AllowsDetailedTitleDisplay in self.capabilities: if ItemCapabilities.HasDetailedTitleDisplay in self.capabilities:
return self._raw_frames[0][u'title'] return self._raw_frames[0][u'title']
elif len(self._raw_frames) > 1: elif len(self._raw_frames) > 1:
return self.title return self.title
@ -359,6 +364,8 @@ class ServiceItem(object):
""" """
self._uuid = other._uuid self._uuid = other._uuid
self.notes = other.notes self.notes = other.notes
if self.is_capable(ItemCapabilities.HasBackgroundAudio):
log.debug(self.background_audio)
def __eq__(self, other): def __eq__(self, other):
""" """

View File

@ -135,6 +135,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
""" """
Determine the next page in the Wizard to go to. Determine the next page in the Wizard to go to.
""" """
Receiver.send_message(u'openlp_process_events')
if self.currentId() == FirstTimePage.Plugins: if self.currentId() == FirstTimePage.Plugins:
if not self.webAccess: if not self.webAccess:
return FirstTimePage.NoInternet return FirstTimePage.NoInternet
@ -175,9 +176,12 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
elif pageId == FirstTimePage.Progress: elif pageId == FirstTimePage.Progress:
Receiver.send_message(u'cursor_busy') Receiver.send_message(u'cursor_busy')
self._preWizard() self._preWizard()
Receiver.send_message(u'openlp_process_events')
self._performWizard() self._performWizard()
Receiver.send_message(u'openlp_process_events')
self._postWizard() self._postWizard()
Receiver.send_message(u'cursor_normal') Receiver.send_message(u'cursor_normal')
Receiver.send_message(u'openlp_process_events')
def updateScreenListCombo(self): def updateScreenListCombo(self):
""" """
@ -219,6 +223,8 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
Prepare the UI for the process. Prepare the UI for the process.
""" """
self.max_progress = 0 self.max_progress = 0
self.finishButton.setVisible(False)
Receiver.send_message(u'openlp_process_events')
# Loop through the songs list and increase for each selected item # Loop through the songs list and increase for each selected item
for i in xrange(self.songsListWidget.count()): for i in xrange(self.songsListWidget.count()):
item = self.songsListWidget.item(i) item = self.songsListWidget.item(i)
@ -242,7 +248,6 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
filename = item.data(QtCore.Qt.UserRole).toString() filename = item.data(QtCore.Qt.UserRole).toString()
size = self._getFileSize(u'%s%s' % (self.web, filename)) size = self._getFileSize(u'%s%s' % (self.web, filename))
self.max_progress += size self.max_progress += size
self.finishButton.setVisible(False)
if self.max_progress: if self.max_progress:
# Add on 2 for plugins status setting plus a "finished" point. # Add on 2 for plugins status setting plus a "finished" point.
self.max_progress = self.max_progress + 2 self.max_progress = self.max_progress + 2

View File

@ -62,6 +62,10 @@ class MainDisplay(QtGui.QGraphicsView):
self.override = {} self.override = {}
self.retranslateUi() self.retranslateUi()
self.mediaObject = None self.mediaObject = None
if live:
self.audioPlayer = AudioPlayer(self)
else:
self.audioPlayer = None
self.firstTime = True self.firstTime = True
self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;') self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;')
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool | self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool |
@ -587,61 +591,76 @@ class AudioPlayer(QtCore.QObject):
""" """
log.debug(u'AudioPlayer Initialisation started') log.debug(u'AudioPlayer Initialisation started')
QtCore.QObject.__init__(self, parent) QtCore.QObject.__init__(self, parent)
self.message = None self.currentIndex = -1
self.playlist = []
self.mediaObject = Phonon.MediaObject() self.mediaObject = Phonon.MediaObject()
self.audioObject = Phonon.AudioOutput(Phonon.VideoCategory) self.audioObject = Phonon.AudioOutput(Phonon.VideoCategory)
Phonon.createPath(self.mediaObject, self.audioObject) Phonon.createPath(self.mediaObject, self.audioObject)
QtCore.QObject.connect(self.mediaObject,
QtCore.SIGNAL(u'aboutToFinish()'), self.onAboutToFinish)
def setup(self): def __del__(self):
"""
Sets up the Audio Player for use
"""
log.debug(u'AudioPlayer Setup')
def close(self):
""" """
Shutting down so clean up connections Shutting down so clean up connections
""" """
self.onMediaStop() self.stop()
for path in self.mediaObject.outputPaths(): for path in self.mediaObject.outputPaths():
path.disconnect() path.disconnect()
QtCore.QObject.__del__(self)
def onMediaQueue(self, message): def onAboutToFinish(self):
""" """
Set up a video to play from the serviceitem. Just before the audio player finishes the current track, queue the next
item in the playlist, if there is one.
""" """
log.debug(u'AudioPlayer Queue new media message %s' % message) self.currentIndex += 1
mfile = os.path.join(message[0].get_frame_path(), if len(self.playlist) > self.currentIndex:
message[0].get_frame_title()) self.mediaObject.enqueue(self.playlist[self.currentIndex])
self.mediaObject.setCurrentSource(Phonon.MediaSource(mfile))
self.onMediaPlay()
def onMediaPlay(self): def connectVolumeSlider(self, slider):
slider.setAudioOutput(self.audioObject)
def reset(self):
""" """
We want to play the play so start it Reset the audio player, clearing the playlist and the queue.
""" """
log.debug(u'AudioPlayer _play called') self.currentIndex = -1
self.playlist = []
self.stop()
self.mediaObject.clear()
def play(self):
"""
We want to play the file so start it
"""
log.debug(u'AudioPlayer.play() called')
if self.currentIndex == -1:
self.onAboutToFinish()
self.mediaObject.play() self.mediaObject.play()
def onMediaPause(self): def pause(self):
""" """
Pause the Audio Pause the Audio
""" """
log.debug(u'AudioPlayer Media paused by user') log.debug(u'AudioPlayer.pause() called')
self.mediaObject.pause() self.mediaObject.pause()
def onMediaStop(self): def stop(self):
""" """
Stop the Audio and clean up Stop the Audio and clean up
""" """
log.debug(u'AudioPlayer Media stopped by user') log.debug(u'AudioPlayer.stop() called')
self.message = None
self.mediaObject.stop() self.mediaObject.stop()
self.onMediaFinish()
def onMediaFinish(self): def addToPlaylist(self, filenames):
""" """
Clean up the Object queue Add another file to the playlist.
``filename``
The file to add to the playlist.
""" """
log.debug(u'AudioPlayer Reached end of media playlist') if not isinstance(filenames, list):
self.mediaObject.clearQueue() filenames = [filenames]
for filename in filenames:
self.playlist.append(Phonon.MediaSource(filename))

View File

@ -28,6 +28,7 @@ import cgi
import cPickle import cPickle
import logging import logging
import os import os
import shutil
import zipfile import zipfile
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -471,23 +472,34 @@ class ServiceManager(QtGui.QWidget):
if not self.fileName(): if not self.fileName():
return self.saveFileAs() return self.saveFileAs()
path_file_name = unicode(self.fileName()) path_file_name = unicode(self.fileName())
(path, file_name) = os.path.split(path_file_name) path, file_name = os.path.split(path_file_name)
(basename, extension) = os.path.splitext(file_name) basename, extension = os.path.splitext(file_name)
service_file_name = basename + '.osd' service_file_name = '%s.osd' % basename
log.debug(u'ServiceManager.saveFile - %s' % path_file_name) log.debug(u'ServiceManager.saveFile - %s' % path_file_name)
SettingsManager.set_last_dir( SettingsManager.set_last_dir(
self.mainwindow.servicemanagerSettingsSection, self.mainwindow.servicemanagerSettingsSection,
path) path)
service = [] service = []
write_list = [] write_list = []
audio_files = []
total_size = 0 total_size = 0
Receiver.send_message(u'cursor_busy') Receiver.send_message(u'cursor_busy')
# Number of items + 1 to zip it # Number of items + 1 to zip it
self.mainwindow.displayProgressBar(len(self.serviceItems) + 1) self.mainwindow.displayProgressBar(len(self.serviceItems) + 1)
for item in self.serviceItems: for item in self.serviceItems:
self.mainwindow.incrementProgressBar() self.mainwindow.incrementProgressBar()
service.append({u'serviceitem': service_item = item[u'service_item'].get_service_repr()
item[u'service_item'].get_service_repr()}) # Get all the audio files, and ready them for embedding in the
# service file.
if len(service_item[u'header'][u'background_audio']) > 0:
for i, filename in \
enumerate(service_item[u'header'][u'background_audio']):
new_file = os.path.join(u'audio', item[u'service_item']._uuid,
os.path.split(filename)[1])
audio_files.append((filename, new_file))
service_item[u'header'][u'background_audio'][i] = new_file
# Add the service item to the service.
service.append({u'serviceitem': service_item})
if not item[u'service_item'].uses_file(): if not item[u'service_item'].uses_file():
continue continue
skipMissing = False skipMissing = False
@ -541,6 +553,8 @@ class ServiceManager(QtGui.QWidget):
# Finally add all the listed media files. # Finally add all the listed media files.
for path_from in write_list: for path_from in write_list:
zip.write(path_from, path_from.encode(u'utf-8')) zip.write(path_from, path_from.encode(u'utf-8'))
for path_from, path_to in audio_files:
zip.write(path_from, path_to.encode(u'utf-8'))
except IOError: except IOError:
log.exception(u'Failed to save service to disk') log.exception(u'Failed to save service to disk')
success = False success = False
@ -595,11 +609,12 @@ class ServiceManager(QtGui.QWidget):
'The content encoding is not UTF-8.')) 'The content encoding is not UTF-8.'))
continue continue
osfile = unicode(QtCore.QDir.toNativeSeparators(ucsfile)) osfile = unicode(QtCore.QDir.toNativeSeparators(ucsfile))
filename_only = os.path.split(osfile)[1] if not osfile.startswith(u'audio'):
zipinfo.filename = filename_only osfile = os.path.split(osfile)[1]
zipinfo.filename = osfile
zip.extract(zipinfo, self.servicePath) zip.extract(zipinfo, self.servicePath)
if filename_only.endswith(u'osd'): if osfile.endswith(u'osd'):
p_file = os.path.join(self.servicePath, filename_only) p_file = os.path.join(self.servicePath, osfile)
if 'p_file' in locals(): if 'p_file' in locals():
Receiver.send_message(u'cursor_busy') Receiver.send_message(u'cursor_busy')
fileTo = open(p_file, u'r') fileTo = open(p_file, u'r')
@ -630,10 +645,10 @@ class ServiceManager(QtGui.QWidget):
'File is not a valid service.')) 'File is not a valid service.'))
log.exception(u'File contains no service data') log.exception(u'File contains no service data')
except (IOError, NameError, zipfile.BadZipfile): except (IOError, NameError, zipfile.BadZipfile):
log.exception(u'Problem loading service file %s' % fileName)
critical_error_message_box( critical_error_message_box(
message=translate('OpenLP.ServiceManager', message=translate('OpenLP.ServiceManager',
'File could not be opened because it is corrupt.')) 'File could not be opened because it is corrupt.'))
log.exception(u'Problem loading service file %s' % fileName)
except zipfile.BadZipfile: except zipfile.BadZipfile:
if os.path.getsize(fileName) == 0: if os.path.getsize(fileName) == 0:
log.exception(u'Service file is zero sized: %s' % fileName) log.exception(u'Service file is zero sized: %s' % fileName)
@ -682,16 +697,16 @@ class ServiceManager(QtGui.QWidget):
self.maintainAction.setVisible(False) self.maintainAction.setVisible(False)
self.notesAction.setVisible(False) self.notesAction.setVisible(False)
self.timeAction.setVisible(False) self.timeAction.setVisible(False)
if serviceItem[u'service_item'].is_capable(ItemCapabilities.AllowsEdit)\ if serviceItem[u'service_item'].is_capable(ItemCapabilities.CanEdit)\
and serviceItem[u'service_item'].edit_id: and serviceItem[u'service_item'].edit_id:
self.editAction.setVisible(True) self.editAction.setVisible(True)
if serviceItem[u'service_item']\ if serviceItem[u'service_item']\
.is_capable(ItemCapabilities.AllowsMaintain): .is_capable(ItemCapabilities.CanMaintain):
self.maintainAction.setVisible(True) self.maintainAction.setVisible(True)
if item.parent() is None: if item.parent() is None:
self.notesAction.setVisible(True) self.notesAction.setVisible(True)
if serviceItem[u'service_item']\ if serviceItem[u'service_item']\
.is_capable(ItemCapabilities.AllowsVariableStartTime): .is_capable(ItemCapabilities.HasVariableStartTime):
self.timeAction.setVisible(True) self.timeAction.setVisible(True)
self.themeMenu.menuAction().setVisible(False) self.themeMenu.menuAction().setVisible(False)
# Set up the theme menu. # Set up the theme menu.
@ -962,7 +977,7 @@ class ServiceManager(QtGui.QWidget):
(unicode(translate('OpenLP.ServiceManager', 'Notes')), (unicode(translate('OpenLP.ServiceManager', 'Notes')),
cgi.escape(unicode(serviceitem.notes)))) cgi.escape(unicode(serviceitem.notes))))
if item[u'service_item'] \ if item[u'service_item'] \
.is_capable(ItemCapabilities.AllowsVariableStartTime): .is_capable(ItemCapabilities.HasVariableStartTime):
tips.append(item[u'service_item'].get_media_time()) tips.append(item[u'service_item'].get_media_time())
treewidgetitem.setToolTip(0, u'<br>'.join(tips)) treewidgetitem.setToolTip(0, u'<br>'.join(tips))
treewidgetitem.setData(0, QtCore.Qt.UserRole, treewidgetitem.setData(0, QtCore.Qt.UserRole,
@ -998,6 +1013,8 @@ class ServiceManager(QtGui.QWidget):
for file in os.listdir(self.servicePath): for file in os.listdir(self.servicePath):
file_path = os.path.join(self.servicePath, file) file_path = os.path.join(self.servicePath, file)
delete_file(file_path) delete_file(file_path)
if os.path.exists(os.path.join(self.servicePath, u'audio')):
shutil.rmtree(os.path.join(self.servicePath, u'audio'), False)
def onThemeComboBoxSelected(self, currentIndex): def onThemeComboBoxSelected(self, currentIndex):
""" """
@ -1196,7 +1213,7 @@ class ServiceManager(QtGui.QWidget):
item += 1 item += 1
if self.serviceItems and item < len(self.serviceItems) and \ if self.serviceItems and item < len(self.serviceItems) and \
self.serviceItems[item][u'service_item'].is_capable( self.serviceItems[item][u'service_item'].is_capable(
ItemCapabilities.AllowsPreview): ItemCapabilities.CanPreview):
self.mainwindow.previewController.addServiceManagerItem( self.mainwindow.previewController.addServiceManagerItem(
self.serviceItems[item][u'service_item'], 0) self.serviceItems[item][u'service_item'], 0)
self.mainwindow.liveController.previewListWidget.setFocus() self.mainwindow.liveController.previewListWidget.setFocus()
@ -1214,7 +1231,7 @@ class ServiceManager(QtGui.QWidget):
""" """
item = self.findServiceItem()[0] item = self.findServiceItem()[0]
if self.serviceItems[item][u'service_item']\ if self.serviceItems[item][u'service_item']\
.is_capable(ItemCapabilities.AllowsEdit): .is_capable(ItemCapabilities.CanEdit):
Receiver.send_message(u'%s_edit' % Receiver.send_message(u'%s_edit' %
self.serviceItems[item][u'service_item'].name.lower(), self.serviceItems[item][u'service_item'].name.lower(),
u'L:%s' % self.serviceItems[item][u'service_item'].edit_id) u'L:%s' % self.serviceItems[item][u'service_item'].edit_id)
@ -1297,7 +1314,7 @@ class ServiceManager(QtGui.QWidget):
serviceItem = self.serviceItems[pos] serviceItem = self.serviceItems[pos]
if (plugin == serviceItem[u'service_item'].name and if (plugin == serviceItem[u'service_item'].name and
serviceItem[u'service_item'].is_capable( serviceItem[u'service_item'].is_capable(
ItemCapabilities.AllowsAdditions)): ItemCapabilities.CanAppend)):
action = self.dndMenu.exec_(QtGui.QCursor.pos()) action = self.dndMenu.exec_(QtGui.QCursor.pos())
# New action required # New action required
if action == self.newAction: if action == self.newAction:

View File

@ -256,6 +256,12 @@ class SlideController(QtGui.QWidget):
self.songMenu.setMenu(QtGui.QMenu( self.songMenu.setMenu(QtGui.QMenu(
translate('OpenLP.SlideController', 'Go To'), self.toolbar)) translate('OpenLP.SlideController', 'Go To'), self.toolbar))
self.toolbar.makeWidgetsInvisible([u'Song Menu']) self.toolbar.makeWidgetsInvisible([u'Song Menu'])
# Stuff for items with background audio.
self.audioPauseItem = self.toolbar.addToolbarButton(
u'Pause Audio', u':/slides/media_playback_pause.png',
translate('OpenLP.SlideController', 'Pause audio.'),
self.onAudioPauseClicked, True)
self.audioPauseItem.setVisible(False)
# Build the volumeSlider. # Build the volumeSlider.
self.volumeSlider = QtGui.QSlider(QtCore.Qt.Horizontal) self.volumeSlider = QtGui.QSlider(QtCore.Qt.Horizontal)
self.volumeSlider.setTickInterval(1) self.volumeSlider.setTickInterval(1)
@ -512,13 +518,13 @@ class SlideController(QtGui.QWidget):
self.playSlidesOnce.setChecked(False) self.playSlidesOnce.setChecked(False)
self.playSlidesOnce.setIcon(build_icon(u':/media/media_time.png')) self.playSlidesOnce.setIcon(build_icon(u':/media/media_time.png'))
self.playSlidesLoop.setChecked(False) self.playSlidesLoop.setChecked(False)
self.playSlidesLoop.setIcon(build_icon(u':/media/media_time.png')) self.playSlidesLoop.setIcon(build_icon(u':/media/media_time.png'))
if item.is_text(): if item.is_text():
if QtCore.QSettings().value( if QtCore.QSettings().value(
self.parent().songsSettingsSection + u'/display songbar', self.parent().songsSettingsSection + u'/display songbar',
QtCore.QVariant(True)).toBool() and len(self.slideList) > 0: QtCore.QVariant(True)).toBool() and len(self.slideList) > 0:
self.toolbar.makeWidgetsVisible([u'Song Menu']) self.toolbar.makeWidgetsVisible([u'Song Menu'])
if item.is_capable(ItemCapabilities.AllowsLoop) and \ if item.is_capable(ItemCapabilities.CanLoop) and \
len(item.get_frames()) > 1: len(item.get_frames()) > 1:
self.toolbar.makeWidgetsVisible(self.loopList) self.toolbar.makeWidgetsVisible(self.loopList)
if item.is_media(): if item.is_media():
@ -538,7 +544,7 @@ class SlideController(QtGui.QWidget):
self.toolbar.hide() self.toolbar.hide()
self.mediabar.setVisible(False) self.mediabar.setVisible(False)
self.toolbar.makeWidgetsInvisible(self.songEditList) self.toolbar.makeWidgetsInvisible(self.songEditList)
if item.is_capable(ItemCapabilities.AllowsEdit) and item.from_plugin: if item.is_capable(ItemCapabilities.CanEdit) and item.from_plugin:
self.toolbar.makeWidgetsVisible(self.songEditList) self.toolbar.makeWidgetsVisible(self.songEditList)
elif item.is_media(): elif item.is_media():
self.toolbar.setVisible(False) self.toolbar.setVisible(False)
@ -576,7 +582,7 @@ class SlideController(QtGui.QWidget):
""" """
Replacement item following a remote edit Replacement item following a remote edit
""" """
if item.__eq__(self.serviceItem): if item == self.serviceItem:
self._processItem(item, self.previewListWidget.currentRow()) self._processItem(item, self.previewListWidget.currentRow())
def addServiceManagerItem(self, item, slideno): def addServiceManagerItem(self, item, slideno):
@ -586,15 +592,17 @@ class SlideController(QtGui.QWidget):
Called by ServiceManager Called by ServiceManager
""" """
log.debug(u'addServiceManagerItem live = %s' % self.isLive) log.debug(u'addServiceManagerItem live = %s' % self.isLive)
# If no valid slide number is specified we take the first one. # If no valid slide number is specified we take the first one, but we
# remember the initial value to see if we should reload the song or not
slidenum = slideno
if slideno == -1: if slideno == -1:
slideno = 0 slidenum = 0
# If service item is the same as the current on only change slide # If service item is the same as the current one, only change slide
if item.__eq__(self.serviceItem): if slideno >= 0 and item == self.serviceItem:
self.__checkUpdateSelectedSlide(slideno) self.__checkUpdateSelectedSlide(slidenum)
self.slideSelected() self.slideSelected()
return else:
self._processItem(item, slideno) self._processItem(item, slidenum)
def _processItem(self, serviceItem, slideno): def _processItem(self, serviceItem, slideno):
""" """
@ -618,6 +626,15 @@ class SlideController(QtGui.QWidget):
self.previewListWidget.setColumnWidth(0, width) self.previewListWidget.setColumnWidth(0, width)
if self.isLive: if self.isLive:
self.songMenu.menu().clear() self.songMenu.menu().clear()
self.display.audioPlayer.reset()
self.setAudioItemsVisibility(False)
self.audioPauseItem.setChecked(False)
if self.serviceItem.is_capable(ItemCapabilities.HasBackgroundAudio):
log.debug(u'Starting to play...')
self.display.audioPlayer.addToPlaylist(
self.serviceItem.background_audio)
self.display.audioPlayer.play()
self.setAudioItemsVisibility(True)
row = 0 row = 0
text = [] text = []
for framenumber, frame in enumerate(self.serviceItem.get_frames()): for framenumber, frame in enumerate(self.serviceItem.get_frames()):
@ -1097,6 +1114,17 @@ class SlideController(QtGui.QWidget):
self.playSlidesLoop.setChecked(False) self.playSlidesLoop.setChecked(False)
self.onToggleLoop() self.onToggleLoop()
def setAudioItemsVisibility(self, visible):
self.audioPauseItem.setVisible(visible)
def onAudioPauseClicked(self, checked):
if not self.audioPauseItem.isVisible():
return
if checked:
self.display.audioPlayer.pause()
else:
self.display.audioPlayer.play()
def timerEvent(self, event): def timerEvent(self, event):
""" """
If the timer event is for this window select next slide If the timer event is for this window select next slide

View File

@ -67,7 +67,7 @@ class BibleMediaItem(MediaManagerItem):
self.hasSearch = True self.hasSearch = True
self.search_results = {} self.search_results = {}
self.second_search_results = {} self.second_search_results = {}
self.check_search_result() self.checkSearchResult()
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'bibles_load_list'), self.reloadBibles) QtCore.SIGNAL(u'bibles_load_list'), self.reloadBibles)
@ -651,7 +651,7 @@ class BibleMediaItem(MediaManagerItem):
elif self.search_results: elif self.search_results:
self.displayResults(bible, second_bible) self.displayResults(bible, second_bible)
self.advancedSearchButton.setEnabled(True) self.advancedSearchButton.setEnabled(True)
self.check_search_result() self.checkSearchResult()
Receiver.send_message(u'cursor_normal') Receiver.send_message(u'cursor_normal')
Receiver.send_message(u'openlp_process_events') Receiver.send_message(u'openlp_process_events')
@ -715,7 +715,7 @@ class BibleMediaItem(MediaManagerItem):
elif self.search_results: elif self.search_results:
self.displayResults(bible, second_bible) self.displayResults(bible, second_bible)
self.quickSearchButton.setEnabled(True) self.quickSearchButton.setEnabled(True)
self.check_search_result() self.checkSearchResult()
Receiver.send_message(u'cursor_normal') Receiver.send_message(u'cursor_normal')
Receiver.send_message(u'openlp_process_events') Receiver.send_message(u'openlp_process_events')
@ -863,9 +863,9 @@ class BibleMediaItem(MediaManagerItem):
not second_bible: not second_bible:
# Split the line but do not replace line breaks in renderer. # Split the line but do not replace line breaks in renderer.
service_item.add_capability(ItemCapabilities.NoLineBreaks) service_item.add_capability(ItemCapabilities.NoLineBreaks)
service_item.add_capability(ItemCapabilities.AllowsPreview) service_item.add_capability(ItemCapabilities.CanPreview)
service_item.add_capability(ItemCapabilities.AllowsLoop) service_item.add_capability(ItemCapabilities.CanLoop)
service_item.add_capability(ItemCapabilities.AllowsWordSplit) service_item.add_capability(ItemCapabilities.CanWordSplit)
# Service Item: Title # Service Item: Title
service_item.title = u', '.join(raw_title) service_item.title = u', '.join(raw_title)
# Service Item: Theme # Service Item: Theme

View File

@ -135,7 +135,7 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
self.customSlide.credits = unicode(self.creditEdit.text()) self.customSlide.credits = unicode(self.creditEdit.text())
self.customSlide.theme_name = unicode(self.themeComboBox.currentText()) self.customSlide.theme_name = unicode(self.themeComboBox.currentText())
success = self.manager.save_object(self.customSlide) success = self.manager.save_object(self.customSlide)
self.mediaitem.auto_select_id = self.customSlide.id self.mediaitem.autoSelectId = self.customSlide.id
return success return success
def onUpButtonClicked(self): def onUpButtonClicked(self):

View File

@ -132,7 +132,7 @@ class CustomMediaItem(MediaManagerItem):
def loadList(self, custom_slides): def loadList(self, custom_slides):
# Sort out what custom we want to select after loading the list. # Sort out what custom we want to select after loading the list.
self.save_auto_select_id() self.saveAutoSelectId()
self.listView.clear() self.listView.clear()
# Sort the customs by its title considering language specific # Sort the customs by its title considering language specific
# characters. lower() is needed for windows! # characters. lower() is needed for windows!
@ -144,9 +144,9 @@ class CustomMediaItem(MediaManagerItem):
QtCore.Qt.UserRole, QtCore.QVariant(custom_slide.id)) QtCore.Qt.UserRole, QtCore.QVariant(custom_slide.id))
self.listView.addItem(custom_name) self.listView.addItem(custom_name)
# Auto-select the custom. # Auto-select the custom.
if custom_slide.id == self.auto_select_id: if custom_slide.id == self.autoSelectId:
self.listView.setCurrentItem(custom_name) self.listView.setCurrentItem(custom_name)
self.auto_select_id = -1 self.autoSelectId = -1
# Called to redisplay the custom list screen edith from a search # Called to redisplay the custom list screen edith from a search
# or from the exit of the Custom edit dialog. If remote editing is # or from the exit of the Custom edit dialog. If remote editing is
# active trigger it and clean up so it will not update again. # active trigger it and clean up so it will not update again.
@ -180,7 +180,7 @@ class CustomMediaItem(MediaManagerItem):
self.remoteTriggered = remote_type self.remoteTriggered = remote_type
self.edit_custom_form.loadCustom(custom_id, (remote_type == u'P')) self.edit_custom_form.loadCustom(custom_id, (remote_type == u'P'))
self.edit_custom_form.exec_() self.edit_custom_form.exec_()
self.auto_select_id = -1 self.autoSelectId = -1
self.onSearchTextButtonClick() self.onSearchTextButtonClick()
def onEditClick(self): def onEditClick(self):
@ -192,7 +192,7 @@ class CustomMediaItem(MediaManagerItem):
item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0] item_id = (item.data(QtCore.Qt.UserRole)).toInt()[0]
self.edit_custom_form.loadCustom(item_id, False) self.edit_custom_form.loadCustom(item_id, False)
self.edit_custom_form.exec_() self.edit_custom_form.exec_()
self.auto_select_id = -1 self.autoSelectId = -1
self.onSearchTextButtonClick() self.onSearchTextButtonClick()
def onDeleteClick(self): def onDeleteClick(self):
@ -227,10 +227,10 @@ class CustomMediaItem(MediaManagerItem):
slide = None slide = None
theme = None theme = None
item_id = self._getIdOfItemToGenerate(item, self.remoteCustom) item_id = self._getIdOfItemToGenerate(item, self.remoteCustom)
service_item.add_capability(ItemCapabilities.AllowsEdit) service_item.add_capability(ItemCapabilities.CanEdit)
service_item.add_capability(ItemCapabilities.AllowsPreview) service_item.add_capability(ItemCapabilities.CanPreview)
service_item.add_capability(ItemCapabilities.AllowsLoop) service_item.add_capability(ItemCapabilities.CanLoop)
service_item.add_capability(ItemCapabilities.AllowsVirtualSplit) service_item.add_capability(ItemCapabilities.CanSoftBreak)
customSlide = self.plugin.manager.get_object(CustomSlide, item_id) customSlide = self.plugin.manager.get_object(CustomSlide, item_id)
title = customSlide.title title = customSlide.title
credit = customSlide.credits credit = customSlide.credits
@ -273,7 +273,7 @@ class CustomMediaItem(MediaManagerItem):
CustomSlide.theme_name.like(u'%' + self.whitespace.sub(u' ', CustomSlide.theme_name.like(u'%' + self.whitespace.sub(u' ',
search_keywords) + u'%'), order_by_ref=CustomSlide.title) search_keywords) + u'%'), order_by_ref=CustomSlide.title)
self.loadList(search_results) self.loadList(search_results)
self.check_search_result() self.checkSearchResult()
def onSearchTextEditChanged(self, text): def onSearchTextEditChanged(self, text):
""" """

View File

@ -149,10 +149,10 @@ class ImageMediaItem(MediaManagerItem):
if not items: if not items:
return False return False
service_item.title = unicode(self.plugin.nameStrings[u'plural']) service_item.title = unicode(self.plugin.nameStrings[u'plural'])
service_item.add_capability(ItemCapabilities.AllowsMaintain) service_item.add_capability(ItemCapabilities.CanMaintain)
service_item.add_capability(ItemCapabilities.AllowsPreview) service_item.add_capability(ItemCapabilities.CanPreview)
service_item.add_capability(ItemCapabilities.AllowsLoop) service_item.add_capability(ItemCapabilities.CanLoop)
service_item.add_capability(ItemCapabilities.AllowsAdditions) service_item.add_capability(ItemCapabilities.CanAppend)
# force a nonexistent theme # force a nonexistent theme
service_item.theme = -1 service_item.theme = -1
missing_items = [] missing_items = []

View File

@ -31,11 +31,11 @@ import os
import locale import locale
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from PyQt4.phonon import Phonon
from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \ from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \
SettingsManager, translate, check_item_selected, Receiver SettingsManager, translate, check_item_selected, Receiver, MediaType
from openlp.core.lib.ui import UiStrings, critical_error_message_box from openlp.core.lib.ui import UiStrings, critical_error_message_box
from PyQt4.phonon import Phonon
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -48,9 +48,9 @@ class MediaMediaItem(MediaManagerItem):
log.info(u'%s MediaMediaItem loaded', __name__) log.info(u'%s MediaMediaItem loaded', __name__)
def __init__(self, parent, plugin, icon): def __init__(self, parent, plugin, icon):
self.IconPath = u'images/image' self.iconPath = u'images/image'
self.background = False self.background = False
self.PreviewFunction = CLAPPERBOARD self.previewFunction = CLAPPERBOARD
MediaManagerItem.__init__(self, parent, plugin, icon) MediaManagerItem.__init__(self, parent, plugin, icon)
self.singleServiceItem = False self.singleServiceItem = False
self.hasSearch = True self.hasSearch = True
@ -139,8 +139,8 @@ class MediaMediaItem(MediaManagerItem):
# File is no longer present # File is no longer present
critical_error_message_box( critical_error_message_box(
translate('MediaPlugin.MediaItem', 'Missing Media File'), translate('MediaPlugin.MediaItem', 'Missing Media File'),
unicode(translate('MediaPlugin.MediaItem', unicode(translate('MediaPlugin.MediaItem',
'The file %s no longer exists.')) % filename) 'The file %s no longer exists.')) % filename)
return False return False
self.mediaObject.stop() self.mediaObject.stop()
self.mediaObject.clearQueue() self.mediaObject.clearQueue()
@ -156,13 +156,16 @@ class MediaMediaItem(MediaManagerItem):
or self.mediaObject.currentSource().type() \ or self.mediaObject.currentSource().type() \
== Phonon.MediaSource.Invalid: == Phonon.MediaSource.Invalid:
self.mediaObject.stop() self.mediaObject.stop()
critical_error_message_box(UiStrings().UnsupportedFile, critical_error_message_box(
UiStrings().UnsupportedFile) translate('MediaPlugin.MediaItem', 'File Too Big'),
translate('MediaPlugin.MediaItem', 'The file you are '
'trying to load is too big. Please reduce it to less '
'than 50MiB.'))
return False return False
self.mediaObject.stop() self.mediaObject.stop()
service_item.media_length = self.mediaObject.totalTime() / 1000 service_item.media_length = self.mediaObject.totalTime() / 1000
service_item.add_capability( service_item.add_capability(
ItemCapabilities.AllowsVariableStartTime) ItemCapabilities.HasVariableStartTime)
service_item.title = unicode(self.plugin.nameStrings[u'singular']) service_item.title = unicode(self.plugin.nameStrings[u'singular'])
service_item.add_capability(ItemCapabilities.RequiresMedia) service_item.add_capability(ItemCapabilities.RequiresMedia)
# force a non-existent theme # force a non-existent theme
@ -217,6 +220,19 @@ class MediaMediaItem(MediaManagerItem):
item_name.setToolTip(track) item_name.setToolTip(track)
self.listView.addItem(item_name) self.listView.addItem(item_name)
def getList(self, type=MediaType.Audio):
media = SettingsManager.load_list(self.settingsSection, u'media')
media.sort(cmp=locale.strcoll,
key=lambda filename: os.path.split(unicode(filename))[1].lower())
ext = []
if type == MediaType.Audio:
ext = self.plugin.audio_extensions_list
else:
ext = self.plugin.video_extensions_list
ext = map(lambda x: x[1:], ext)
media = filter(lambda x: os.path.splitext(x)[1] in ext, media)
return media
def createPhonon(self): def createPhonon(self):
log.debug(u'CreatePhonon') log.debug(u'CreatePhonon')
if not self.mediaObject: if not self.mediaObject:

View File

@ -248,7 +248,7 @@ class PresentationMediaItem(MediaManagerItem):
service_item.title = unicode(self.displayTypeComboBox.currentText()) service_item.title = unicode(self.displayTypeComboBox.currentText())
service_item.shortname = unicode(self.displayTypeComboBox.currentText()) service_item.shortname = unicode(self.displayTypeComboBox.currentText())
service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay) service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay)
service_item.add_capability(ItemCapabilities.AllowsDetailedTitleDisplay) service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay)
shortname = service_item.shortname shortname = service_item.shortname
if shortname: if shortname:
for bitem in items: for bitem in items:

View File

@ -52,6 +52,7 @@ them separate from the functionality, so that it is easier to recreate the GUI
from the .ui files later if necessary. from the .ui files later if necessary.
""" """
from mediafilesform import MediaFilesForm
from authorsform import AuthorsForm from authorsform import AuthorsForm
from topicsform import TopicsForm from topicsform import TopicsForm
from songbookform import SongBookForm from songbookform import SongBookForm

View File

@ -28,7 +28,8 @@
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import build_icon, translate from openlp.core.lib import build_icon, translate
from openlp.core.lib.ui import UiStrings, create_accept_reject_button_box from openlp.core.lib.ui import UiStrings, create_accept_reject_button_box, \
create_up_down_push_button_set
from openlp.plugins.songs.lib.ui import SongStrings from openlp.plugins.songs.lib.ui import SongStrings
class Ui_EditSongDialog(object): class Ui_EditSongDialog(object):
@ -36,9 +37,11 @@ class Ui_EditSongDialog(object):
editSongDialog.setObjectName(u'editSongDialog') editSongDialog.setObjectName(u'editSongDialog')
editSongDialog.resize(650, 400) editSongDialog.resize(650, 400)
editSongDialog.setWindowIcon( editSongDialog.setWindowIcon(
build_icon(u':/icon/openlp.org-icon-32.bmp')) build_icon(u':/icon/openlp-logo-16x16.png'))
editSongDialog.setModal(True) editSongDialog.setModal(True)
self.dialogLayout = QtGui.QVBoxLayout(editSongDialog) self.dialogLayout = QtGui.QVBoxLayout(editSongDialog)
self.dialogLayout.setSpacing(8)
self.dialogLayout.setContentsMargins(8, 8, 8, 8)
self.dialogLayout.setObjectName(u'dialogLayout') self.dialogLayout.setObjectName(u'dialogLayout')
self.songTabWidget = QtGui.QTabWidget(editSongDialog) self.songTabWidget = QtGui.QTabWidget(editSongDialog)
self.songTabWidget.setObjectName(u'songTabWidget') self.songTabWidget.setObjectName(u'songTabWidget')
@ -246,6 +249,36 @@ class Ui_EditSongDialog(object):
self.commentsLayout.addWidget(self.commentsEdit) self.commentsLayout.addWidget(self.commentsEdit)
self.themeTabLayout.addWidget(self.commentsGroupBox) self.themeTabLayout.addWidget(self.commentsGroupBox)
self.songTabWidget.addTab(self.themeTab, u'') self.songTabWidget.addTab(self.themeTab, u'')
# audio tab
self.audioTab = QtGui.QWidget()
self.audioTab.setObjectName(u'audioTab')
self.audioLayout = QtGui.QHBoxLayout(self.audioTab)
self.audioLayout.setObjectName(u'audioLayout')
self.audioListWidget = QtGui.QListWidget(self.audioTab)
self.audioListWidget.setObjectName(u'audioListWidget')
self.audioLayout.addWidget(self.audioListWidget)
self.audioButtonsLayout = QtGui.QVBoxLayout()
self.audioButtonsLayout.setObjectName(u'audioButtonsLayout')
self.audioAddFromFileButton = QtGui.QPushButton(self.audioTab)
self.audioAddFromFileButton.setObjectName(u'audioAddFromFileButton')
self.audioButtonsLayout.addWidget(self.audioAddFromFileButton)
self.audioAddFromMediaButton = QtGui.QPushButton(self.audioTab)
self.audioAddFromMediaButton.setObjectName(u'audioAddFromMediaButton')
self.audioButtonsLayout.addWidget(self.audioAddFromMediaButton)
self.audioRemoveButton = QtGui.QPushButton(self.audioTab)
self.audioRemoveButton.setObjectName(u'audioRemoveButton')
self.audioButtonsLayout.addWidget(self.audioRemoveButton)
self.audioRemoveAllButton = QtGui.QPushButton(self.audioTab)
self.audioRemoveAllButton.setObjectName(u'audioRemoveAllButton')
self.audioButtonsLayout.addWidget(self.audioRemoveAllButton)
self.audioButtonsLayout.addStretch(1)
self.upButton, self.downButton = \
create_up_down_push_button_set(self)
self.audioButtonsLayout.addWidget(self.upButton)
self.audioButtonsLayout.addWidget(self.downButton)
self.audioLayout.addLayout(self.audioButtonsLayout)
self.songTabWidget.addTab(self.audioTab, u'')
# Last few bits
self.dialogLayout.addWidget(self.songTabWidget) self.dialogLayout.addWidget(self.songTabWidget)
self.buttonBox = create_accept_reject_button_box(editSongDialog) self.buttonBox = create_accept_reject_button_box(editSongDialog)
self.dialogLayout.addWidget(self.buttonBox) self.dialogLayout.addWidget(self.buttonBox)
@ -305,6 +338,17 @@ class Ui_EditSongDialog(object):
self.songTabWidget.indexOf(self.themeTab), self.songTabWidget.indexOf(self.themeTab),
translate('SongsPlugin.EditSongForm', translate('SongsPlugin.EditSongForm',
'Theme, Copyright Info && Comments')) 'Theme, Copyright Info && Comments'))
self.songTabWidget.setTabText(
self.songTabWidget.indexOf(self.audioTab),
translate('SongsPlugin.EditSongForm', 'Linked Audio'))
self.audioAddFromFileButton.setText(
translate('SongsPlugin.EditSongForm', 'Add &File(s)'))
self.audioAddFromMediaButton.setText(
translate('SongsPlugin.EditSongForm', 'Add &Media'))
self.audioRemoveButton.setText(
translate('SongsPlugin.EditSongForm', '&Remove'))
self.audioRemoveAllButton.setText(
translate('SongsPlugin.EditSongForm', 'Remove &All'))
def editSongDialogComboBox(parent, name): def editSongDialogComboBox(parent, name):
""" """

View File

@ -27,15 +27,18 @@
import logging import logging
import re import re
import os
import shutil
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import Receiver, translate from openlp.core.lib import PluginStatus, Receiver, MediaType, translate
from openlp.core.lib.ui import UiStrings, add_widget_completer, \ from openlp.core.lib.ui import UiStrings, add_widget_completer, \
critical_error_message_box, find_and_set_in_combo_box critical_error_message_box, find_and_set_in_combo_box
from openlp.plugins.songs.forms import EditVerseForm from openlp.core.utils import AppLocation
from openlp.plugins.songs.forms import EditVerseForm, MediaFilesForm
from openlp.plugins.songs.lib import SongXML, VerseType, clean_song from openlp.plugins.songs.lib import SongXML, VerseType, clean_song
from openlp.plugins.songs.lib.db import Book, Song, Author, Topic from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile
from openlp.plugins.songs.lib.ui import SongStrings from openlp.plugins.songs.lib.ui import SongStrings
from editsongdialog import Ui_EditSongDialog from editsongdialog import Ui_EditSongDialog
@ -93,6 +96,14 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.mediaitem.plugin.renderer.themeManager.onAddTheme) self.mediaitem.plugin.renderer.themeManager.onAddTheme)
QtCore.QObject.connect(self.maintenanceButton, QtCore.QObject.connect(self.maintenanceButton,
QtCore.SIGNAL(u'clicked()'), self.onMaintenanceButtonClicked) QtCore.SIGNAL(u'clicked()'), self.onMaintenanceButtonClicked)
QtCore.QObject.connect(self.audioAddFromFileButton,
QtCore.SIGNAL(u'clicked()'), self.onAudioAddFromFileButtonClicked)
QtCore.QObject.connect(self.audioAddFromMediaButton,
QtCore.SIGNAL(u'clicked()'), self.onAudioAddFromMediaButtonClicked)
QtCore.QObject.connect(self.audioRemoveButton,
QtCore.SIGNAL(u'clicked()'), self.onAudioRemoveButtonClicked)
QtCore.QObject.connect(self.audioRemoveAllButton,
QtCore.SIGNAL(u'clicked()'), self.onAudioRemoveAllButtonClicked)
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'theme_update_list'), self.loadThemes) QtCore.SIGNAL(u'theme_update_list'), self.loadThemes)
self.previewButton = QtGui.QPushButton() self.previewButton = QtGui.QPushButton()
@ -104,12 +115,14 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
QtCore.SIGNAL(u'clicked(QAbstractButton*)'), self.onPreview) QtCore.SIGNAL(u'clicked(QAbstractButton*)'), self.onPreview)
# Create other objects and forms # Create other objects and forms
self.manager = manager self.manager = manager
self.verse_form = EditVerseForm(self) self.verseForm = EditVerseForm(self)
self.mediaForm = MediaFilesForm(self)
self.initialise() self.initialise()
self.authorsListView.setSortingEnabled(False) self.authorsListView.setSortingEnabled(False)
self.authorsListView.setAlternatingRowColors(True) self.authorsListView.setAlternatingRowColors(True)
self.topicsListView.setSortingEnabled(False) self.topicsListView.setSortingEnabled(False)
self.topicsListView.setAlternatingRowColors(True) self.topicsListView.setAlternatingRowColors(True)
self.audioListWidget.setAlternatingRowColors(True)
self.findVerseSplit = re.compile(u'---\[\]---\n', re.UNICODE) self.findVerseSplit = re.compile(u'---\[\]---\n', re.UNICODE)
self.whitespace = re.compile(r'\W+', re.UNICODE) self.whitespace = re.compile(r'\W+', re.UNICODE)
@ -161,6 +174,16 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.themes.append(theme) self.themes.append(theme)
add_widget_completer(self.themes, self.themeComboBox) add_widget_completer(self.themes, self.themeComboBox)
def loadMediaFiles(self):
self.audioAddFromMediaButton.setVisible(False)
for plugin in self.parent().pluginManager.plugins:
if plugin.name == u'media' and \
plugin.status == PluginStatus.Active:
self.audioAddFromMediaButton.setVisible(True)
self.mediaForm.populateFiles(
plugin.getMediaManagerItem().getList(MediaType.Audio))
break
def newSong(self): def newSong(self):
log.debug(u'New Song') log.debug(u'New Song')
self.song = None self.song = None
@ -176,11 +199,13 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.verseListWidget.setRowCount(0) self.verseListWidget.setRowCount(0)
self.authorsListView.clear() self.authorsListView.clear()
self.topicsListView.clear() self.topicsListView.clear()
self.audioListWidget.clear()
self.titleEdit.setFocus(QtCore.Qt.OtherFocusReason) self.titleEdit.setFocus(QtCore.Qt.OtherFocusReason)
self.songBookNumberEdit.setText(u'') self.songBookNumberEdit.setText(u'')
self.loadAuthors() self.loadAuthors()
self.loadTopics() self.loadTopics()
self.loadBooks() self.loadBooks()
self.loadMediaFiles()
self.themeComboBox.setCurrentIndex(0) self.themeComboBox.setCurrentIndex(0)
# 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)
@ -201,6 +226,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.loadAuthors() self.loadAuthors()
self.loadTopics() self.loadTopics()
self.loadBooks() self.loadBooks()
self.loadMediaFiles()
self.song = self.manager.get_object(Song, id) self.song = self.manager.get_object(Song, id)
self.titleEdit.setText(self.song.title) self.titleEdit.setText(self.song.title)
if self.song.alternate_title: if self.song.alternate_title:
@ -303,6 +329,11 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
topic_name = QtGui.QListWidgetItem(unicode(topic.name)) topic_name = QtGui.QListWidgetItem(unicode(topic.name))
topic_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(topic.id)) topic_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(topic.id))
self.topicsListView.addItem(topic_name) self.topicsListView.addItem(topic_name)
self.audioListWidget.clear()
for media in self.song.media_files:
media_file = QtGui.QListWidgetItem(os.path.split(media.file_name)[1])
media_file.setData(QtCore.Qt.UserRole, QtCore.QVariant(media.file_name))
self.audioListWidget.addItem(media_file)
self.titleEdit.setFocus(QtCore.Qt.OtherFocusReason) self.titleEdit.setFocus(QtCore.Qt.OtherFocusReason)
# Hide or show the preview button. # Hide or show the preview button.
self.previewButton.setVisible(preview) self.previewButton.setVisible(preview)
@ -436,9 +467,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.verseDeleteButton.setEnabled(True) self.verseDeleteButton.setEnabled(True)
def onVerseAddButtonClicked(self): def onVerseAddButtonClicked(self):
self.verse_form.setVerse(u'', True) self.verseForm.setVerse(u'', True)
if self.verse_form.exec_(): if self.verseForm.exec_():
after_text, verse_tag, verse_num = self.verse_form.getVerse() after_text, verse_tag, verse_num = self.verseForm.getVerse()
verse_def = u'%s%s' % (verse_tag, verse_num) verse_def = u'%s%s' % (verse_tag, verse_num)
item = QtGui.QTableWidgetItem(after_text) item = QtGui.QTableWidgetItem(after_text)
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def)) item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def))
@ -454,20 +485,21 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
if item: if item:
tempText = item.text() tempText = item.text()
verseId = unicode(item.data(QtCore.Qt.UserRole).toString()) verseId = unicode(item.data(QtCore.Qt.UserRole).toString())
self.verse_form.setVerse(tempText, True, verseId) self.verseForm.setVerse(tempText, True, verseId)
if self.verse_form.exec_(): if self.verseForm.exec_():
after_text, verse_tag, verse_num = self.verse_form.getVerse() after_text, verse_tag, verse_num = self.verseForm.getVerse()
verse_def = u'%s%s' % (verse_tag, verse_num) verse_def = u'%s%s' % (verse_tag, verse_num)
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def)) item.setData(QtCore.Qt.UserRole, QtCore.QVariant(verse_def))
item.setText(after_text) item.setText(after_text)
# number of lines has change so repaint the list moving the data # number of lines has changed, repaint the list moving the data
if len(tempText.split(u'\n')) != len(after_text.split(u'\n')): if len(tempText.split(u'\n')) != len(after_text.split(u'\n')):
tempList = {} tempList = {}
tempId = {} tempId = {}
for row in range(0, self.verseListWidget.rowCount()): for row in range(0, self.verseListWidget.rowCount()):
tempList[row] = self.verseListWidget.item(row, 0).text() tempList[row] = self.verseListWidget.item(row, 0)\
tempId[row] = self.verseListWidget.item(row, 0).\ .text()
data(QtCore.Qt.UserRole) tempId[row] = self.verseListWidget.item(row, 0)\
.data(QtCore.Qt.UserRole)
self.verseListWidget.clear() self.verseListWidget.clear()
for row in range (0, len(tempList)): for row in range (0, len(tempList)):
item = QtGui.QTableWidgetItem(tempList[row], 0) item = QtGui.QTableWidgetItem(tempList[row], 0)
@ -486,12 +518,12 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
verse_list += u'---[%s:%s]---\n' % (verse_tag, verse_num) verse_list += u'---[%s:%s]---\n' % (verse_tag, verse_num)
verse_list += item.text() verse_list += item.text()
verse_list += u'\n' verse_list += u'\n'
self.verse_form.setVerse(verse_list) self.verseForm.setVerse(verse_list)
else: else:
self.verse_form.setVerse(u'') self.verseForm.setVerse(u'')
if not self.verse_form.exec_(): if not self.verseForm.exec_():
return return
verse_list = self.verse_form.getVerseAll() verse_list = self.verseForm.getVerseAll()
verse_list = unicode(verse_list.replace(u'\r\n', u'\n')) verse_list = unicode(verse_list.replace(u'\r\n', u'\n'))
self.verseListWidget.clear() self.verseListWidget.clear()
self.verseListWidget.setRowCount(0) self.verseListWidget.setRowCount(0)
@ -670,6 +702,66 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.saveSong(True) self.saveSong(True)
Receiver.send_message(u'songs_preview') Receiver.send_message(u'songs_preview')
def onAudioAddFromFileButtonClicked(self):
"""
Loads file(s) from the filesystem.
"""
filters = u'%s (*)' % UiStrings().AllFiles
filenames = QtGui.QFileDialog.getOpenFileNames(self,
translate('SongsPlugin.EditSongForm', 'Open File(s)'),
QtCore.QString(), filters)
for filename in filenames:
item = QtGui.QListWidgetItem(os.path.split(unicode(filename))[1])
item.setData(QtCore.Qt.UserRole, filename)
self.audioListWidget.addItem(item)
def onAudioAddFromMediaButtonClicked(self):
"""
Loads file(s) from the media plugin.
"""
if self.mediaForm.exec_():
for filename in self.mediaForm.getSelectedFiles():
item = QtGui.QListWidgetItem(os.path.split(unicode(filename))[1])
item.setData(QtCore.Qt.UserRole, filename)
self.audioListWidget.addItem(item)
def onAudioRemoveButtonClicked(self):
"""
Removes a file from the list.
"""
row = self.audioListWidget.currentRow()
if row == -1:
return
self.audioListWidget.takeItem(row)
def onAudioRemoveAllButtonClicked(self):
"""
Removes all files from the list.
"""
self.audioListWidget.clear()
def onUpButtonClicked(self):
"""
Moves a file up when the user clicks the up button on the audio tab.
"""
row = self.audioListWidget.currentRow()
if row <= 0:
return
item = self.audioListWidget.takeItem(row)
self.audioListWidget.insertItem(row - 1, item)
self.audioListWidget.setCurrentRow(row - 1)
def onDownButtonClicked(self):
"""
Moves a file down when the user clicks the up button on the audio tab.
"""
row = self.audioListWidget.currentRow()
if row == -1 or row > self.audioListWidget.count() - 1:
return
item = self.audioListWidget.takeItem(row)
self.audioListWidget.insertItem(row + 1, item)
self.audioListWidget.setCurrentRow(row + 1)
def clearCaches(self): def clearCaches(self):
""" """
Free up autocompletion memory on dialog exit Free up autocompletion memory on dialog exit
@ -744,18 +836,55 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.song.theme_name = None self.song.theme_name = None
self._processLyrics() self._processLyrics()
self.song.authors = [] self.song.authors = []
for row in range(self.authorsListView.count()): for row in xrange(self.authorsListView.count()):
item = self.authorsListView.item(row) item = self.authorsListView.item(row)
authorId = (item.data(QtCore.Qt.UserRole)).toInt()[0] authorId = (item.data(QtCore.Qt.UserRole)).toInt()[0]
self.song.authors.append(self.manager.get_object(Author, authorId)) self.song.authors.append(self.manager.get_object(Author, authorId))
self.song.topics = [] self.song.topics = []
for row in range(self.topicsListView.count()): for row in xrange(self.topicsListView.count()):
item = self.topicsListView.item(row) item = self.topicsListView.item(row)
topicId = (item.data(QtCore.Qt.UserRole)).toInt()[0] topicId = (item.data(QtCore.Qt.UserRole)).toInt()[0]
self.song.topics.append(self.manager.get_object(Topic, topicId)) self.song.topics.append(self.manager.get_object(Topic, topicId))
# Save the song here because we need a valid id for the audio files.
clean_song(self.manager, self.song) clean_song(self.manager, self.song)
self.manager.save_object(self.song) self.manager.save_object(self.song)
self.mediaitem.auto_select_id = self.song.id audio_files = map(lambda a: a.file_name, self.song.media_files)
log.debug(audio_files)
save_path = os.path.join(
AppLocation.get_section_data_path(self.mediaitem.plugin.name),
'audio', str(self.song.id))
if not os.path.exists(save_path):
os.makedirs(save_path)
self.song.media_files = []
files = []
for row in xrange(self.audioListWidget.count()):
item = self.audioListWidget.item(row)
filename = unicode(item.data(QtCore.Qt.UserRole).toString())
if not filename.startswith(save_path):
oldfile, filename = filename, os.path.join(save_path,
os.path.split(filename)[1])
shutil.copyfile(oldfile, filename)
files.append(filename)
media_file = MediaFile()
media_file.file_name = filename
media_file.type = u'audio'
media_file.weight = row
self.song.media_files.append(media_file)
for audio in audio_files:
if audio not in files:
try:
os.remove(audio)
except:
log.exception('Could not remove file: %s', audio)
pass
if not files:
try:
os.rmdir(save_path)
except OSError:
log.exception(u'Could not remove directory: %s', save_path)
clean_song(self.manager, self.song)
self.manager.save_object(self.song)
self.mediaitem.autoSelectId = self.song.id
def _processLyrics(self): def _processLyrics(self):
""" """
@ -783,3 +912,4 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
except: except:
log.exception(u'Problem processing song Lyrics \n%s', log.exception(u'Problem processing song Lyrics \n%s',
sxml.dump_xml()) sxml.dump_xml())

View File

@ -0,0 +1,75 @@
# -*- 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, Gerald Britton, Jonathan #
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from PyQt4 import QtCore, QtGui
from openlp.core.lib import translate, build_icon
class Ui_MediaFilesDialog(object):
def setupUi(self, mediaFilesDialog):
mediaFilesDialog.setObjectName(u'mediaFilesDialog')
mediaFilesDialog.setWindowModality(QtCore.Qt.ApplicationModal)
mediaFilesDialog.resize(400, 300)
mediaFilesDialog.setModal(True)
mediaFilesDialog.setWindowIcon(
build_icon(u':/icon/openlp-logo-16x16.png'))
self.filesVerticalLayout = QtGui.QVBoxLayout(mediaFilesDialog)
self.filesVerticalLayout.setSpacing(8)
self.filesVerticalLayout.setMargin(8)
self.filesVerticalLayout.setObjectName(u'filesVerticalLayout')
self.selectLabel = QtGui.QLabel(mediaFilesDialog)
self.selectLabel.setWordWrap(True)
self.selectLabel.setObjectName(u'selectLabel')
self.filesVerticalLayout.addWidget(self.selectLabel)
self.fileListWidget = QtGui.QListWidget(mediaFilesDialog)
self.fileListWidget.setAlternatingRowColors(True)
self.fileListWidget.setSelectionMode(
QtGui.QAbstractItemView.ExtendedSelection)
self.fileListWidget.setObjectName(u'fileListWidget')
self.filesVerticalLayout.addWidget(self.fileListWidget)
self.buttonBox = QtGui.QDialogButtonBox(mediaFilesDialog)
self.buttonBox.setOrientation(QtCore.Qt.Horizontal)
self.buttonBox.setStandardButtons(
QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok)
self.buttonBox.setObjectName(u'buttonBox')
self.filesVerticalLayout.addWidget(self.buttonBox)
self.retranslateUi(mediaFilesDialog)
QtCore.QObject.connect(self.buttonBox,
QtCore.SIGNAL(u'accepted()'), mediaFilesDialog.accept)
QtCore.QObject.connect(self.buttonBox,
QtCore.SIGNAL(u'rejected()'), mediaFilesDialog.reject)
QtCore.QMetaObject.connectSlotsByName(mediaFilesDialog)
def retranslateUi(self, mediaFilesDialog):
mediaFilesDialog.setWindowTitle(
translate('SongsPlugin.MediaFilesForm', 'Select Media File(s)'))
self.selectLabel.setText(
translate('SongsPlugin.MediaFilesForm', u'Select one or more '
'audio files from the list below, and click OK to import them '
'into this song.'))

View File

@ -0,0 +1,57 @@
# -*- 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, Gerald Britton, Jonathan #
# Corwin, Michael Gorven, Scott Guerrieri, Matthias Hub, Meinert Jordan, #
# Armin Köhler, Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias #
# Põldaru, Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Frode Woldsund #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import logging
import os
from PyQt4 import QtCore, QtGui
from mediafilesdialog import Ui_MediaFilesDialog
log = logging.getLogger(__name__)
class MediaFilesForm(QtGui.QDialog, Ui_MediaFilesDialog):
"""
Class to show a list of files from the
"""
log.info(u'%s MediaFilesForm loaded', __name__)
def __init__(self, parent):
QtGui.QDialog.__init__(self)
self.setupUi(self)
def populateFiles(self, files):
self.fileListWidget.clear()
for file in files:
item = QtGui.QListWidgetItem(os.path.split(file)[1])
item.setData(QtCore.Qt.UserRole, file)
self.fileListWidget.addItem(item)
def getSelectedFiles(self):
return map(lambda x: unicode(x.data(QtCore.Qt.UserRole).toString()),
self.fileListWidget.selectedItems())

View File

@ -232,7 +232,8 @@ def init_schema(url):
'authors': relation(Author, backref='songs', 'authors': relation(Author, backref='songs',
secondary=authors_songs_table, lazy=False), secondary=authors_songs_table, lazy=False),
'book': relation(Book, backref='songs'), 'book': relation(Book, backref='songs'),
'media_files': relation(MediaFile, backref='songs'), 'media_files': relation(MediaFile, backref='songs',
order_by=media_files_table.c.weight),
'topics': relation(Topic, backref='songs', 'topics': relation(Topic, backref='songs',
secondary=songs_topics_table) secondary=songs_topics_table)
}) })

View File

@ -28,6 +28,8 @@
import logging import logging
import locale import locale
import re import re
import os
import shutil
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from sqlalchemy.sql import or_ from sqlalchemy.sql import or_
@ -37,11 +39,12 @@ from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \
from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.searchedit import SearchEdit
from openlp.core.lib.ui import UiStrings, context_menu_action, \ from openlp.core.lib.ui import UiStrings, context_menu_action, \
context_menu_separator context_menu_separator
from openlp.core.utils import AppLocation
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \ from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
SongImportForm, SongExportForm SongImportForm, SongExportForm
from openlp.plugins.songs.lib import OpenLyrics, SongXML, VerseType, \ from openlp.plugins.songs.lib import OpenLyrics, SongXML, VerseType, \
clean_string clean_string
from openlp.plugins.songs.lib.db import Author, Song from openlp.plugins.songs.lib.db import Author, Song, MediaFile
from openlp.plugins.songs.lib.ui import SongStrings from openlp.plugins.songs.lib.ui import SongStrings
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -79,6 +82,22 @@ class SongMediaItem(MediaManagerItem):
self.quickPreviewAllowed = True self.quickPreviewAllowed = True
self.hasSearch = True self.hasSearch = True
def _updateBackgroundAudio(self, song, item):
song.media_files = []
for i, bga in enumerate(item.background_audio):
dest_file = os.path.join(
AppLocation.get_section_data_path(self.plugin.name),
u'audio', str(song.id), os.path.split(bga)[1])
if not os.path.exists(os.path.split(dest_file)[0]):
os.makedirs(os.path.split(dest_file)[0])
shutil.copyfile(os.path.join(
AppLocation.get_section_data_path(
u'servicemanager'), bga),
dest_file)
song.media_files.append(MediaFile.populate(
weight=i, file_name=dest_file))
self.plugin.manager.save_object(song, True)
def addEndHeaderBar(self): def addEndHeaderBar(self):
self.addToolbarSeparator() self.addToolbarSeparator()
## Song Maintenance Button ## ## Song Maintenance Button ##
@ -210,7 +229,7 @@ class SongMediaItem(MediaManagerItem):
search_results = self.plugin.manager.get_all_objects(Song, search_results = self.plugin.manager.get_all_objects(Song,
Song.theme_name.like(u'%' + search_keywords + u'%')) Song.theme_name.like(u'%' + search_keywords + u'%'))
self.displayResultsSong(search_results) self.displayResultsSong(search_results)
self.check_search_result() self.checkSearchResult()
def searchEntire(self, search_keywords): def searchEntire(self, search_keywords):
return self.plugin.manager.get_all_objects(Song, return self.plugin.manager.get_all_objects(Song,
@ -244,7 +263,7 @@ class SongMediaItem(MediaManagerItem):
def displayResultsSong(self, searchresults): def displayResultsSong(self, searchresults):
log.debug(u'display results Song') log.debug(u'display results Song')
self.save_auto_select_id() self.saveAutoSelectId()
self.listView.clear() self.listView.clear()
# Sort the songs by its title considering language specific characters. # Sort the songs by its title considering language specific characters.
# lower() is needed for windows! # lower() is needed for windows!
@ -258,9 +277,9 @@ class SongMediaItem(MediaManagerItem):
song_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(song.id)) song_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(song.id))
self.listView.addItem(song_name) self.listView.addItem(song_name)
# Auto-select the item if name has been set # Auto-select the item if name has been set
if song.id == self.auto_select_id: if song.id == self.autoSelectId:
self.listView.setCurrentItem(song_name) self.listView.setCurrentItem(song_name)
self.auto_select_id = -1 self.autoSelectId = -1
def displayResultsAuthor(self, searchresults): def displayResultsAuthor(self, searchresults):
log.debug(u'display results Author') log.debug(u'display results Author')
@ -312,7 +331,7 @@ class SongMediaItem(MediaManagerItem):
self.edit_song_form.exec_() self.edit_song_form.exec_()
self.onClearTextButtonClick() self.onClearTextButtonClick()
self.onSelectionChange() self.onSelectionChange()
self.auto_select_id = -1 self.autoSelectId = -1
def onSongMaintenanceClick(self): def onSongMaintenanceClick(self):
self.song_maintenance_form.exec_() self.song_maintenance_form.exec_()
@ -335,9 +354,9 @@ class SongMediaItem(MediaManagerItem):
if valid: if valid:
self.remoteSong = song_id self.remoteSong = song_id
self.remoteTriggered = remote_type self.remoteTriggered = remote_type
self.edit_song_form.loadSong(song_id, (remote_type == u'P')) self.edit_song_form.loadSong(song_id, remote_type == u'P')
self.edit_song_form.exec_() self.edit_song_form.exec_()
self.auto_select_id = -1 self.autoSelectId = -1
self.onSongListLoad() self.onSongListLoad()
def onEditClick(self): def onEditClick(self):
@ -350,7 +369,7 @@ class SongMediaItem(MediaManagerItem):
item_id = (self.editItem.data(QtCore.Qt.UserRole)).toInt()[0] item_id = (self.editItem.data(QtCore.Qt.UserRole)).toInt()[0]
self.edit_song_form.loadSong(item_id, False) self.edit_song_form.loadSong(item_id, False)
self.edit_song_form.exec_() self.edit_song_form.exec_()
self.auto_select_id = -1 self.autoSelectId = -1
self.onSongListLoad() self.onSongListLoad()
self.editItem = None self.editItem = None
@ -395,12 +414,12 @@ class SongMediaItem(MediaManagerItem):
def generateSlideData(self, service_item, item=None, xmlVersion=False): 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))
item_id = self._getIdOfItemToGenerate(item, self.remoteSong) item_id = self._getIdOfItemToGenerate(item, self.remoteSong)
service_item.add_capability(ItemCapabilities.AllowsEdit) service_item.add_capability(ItemCapabilities.CanEdit)
service_item.add_capability(ItemCapabilities.AllowsPreview) service_item.add_capability(ItemCapabilities.CanPreview)
service_item.add_capability(ItemCapabilities.AllowsLoop) service_item.add_capability(ItemCapabilities.CanLoop)
service_item.add_capability(ItemCapabilities.OnLoadUpdate) service_item.add_capability(ItemCapabilities.OnLoadUpdate)
service_item.add_capability(ItemCapabilities.AddIfNewItem) service_item.add_capability(ItemCapabilities.AddIfNewItem)
service_item.add_capability(ItemCapabilities.AllowsVirtualSplit) service_item.add_capability(ItemCapabilities.CanSoftBreak)
song = self.plugin.manager.get_object(Song, item_id) song = self.plugin.manager.get_object(Song, item_id)
service_item.theme = song.theme_name service_item.theme = song.theme_name
service_item.edit_id = item_id service_item.edit_id = item_id
@ -471,6 +490,10 @@ class SongMediaItem(MediaManagerItem):
service_item.data_string = {u'title': song.search_title, service_item.data_string = {u'title': song.search_title,
u'authors': u', '.join(author_list)} u'authors': u', '.join(author_list)}
service_item.xml_version = self.openLyrics.song_to_xml(song) service_item.xml_version = self.openLyrics.song_to_xml(song)
# Add the audio file to the service item.
if len(song.media_files) > 0:
service_item.add_capability(ItemCapabilities.HasBackgroundAudio)
service_item.background_audio = [m.file_name for m in song.media_files]
return True return True
def serviceLoad(self, item): def serviceLoad(self, item):
@ -510,8 +533,15 @@ class SongMediaItem(MediaManagerItem):
add_song = False add_song = False
editId = song.id editId = song.id
break break
# If there's any backing tracks, copy them over.
if len(item.background_audio) > 0:
self._updateBackgroundAudio(song, item)
if add_song and self.addSongFromService: if add_song and self.addSongFromService:
editId = self.openLyrics.xml_to_song(item.xml_version) song = self.openLyrics.xml_to_song(item.xml_version)
# If there's any backing tracks, copy them over.
if len(item.background_audio) > 0:
self._updateBackgroundAudio(song, item)
editId = song.id
self.onSearchTextButtonClick() self.onSearchTextButtonClick()
else: else:
# Make sure we temporary import formatting tags. # Make sure we temporary import formatting tags.

View File

@ -372,7 +372,7 @@ class OpenLyrics(object):
self._process_topics(properties, song) self._process_topics(properties, song)
clean_song(self.manager, song) clean_song(self.manager, song)
self.manager.save_object(song) self.manager.save_object(song)
return song.id return song
def _add_text_to_element(self, tag, parent, text=None, label=None): def _add_text_to_element(self, tag, parent, text=None, label=None):
if label: if label:

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MediaFilesDialog</class>
<widget class="QDialog" name="MediaFilesDialog">
<property name="windowModality">
<enum>Qt::ApplicationModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>Select Media File(s)</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="filesVerticalLayout">
<property name="spacing">
<number>8</number>
</property>
<property name="margin">
<number>8</number>
</property>
<item>
<widget class="QLabel" name="selectLabel">
<property name="text">
<string>Select one or more audio files from the list below, and click OK to import them into this song.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QListView" name="fileListView">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="../images/openlp-2.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>MediaFilesDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>MediaFilesDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff