This commit is contained in:
Tim Bentley 2011-01-21 20:51:08 +00:00
commit 84b252fe70
14 changed files with 299 additions and 183 deletions

View File

@ -28,8 +28,6 @@ Provide common toolbar handling for OpenLP
""" """
import logging import logging
from PyQt4 import QtCore, QtGui
from openlp.core.lib import build_icon from openlp.core.lib import build_icon
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -51,7 +49,8 @@ class OpenLPToolbar(QtGui.QToolBar):
log.debug(u'Init done') log.debug(u'Init done')
def addToolbarButton(self, title, icon, tooltip=None, slot=None, def addToolbarButton(self, title, icon, tooltip=None, slot=None,
checkable=False): checkable=False, shortcut=0, alternate=0,
context=QtCore.Qt.WidgetShortcut):
""" """
A method to help developers easily add a button to the toolbar. A method to help developers easily add a button to the toolbar.
@ -72,6 +71,15 @@ class OpenLPToolbar(QtGui.QToolBar):
``checkable`` ``checkable``
If *True* the button has two, *off* and *on*, states. Default is If *True* the button has two, *off* and *on*, states. Default is
*False*, which means the buttons has only one state. *False*, which means the buttons has only one state.
``shortcut``
The primary shortcut for this action
``alternate``
The alternate shortcut for this action
``context``
Specify the context in which this shortcut is valid
""" """
newAction = None newAction = None
if icon: if icon:
@ -93,6 +101,8 @@ class OpenLPToolbar(QtGui.QToolBar):
QtCore.QObject.connect(newAction, QtCore.QObject.connect(newAction,
QtCore.SIGNAL(u'toggled(bool)'), slot) QtCore.SIGNAL(u'toggled(bool)'), slot)
self.actions[title] = newAction self.actions[title] = newAction
newAction.setShortcuts([shortcut, alternate])
newAction.setShortcutContext(context)
return newAction return newAction
def addToolbarSeparator(self, handle): def addToolbarSeparator(self, handle):

View File

@ -54,45 +54,6 @@ class DisplayWidget(QtGui.QGraphicsView):
QtGui.QGraphicsView.__init__(self) QtGui.QGraphicsView.__init__(self)
self.parent = parent self.parent = parent
self.live = live self.live = live
self.hotkey_map = {
QtCore.Qt.Key_Return: 'servicemanager_next_item',
QtCore.Qt.Key_Space: 'slidecontroller_live_next_noloop',
QtCore.Qt.Key_Enter: 'slidecontroller_live_next_noloop',
QtCore.Qt.Key_0: 'servicemanager_next_item',
QtCore.Qt.Key_Backspace: 'slidecontroller_live_previous_noloop'}
self.setStyleSheet(u'border: none;')
def keyPressEvent(self, event):
"""
Handle key events from display screen
"""
# Key events only needed for live
if not self.live:
return
if isinstance(event, QtGui.QKeyEvent):
# Here accept the event and do something
if event.key() == QtCore.Qt.Key_Up:
Receiver.send_message(u'slidecontroller_live_previous')
event.accept()
elif event.key() == QtCore.Qt.Key_Down:
Receiver.send_message(u'slidecontroller_live_next')
event.accept()
elif event.key() == QtCore.Qt.Key_PageUp:
Receiver.send_message(u'slidecontroller_live_first')
event.accept()
elif event.key() == QtCore.Qt.Key_PageDown:
Receiver.send_message(u'slidecontroller_live_last')
event.accept()
elif event.key() in self.hotkey_map:
Receiver.send_message(self.hotkey_map[event.key()])
event.accept()
elif event.key() == QtCore.Qt.Key_Escape:
self.setVisible(False)
self.videoStop()
event.accept()
event.ignore()
else:
event.ignore()
class MainDisplay(DisplayWidget): class MainDisplay(DisplayWidget):

View File

@ -49,34 +49,6 @@ class ServiceManagerList(QtGui.QTreeWidget):
QtGui.QTreeWidget.__init__(self, parent) QtGui.QTreeWidget.__init__(self, parent)
self.mainwindow = mainwindow self.mainwindow = mainwindow
def keyPressEvent(self, event):
if isinstance(event, QtGui.QKeyEvent):
#here accept the event and do something
if event.key() == QtCore.Qt.Key_Enter:
self.mainwindow.makeLive()
event.accept()
elif event.key() == QtCore.Qt.Key_Home:
self.mainwindow.onServiceTop()
event.accept()
elif event.key() == QtCore.Qt.Key_End:
self.mainwindow.onServiceEnd()
event.accept()
elif event.key() == QtCore.Qt.Key_PageUp:
self.mainwindow.onServiceUp()
event.accept()
elif event.key() == QtCore.Qt.Key_PageDown:
self.mainwindow.onServiceDown()
event.accept()
elif event.key() == QtCore.Qt.Key_Up:
self.mainwindow.onMoveSelectionUp()
event.accept()
elif event.key() == QtCore.Qt.Key_Down:
self.mainwindow.onMoveSelectionDown()
event.accept()
event.ignore()
else:
event.ignore()
def mouseMoveEvent(self, event): def mouseMoveEvent(self, event):
""" """
Drag and drop event does not care what data is selected Drag and drop event does not care what data is selected
@ -178,50 +150,72 @@ class ServiceManager(QtGui.QWidget):
self.layout.addWidget(self.serviceManagerList) self.layout.addWidget(self.serviceManagerList)
# Add the bottom toolbar # Add the bottom toolbar
self.orderToolbar = OpenLPToolbar(self) self.orderToolbar = OpenLPToolbar(self)
self.orderToolbar.addToolbarButton( self.serviceManagerList.moveTop = self.orderToolbar.addToolbarButton(
translate('OpenLP.ServiceManager', 'Move to &top'), translate('OpenLP.ServiceManager', 'Move to &top'),
u':/services/service_top.png', u':/services/service_top.png',
translate('OpenLP.ServiceManager', translate('OpenLP.ServiceManager',
'Move item to the top of the service.'), 'Move item to the top of the service.'),
self.onServiceTop) self.onServiceTop, shortcut=QtCore.Qt.Key_Home)
self.orderToolbar.addToolbarButton( self.serviceManagerList.moveUp = self.orderToolbar.addToolbarButton(
translate('OpenLP.ServiceManager', 'Move &up'), translate('OpenLP.ServiceManager', 'Move &up'),
u':/services/service_up.png', u':/services/service_up.png',
translate('OpenLP.ServiceManager', translate('OpenLP.ServiceManager',
'Move item up one position in the service.'), 'Move item up one position in the service.'),
self.onServiceUp) self.onServiceUp, shortcut=QtCore.Qt.Key_PageUp)
self.orderToolbar.addToolbarButton( self.serviceManagerList.moveDown = self.orderToolbar.addToolbarButton(
translate('OpenLP.ServiceManager', 'Move &down'), translate('OpenLP.ServiceManager', 'Move &down'),
u':/services/service_down.png', u':/services/service_down.png',
translate('OpenLP.ServiceManager', translate('OpenLP.ServiceManager',
'Move item down one position in the service.'), 'Move item down one position in the service.'),
self.onServiceDown) self.onServiceDown, shortcut=QtCore.Qt.Key_PageDown)
self.orderToolbar.addToolbarButton( self.serviceManagerList.moveBottom = self.orderToolbar.addToolbarButton(
translate('OpenLP.ServiceManager', 'Move to &bottom'), translate('OpenLP.ServiceManager', 'Move to &bottom'),
u':/services/service_bottom.png', u':/services/service_bottom.png',
translate('OpenLP.ServiceManager', translate('OpenLP.ServiceManager',
'Move item to the end of the service.'), 'Move item to the end of the service.'),
self.onServiceEnd) self.onServiceEnd, shortcut=QtCore.Qt.Key_End)
self.serviceManagerList.down = self.orderToolbar.addToolbarButton(
translate('OpenLP.ServiceManager', 'Move &down'),
None,
translate('OpenLP.ServiceManager',
'Moves the selection up the window.'),
self.onMoveSelectionDown, shortcut=QtCore.Qt.Key_Up)
self.serviceManagerList.down.setVisible(False)
self.serviceManagerList.up = self.orderToolbar.addToolbarButton(
translate('OpenLP.ServiceManager', 'Move up'),
None,
translate('OpenLP.ServiceManager',
'Moves the selection up the window.'),
self.onMoveSelectionUp, shortcut=QtCore.Qt.Key_Up)
self.serviceManagerList.up.setVisible(False)
self.orderToolbar.addSeparator() self.orderToolbar.addSeparator()
self.orderToolbar.addToolbarButton( self.serviceManagerList.delete = self.orderToolbar.addToolbarButton(
translate('OpenLP.ServiceManager', '&Delete From Service'), translate('OpenLP.ServiceManager', '&Delete From Service'),
u':/general/general_delete.png', u':/general/general_delete.png',
translate('OpenLP.ServiceManager', translate('OpenLP.ServiceManager',
'Delete the selected item from the service.'), 'Delete the selected item from the service.'),
self.onDeleteFromService) self.onDeleteFromService)
self.orderToolbar.addSeparator() self.orderToolbar.addSeparator()
self.orderToolbar.addToolbarButton( self.serviceManagerList.expand = self.orderToolbar.addToolbarButton(
translate('OpenLP.ServiceManager', '&Expand all'), translate('OpenLP.ServiceManager', '&Expand all'),
u':/services/service_expand_all.png', u':/services/service_expand_all.png',
translate('OpenLP.ServiceManager', translate('OpenLP.ServiceManager',
'Expand all the service items.'), 'Expand all the service items.'),
self.onExpandAll) self.onExpandAll)
self.orderToolbar.addToolbarButton( self.serviceManagerList.collapse = self.orderToolbar.addToolbarButton(
translate('OpenLP.ServiceManager', '&Collapse all'), translate('OpenLP.ServiceManager', '&Collapse all'),
u':/services/service_collapse_all.png', u':/services/service_collapse_all.png',
translate('OpenLP.ServiceManager', translate('OpenLP.ServiceManager',
'Collapse all the service items.'), 'Collapse all the service items.'),
self.onCollapseAll) self.onCollapseAll)
self.orderToolbar.addSeparator()
self.serviceManagerList.makeLive = self.orderToolbar.addToolbarButton(
translate('OpenLP.ServiceManager', 'Go Live'),
u':/general/general_live.png',
translate('OpenLP.ServiceManager',
'Send the selected item to Live.'),
self.makeLive, shortcut=QtCore.Qt.Key_Enter,
alternate=QtCore.Qt.Key_Return)
self.orderToolbar.setObjectName(u'orderToolbar') self.orderToolbar.setObjectName(u'orderToolbar')
self.layout.addWidget(self.orderToolbar) self.layout.addWidget(self.orderToolbar)
# Connect up our signals and slots # Connect up our signals and slots
@ -292,8 +286,28 @@ class ServiceManager(QtGui.QWidget):
self.themeMenu = QtGui.QMenu( self.themeMenu = QtGui.QMenu(
translate('OpenLP.ServiceManager', '&Change Item Theme')) translate('OpenLP.ServiceManager', '&Change Item Theme'))
self.menu.addMenu(self.themeMenu) self.menu.addMenu(self.themeMenu)
self.setServiceHotkeys()
self.serviceManagerList.addActions(
[self.serviceManagerList.moveDown,
self.serviceManagerList.moveUp,
self.serviceManagerList.makeLive,
self.serviceManagerList.moveTop,
self.serviceManagerList.moveBottom,
self.serviceManagerList.up,
self.serviceManagerList.down
])
self.configUpdated() self.configUpdated()
def setServiceHotkeys(self):
actionList = self.mainwindow.actionList
actionList.add_action(self.serviceManagerList.moveDown, u'Service')
actionList.add_action(self.serviceManagerList.moveUp, u'Service')
actionList.add_action(self.serviceManagerList.moveTop, u'Service')
actionList.add_action(self.serviceManagerList.moveBottom, u'Service')
actionList.add_action(self.serviceManagerList.makeLive, u'Service')
actionList.add_action(self.serviceManagerList.up, u'Service')
actionList.add_action(self.serviceManagerList.down, u'Service')
def setModified(self, modified=True): def setModified(self, modified=True):
""" """
Setter for property "modified". Sets whether or not the current service Setter for property "modified". Sets whether or not the current service

View File

@ -33,6 +33,7 @@ from PyQt4.phonon import Phonon
from openlp.core.ui import HideMode, MainDisplay from openlp.core.ui import HideMode, MainDisplay
from openlp.core.lib import OpenLPToolbar, Receiver, resize_image, \ from openlp.core.lib import OpenLPToolbar, Receiver, resize_image, \
ItemCapabilities, translate ItemCapabilities, translate
from openlp.core.utils import ActionList
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -44,34 +45,6 @@ class SlideList(QtGui.QTableWidget):
def __init__(self, parent=None, name=None): def __init__(self, parent=None, name=None):
QtGui.QTableWidget.__init__(self, parent.Controller) QtGui.QTableWidget.__init__(self, parent.Controller)
self.parent = parent self.parent = parent
self.hotkeyMap = {
QtCore.Qt.Key_Return: 'servicemanager_next_item',
QtCore.Qt.Key_Space: 'slidecontroller_live_next_noloop',
QtCore.Qt.Key_Enter: 'slidecontroller_live_next_noloop',
QtCore.Qt.Key_0: 'servicemanager_next_item',
QtCore.Qt.Key_Backspace: 'slidecontroller_live_previous_noloop'}
def keyPressEvent(self, event):
if isinstance(event, QtGui.QKeyEvent):
#here accept the event and do something
if event.key() == QtCore.Qt.Key_Up:
self.parent.onSlideSelectedPrevious()
event.accept()
elif event.key() == QtCore.Qt.Key_Down:
self.parent.onSlideSelectedNext()
event.accept()
elif event.key() == QtCore.Qt.Key_PageUp:
self.parent.onSlideSelectedFirst()
event.accept()
elif event.key() == QtCore.Qt.Key_PageDown:
self.parent.onSlideSelectedLast()
event.accept()
elif event.key() in self.hotkeyMap and self.parent.isLive:
Receiver.send_message(self.hotkeyMap[event.key()])
event.accept()
event.ignore()
else:
event.ignore()
class SlideController(QtGui.QWidget): class SlideController(QtGui.QWidget):
@ -162,12 +135,14 @@ class SlideController(QtGui.QWidget):
sizeToolbarPolicy.setHeightForWidth( sizeToolbarPolicy.setHeightForWidth(
self.Toolbar.sizePolicy().hasHeightForWidth()) self.Toolbar.sizePolicy().hasHeightForWidth())
self.Toolbar.setSizePolicy(sizeToolbarPolicy) self.Toolbar.setSizePolicy(sizeToolbarPolicy)
self.Toolbar.addToolbarButton( self.previousItem = self.Toolbar.addToolbarButton(
u'Previous Slide', u':/slides/slide_previous.png', translate('OpenLP.SlideController', 'Previous Slide'),
u':/slides/slide_previous.png',
translate('OpenLP.SlideController', 'Move to previous'), translate('OpenLP.SlideController', 'Move to previous'),
self.onSlideSelectedPrevious) self.onSlideSelectedPrevious)
self.Toolbar.addToolbarButton( self.nextItem = self.Toolbar.addToolbarButton(
u'Next Slide', u':/slides/slide_next.png', translate('OpenLP.SlideController', 'Next Slide'),
u':/slides/slide_next.png',
translate('OpenLP.SlideController', 'Move to next'), translate('OpenLP.SlideController', 'Move to next'),
self.onSlideSelectedNext) self.onSlideSelectedNext)
if self.isLive: if self.isLive:
@ -339,6 +314,25 @@ class SlideController(QtGui.QWidget):
self.onGoLiveClick) self.onGoLiveClick)
self.Toolbar.makeWidgetsInvisible(self.songEditList) self.Toolbar.makeWidgetsInvisible(self.songEditList)
self.Mediabar.setVisible(False) self.Mediabar.setVisible(False)
if self.isLive:
self.setLiveHotkeys(self)
self.PreviewListWidget.addActions(
[self.previousItem,
self.nextItem,
self.previousService,
self.nextService,
self.escapeItem])
self.display.addActions(
[self.previousItem,
self.nextItem,
self.previousService,
self.nextService,
self.escapeItem])
else:
self.setPreviewHotkeys()
self.PreviewListWidget.addActions(
[self.nextItem,
self.previousItem])
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'slidecontroller_%s_stop_loop' % self.typePrefix), QtCore.SIGNAL(u'slidecontroller_%s_stop_loop' % self.typePrefix),
self.onStopLoop) self.onStopLoop)
@ -379,6 +373,55 @@ class SlideController(QtGui.QWidget):
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'config_screen_changed'), self.screenSizeChanged) QtCore.SIGNAL(u'config_screen_changed'), self.screenSizeChanged)
def setPreviewHotkeys(self, parent=None):
actionList = self.parent.actionList
self.previousItem.setShortcuts([QtCore.Qt.Key_Up, 0])
actionList.add_action(self.previousItem, u'Preview')
self.nextItem.setShortcuts([QtCore.Qt.Key_Down, 0])
actionList.add_action(self.nextItem, u'Preview')
def setLiveHotkeys(self, parent=None):
actionList = self.parent.actionList
self.previousItem.setShortcuts([QtCore.Qt.Key_Up, QtCore.Qt.Key_PageUp])
self.previousItem.setShortcutContext(
QtCore.Qt.WidgetWithChildrenShortcut)
actionList.add_action(self.nextItem, u'Live')
self.nextItem.setShortcuts([QtCore.Qt.Key_Down, QtCore.Qt.Key_PageDown])
self.nextItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
actionList.add_action(self.nextItem, u'Live')
self.previousService = QtGui.QAction(translate(
'OpenLP.SlideController', 'Previous Service'), parent)
self.previousService.setShortcuts([QtCore.Qt.Key_Left, 0])
self.previousService.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
QtCore.QObject.connect(self.previousService,
QtCore.SIGNAL(u'triggered()'), self.servicePrevious)
actionList.add_action(self.previousService, u'Live')
self.nextService = QtGui.QAction(translate(
'OpenLP.SlideController', 'Next Service'), parent)
self.nextService.setShortcuts([QtCore.Qt.Key_Right, 0])
self.nextService.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
QtCore.QObject.connect(self.nextService,
QtCore.SIGNAL(u'triggered()'), self.serviceNext)
actionList.add_action(self.nextService, u'Live')
self.escapeItem = QtGui.QAction(translate(
'OpenLP.SlideController', 'Escape Item'), parent)
self.escapeItem.setShortcuts([QtCore.Qt.Key_Escape, 0])
self.escapeItem.setShortcutContext(QtCore.Qt.WidgetWithChildrenShortcut)
QtCore.QObject.connect(self.escapeItem,
QtCore.SIGNAL(u'triggered()'), self.liveEscape)
actionList.add_action(self.escapeItem, u'Live')
def liveEscape(self):
self.display.setVisible(False)
self.display.videoStop()
def servicePrevious(self):
Receiver.send_message('servicemanager_previous_item')
def serviceNext(self):
Receiver.send_message('servicemanager_next_item')
def screenSizeChanged(self): def screenSizeChanged(self):
""" """
Settings dialog has changed the screen size of adjust output and Settings dialog has changed the screen size of adjust output and
@ -389,6 +432,13 @@ class SlideController(QtGui.QWidget):
self.display.imageManager = self.parent.renderManager.image_manager self.display.imageManager = self.parent.renderManager.image_manager
self.display.alertTab = self.alertTab self.display.alertTab = self.alertTab
self.display.setup() self.display.setup()
if self.isLive:
self.display.addActions(
[self.previousItem,
self.nextItem,
self.previousService,
self.nextService,
self.escapeItem])
# The SlidePreview's ratio. # The SlidePreview's ratio.
self.ratio = float(self.screens.current[u'size'].width()) / \ self.ratio = float(self.screens.current[u'size'].width()) / \
float(self.screens.current[u'size'].height()) float(self.screens.current[u'size'].height())
@ -1071,4 +1121,3 @@ class SlideController(QtGui.QWidget):
if self.DesktopScreen.isChecked: if self.DesktopScreen.isChecked:
self.DesktopScreen.setChecked(False) self.DesktopScreen.setChecked(False)
self.HideMenu.setDefaultAction(self.DesktopScreen) self.HideMenu.setDefaultAction(self.DesktopScreen)

View File

@ -27,6 +27,7 @@
The :mod:`~openlp.core.utils.actions` module provides action list classes used The :mod:`~openlp.core.utils.actions` module provides action list classes used
by the shortcuts system. by the shortcuts system.
""" """
from PyQt4 import QtCore
class ActionCategory(object): class ActionCategory(object):
""" """

View File

@ -177,7 +177,7 @@ def parse_reference(reference):
to_verse = -1 to_verse = -1
if to_chapter > from_chapter: if to_chapter > from_chapter:
ref_list.append((book, from_chapter, from_verse, -1)) ref_list.append((book, from_chapter, from_verse, -1))
for i in range(from_chapter + 1, to_chapter - 1): for i in range(from_chapter + 1, to_chapter):
ref_list.append((book, i, 1, -1)) ref_list.append((book, i, 1, -1))
ref_list.append((book, to_chapter, 1, to_verse)) ref_list.append((book, to_chapter, 1, to_verse))
elif to_verse >= from_verse or to_verse == -1: elif to_verse >= from_verse or to_verse == -1:

View File

@ -784,7 +784,6 @@ class SongImportForm(OpenLPWizard):
filenames=self.getListOfFiles(self.songBeamerFileListWidget) filenames=self.getListOfFiles(self.songBeamerFileListWidget)
) )
if importer.do_import(): if importer.do_import():
# reload songs
self.progressLabel.setText( self.progressLabel.setText(
translate('SongsPlugin.SongImportForm', 'Finished import.')) translate('SongsPlugin.SongImportForm', 'Finished import.'))
else: else:

View File

@ -50,6 +50,14 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
self.authorform = AuthorsForm(self) self.authorform = AuthorsForm(self)
self.topicform = TopicsForm(self) self.topicform = TopicsForm(self)
self.bookform = SongBookForm(self) self.bookform = SongBookForm(self)
# Disable all edit and delete buttons, as there is no row selected.
self.authorsDeleteButton.setEnabled(False)
self.authorsEditButton.setEnabled(False)
self.topicsDeleteButton.setEnabled(False)
self.topicsEditButton.setEnabled(False)
self.booksDeleteButton.setEnabled(False)
self.booksEditButton.setEnabled(False)
# Signals
QtCore.QObject.connect(self.authorsAddButton, QtCore.QObject.connect(self.authorsAddButton,
QtCore.SIGNAL(u'pressed()'), self.onAuthorAddButtonClick) QtCore.SIGNAL(u'pressed()'), self.onAuthorAddButtonClick)
QtCore.QObject.connect(self.topicsAddButton, QtCore.QObject.connect(self.topicsAddButton,
@ -68,6 +76,15 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
QtCore.SIGNAL(u'pressed()'), self.onTopicDeleteButtonClick) QtCore.SIGNAL(u'pressed()'), self.onTopicDeleteButtonClick)
QtCore.QObject.connect(self.booksDeleteButton, QtCore.QObject.connect(self.booksDeleteButton,
QtCore.SIGNAL(u'pressed()'), self.onBookDeleteButtonClick) QtCore.SIGNAL(u'pressed()'), self.onBookDeleteButtonClick)
QtCore.QObject.connect(self.authorsListWidget,
QtCore.SIGNAL(u'currentRowChanged(int)'),
self.onAuthorsListRowChanged)
QtCore.QObject.connect(self.topicsListWidget,
QtCore.SIGNAL(u'currentRowChanged(int)'),
self.onTopicsListRowChanged)
QtCore.QObject.connect(self.booksListWidget,
QtCore.SIGNAL(u'currentRowChanged(int)'),
self.onBooksListRowChanged)
def exec_(self): def exec_(self):
self.typeListWidget.setCurrentRow(0) self.typeListWidget.setCurrentRow(0)
@ -115,12 +132,6 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
u' '.join([author.first_name, author.last_name])) u' '.join([author.first_name, author.last_name]))
author_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(author.id)) author_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(author.id))
self.authorsListWidget.addItem(author_name) self.authorsListWidget.addItem(author_name)
if self.authorsListWidget.count() == 0:
self.authorsDeleteButton.setEnabled(False)
self.authorsEditButton.setEnabled(False)
else:
self.authorsDeleteButton.setEnabled(True)
self.authorsEditButton.setEnabled(True)
def resetTopics(self): def resetTopics(self):
""" """
@ -132,12 +143,6 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
topic_name = QtGui.QListWidgetItem(topic.name) topic_name = QtGui.QListWidgetItem(topic.name)
topic_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(topic.id)) topic_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(topic.id))
self.topicsListWidget.addItem(topic_name) self.topicsListWidget.addItem(topic_name)
if self.topicsListWidget.count() == 0:
self.topicsDeleteButton.setEnabled(False)
self.topicsEditButton.setEnabled(False)
else:
self.topicsDeleteButton.setEnabled(True)
self.topicsEditButton.setEnabled(True)
def resetBooks(self): def resetBooks(self):
""" """
@ -150,26 +155,22 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
book.publisher)) book.publisher))
book_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(book.id)) book_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(book.id))
self.booksListWidget.addItem(book_name) self.booksListWidget.addItem(book_name)
if self.booksListWidget.count() == 0:
self.booksDeleteButton.setEnabled(False)
self.booksEditButton.setEnabled(False)
else:
self.booksDeleteButton.setEnabled(True)
self.booksEditButton.setEnabled(True)
def checkAuthor(self, new_author, edit=False): def checkAuthor(self, new_author, edit=False):
""" """
Returns False if the given Author is already in the list otherwise Returns *False* if the given Author already exists, otherwise *True*.
True.
``edit``
If we edit an item, this should be *True*.
""" """
authors = self.manager.get_all_objects(Author, authors = self.manager.get_all_objects(Author,
and_(Author.first_name == new_author.first_name, and_(Author.first_name == new_author.first_name,
Author.last_name == new_author.last_name, Author.last_name == new_author.last_name,
Author.display_name == new_author.display_name)) Author.display_name == new_author.display_name))
# Check if this author already exists.
if len(authors) > 0: if len(authors) > 0:
# If we edit an existing Author, we need to make sure that we do # If we edit an existing Author, we need to make sure that we do
# not return False when nothing has changed (because this would # not return False when nothing has changed.
# cause an error message later on).
if edit: if edit:
for author in authors: for author in authors:
if author.id != new_author.id: if author.id != new_author.id:
@ -182,14 +183,16 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
def checkTopic(self, new_topic, edit=False): def checkTopic(self, new_topic, edit=False):
""" """
Returns False if the given Topic is already in the list otherwise True. Returns *False* if the given Topic already exists, otherwise *True*.
``edit``
If we edit an item, this should be *True*.
""" """
topics = self.manager.get_all_objects(Topic, topics = self.manager.get_all_objects(Topic,
Topic.name == new_topic.name) Topic.name == new_topic.name)
if len(topics) > 0: if len(topics) > 0:
# If we edit an existing Topic, we need to make sure that we do # If we edit an existing Topic, we need to make sure that we do
# not return False when nothing has changed (because this would # not return False when nothing has changed.
# cause an error message later on).
if edit: if edit:
for topic in topics: for topic in topics:
if topic.id != new_topic.id: if topic.id != new_topic.id:
@ -202,15 +205,17 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
def checkBook(self, new_book, edit=False): def checkBook(self, new_book, edit=False):
""" """
Returns False if the given Book is already in the list otherwise True. Returns *False* if the given Topic already exists, otherwise *True*.
``edit``
If we edit an item, this should be *True*.
""" """
books = self.manager.get_all_objects(Book, books = self.manager.get_all_objects(Book,
and_(Book.name == new_book.name, and_(Book.name == new_book.name,
Book.publisher == new_book.publisher)) Book.publisher == new_book.publisher))
if len(books) > 0: if len(books) > 0:
# If we edit an existing Book, we need to make sure that we do # If we edit an existing Book, we need to make sure that we do
# not return False when nothing has changed (because this would # not return False when nothing has changed.
# cause an error message later on).
if edit: if edit:
for book in books: for book in books:
if book.id != new_book.id: if book.id != new_book.id:
@ -390,12 +395,14 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
Merges two authors into one author. Merges two authors into one author.
``old_author`` ``old_author``
The author which will be deleted afterwards. The object, which was edited, that will be deleted
""" """
# Find the duplicate.
existing_author = self.manager.get_object_filtered(Author, existing_author = self.manager.get_object_filtered(Author,
and_(Author.first_name == old_author.first_name, and_(Author.first_name == old_author.first_name,
Author.last_name == old_author.last_name, Author.last_name == old_author.last_name,
Author.display_name == old_author.display_name)) Author.display_name == old_author.display_name))
# Find the songs, which have the old_author as author.
songs = self.manager.get_all_objects(Song, songs = self.manager.get_all_objects(Song,
Song.authors.contains(old_author)) Song.authors.contains(old_author))
for song in songs: for song in songs:
@ -412,10 +419,12 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
Merges two topics into one topic. Merges two topics into one topic.
``old_topic`` ``old_topic``
The topic which will be deleted afterwards. The object, which was edited, that will be deleted
""" """
# Find the duplicate.
existing_topic = self.manager.get_object_filtered(Topic, existing_topic = self.manager.get_object_filtered(Topic,
Topic.name == old_topic.name) Topic.name == old_topic.name)
# Find the songs, which have the old_topic as topic.
songs = self.manager.get_all_objects(Song, songs = self.manager.get_all_objects(Song,
Song.topics.contains(old_topic)) Song.topics.contains(old_topic))
for song in songs: for song in songs:
@ -432,11 +441,13 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
Merges two books into one book. Merges two books into one book.
``old_book`` ``old_book``
The book which will be deleted afterwards. The object, which was edited, that will be deleted
""" """
# Find the duplicate.
existing_book = self.manager.get_object_filtered(Book, existing_book = self.manager.get_object_filtered(Book,
and_(Book.name == old_book.name, and_(Book.name == old_book.name,
Book.publisher == old_book.publisher)) Book.publisher == old_book.publisher))
# Find the songs, which have the old_book as book.
songs = self.manager.get_all_objects(Song, songs = self.manager.get_all_objects(Song,
Song.song_book_id == old_book.id) Song.song_book_id == old_book.id)
for song in songs: for song in songs:
@ -482,3 +493,46 @@ class SongMaintenanceForm(QtGui.QDialog, Ui_SongMaintenanceDialog):
'This book cannot be deleted, it is currently ' 'This book cannot be deleted, it is currently '
'assigned to at least one song.'), 'assigned to at least one song.'),
translate('SongsPlugin.SongMaintenanceForm', 'No book selected!')) translate('SongsPlugin.SongMaintenanceForm', 'No book selected!'))
def onAuthorsListRowChanged(self, row):
"""
Called when the *authorsListWidget* current's row has changed.
``row``
The current row. If there is no current row, the value is -1
"""
if row == -1:
self.authorsDeleteButton.setEnabled(False)
self.authorsEditButton.setEnabled(False)
else:
self.authorsDeleteButton.setEnabled(True)
self.authorsEditButton.setEnabled(True)
def onTopicsListRowChanged(self, row):
"""
Called when the *booksListWidget* current's row has changed.
``row``
The current row. If there is no current row, the value is -1.
"""
if row == -1:
self.topicsDeleteButton.setEnabled(False)
self.topicsEditButton.setEnabled(False)
else:
self.topicsDeleteButton.setEnabled(True)
self.topicsEditButton.setEnabled(True)
def onBooksListRowChanged(self, row):
"""
Called when the *booksListWidget* current's row has changed.
``row``
The current row. If there is no current row, the value is -1.
"""
if row == -1:
self.booksDeleteButton.setEnabled(False)
self.booksEditButton.setEnabled(False)
else:
self.booksDeleteButton.setEnabled(True)
self.booksEditButton.setEnabled(True)

View File

@ -313,7 +313,6 @@ class EasiSlidesImport(SongImport):
tag = SeqTypes[tag.lower()] tag = SeqTypes[tag.lower()]
else: else:
continue continue
if tag in versetags: if tag in versetags:
self.verse_order_list.append(tag) self.verse_order_list.append(tag)
else: else:

View File

@ -157,7 +157,6 @@ class SongMediaItem(MediaManagerItem):
(5, u':/slides/slide_theme.png', (5, u':/slides/slide_theme.png',
translate('SongsPlugin.MediaItem', 'Themes')) translate('SongsPlugin.MediaItem', 'Themes'))
]) ])
self.configUpdated() self.configUpdated()
def onSearchTextButtonClick(self): def onSearchTextButtonClick(self):
@ -269,7 +268,7 @@ class SongMediaItem(MediaManagerItem):
def onImportClick(self): def onImportClick(self):
if not hasattr(self, u'import_wizard'): if not hasattr(self, u'import_wizard'):
self.import_wizard = SongImportForm(self, self.parent) self.import_wizard = SongImportForm(self, self.parent)
self.import_wizard.exec_() if self.import_wizard.exec_() == QtGui.QDialog.Accepted:
Receiver.send_message(u'songs_load_list') Receiver.send_message(u'songs_load_list')
def onNewClick(self): def onNewClick(self):

View File

@ -61,17 +61,18 @@ class OpenLyricsImport(SongImport):
Imports the songs. Imports the songs.
""" """
self.import_wizard.progressBar.setMaximum(len(self.import_source)) self.import_wizard.progressBar.setMaximum(len(self.import_source))
parser = etree.XMLParser(remove_blank_text=True)
for file_path in self.import_source: for file_path in self.import_source:
if self.stop_import_flag: if self.stop_import_flag:
return False return False
self.import_wizard.incrementProgressBar(unicode(translate( self.import_wizard.incrementProgressBar(unicode(translate(
'SongsPlugin.OpenLyricsImport', 'Importing %s...')) % 'SongsPlugin.OpenLyricsImport', 'Importing %s...')) %
os.path.basename(file_path)) os.path.basename(file_path))
parser = etree.XMLParser(remove_blank_text=True) try:
parsed_file = etree.parse(file_path, parser) parsed_file = etree.parse(file_path, parser)
xml = unicode(etree.tostring(parsed_file)) xml = unicode(etree.tostring(parsed_file))
if self.openLyrics.xml_to_song(xml) is None: if self.openLyrics.xml_to_song(xml) is None:
log.debug(u'File could not be imported: %s' % file_path) log.debug(u'File could not be imported: %s' % file_path)
# Importing this song failed! For now we stop import. except etree.XMLSyntaxError:
return False log.exception(u'XML syntax error in file %s' % file_path)
return True return True

View File

@ -197,16 +197,24 @@ class SongImport(QtCore.QObject):
return return
self.media_files.append(filename) self.media_files.append(filename)
def add_verse(self, verse, versetag=u'V'): def add_verse(self, versetext, versetag=u'V', lang=None):
""" """
Add a verse. This is the whole verse, lines split by \n Add a verse. This is the whole verse, lines split by \n. It will also
Verse tag can be V1/C1/B etc, or 'V' and 'C' (will count the verses/ attempt to detect duplicates. In this case it will just add to the verse
choruses itself) or None, where it will assume verse order.
It will also attempt to detect duplicates. In this case it will just
add to the verse order ``versetext``
The text of the verse.
``versetag``
The verse tag can be V1/C1/B etc, or 'V' and 'C' (will count the
verses/choruses itself) or None, where it will assume verse.
``lang``
The language code (ISO-639) of the verse, for example *en* or *de*.
""" """
for (oldversetag, oldverse) in self.verses: for (oldversetag, oldverse, oldlang) in self.verses:
if oldverse.strip() == verse.strip(): if oldverse.strip() == versetext.strip():
self.verse_order_list.append(oldversetag) self.verse_order_list.append(oldversetag)
return return
if versetag[0] in self.versecounts: if versetag[0] in self.versecounts:
@ -217,7 +225,7 @@ class SongImport(QtCore.QObject):
versetag += unicode(self.versecounts[versetag[0]]) versetag += unicode(self.versecounts[versetag[0]])
elif int(versetag[1:]) > self.versecounts[versetag[0]]: elif int(versetag[1:]) > self.versecounts[versetag[0]]:
self.versecounts[versetag[0]] = int(versetag[1:]) self.versecounts[versetag[0]] = int(versetag[1:])
self.verses.append([versetag, verse.rstrip()]) self.verses.append([versetag, versetext.rstrip(), lang])
self.verse_order_list.append(versetag) self.verse_order_list.append(versetag)
if versetag.startswith(u'V') and self.contains_verse(u'C1'): if versetag.startswith(u'V') and self.contains_verse(u'C1'):
self.verse_order_list.append(u'C1') self.verse_order_list.append(u'C1')
@ -266,7 +274,7 @@ class SongImport(QtCore.QObject):
verses_changed_to_other = {} verses_changed_to_other = {}
sxml = SongXML() sxml = SongXML()
other_count = 1 other_count = 1
for (versetag, versetext) in self.verses: for (versetag, versetext, lang) in self.verses:
if versetag[0] == u'C': if versetag[0] == u'C':
versetype = VerseType.to_string(VerseType.Chorus) versetype = VerseType.to_string(VerseType.Chorus)
elif versetag[0] == u'V': elif versetag[0] == u'V':
@ -286,7 +294,7 @@ class SongImport(QtCore.QObject):
versetype = VerseType.to_string(VerseType.Other) versetype = VerseType.to_string(VerseType.Other)
log.info(u'Versetype %s changing to %s' , versetag, newversetag) log.info(u'Versetype %s changing to %s' , versetag, newversetag)
versetag = newversetag versetag = newversetag
sxml.add_verse_to_lyrics(versetype, versetag[1:], versetext) sxml.add_verse_to_lyrics(versetype, versetag[1:], versetext, lang)
song.search_lyrics += u' ' + self.remove_punctuation(versetext) song.search_lyrics += u' ' + self.remove_punctuation(versetext)
song.search_lyrics = song.search_lyrics.lower() song.search_lyrics = song.search_lyrics.lower()
song.lyrics = unicode(sxml.extract_xml(), u'utf-8') song.lyrics = unicode(sxml.extract_xml(), u'utf-8')
@ -338,7 +346,7 @@ class SongImport(QtCore.QObject):
+ u'========================================' + u'========================================'
print u'TITLE: ' + self.title print u'TITLE: ' + self.title
print u'ALT TITLE: ' + self.alternate_title print u'ALT TITLE: ' + self.alternate_title
for (versetag, versetext) in self.verses: for (versetag, versetext, lang) in self.verses:
print u'VERSE ' + versetag + u': ' + versetext print u'VERSE ' + versetag + u': ' + versetext
print u'ORDER: ' + u' '.join(self.verse_order_list) print u'ORDER: ' + u' '.join(self.verse_order_list)
for author in self.authors: for author in self.authors:

View File

@ -31,7 +31,7 @@ The basic XML for storing the lyrics in the song database is of the format::
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<song version="1.0"> <song version="1.0">
<lyrics> <lyrics>
<verse type="chorus" label="1"> <verse type="Chorus" label="1" lang="en">
<![CDATA[ ... ]]> <![CDATA[ ... ]]>
</verse> </verse>
</lyrics> </lyrics>
@ -84,7 +84,7 @@ class SongXML(object):
self.song_xml = objectify.fromstring(u'<song version="1.0" />') self.song_xml = objectify.fromstring(u'<song version="1.0" />')
self.lyrics = etree.SubElement(self.song_xml, u'lyrics') self.lyrics = etree.SubElement(self.song_xml, u'lyrics')
def add_verse_to_lyrics(self, type, number, content): def add_verse_to_lyrics(self, type, number, content, lang=None):
""" """
Add a verse to the *<lyrics>* tag. Add a verse to the *<lyrics>* tag.
@ -97,9 +97,15 @@ class SongXML(object):
``content`` ``content``
The actual text of the verse to be stored. The actual text of the verse to be stored.
``lang``
The verse's language code (ISO-639). This is not required, but
should be added if available.
""" """
verse = etree.Element(u'verse', type=unicode(type), verse = etree.Element(u'verse', type=unicode(type),
label=unicode(number)) label=unicode(number))
if lang:
verse.set(u'lang', lang)
verse.text = etree.CDATA(content) verse.text = etree.CDATA(content)
self.lyrics.append(verse) self.lyrics.append(verse)
@ -117,6 +123,11 @@ class SongXML(object):
``xml`` ``xml``
The XML of the song to be parsed. The XML of the song to be parsed.
The returned list has the following format::
[[{'lang': 'en', 'type': 'V', 'label': '1'}, u"The English verse."],
[{'lang': 'en', 'type': 'C', 'label': '1'}, u"The English chorus."]]
""" """
self.song_xml = None self.song_xml = None
if xml[:5] == u'<?xml': if xml[:5] == u'<?xml':
@ -196,7 +207,7 @@ class OpenLyrics(object):
This property is not supported. This property is not supported.
*<verse name="v1a" lang="he" translit="en">* *<verse name="v1a" lang="he" translit="en">*
The attribute *translit* and *lang* are not supported. The attribute *translit* is not supported.
*<verseOrder>* *<verseOrder>*
OpenLP supports this property. OpenLP supports this property.
@ -268,13 +279,16 @@ class OpenLyrics(object):
# No xml get out of here. # No xml get out of here.
if not xml: if not xml:
return None return None
song = Song()
if xml[:5] == u'<?xml': if xml[:5] == u'<?xml':
xml = xml[38:] xml = xml[38:]
# Remove chords from xml. # Remove chords from xml.
xml = re.compile(u'<chord name=".*?"/>').sub(u'', xml) xml = re.compile(u'<chord name=".*?"/>').sub(u'', xml)
song_xml = objectify.fromstring(xml) song_xml = objectify.fromstring(xml)
try:
properties = song_xml.properties properties = song_xml.properties
except AttributeError:
return None
song = Song()
self._process_copyright(properties, song) self._process_copyright(properties, song)
self._process_cclinumber(properties, song) self._process_cclinumber(properties, song)
self._process_titles(properties, song) self._process_titles(properties, song)
@ -442,7 +456,10 @@ class OpenLyrics(object):
if not verse_number: if not verse_number:
verse_number = u'1' verse_number = u'1'
temp_verse_order.append((verse_type, verse_number, verse_part)) temp_verse_order.append((verse_type, verse_number, verse_part))
sxml.add_verse_to_lyrics(verse_type, verse_number, text) lang = None
if self._get(verse, u'lang'):
lang = self._get(verse, u'lang')
sxml.add_verse_to_lyrics(verse_type, verse_number, text, lang)
search_text = search_text + text search_text = search_text + text
song.search_lyrics = search_text.lower() song.search_lyrics = search_text.lower()
song.lyrics = unicode(sxml.extract_xml(), u'utf-8') song.lyrics = unicode(sxml.extract_xml(), u'utf-8')

View File

@ -134,7 +134,7 @@ class SongsPlugin(Plugin):
def onToolsReindexItemTriggered(self): def onToolsReindexItemTriggered(self):
""" """
Rebuild the search title of each song. Rebuild each song.
""" """
maxSongs = self.manager.get_object_count(Song) maxSongs = self.manager.get_object_count(Song)
progressDialog = QtGui.QProgressDialog( progressDialog = QtGui.QProgressDialog(
@ -150,8 +150,13 @@ class SongsPlugin(Plugin):
song.title = u'' song.title = u''
if song.alternate_title is None: if song.alternate_title is None:
song.alternate_title = u'' song.alternate_title = u''
song.search_title = self.whitespace.sub(u' ', song.title.lower() + \ song.search_title = self.whitespace.sub(u' ', song.title.lower() +
u' ' + song.alternate_title.lower()) u' ' + song.alternate_title.lower())
# Remove the "language" attribute from lyrics tag. This is not very
# important, but this keeps the database clean. This can be removed
# when everybody has run the reindex tool once.
song.lyrics = song.lyrics.replace(
u'<lyrics language="en">', u'<lyrics>')
lyrics = u'' lyrics = u''
verses = SongXML().get_verses(song.lyrics) verses = SongXML().get_verses(song.lyrics)
for verse in verses: for verse in verses:
@ -159,8 +164,7 @@ class SongsPlugin(Plugin):
song.search_lyrics = lyrics.lower() song.search_lyrics = lyrics.lower()
progressDialog.setValue(counter) progressDialog.setValue(counter)
self.manager.save_objects(songs) self.manager.save_objects(songs)
counter += 1 progressDialog.setValue(counter + 1)
progressDialog.setValue(counter)
self.mediaItem.displayResultsSong( self.mediaItem.displayResultsSong(
self.manager.get_all_objects(Song, order_by_ref=Song.search_title)) self.manager.get_all_objects(Song, order_by_ref=Song.search_title))