This commit is contained in:
rimach crichter@web.de 2011-05-23 21:42:10 +02:00
commit d39ecb8e8e
34 changed files with 660 additions and 164 deletions

View File

@ -85,10 +85,13 @@ class OpenLP(QtGui.QApplication):
QtGui.QApplication.exec_() QtGui.QApplication.exec_()
self.sharedMemory.detach() self.sharedMemory.detach()
def run(self): def run(self, args):
""" """
Run the OpenLP application. Run the OpenLP application.
""" """
# On Windows, the args passed into the constructor are
# ignored. Not very handy, so set the ones we want to use.
self.args = args
# provide a listener for widgets to reqest a screen update. # provide a listener for widgets to reqest a screen update.
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'openlp_process_events'), self.processEvents) QtCore.SIGNAL(u'openlp_process_events'), self.processEvents)
@ -115,7 +118,7 @@ class OpenLP(QtGui.QApplication):
# make sure Qt really display the splash screen # make sure Qt really display the splash screen
self.processEvents() self.processEvents()
# start the main app window # start the main app window
self.mainWindow = MainWindow(self.clipboard(), self.arguments()) self.mainWindow = MainWindow(self.clipboard(), self.args)
self.mainWindow.show() self.mainWindow.show()
if show_splash: if show_splash:
# now kill the splashscreen # now kill the splashscreen
@ -250,7 +253,7 @@ def main():
log.debug(u'Could not find default_translator.') log.debug(u'Could not find default_translator.')
if not options.no_error_form: if not options.no_error_form:
sys.excepthook = app.hookException sys.excepthook = app.hookException
sys.exit(app.run()) sys.exit(app.run(qt_args))
if __name__ == u'__main__': if __name__ == u'__main__':
""" """

View File

@ -28,6 +28,7 @@ Provides the generic functions for interfacing plugins with the Media Manager.
""" """
import logging import logging
import os import os
import re
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
@ -90,6 +91,7 @@ class MediaManagerItem(QtGui.QWidget):
""" """
QtGui.QWidget.__init__(self) QtGui.QWidget.__init__(self)
self.parent = parent self.parent = parent
self.whitespace = re.compile(r'\W+', re.UNICODE)
#TODO: plugin should not be the parent in future #TODO: plugin should not be the parent in future
self.plugin = parent # plugin self.plugin = parent # plugin
visible_title = self.plugin.getString(StringContent.VisibleName) visible_title = self.plugin.getString(StringContent.VisibleName)
@ -102,16 +104,21 @@ class MediaManagerItem(QtGui.QWidget):
self.remoteTriggered = None self.remoteTriggered = None
self.singleServiceItem = True self.singleServiceItem = True
self.quickPreviewAllowed = False self.quickPreviewAllowed = False
self.hasSearch = False
self.pageLayout = QtGui.QVBoxLayout(self) self.pageLayout = QtGui.QVBoxLayout(self)
self.pageLayout.setSpacing(0) self.pageLayout.setSpacing(0)
self.pageLayout.setMargin(0) self.pageLayout.setMargin(0)
self.requiredIcons() self.requiredIcons()
self.setupUi() self.setupUi()
self.retranslateUi() self.retranslateUi()
self.autoSelectItem = None
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_service_load' % self.parent.name.lower()), QtCore.SIGNAL(u'%s_service_load' % self.parent.name.lower()),
self.serviceLoad) self.serviceLoad)
QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'%s_set_autoselect_item' % self.parent.name.lower()),
self.setAutoSelectItem)
def requiredIcons(self): def requiredIcons(self):
""" """
This method is called to define the icons for the plugin. This method is called to define the icons for the plugin.
@ -464,6 +471,9 @@ class MediaManagerItem(QtGui.QWidget):
if keepFocus: if keepFocus:
self.listView.setFocus() self.listView.setFocus()
def setAutoSelectItem(self, itemToSelect=None):
self.autoSelectItem = itemToSelect
def onLiveClick(self): def onLiveClick(self):
""" """
Send an item live by building a service item then adding that service Send an item live by building a service item then adding that service
@ -474,11 +484,23 @@ class MediaManagerItem(QtGui.QWidget):
translate('OpenLP.MediaManagerItem', translate('OpenLP.MediaManagerItem',
'You must select one or more items to send live.')) 'You must select one or more items to send live.'))
else: else:
log.debug(u'%s Live requested', self.plugin.name) self.goLive()
serviceItem = self.buildServiceItem()
if serviceItem: def goLive(self, item_id=None):
log.debug(u'%s Live requested', self.plugin.name)
item = None
if item_id:
item = self.createItemFromId(item_id)
serviceItem = self.buildServiceItem(item)
if serviceItem:
if not item_id:
serviceItem.from_plugin = True serviceItem.from_plugin = True
self.parent.liveController.addServiceItem(serviceItem) self.parent.liveController.addServiceItem(serviceItem)
def createItemFromId(self, item_id):
item = QtGui.QListWidgetItem()
item.setData(QtCore.Qt.UserRole, QtCore.QVariant(item_id))
return item
def onAddClick(self): def onAddClick(self):
""" """
@ -487,24 +509,24 @@ class MediaManagerItem(QtGui.QWidget):
if not self.listView.selectedIndexes() and not self.remoteTriggered: if not self.listView.selectedIndexes() and not self.remoteTriggered:
QtGui.QMessageBox.information(self, UiStrings().NISp, QtGui.QMessageBox.information(self, UiStrings().NISp,
translate('OpenLP.MediaManagerItem', translate('OpenLP.MediaManagerItem',
'You must select one or more items.')) 'You must select one or more items to add.'))
else: else:
# Is it posssible to process multiple list items to generate # Is it posssible to process multiple list items to generate
# multiple service items? # multiple service items?
if self.singleServiceItem or self.remoteTriggered: if self.singleServiceItem or self.remoteTriggered:
log.debug(u'%s Add requested', self.plugin.name) log.debug(u'%s Add requested', self.plugin.name)
serviceItem = self.buildServiceItem(None, True) self.addToService(replace=self.remoteTriggered)
if serviceItem:
serviceItem.from_plugin = False
self.parent.serviceManager.addServiceItem(serviceItem,
replace=self.remoteTriggered)
else: else:
items = self.listView.selectedIndexes() items = self.listView.selectedIndexes()
for item in items: for item in items:
serviceItem = self.buildServiceItem(item, True) self.addToService(item)
if serviceItem:
serviceItem.from_plugin = False def addToService(self, item=None, replace=None):
self.parent.serviceManager.addServiceItem(serviceItem) serviceItem = self.buildServiceItem(item, True)
if serviceItem:
serviceItem.from_plugin = False
self.parent.serviceManager.addServiceItem(serviceItem,
replace=replace)
def onAddEditClick(self): def onAddEditClick(self):
""" """
@ -586,3 +608,10 @@ class MediaManagerItem(QtGui.QWidget):
else: else:
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 search(self, string):
"""
Performs a plugin specific search for items containing ``string``
"""
raise NotImplementedError(
u'Plugin.search needs to be defined by the plugin')

View File

@ -211,3 +211,12 @@ class PluginManager(object):
if plugin.isActive(): if plugin.isActive():
plugin.finalise() plugin.finalise()
log.info(u'Finalisation Complete for %s ' % plugin.name) log.info(u'Finalisation Complete for %s ' % plugin.name)
def get_plugin_by_name(self, name):
"""
Return the plugin which has a name with value ``name``
"""
for plugin in self.plugins:
if plugin.name == name:
return plugin
return None

View File

@ -300,7 +300,7 @@ class Renderer(object):
# Adjust width and height to account for shadow. outline done in css # Adjust width and height to account for shadow. outline done in css
self.page_shell = u'<html><head><style>' \ self.page_shell = u'<html><head><style>' \
u'*{margin: 0; padding: 0; border: 0;} '\ u'*{margin: 0; padding: 0; border: 0;} '\
u'#main {position:absolute; top:0px; %s %s}</style><body>' \ u'#main {position:absolute; top:0px; %s %s}</style></head><body>' \
u'<div id="main">' % \ u'<div id="main">' % \
(build_lyrics_format_css(self.theme_data, self.page_width, (build_lyrics_format_css(self.theme_data, self.page_width,
self.page_height), build_lyrics_outline_css(self.theme_data)) self.page_height), build_lyrics_outline_css(self.theme_data))

View File

@ -207,6 +207,8 @@ class ThemeXML(object):
""" """
A class to encapsulate the Theme XML. A class to encapsulate the Theme XML.
""" """
FIRST_CAMEL_REGEX = re.compile(u'(.)([A-Z][a-z]+)')
SECOND_CAMEL_REGEX = re.compile(u'([a-z0-9])([A-Z])')
def __init__(self): def __init__(self):
""" """
Initialise the theme object. Initialise the theme object.
@ -581,8 +583,8 @@ class ThemeXML(object):
""" """
Change Camel Case string to python string Change Camel Case string to python string
""" """
sub_name = re.sub(u'(.)([A-Z][a-z]+)', r'\1_\2', name) sub_name = ThemeXML.FIRST_CAMEL_REGEX.sub(r'\1_\2', name)
return re.sub(u'([a-z0-9])([A-Z])', r'\1_\2', sub_name).lower() return ThemeXML.SECOND_CAMEL_REGEX.sub(r'\1_\2', sub_name).lower()
def _build_xml_from_attrs(self): def _build_xml_from_attrs(self):
""" """

View File

@ -229,6 +229,8 @@ class Ui_MainWindow(object):
self.ToolsOpenDataFolder = icon_action(mainWindow, self.ToolsOpenDataFolder = icon_action(mainWindow,
u'ToolsOpenDataFolder', u':/general/general_open.png', u'ToolsOpenDataFolder', u':/general/general_open.png',
category=UiStrings().Tools) category=UiStrings().Tools)
self.updateThemeImages = base_action(mainWindow,
u'updateThemeImages', category=UiStrings().Tools)
action_list.add_category(UiStrings().Settings, CategoryOrder.standardMenu) action_list.add_category(UiStrings().Settings, CategoryOrder.standardMenu)
self.settingsPluginListItem = shortcut_action(mainWindow, self.settingsPluginListItem = shortcut_action(mainWindow,
u'settingsPluginListItem', [QtGui.QKeySequence(u'Alt+F7')], u'settingsPluginListItem', [QtGui.QKeySequence(u'Alt+F7')],
@ -292,6 +294,7 @@ class Ui_MainWindow(object):
self.SettingsConfigureItem)) self.SettingsConfigureItem))
add_actions(self.ToolsMenu, (self.ToolsAddToolItem, None)) add_actions(self.ToolsMenu, (self.ToolsAddToolItem, None))
add_actions(self.ToolsMenu, (self.ToolsOpenDataFolder, None)) add_actions(self.ToolsMenu, (self.ToolsOpenDataFolder, None))
add_actions(self.ToolsMenu, [self.updateThemeImages])
add_actions(self.HelpMenu, (self.HelpDocumentationItem, add_actions(self.HelpMenu, (self.HelpDocumentationItem,
self.HelpOnlineHelpItem, None, self.helpWebSiteItem, self.HelpOnlineHelpItem, None, self.helpWebSiteItem,
self.HelpAboutItem)) self.HelpAboutItem))
@ -433,6 +436,11 @@ class Ui_MainWindow(object):
translate('OpenLP.MainWindow', 'Open &Data Folder...')) translate('OpenLP.MainWindow', 'Open &Data Folder...'))
self.ToolsOpenDataFolder.setStatusTip(translate('OpenLP.MainWindow', self.ToolsOpenDataFolder.setStatusTip(translate('OpenLP.MainWindow',
'Open the folder where songs, bibles and other data resides.')) 'Open the folder where songs, bibles and other data resides.'))
self.updateThemeImages.setText(
translate('OpenLP.MainWindow', 'Update Theme Images'))
self.updateThemeImages.setStatusTip(
translate('OpenLP.MainWindow', 'Update the preview images for all '
'themes.'))
self.ModeDefaultItem.setText( self.ModeDefaultItem.setText(
translate('OpenLP.MainWindow', '&Default')) translate('OpenLP.MainWindow', '&Default'))
self.ModeDefaultItem.setStatusTip(translate('OpenLP.MainWindow', self.ModeDefaultItem.setStatusTip(translate('OpenLP.MainWindow',
@ -505,6 +513,8 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
QtCore.SIGNAL(u'triggered()'), self.onHelpOnLineHelpClicked) QtCore.SIGNAL(u'triggered()'), self.onHelpOnLineHelpClicked)
QtCore.QObject.connect(self.ToolsOpenDataFolder, QtCore.QObject.connect(self.ToolsOpenDataFolder,
QtCore.SIGNAL(u'triggered()'), self.onToolsOpenDataFolderClicked) QtCore.SIGNAL(u'triggered()'), self.onToolsOpenDataFolderClicked)
QtCore.QObject.connect(self.updateThemeImages,
QtCore.SIGNAL(u'triggered()'), self.onUpdateThemeImages)
QtCore.QObject.connect(self.DisplayTagItem, QtCore.QObject.connect(self.DisplayTagItem,
QtCore.SIGNAL(u'triggered()'), self.onDisplayTagItemClicked) QtCore.SIGNAL(u'triggered()'), self.onDisplayTagItemClicked)
QtCore.QObject.connect(self.SettingsConfigureItem, QtCore.QObject.connect(self.SettingsConfigureItem,
@ -615,11 +625,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
if self.liveController.display.isVisible(): if self.liveController.display.isVisible():
self.liveController.display.setFocus() self.liveController.display.setFocus()
self.activateWindow() self.activateWindow()
# On Windows, arguments contains the entire commandline if len(self.arguments):
# So args[0]=='python' args[1]=='openlp.pyw'
# Therefore this approach is not going to work
# Bypass for now.
if len(self.arguments) and os.name != u'nt':
args = [] args = []
for a in self.arguments: for a in self.arguments:
args.extend([a]) args.extend([a])
@ -717,6 +723,12 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
path = AppLocation.get_data_path() path = AppLocation.get_data_path()
QtGui.QDesktopServices.openUrl(QtCore.QUrl("file:///" + path)) QtGui.QDesktopServices.openUrl(QtCore.QUrl("file:///" + path))
def onUpdateThemeImages(self):
"""
Updates the new theme preview images.
"""
self.themeManagerContents.updatePreviewImages()
def onDisplayTagItemClicked(self): def onDisplayTagItemClicked(self):
""" """
Show the Settings dialog Show the Settings dialog
@ -778,16 +790,18 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
def screenChanged(self): def screenChanged(self):
""" """
The screen has changed to so tell the displays to update_display The screen has changed so we have to update components such as the
their locations renderer.
""" """
log.debug(u'screenChanged') log.debug(u'screenChanged')
Receiver.send_message(u'cursor_busy')
self.image_manager.update_display() self.image_manager.update_display()
self.renderer.update_display() self.renderer.update_display()
self.liveController.screenSizeChanged()
self.previewController.screenSizeChanged() self.previewController.screenSizeChanged()
self.liveController.screenSizeChanged()
self.setFocus() self.setFocus()
self.activateWindow() self.activateWindow()
Receiver.send_message(u'cursor_normal')
def closeEvent(self, event): def closeEvent(self, event):
""" """

View File

@ -757,7 +757,7 @@ class ServiceManager(QtGui.QWidget):
""" """
Called by a signal to select a specific item. Called by a signal to select a specific item.
""" """
self.setItem(int(message[0])) self.setItem(int(message))
def setItem(self, index): def setItem(self, index):
""" """

View File

@ -211,6 +211,11 @@ class SlideController(QtGui.QWidget):
u'Go Live', u':/general/general_live.png', u'Go Live', u':/general/general_live.png',
translate('OpenLP.SlideController', 'Move to live'), translate('OpenLP.SlideController', 'Move to live'),
self.onGoLive) self.onGoLive)
self.toolbar.addToolbarButton(
# Does not need translating - control string.
u'Add to Service', u':/general/general_add.png',
translate('OpenLP.SlideController', 'Add to Service'),
self.onPreviewAddToService)
self.toolbar.addToolbarSeparator(u'Close Separator') self.toolbar.addToolbarSeparator(u'Close Separator')
self.toolbar.addToolbarButton( self.toolbar.addToolbarButton(
# Does not need translating - control string. # Does not need translating - control string.
@ -1044,12 +1049,26 @@ class SlideController(QtGui.QWidget):
Receiver.send_message(u'%s_edit' % self.serviceItem.name.lower(), Receiver.send_message(u'%s_edit' % self.serviceItem.name.lower(),
u'P:%s' % self.serviceItem.edit_id) u'P:%s' % self.serviceItem.edit_id)
def onPreviewAddToService(self):
"""
From the preview display request the Item to be added to service
"""
self.parent.ServiceManagerContents.addServiceItem(self.serviceItem)
def onGoLiveClick(self): def onGoLiveClick(self):
""" """
triggered by clicking the Preview slide items triggered by clicking the Preview slide items
""" """
if QtCore.QSettings().value(u'advanced/double click live', if QtCore.QSettings().value(u'advanced/double click live',
QtCore.QVariant(False)).toBool(): QtCore.QVariant(False)).toBool():
# Live and Preview have issues if we have video or presentations
# playing in both at the same time.
if self.serviceItem.is_command():
Receiver.send_message(u'%s_stop' %
self.serviceItem.name.lower(),
[self.serviceItem, self.isLive])
if self.serviceItem.is_media():
self.onMediaClose()
self.onGoLive() self.onGoLive()
def onGoLive(self): def onGoLive(self):

View File

@ -660,6 +660,18 @@ class ThemeManager(QtGui.QWidget):
pixmap.save(thumb, u'png') pixmap.save(thumb, u'png')
log.debug(u'Theme image written to %s', samplepathname) log.debug(u'Theme image written to %s', samplepathname)
def updatePreviewImages(self):
"""
Called to update the themes' preview images.
"""
self.mainwindow.displayProgressBar(len(self.themelist))
for theme in self.themelist:
self.mainwindow.incrementProgressBar()
self.generateAndSaveImage(
self.path, theme, self.getThemeData(theme))
self.mainwindow.finishedProgressBar()
self.loadThemes()
def generateImage(self, themeData, forcePage=False): def generateImage(self, themeData, forcePage=False):
""" """
Call the renderer to build a Sample Image Call the renderer to build a Sample Image

View File

@ -342,7 +342,7 @@ def add_actions(target, actions):
The menu or toolbar to add actions to. The menu or toolbar to add actions to.
``actions`` ``actions``
The actions to be added. An action consisting of the keyword 'None' The actions to be added. An action consisting of the keyword ``None``
will result in a separator being inserted into the target. will result in a separator being inserted into the target.
""" """
for action in actions: for action in actions:

View File

@ -323,7 +323,7 @@ class BibleDB(QtCore.QObject, Manager):
""" """
return self.get_all_objects(Book, order_by_ref=Book.id) return self.get_all_objects(Book, order_by_ref=Book.id)
def get_verses(self, reference_list): def get_verses(self, reference_list, show_error=True):
""" """
This is probably the most used function. It retrieves the list of This is probably the most used function. It retrieves the list of
verses based on the user's query. verses based on the user's query.
@ -360,11 +360,12 @@ class BibleDB(QtCore.QObject, Manager):
verse_list.extend(verses) verse_list.extend(verses)
else: else:
log.debug(u'OpenLP failed to find book %s', book) log.debug(u'OpenLP failed to find book %s', book)
critical_error_message_box( if show_error:
translate('BiblesPlugin', 'No Book Found'), critical_error_message_box(
translate('BiblesPlugin', 'No matching book ' translate('BiblesPlugin', 'No Book Found'),
'could be found in this Bible. Check that you have ' translate('BiblesPlugin', 'No matching book '
'spelled the name of the book correctly.')) 'could be found in this Bible. Check that you '
'have spelled the name of the book correctly.'))
return verse_list return verse_list
def verse_search(self, text): def verse_search(self, text):

View File

@ -425,7 +425,7 @@ class HTTPBible(BibleDB):
self.create_meta(u'proxy password', self.proxy_password) self.create_meta(u'proxy password', self.proxy_password)
return True return True
def get_verses(self, reference_list): def get_verses(self, reference_list, show_error=True):
""" """
A reimplementation of the ``BibleDB.get_verses`` method, this one is A reimplementation of the ``BibleDB.get_verses`` method, this one is
specifically for web Bibles. It first checks to see if the particular specifically for web Bibles. It first checks to see if the particular
@ -453,11 +453,12 @@ class HTTPBible(BibleDB):
if not db_book: if not db_book:
book_details = HTTPBooks.get_book(book) book_details = HTTPBooks.get_book(book)
if not book_details: if not book_details:
critical_error_message_box( if show_error:
translate('BiblesPlugin', 'No Book Found'), critical_error_message_box(
translate('BiblesPlugin', 'No matching ' translate('BiblesPlugin', 'No Book Found'),
'book could be found in this Bible. Check that you ' translate('BiblesPlugin', 'No matching '
'have spelled the name of the book correctly.')) 'book could be found in this Bible. Check that you '
'have spelled the name of the book correctly.'))
return [] return []
db_book = self.create_book(book_details[u'name'], db_book = self.create_book(book_details[u'name'],
book_details[u'abbreviation'], book_details[u'abbreviation'],
@ -480,7 +481,7 @@ class HTTPBible(BibleDB):
Receiver.send_message(u'openlp_process_events') Receiver.send_message(u'openlp_process_events')
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')
return BibleDB.get_verses(self, reference_list) return BibleDB.get_verses(self, reference_list, show_error)
def get_chapter(self, book, chapter): def get_chapter(self, book, chapter):
""" """

View File

@ -231,7 +231,7 @@ class BibleManager(object):
bible, book, chapter) bible, book, chapter)
return self.db_cache[bible].get_verse_count(book, chapter) return self.db_cache[bible].get_verse_count(book, chapter)
def get_verses(self, bible, versetext): def get_verses(self, bible, versetext, show_error=True):
""" """
Parses a scripture reference, fetches the verses from the Bible Parses a scripture reference, fetches the verses from the Bible
specified, and returns a list of ``Verse`` objects. specified, and returns a list of ``Verse`` objects.
@ -252,32 +252,34 @@ class BibleManager(object):
""" """
log.debug(u'BibleManager.get_verses("%s", "%s")', bible, versetext) log.debug(u'BibleManager.get_verses("%s", "%s")', bible, versetext)
if not bible: if not bible:
Receiver.send_message(u'openlp_information_message', { if show_error:
u'title': translate('BiblesPlugin.BibleManager', Receiver.send_message(u'openlp_information_message', {
'No Bibles Available'), u'title': translate('BiblesPlugin.BibleManager',
u'message': translate('BiblesPlugin.BibleManager', 'No Bibles Available'),
'There are no Bibles currently installed. Please use the ' u'message': translate('BiblesPlugin.BibleManager',
'Import Wizard to install one or more Bibles.') 'There are no Bibles currently installed. Please use the '
}) 'Import Wizard to install one or more Bibles.')
})
return None return None
reflist = parse_reference(versetext) reflist = parse_reference(versetext)
if reflist: if reflist:
return self.db_cache[bible].get_verses(reflist) return self.db_cache[bible].get_verses(reflist, show_error)
else: else:
Receiver.send_message(u'openlp_information_message', { if show_error:
u'title': translate('BiblesPlugin.BibleManager', Receiver.send_message(u'openlp_information_message', {
'Scripture Reference Error'), u'title': translate('BiblesPlugin.BibleManager',
u'message': translate('BiblesPlugin.BibleManager', 'Scripture Reference Error'),
'Your scripture reference is either not supported by OpenLP ' u'message': translate('BiblesPlugin.BibleManager',
'or is invalid. Please make sure your reference conforms to ' 'Your scripture reference is either not supported by '
'one of the following patterns:\n\n' 'OpenLP or is invalid. Please make sure your reference '
'Book Chapter\n' 'conforms to one of the following patterns:\n\n'
'Book Chapter-Chapter\n' 'Book Chapter\n'
'Book Chapter:Verse-Verse\n' 'Book Chapter-Chapter\n'
'Book Chapter:Verse-Verse,Verse-Verse\n' 'Book Chapter:Verse-Verse\n'
'Book Chapter:Verse-Verse,Chapter:Verse-Verse\n' 'Book Chapter:Verse-Verse,Verse-Verse\n'
'Book Chapter:Verse-Chapter:Verse') 'Book Chapter:Verse-Verse,Chapter:Verse-Verse\n'
}) 'Book Chapter:Verse-Chapter:Verse')
})
return None return None
def verse_search(self, bible, second_bible, text): def verse_search(self, bible, second_bible, text):

View File

@ -61,6 +61,7 @@ class BibleMediaItem(MediaManagerItem):
# Place to store the search results for both bibles. # Place to store the search results for both bibles.
self.settings = self.parent.settings_tab self.settings = self.parent.settings_tab
self.quickPreviewAllowed = True self.quickPreviewAllowed = True
self.hasSearch = True
self.search_results = {} self.search_results = {}
self.second_search_results = {} self.second_search_results = {}
self.check_search_result() self.check_search_result()
@ -646,6 +647,19 @@ class BibleMediaItem(MediaManagerItem):
Receiver.send_message(u'openlp_process_events') Receiver.send_message(u'openlp_process_events')
def displayResults(self, bible, second_bible=u''): def displayResults(self, bible, second_bible=u''):
"""
Displays the search results in the media manager. All data needed for
further action is saved for/in each row.
"""
items = self.buildDisplayResults(bible, second_bible,
self.search_results)
for bible_verse in items:
self.listView.addItem(bible_verse)
self.listView.selectAll()
self.search_results = {}
self.second_search_results = {}
def buildDisplayResults(self, bible, second_bible, search_results):
""" """
Displays the search results in the media manager. All data needed for Displays the search results in the media manager. All data needed for
further action is saved for/in each row. further action is saved for/in each row.
@ -665,7 +679,8 @@ class BibleMediaItem(MediaManagerItem):
second_bible, u'Copyright').value second_bible, u'Copyright').value
second_permissions = self.parent.manager.get_meta_data( second_permissions = self.parent.manager.get_meta_data(
second_bible, u'Permissions').value second_bible, u'Permissions').value
for count, verse in enumerate(self.search_results): items = []
for count, verse in enumerate(search_results):
data = { data = {
'book': QtCore.QVariant(verse.book.name), 'book': QtCore.QVariant(verse.book.name),
'chapter': QtCore.QVariant(verse.chapter), 'chapter': QtCore.QVariant(verse.chapter),
@ -697,10 +712,8 @@ class BibleMediaItem(MediaManagerItem):
verse.chapter, verse_separator, verse.verse, version) verse.chapter, verse_separator, verse.verse, version)
bible_verse = QtGui.QListWidgetItem(bible_text) bible_verse = QtGui.QListWidgetItem(bible_text)
bible_verse.setData(QtCore.Qt.UserRole, QtCore.QVariant(data)) bible_verse.setData(QtCore.Qt.UserRole, QtCore.QVariant(data))
self.listView.addItem(bible_verse) items.append(bible_verse)
self.listView.selectAll() return items
self.search_results = {}
self.second_search_results = {}
def generateSlideData(self, service_item, item=None, xmlVersion=False): def generateSlideData(self, service_item, item=None, xmlVersion=False):
""" """
@ -708,7 +721,10 @@ class BibleMediaItem(MediaManagerItem):
service item's title. service item's title.
""" """
log.debug(u'generating slide data') log.debug(u'generating slide data')
items = self.listView.selectedIndexes() if item:
items = item
else:
items = self.listView.selectedItems()
if len(items) == 0: if len(items) == 0:
return False return False
bible_text = u'' bible_text = u''
@ -717,8 +733,7 @@ class BibleMediaItem(MediaManagerItem):
raw_slides = [] raw_slides = []
raw_title = [] raw_title = []
verses = VerseReferenceList() verses = VerseReferenceList()
for item in items: for bitem in items:
bitem = self.listView.item(item.row())
book = self._decodeQtObject(bitem, 'book') book = self._decodeQtObject(bitem, 'book')
chapter = int(self._decodeQtObject(bitem, 'chapter')) chapter = int(self._decodeQtObject(bitem, 'chapter'))
verse = int(self._decodeQtObject(bitem, 'verse')) verse = int(self._decodeQtObject(bitem, 'verse'))
@ -752,11 +767,11 @@ class BibleMediaItem(MediaManagerItem):
else: else:
bible_text = u'%s %s&nbsp;%s\n' % (bible_text, verse_text, text) bible_text = u'%s %s&nbsp;%s\n' % (bible_text, verse_text, text)
if not old_item: if not old_item:
start_item = item start_item = bitem
elif self.checkTitle(item, old_item): elif self.checkTitle(bitem, old_item):
raw_title.append(self.formatTitle(start_item, old_item)) raw_title.append(self.formatTitle(start_item, old_item))
start_item = item start_item = bitem
old_item = item old_item = bitem
old_chapter = chapter old_chapter = chapter
# Add footer # Add footer
service_item.raw_footer.append(verses.format_verses()) service_item.raw_footer.append(verses.format_verses())
@ -764,7 +779,7 @@ class BibleMediaItem(MediaManagerItem):
verses.add_version(second_version, second_copyright, verses.add_version(second_version, second_copyright,
second_permissions) second_permissions)
service_item.raw_footer.append(verses.format_versions()) service_item.raw_footer.append(verses.format_versions())
raw_title.append(self.formatTitle(start_item, item)) raw_title.append(self.formatTitle(start_item, bitem))
# If there are no more items we check whether we have to add bible_text. # If there are no more items we check whether we have to add bible_text.
if bible_text: if bible_text:
raw_slides.append(bible_text.lstrip()) raw_slides.append(bible_text.lstrip())
@ -787,9 +802,9 @@ class BibleMediaItem(MediaManagerItem):
[service_item.add_from_text(slide[:30], slide) for slide in raw_slides] [service_item.add_from_text(slide[:30], slide) for slide in raw_slides]
return True return True
def formatTitle(self, start_item, old_item): def formatTitle(self, start_bitem, old_bitem):
""" """
This methode is called, when we have to change the title, because This method is called, when we have to change the title, because
we are at the end of a verse range. E. g. if we want to add we are at the end of a verse range. E. g. if we want to add
Genesis 1:1-6 as well as Daniel 2:14. Genesis 1:1-6 as well as Daniel 2:14.
@ -801,10 +816,8 @@ class BibleMediaItem(MediaManagerItem):
""" """
verse_separator = get_reference_match(u'sep_v_display') verse_separator = get_reference_match(u'sep_v_display')
range_separator = get_reference_match(u'sep_r_display') range_separator = get_reference_match(u'sep_r_display')
old_bitem = self.listView.item(old_item.row())
old_chapter = self._decodeQtObject(old_bitem, 'chapter') old_chapter = self._decodeQtObject(old_bitem, 'chapter')
old_verse = self._decodeQtObject(old_bitem, 'verse') old_verse = self._decodeQtObject(old_bitem, 'verse')
start_bitem = self.listView.item(start_item.row())
start_book = self._decodeQtObject(start_bitem, 'book') start_book = self._decodeQtObject(start_bitem, 'book')
start_chapter = self._decodeQtObject(start_bitem, 'chapter') start_chapter = self._decodeQtObject(start_bitem, 'chapter')
start_verse = self._decodeQtObject(start_bitem, 'verse') start_verse = self._decodeQtObject(start_bitem, 'verse')
@ -825,9 +838,9 @@ class BibleMediaItem(MediaManagerItem):
range_separator + old_chapter + verse_separator + old_verse range_separator + old_chapter + verse_separator + old_verse
return u'%s %s (%s)' % (start_book, verse_range, bibles) return u'%s %s (%s)' % (start_book, verse_range, bibles)
def checkTitle(self, item, old_item): def checkTitle(self, bitem, old_bitem):
""" """
This methode checks if we are at the end of an verse range. If that is This method checks if we are at the end of an verse range. If that is
the case, we return True, otherwise False. E. g. if we added the case, we return True, otherwise False. E. g. if we added
Genesis 1:1-6, but the next verse is Daniel 2:14, we return True. Genesis 1:1-6, but the next verse is Daniel 2:14, we return True.
@ -838,13 +851,11 @@ class BibleMediaItem(MediaManagerItem):
The item we were previously dealing with. The item we were previously dealing with.
""" """
# Get all the necessary meta data. # Get all the necessary meta data.
bitem = self.listView.item(item.row())
book = self._decodeQtObject(bitem, 'book') book = self._decodeQtObject(bitem, 'book')
chapter = int(self._decodeQtObject(bitem, 'chapter')) chapter = int(self._decodeQtObject(bitem, 'chapter'))
verse = int(self._decodeQtObject(bitem, 'verse')) verse = int(self._decodeQtObject(bitem, 'verse'))
bible = self._decodeQtObject(bitem, 'bible') bible = self._decodeQtObject(bitem, 'bible')
second_bible = self._decodeQtObject(bitem, 'second_bible') second_bible = self._decodeQtObject(bitem, 'second_bible')
old_bitem = self.listView.item(old_item.row())
old_book = self._decodeQtObject(old_bitem, 'book') old_book = self._decodeQtObject(old_bitem, 'book')
old_chapter = int(self._decodeQtObject(old_bitem, 'chapter')) old_chapter = int(self._decodeQtObject(old_bitem, 'chapter'))
old_verse = int(self._decodeQtObject(old_bitem, 'verse')) old_verse = int(self._decodeQtObject(old_bitem, 'verse'))
@ -896,3 +907,22 @@ class BibleMediaItem(MediaManagerItem):
if self.settings.display_style == DisplayStyle.Square: if self.settings.display_style == DisplayStyle.Square:
return u'{su}[%s]{/su}' % verse_text return u'{su}[%s]{/su}' % verse_text
return u'{su}%s{/su}' % verse_text return u'{su}%s{/su}' % verse_text
def search(self, string):
"""
Search for some Bible verses (by reference).
"""
bible = unicode(self.quickVersionComboBox.currentText())
search_results = self.parent.manager.get_verses(bible, string, False)
results = []
if search_results:
versetext = u' '.join([verse.text for verse in search_results])
return [[string, versetext]]
return []
def createItemFromId(self, item_id):
item = QtGui.QListWidgetItem()
bible = unicode(self.quickVersionComboBox.currentText())
search_results = self.parent.manager.get_verses(bible, item_id, False)
items = self.buildDisplayResults(bible, u'', search_results)
return items

View File

@ -65,6 +65,9 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
QtCore.SIGNAL(u'theme_update_list'), self.loadThemes) QtCore.SIGNAL(u'theme_update_list'), self.loadThemes)
QtCore.QObject.connect(self.slideListView, QtCore.QObject.connect(self.slideListView,
QtCore.SIGNAL(u'currentRowChanged(int)'), self.onCurrentRowChanged) QtCore.SIGNAL(u'currentRowChanged(int)'), self.onCurrentRowChanged)
QtCore.QObject.connect(self.slideListView,
QtCore.SIGNAL(u'doubleClicked(QModelIndex)'),
self.onEditButtonPressed)
def loadThemes(self, themelist): def loadThemes(self, themelist):
self.themeComboBox.clear() self.themeComboBox.clear()
@ -111,6 +114,8 @@ class EditCustomForm(QtGui.QDialog, Ui_CustomEditDialog):
def accept(self): def accept(self):
log.debug(u'accept') log.debug(u'accept')
if self.saveCustom(): if self.saveCustom():
Receiver.send_message(u'custom_set_autoselect_item',
self.customSlide.title)
Receiver.send_message(u'custom_load_list') Receiver.send_message(u'custom_load_list')
QtGui.QDialog.accept(self) QtGui.QDialog.accept(self)

View File

@ -26,7 +26,7 @@
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import translate, SpellTextEdit from openlp.core.lib import translate, SpellTextEdit, build_icon
from openlp.core.lib.ui import create_accept_reject_button_box from openlp.core.lib.ui import create_accept_reject_button_box
class Ui_CustomSlideEditDialog(object): class Ui_CustomSlideEditDialog(object):
@ -39,9 +39,15 @@ class Ui_CustomSlideEditDialog(object):
self.dialogLayout.addWidget(self.slideTextEdit) self.dialogLayout.addWidget(self.slideTextEdit)
self.buttonBox = create_accept_reject_button_box(customSlideEditDialog) self.buttonBox = create_accept_reject_button_box(customSlideEditDialog)
self.splitButton = QtGui.QPushButton(customSlideEditDialog) self.splitButton = QtGui.QPushButton(customSlideEditDialog)
self.splitButton.setIcon(build_icon(u':/general/general_add.png'))
self.splitButton.setObjectName(u'splitButton') self.splitButton.setObjectName(u'splitButton')
self.buttonBox.addButton(self.splitButton, self.buttonBox.addButton(self.splitButton,
QtGui.QDialogButtonBox.ActionRole) QtGui.QDialogButtonBox.ActionRole)
self.insertButton = QtGui.QPushButton(customSlideEditDialog)
self.insertButton.setIcon(build_icon(u':/general/general_add.png'))
self.insertButton.setObjectName(u'insertButton')
self.buttonBox.addButton(self.insertButton,
QtGui.QDialogButtonBox.ActionRole)
self.dialogLayout.addWidget(self.buttonBox) self.dialogLayout.addWidget(self.buttonBox)
self.retranslateUi(customSlideEditDialog) self.retranslateUi(customSlideEditDialog)
QtCore.QMetaObject.connectSlotsByName(customSlideEditDialog) QtCore.QMetaObject.connectSlotsByName(customSlideEditDialog)
@ -50,5 +56,10 @@ class Ui_CustomSlideEditDialog(object):
self.splitButton.setText( self.splitButton.setText(
translate('CustomPlugin.EditCustomForm', 'Split Slide')) translate('CustomPlugin.EditCustomForm', 'Split Slide'))
self.splitButton.setToolTip( self.splitButton.setToolTip(
translate('CustomPlugin.EditCustomForm', 'Split a slide into two '
'only if it does not fit on the screen as one slide.'))
self.insertButton.setText(
translate('CustomPlugin.EditCustomForm', 'Insert Slide'))
self.insertButton.setToolTip(
translate('CustomPlugin.EditCustomForm', 'Split a slide into two ' translate('CustomPlugin.EditCustomForm', 'Split a slide into two '
'by inserting a slide splitter.')) 'by inserting a slide splitter.'))

View File

@ -44,6 +44,8 @@ class EditCustomSlideForm(QtGui.QDialog, Ui_CustomSlideEditDialog):
QtGui.QDialog.__init__(self, parent) QtGui.QDialog.__init__(self, parent)
self.setupUi(self) self.setupUi(self)
# Connecting signals and slots # Connecting signals and slots
QtCore.QObject.connect(self.insertButton,
QtCore.SIGNAL(u'clicked()'), self.onInsertButtonPressed)
QtCore.QObject.connect(self.splitButton, QtCore.QObject.connect(self.splitButton,
QtCore.SIGNAL(u'clicked()'), self.onSplitButtonPressed) QtCore.SIGNAL(u'clicked()'), self.onSplitButtonPressed)
@ -65,7 +67,7 @@ class EditCustomSlideForm(QtGui.QDialog, Ui_CustomSlideEditDialog):
""" """
return self.slideTextEdit.toPlainText().split(u'\n[===]\n') return self.slideTextEdit.toPlainText().split(u'\n[===]\n')
def onSplitButtonPressed(self): def onInsertButtonPressed(self):
""" """
Adds a slide split at the cursor. Adds a slide split at the cursor.
""" """
@ -73,3 +75,12 @@ class EditCustomSlideForm(QtGui.QDialog, Ui_CustomSlideEditDialog):
self.slideTextEdit.insertPlainText(u'\n') self.slideTextEdit.insertPlainText(u'\n')
self.slideTextEdit.insertPlainText(u'[===]\n') self.slideTextEdit.insertPlainText(u'[===]\n')
self.slideTextEdit.setFocus() self.slideTextEdit.setFocus()
def onSplitButtonPressed(self):
"""
Adds a virtual split at cursor.
"""
if self.slideTextEdit.textCursor().columnNumber() != 0:
self.slideTextEdit.insertPlainText(u'\n')
self.slideTextEdit.insertPlainText(u'[---]')
self.slideTextEdit.setFocus()

View File

@ -27,15 +27,25 @@
import logging import logging
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from sqlalchemy.sql import or_, func
from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \ from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \
check_item_selected check_item_selected, translate
from openlp.core.lib.searchedit import SearchEdit
from openlp.core.lib.ui import UiStrings from openlp.core.lib.ui import UiStrings
from openlp.plugins.custom.lib import CustomXMLParser from openlp.plugins.custom.lib import CustomXMLParser
from openlp.plugins.custom.lib.db import CustomSlide from openlp.plugins.custom.lib.db import CustomSlide
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class CustomSearch(object):
"""
An enumeration for custom search methods.
"""
Titles = 1
Themes = 2
class CustomMediaItem(MediaManagerItem): class CustomMediaItem(MediaManagerItem):
""" """
This is the custom media manager item for Custom Slides. This is the custom media manager item for Custom Slides.
@ -47,12 +57,48 @@ class CustomMediaItem(MediaManagerItem):
MediaManagerItem.__init__(self, parent, self, icon) MediaManagerItem.__init__(self, parent, self, icon)
self.singleServiceItem = False self.singleServiceItem = False
self.quickPreviewAllowed = True self.quickPreviewAllowed = True
self.hasSearch = True
# Holds information about whether the edit is remotly triggered and # Holds information about whether the edit is remotly triggered and
# which Custom is required. # which Custom is required.
self.remoteCustom = -1 self.remoteCustom = -1
self.manager = parent.manager self.manager = parent.manager
def addEndHeaderBar(self): def addEndHeaderBar(self):
self.addToolbarSeparator()
self.searchWidget = QtGui.QWidget(self)
self.searchWidget.setObjectName(u'searchWidget')
self.searchLayout = QtGui.QVBoxLayout(self.searchWidget)
self.searchLayout.setObjectName(u'searchLayout')
self.searchTextLayout = QtGui.QFormLayout()
self.searchTextLayout.setObjectName(u'searchTextLayout')
self.searchTextLabel = QtGui.QLabel(self.searchWidget)
self.searchTextLabel.setObjectName(u'searchTextLabel')
self.searchTextEdit = SearchEdit(self.searchWidget)
self.searchTextEdit.setObjectName(u'searchTextEdit')
self.searchTextLabel.setBuddy(self.searchTextEdit)
self.searchTextLayout.addRow(self.searchTextLabel, self.searchTextEdit)
self.searchLayout.addLayout(self.searchTextLayout)
self.searchButtonLayout = QtGui.QHBoxLayout()
self.searchButtonLayout.setObjectName(u'searchButtonLayout')
self.searchButtonLayout.addStretch()
self.searchTextButton = QtGui.QPushButton(self.searchWidget)
self.searchTextButton.setObjectName(u'searchTextButton')
self.searchButtonLayout.addWidget(self.searchTextButton)
self.searchLayout.addLayout(self.searchButtonLayout)
self.pageLayout.addWidget(self.searchWidget)
# Signals and slots
QtCore.QObject.connect(self.searchTextEdit,
QtCore.SIGNAL(u'returnPressed()'), self.onSearchTextButtonClick)
QtCore.QObject.connect(self.searchTextButton,
QtCore.SIGNAL(u'pressed()'), self.onSearchTextButtonClick)
QtCore.QObject.connect(self.searchTextEdit,
QtCore.SIGNAL(u'textChanged(const QString&)'),
self.onSearchTextEditChanged)
QtCore.QObject.connect(self.searchTextEdit,
QtCore.SIGNAL(u'cleared()'), self.onClearTextButtonClick)
QtCore.QObject.connect(self.searchTextEdit,
QtCore.SIGNAL(u'searchTypeChanged(int)'),
self.onSearchTextButtonClick)
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'custom_edit'), self.onRemoteEdit) QtCore.SIGNAL(u'custom_edit'), self.onRemoteEdit)
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
@ -62,9 +108,22 @@ class CustomMediaItem(MediaManagerItem):
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'custom_preview'), self.onPreviewClick) QtCore.SIGNAL(u'custom_preview'), self.onPreviewClick)
def retranslateUi(self):
self.searchTextLabel.setText(u'%s:' % UiStrings().Search)
self.searchTextButton.setText(UiStrings().Search)
def initialise(self): def initialise(self):
self.searchTextEdit.setSearchTypes([
(CustomSearch.Titles, u':/songs/song_search_title.png',
translate('SongsPlugin.MediaItem', 'Titles')),
(CustomSearch.Themes, u':/slides/slide_theme.png',
UiStrings().Themes)
])
self.loadList(self.manager.get_all_objects( self.loadList(self.manager.get_all_objects(
CustomSlide, order_by_ref=CustomSlide.title)) CustomSlide, order_by_ref=CustomSlide.title))
self.searchTextEdit.setCurrentSearchType(QtCore.QSettings().value(
u'%s/last search type' % self.settingsSection,
QtCore.QVariant(CustomSearch.Titles)).toInt()[0])
# 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.
@ -81,6 +140,9 @@ class CustomMediaItem(MediaManagerItem):
custom_name.setData( custom_name.setData(
QtCore.Qt.UserRole, QtCore.QVariant(customSlide.id)) QtCore.Qt.UserRole, QtCore.QVariant(customSlide.id))
self.listView.addItem(custom_name) self.listView.addItem(custom_name)
# Auto-select the item if name has been set
if customSlide.title == self.autoSelectItem :
self.listView.setCurrentItem(custom_name)
def onNewClick(self): def onNewClick(self):
self.parent.edit_custom_form.loadCustom(0) self.parent.edit_custom_form.loadCustom(0)
@ -161,4 +223,59 @@ class CustomMediaItem(MediaManagerItem):
else: else:
raw_footer.append(u'') raw_footer.append(u'')
service_item.raw_footer = raw_footer service_item.raw_footer = raw_footer
return True return True
def onSearchTextButtonClick(self):
# Save the current search type to the configuration.
QtCore.QSettings().setValue(u'%s/last search type' %
self.settingsSection,
QtCore.QVariant(self.searchTextEdit.currentSearchType()))
# Reload the list considering the new search type.
search_keywords = unicode(self.searchTextEdit.displayText())
search_results = []
search_type = self.searchTextEdit.currentSearchType()
if search_type == CustomSearch.Titles:
log.debug(u'Titles Search')
search_results = self.parent.manager.get_all_objects(CustomSlide,
CustomSlide.title.like(u'%' + self.whitespace.sub(u' ',
search_keywords) + u'%'), order_by_ref=CustomSlide.title)
self.loadList(search_results)
elif search_type == CustomSearch.Themes:
log.debug(u'Theme Search')
search_results = self.parent.manager.get_all_objects(CustomSlide,
CustomSlide.theme_name.like(u'%' + self.whitespace.sub(u' ',
search_keywords) + u'%'), order_by_ref=CustomSlide.title)
self.loadList(search_results)
self.check_search_result()
def onSearchTextEditChanged(self, text):
"""
If search as type enabled invoke the search on each key press.
If the Title is being searched do not start till 2 characters
have been entered.
"""
search_length = 2
if len(text) > search_length:
self.onSearchTextButtonClick()
elif len(text) == 0:
self.onClearTextButtonClick()
def onClearTextButtonClick(self):
"""
Clear the search text.
"""
self.searchTextEdit.clear()
self.onSearchTextButtonClick()
def search(self, string):
search_results = self.manager.get_all_objects(CustomSlide,
or_(func.lower(CustomSlide.title).like(u'%' +
string.lower() + u'%'),
func.lower(CustomSlide.text).like(u'%' +
string.lower() + u'%')),
order_by_ref=CustomSlide.title)
results = []
for custom in search_results:
results.append([custom.id, custom.title])
return results

View File

@ -47,6 +47,7 @@ class ImageMediaItem(MediaManagerItem):
self.IconPath = u'images/image' self.IconPath = u'images/image'
MediaManagerItem.__init__(self, parent, self, icon) MediaManagerItem.__init__(self, parent, self, icon)
self.quickPreviewAllowed = True self.quickPreviewAllowed = True
self.hasSearch = True
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'live_theme_changed'), self.liveThemeChanged) QtCore.SIGNAL(u'live_theme_changed'), self.liveThemeChanged)
@ -130,51 +131,51 @@ class ImageMediaItem(MediaManagerItem):
self.parent.formparent.finishedProgressBar() self.parent.formparent.finishedProgressBar()
def generateSlideData(self, service_item, item=None, xmlVersion=False): def generateSlideData(self, service_item, item=None, xmlVersion=False):
items = self.listView.selectedIndexes() if item:
if items: items = [item]
service_item.title = unicode(self.plugin.nameStrings[u'plural'])
service_item.add_capability(ItemCapabilities.AllowsMaintain)
service_item.add_capability(ItemCapabilities.AllowsPreview)
service_item.add_capability(ItemCapabilities.AllowsLoop)
service_item.add_capability(ItemCapabilities.AllowsAdditions)
# force a nonexistent theme
service_item.theme = -1
missing_items = []
missing_items_filenames = []
for item in items:
bitem = self.listView.item(item.row())
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
if not os.path.exists(filename):
missing_items.append(item)
missing_items_filenames.append(filename)
for item in missing_items:
items.remove(item)
# We cannot continue, as all images do not exist.
if not items:
critical_error_message_box(
translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
unicode(translate('ImagePlugin.MediaItem',
'The following image(s) no longer exist: %s')) %
u'\n'.join(missing_items_filenames))
return False
# We have missing as well as existing images. We ask what to do.
elif missing_items and QtGui.QMessageBox.question(self,
translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
unicode(translate('ImagePlugin.MediaItem', 'The following '
'image(s) no longer exist: %s\nDo you want to add the other '
'images anyway?')) % u'\n'.join(missing_items_filenames),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
return False
# Continue with the existing images.
for item in items:
bitem = self.listView.item(item.row())
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
(path, name) = os.path.split(filename)
service_item.add_from_image(filename, name)
return True
else: else:
items = self.listView.selectedItems()
if not items:
return False
service_item.title = unicode(self.plugin.nameStrings[u'plural'])
service_item.add_capability(ItemCapabilities.AllowsMaintain)
service_item.add_capability(ItemCapabilities.AllowsPreview)
service_item.add_capability(ItemCapabilities.AllowsLoop)
service_item.add_capability(ItemCapabilities.AllowsAdditions)
# force a nonexistent theme
service_item.theme = -1
missing_items = []
missing_items_filenames = []
for bitem in items:
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
if not os.path.exists(filename):
missing_items.append(item)
missing_items_filenames.append(filename)
for item in missing_items:
items.remove(item)
# We cannot continue, as all images do not exist.
if not items:
critical_error_message_box(
translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
unicode(translate('ImagePlugin.MediaItem',
'The following image(s) no longer exist: %s')) %
u'\n'.join(missing_items_filenames))
return False return False
# We have missing as well as existing images. We ask what to do.
elif missing_items and QtGui.QMessageBox.question(self,
translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
unicode(translate('ImagePlugin.MediaItem', 'The following '
'image(s) no longer exist: %s\nDo you want to add the other '
'images anyway?')) % u'\n'.join(missing_items_filenames),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No |
QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
return False
# Continue with the existing images.
for bitem in items:
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
(path, name) = os.path.split(filename)
service_item.add_from_image(filename, name)
return True
def onResetClick(self): def onResetClick(self):
""" """
@ -208,3 +209,14 @@ class ImageMediaItem(MediaManagerItem):
unicode(translate('ImagePlugin.MediaItem', unicode(translate('ImagePlugin.MediaItem',
'There was a problem replacing your background, ' 'There was a problem replacing your background, '
'the image file "%s" no longer exists.')) % filename) 'the image file "%s" no longer exists.')) % filename)
def search(self, string):
list = SettingsManager.load_list(self.settingsSection,
self.settingsSection)
results = []
string = string.lower()
for file in list:
filename = os.path.split(unicode(file))[1]
if filename.lower().find(string) > -1:
results.append([file, filename])
return results

View File

@ -49,6 +49,7 @@ class MediaMediaItem(MediaManagerItem):
u':/media/media_video.png').toImage() u':/media/media_video.png').toImage()
MediaManagerItem.__init__(self, parent, self, icon) MediaManagerItem.__init__(self, parent, self, icon)
self.singleServiceItem = False self.singleServiceItem = False
self.hasSearch = True
self.mediaObject = None self.mediaObject = None
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'video_background_replaced'), QtCore.SIGNAL(u'video_background_replaced'),
@ -186,3 +187,14 @@ class MediaMediaItem(MediaManagerItem):
item_name.setIcon(build_icon(img)) item_name.setIcon(build_icon(img))
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file)) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(file))
self.listView.addItem(item_name) self.listView.addItem(item_name)
def search(self, string):
list = SettingsManager.load_list(self.settingsSection,
self.settingsSection)
results = []
string = string.lower()
for file in list:
filename = os.path.split(unicode(file))[1]
if filename.lower().find(string) > -1:
results.append([file, filename])
return results

View File

@ -45,6 +45,7 @@ else:
try: try:
import uno import uno
from com.sun.star.beans import PropertyValue from com.sun.star.beans import PropertyValue
from com.sun.star.task import ErrorCodeIOException
uno_available = True uno_available = True
except ImportError: except ImportError:
uno_available = False uno_available = False
@ -286,6 +287,9 @@ class ImpressDocument(PresentationDocument):
doc.storeToURL(urlpath, props) doc.storeToURL(urlpath, props)
self.convert_thumbnail(path, idx + 1) self.convert_thumbnail(path, idx + 1)
delete_file(path) delete_file(path)
except ErrorCodeIOException, exception:
log.exception(u'ERROR! ErrorCodeIOException %d' %
exception.ErrCode)
except: except:
log.exception(u'%s - Unable to store openoffice preview' % path) log.exception(u'%s - Unable to store openoffice preview' % path)

View File

@ -53,6 +53,7 @@ class PresentationMediaItem(MediaManagerItem):
self.Automatic = u'' self.Automatic = u''
MediaManagerItem.__init__(self, parent, self, icon) MediaManagerItem.__init__(self, parent, self, icon)
self.message_listener = MessageListener(self) self.message_listener = MessageListener(self)
self.hasSearch = True
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'mediaitem_presentation_rebuild'), self.rebuild) QtCore.SIGNAL(u'mediaitem_presentation_rebuild'), self.rebuild)
@ -231,17 +232,19 @@ class PresentationMediaItem(MediaManagerItem):
in the slidecontroller. In the case of powerpoints, an image in the slidecontroller. In the case of powerpoints, an image
for each slide for each slide
""" """
items = self.listView.selectedIndexes() if item:
if len(items) > 1: items = [item]
return False else:
items = self.listView.selectedItems()
if len(items) > 1:
return False
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.AllowsDetailedTitleDisplay)
shortname = service_item.shortname shortname = service_item.shortname
if shortname: if shortname:
for item in items: for bitem in items:
bitem = self.listView.item(item.row())
filename = unicode(bitem.data(QtCore.Qt.UserRole).toString()) filename = unicode(bitem.data(QtCore.Qt.UserRole).toString())
if os.path.exists(filename): if os.path.exists(filename):
if shortname == self.Automatic: if shortname == self.Automatic:
@ -303,3 +306,12 @@ class PresentationMediaItem(MediaManagerItem):
if filetype in self.controllers[controller].alsosupports: if filetype in self.controllers[controller].alsosupports:
return controller return controller
return None return None
def search(self, string):
list = SettingsManager.load_list(self.settingsSection, u'presentations')
results = []
string = string.lower()
for file in list:
if file.lower().find(string) > -1:
results.append([file, file])
return results

View File

@ -43,6 +43,7 @@
<a href="#service-manager" data-role="button" data-icon="arrow-r" data-iconpos="right">Service Manager</a> <a href="#service-manager" data-role="button" data-icon="arrow-r" data-iconpos="right">Service Manager</a>
<a href="#slide-controller" data-role="button" data-icon="arrow-r" data-iconpos="right">Slide Controller</a> <a href="#slide-controller" data-role="button" data-icon="arrow-r" data-iconpos="right">Slide Controller</a>
<a href="#alerts" data-role="button" data-icon="arrow-r" data-iconpos="right">Alerts</a> <a href="#alerts" data-role="button" data-icon="arrow-r" data-iconpos="right">Alerts</a>
<a href="#search" data-role="button" data-icon="arrow-r" data-iconpos="right">Search</a>
</div> </div>
</div> </div>
</div> </div>
@ -50,6 +51,7 @@
<div data-role="header"> <div data-role="header">
<a href="#" data-rel="back" data-icon="arrow-l">Back</a> <a href="#" data-rel="back" data-icon="arrow-l">Back</a>
<h1>Service Manager</h1> <h1>Service Manager</h1>
<a href="#" id="service-refresh" data-role="button" data-icon="refresh">Refresh</a>
</div> </div>
<div data-role="content"> <div data-role="content">
<ul data-role="listview" data-inset="true"> <ul data-role="listview" data-inset="true">
@ -57,9 +59,8 @@
</div> </div>
<div data-role="footer" data-theme="b" class="ui-bar"> <div data-role="footer" data-theme="b" class="ui-bar">
<a href="#" id="service-blank" data-role="button" data-icon="blank">Blank</a> <a href="#" id="service-blank" data-role="button" data-icon="blank">Blank</a>
<a href="#" id="service-unblank" data-role="button" data-icon="unblank">Unblank</a> <a href="#" id="service-unblank" data-role="button" data-icon="unblank">Show</a>
<a href="#" id="service-refresh" data-role="button" data-icon="refresh">Refresh</a> <a href="#" id="service-previous" data-role="button" data-icon="arrow-l">Prev</a>
<a href="#" id="service-previous" data-role="button" data-icon="arrow-l">Previous</a>
<a href="#" id="service-next" data-role="button" data-icon="arrow-r" data-iconpos="right">Next</a> <a href="#" id="service-next" data-role="button" data-icon="arrow-r" data-iconpos="right">Next</a>
</div> </div>
</div> </div>
@ -67,6 +68,7 @@
<div data-role="header"> <div data-role="header">
<a href="#" data-rel="back" data-icon="arrow-l">Back</a> <a href="#" data-rel="back" data-icon="arrow-l">Back</a>
<h1>Slide Controller</h1> <h1>Slide Controller</h1>
<a href="#" id="controller-refresh" data-role="button" data-icon="refresh">Refresh</a>
</div> </div>
<div data-role="content"> <div data-role="content">
<ul data-role="listview" data-inset="true"> <ul data-role="listview" data-inset="true">
@ -74,9 +76,8 @@
</div> </div>
<div data-role="footer" data-theme="b" class="ui-bar"> <div data-role="footer" data-theme="b" class="ui-bar">
<a href="#" id="controller-blank" data-role="button" data-icon="blank">Blank</a> <a href="#" id="controller-blank" data-role="button" data-icon="blank">Blank</a>
<a href="#" id="controller-unblank" data-role="button" data-icon="unblank">Unblank</a> <a href="#" id="controller-unblank" data-role="button" data-icon="unblank">Show</a>
<a href="#" id="controller-refresh" data-role="button" data-icon="refresh">Refresh</a> <a href="#" id="controller-previous" data-role="button" data-icon="arrow-l">Prev</a>
<a href="#" id="controller-previous" data-role="button" data-icon="arrow-l">Previous</a>
<a href="#" id="controller-next" data-role="button" data-icon="arrow-r" data-iconpos="right">Next</a> <a href="#" id="controller-next" data-role="button" data-icon="arrow-r" data-iconpos="right">Next</a>
</div> </div>
</div> </div>
@ -93,5 +94,23 @@
<a href="#" id="alert-submit" data-role="button">Show Alert</a> <a href="#" id="alert-submit" data-role="button">Show Alert</a>
</div> </div>
</div> </div>
<div data-role="page" id="search">
<div data-role="header">
<a href="#" data-rel="back" data-icon="arrow-l">Back</a>
<h1>Search</h1>
</div>
<div data-role="content">
<div data-role="fieldcontain">
<label for="search-plugin">Search:</label>
<select name="search-plugin" id="search-plugin" data-native-menu="false"></select>
</div>
<div data-role="fieldcontain">
<label for="search-text">Text:</label>
<input type="search" name="search-text" id="search-text" value="" />
</div>
<a href="#" id="search-submit" data-role="button">Search</a>
<ul data-role="listview" data-inset="true">
</div>
</div>
</body> </body>
</html> </html>

View File

@ -39,6 +39,19 @@ window.OpenLP = {
} }
return $(targ); return $(targ);
}, },
getSearchablePlugins: function (event) {
$.getJSON(
"/api/plugin/search",
function (data, status) {
var select = $("#search-plugin");
select.html("");
$.each(data.results.items, function (idx, value) {
select.append("<option value='" + value + "'>" + value + "</option>");
});
select.selectmenu("refresh");
}
);
},
loadService: function (event) { loadService: function (event) {
$.getJSON( $.getJSON(
"/api/service/list", "/api/service/list",
@ -63,7 +76,9 @@ window.OpenLP = {
var ul = $("#slide-controller > div[data-role=content] > ul[data-role=listview]"); var ul = $("#slide-controller > div[data-role=content] > ul[data-role=listview]");
ul.html(""); ul.html("");
for (idx in data.results.slides) { for (idx in data.results.slides) {
var text = data.results.slides[idx]["text"]; var text = data.results.slides[idx]["tag"];
if (text != "") text = text + ": ";
text = text + data.results.slides[idx]["text"];
text = text.replace(/\n/g, '<br />'); text = text.replace(/\n/g, '<br />');
var li = $("<li data-icon=\"false\">").append( var li = $("<li data-icon=\"false\">").append(
$("<a href=\"#\">").attr("value", parseInt(idx, 10)).html(text)); $("<a href=\"#\">").attr("value", parseInt(idx, 10)).html(text));
@ -189,6 +204,55 @@ window.OpenLP = {
} }
); );
return false; return false;
},
search: function (event) {
var text = JSON.stringify({"request": {"text": $("#search-text").val()}});
$.getJSON(
"/api/" + $("#search-plugin").val() + "/search",
{"data": text},
function (data, status) {
var ul = $("#search > div[data-role=content] > ul[data-role=listview]");
ul.html("");
if (data.results.items.length == 0) {
var li = $("<li data-icon=\"false\">").text('No results');
ul.append(li);
}
else {
$.each(data.results.items, function (idx, value) {
var item = $("<li>").text(value[1]);
var golive = $("<a href=\"#\">Go Live</a>").attr("value", value[0]).click(OpenLP.goLive);
var additem = $("<a href=\"#\">Add To Service</a>").attr("value", value[0]).click(OpenLP.addToService);
item.append($("<ul>").append($("<li>").append(golive)).append($("<li>").append(additem)));
ul.append(item);
});
}
ul.listview("refresh");
}
);
return false;
},
goLive: function (event) {
var item = OpenLP.getElement(event);
var id = item.attr("value");
var text = JSON.stringify({"request": {"id": id}});
$.getJSON(
"/api/" + $("#search-plugin").val() + "/live",
{"data": text})
$.mobile.changePage("slide-controller");
return false;
},
addToService: function (event) {
var item = OpenLP.getElement(event);
var id = item.attr("value");
var text = JSON.stringify({"request": {"id": id}});
$.getJSON(
"/api/" + $("#search-plugin").val() + "/add",
{"data": text},
function () {
history.back();
}
);
return false;
} }
} }
// Service Manager // Service Manager
@ -207,7 +271,10 @@ $("#controller-blank").live("click", OpenLP.blankDisplay);
$("#controller-unblank").live("click", OpenLP.unblankDisplay); $("#controller-unblank").live("click", OpenLP.unblankDisplay);
// Alerts // Alerts
$("#alert-submit").live("click", OpenLP.showAlert); $("#alert-submit").live("click", OpenLP.showAlert);
// Search
$("#search-submit").live("click", OpenLP.search);
// Poll the server twice a second to get any updates. // Poll the server twice a second to get any updates.
OpenLP.getSearchablePlugins();
$.ajaxSetup({ cache: false }); $.ajaxSetup({ cache: false });
setInterval("OpenLP.pollServer();", 500); setInterval("OpenLP.pollServer();", 500);
OpenLP.pollServer(); OpenLP.pollServer();

View File

@ -123,7 +123,7 @@ except ImportError:
from PyQt4 import QtCore, QtNetwork from PyQt4 import QtCore, QtNetwork
from openlp.core.lib import Receiver from openlp.core.lib import Receiver, PluginStatus
from openlp.core.ui import HideMode from openlp.core.ui import HideMode
from openlp.core.utils import AppLocation from openlp.core.utils import AppLocation
@ -250,7 +250,11 @@ class HttpConnection(object):
(r'^/api/controller/(live|preview)/(.*)$', self.controller), (r'^/api/controller/(live|preview)/(.*)$', self.controller),
(r'^/api/service/(.*)$', self.service), (r'^/api/service/(.*)$', self.service),
(r'^/api/display/(hide|show)$', self.display), (r'^/api/display/(hide|show)$', self.display),
(r'^/api/alert$', self.alert) (r'^/api/alert$', self.alert),
(r'^/api/plugin/(search)$', self.pluginInfo),
(r'^/api/(.*)/search$', self.search),
(r'^/api/(.*)/live$', self.go_live),
(r'^/api/(.*)/add$', self.add_to_service)
] ]
QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'readyRead()'), QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'readyRead()'),
self.ready_read) self.ready_read)
@ -409,8 +413,8 @@ class HttpConnection(object):
item[u'html'] = unicode(frame[u'html']) item[u'html'] = unicode(frame[u'html'])
else: else:
item[u'tag'] = unicode(index + 1) item[u'tag'] = unicode(index + 1)
item[u'text'] = u'' item[u'text'] = unicode(frame[u'title'])
item[u'html'] = u'' item[u'html'] = unicode(frame[u'title'])
item[u'selected'] = (self.parent.current_slide == index) item[u'selected'] = (self.parent.current_slide == index)
data.append(item) data.append(item)
json_data = {u'results': {u'slides': data}} json_data = {u'results': {u'slides': data}}
@ -443,6 +447,60 @@ class HttpConnection(object):
return HttpResponse(json.dumps({u'results': {u'success': True}}), return HttpResponse(json.dumps({u'results': {u'success': True}}),
{u'Content-Type': u'application/json'}) {u'Content-Type': u'application/json'})
def pluginInfo(self, action):
"""
Return plugin related information, based on the action
``action`` - The action to perform
if 'search' return a list of plugin names which support search
"""
if action == u'search':
searches = []
for plugin in self.parent.parent.pluginManager.plugins:
if plugin.status == PluginStatus.Active and \
plugin.mediaItem and plugin.mediaItem.hasSearch:
searches.append(plugin.name)
return HttpResponse(
json.dumps({u'results': {u'items': searches}}),
{u'Content-Type': u'application/json'})
def search(self, type):
"""
Return a list of items that match the search text
``type``
The plugin name to search in.
"""
text = json.loads(self.url_params[u'data'][0])[u'request'][u'text']
plugin = self.parent.parent.pluginManager.get_plugin_by_name(type)
if plugin.status == PluginStatus.Active and \
plugin.mediaItem and plugin.mediaItem.hasSearch:
results =plugin.mediaItem.search(text)
else:
results = []
return HttpResponse(
json.dumps({u'results': {u'items': results}}),
{u'Content-Type': u'application/json'})
def go_live(self, type):
"""
Go live on an item of type ``type``.
"""
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id']
plugin = self.parent.parent.pluginManager.get_plugin_by_name(type)
if plugin.status == PluginStatus.Active and plugin.mediaItem:
plugin.mediaItem.goLive(id)
def add_to_service(self, type):
"""
Add item of type ``type`` to the end of the service
"""
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id']
plugin = self.parent.parent.pluginManager.get_plugin_by_name(type)
if plugin.status == PluginStatus.Active and plugin.mediaItem:
item_id = plugin.mediaItem.createItemFromId(id)
plugin.mediaItem.addToService(item_id)
def send_response(self, response): def send_response(self, response):
http = u'HTTP/1.1 %s\r\n' % response.code http = u'HTTP/1.1 %s\r\n' % response.code
for header, value in response.headers.iteritems(): for header, value in response.headers.iteritems():

View File

@ -695,6 +695,8 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.clearCaches() self.clearCaches()
if self._validate_song(): if self._validate_song():
self.saveSong() self.saveSong()
Receiver.send_message(u'songs_set_autoselect_item',
unicode(self.titleEdit.text()))
Receiver.send_message(u'songs_load_list') Receiver.send_message(u'songs_load_list')
QtGui.QDialog.accept(self) QtGui.QDialog.accept(self)

View File

@ -42,6 +42,10 @@ class Ui_EditVerseDialog(object):
self.dialogLayout.addWidget(self.verseTextEdit) self.dialogLayout.addWidget(self.verseTextEdit)
self.verseTypeLayout = QtGui.QHBoxLayout() self.verseTypeLayout = QtGui.QHBoxLayout()
self.verseTypeLayout.setObjectName(u'verseTypeLayout') self.verseTypeLayout.setObjectName(u'verseTypeLayout')
self.splitButton = QtGui.QPushButton(editVerseDialog)
self.splitButton.setIcon(build_icon(u':/general/general_add.png'))
self.splitButton.setObjectName(u'splitButton')
self.verseTypeLayout.addWidget(self.splitButton)
self.verseTypeLabel = QtGui.QLabel(editVerseDialog) self.verseTypeLabel = QtGui.QLabel(editVerseDialog)
self.verseTypeLabel.setObjectName(u'verseTypeLabel') self.verseTypeLabel.setObjectName(u'verseTypeLabel')
self.verseTypeLayout.addWidget(self.verseTypeLabel) self.verseTypeLayout.addWidget(self.verseTypeLabel)
@ -84,5 +88,13 @@ class Ui_EditVerseDialog(object):
VerseType.TranslatedNames[VerseType.Ending]) VerseType.TranslatedNames[VerseType.Ending])
self.verseTypeComboBox.setItemText(VerseType.Other, self.verseTypeComboBox.setItemText(VerseType.Other,
VerseType.TranslatedNames[VerseType.Other]) VerseType.TranslatedNames[VerseType.Other])
self.splitButton.setText(
translate('SongsPlugin.EditVerseForm', '&Split'))
self.splitButton.setToolTip(
translate('SongsPlugin.EditVerseForm', 'Split a slide into two '
'only if it does not fit on the screen as one slide.'))
self.insertButton.setText( self.insertButton.setText(
translate('SongsPlugin.EditVerseForm', '&Insert')) translate('SongsPlugin.EditVerseForm', '&Insert'))
self.insertButton.setToolTip(
translate('SongsPlugin.EditVerseForm', 'Split a slide into two '
'by inserting a verse splitter.'))

View File

@ -51,6 +51,8 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
self.contextMenu) self.contextMenu)
QtCore.QObject.connect(self.insertButton, QtCore.SIGNAL(u'clicked()'), QtCore.QObject.connect(self.insertButton, QtCore.SIGNAL(u'clicked()'),
self.onInsertButtonClicked) self.onInsertButtonClicked)
QtCore.QObject.connect(self.splitButton, QtCore.SIGNAL(u'clicked()'),
self.onSplitButtonClicked)
QtCore.QObject.connect(self.verseTextEdit, QtCore.QObject.connect(self.verseTextEdit,
QtCore.SIGNAL(u'cursorPositionChanged()'), QtCore.SIGNAL(u'cursorPositionChanged()'),
self.onCursorPositionChanged) self.onCursorPositionChanged)
@ -70,6 +72,13 @@ class EditVerseForm(QtGui.QDialog, Ui_EditVerseDialog):
(verse_tag, verse_num)) (verse_tag, verse_num))
self.verseTextEdit.setFocus() self.verseTextEdit.setFocus()
def onSplitButtonClicked(self):
verse_type_index = self.verseTypeComboBox.currentIndex()
if self.verseTextEdit.textCursor().columnNumber() != 0:
self.verseTextEdit.insertPlainText(u'\n')
self.verseTextEdit.insertPlainText(u'[---]')
self.verseTextEdit.setFocus()
def onInsertButtonClicked(self): def onInsertButtonClicked(self):
verse_type_index = self.verseTypeComboBox.currentIndex() verse_type_index = self.verseTypeComboBox.currentIndex()
self.insertVerse(VerseType.Tags[verse_type_index], self.insertVerse(VerseType.Tags[verse_type_index],

View File

@ -422,7 +422,8 @@ class FoilPresenter(object):
VerseType.Tags[VerseType.PreChorus]: 1 VerseType.Tags[VerseType.PreChorus]: 1
} }
for strophe in foilpresenterfolie.strophen.strophe: for strophe in foilpresenterfolie.strophen.strophe:
text = self._child(strophe.text_) text = self._child(strophe.text_) if hasattr(strophe, u'text_') \
else u''
verse_name = self._child(strophe.key) verse_name = self._child(strophe.key)
children = strophe.getchildren() children = strophe.getchildren()
sortnr = False sortnr = False

View File

@ -26,7 +26,6 @@
import logging import logging
import locale import locale
import re
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from sqlalchemy.sql import or_ from sqlalchemy.sql import or_
@ -72,8 +71,8 @@ class SongMediaItem(MediaManagerItem):
# which Song is required. # which Song is required.
self.remoteSong = -1 self.remoteSong = -1
self.editItem = None self.editItem = None
self.whitespace = re.compile(r'\W+', re.UNICODE)
self.quickPreviewAllowed = True self.quickPreviewAllowed = True
self.hasSearch = True
def addEndHeaderBar(self): def addEndHeaderBar(self):
self.addToolbarSeparator() self.addToolbarSeparator()
@ -171,11 +170,7 @@ class SongMediaItem(MediaManagerItem):
search_type = self.searchTextEdit.currentSearchType() search_type = self.searchTextEdit.currentSearchType()
if search_type == SongSearch.Entire: if search_type == SongSearch.Entire:
log.debug(u'Entire Song Search') log.debug(u'Entire Song Search')
search_results = self.parent.manager.get_all_objects(Song, search_results = self.searchEntire(search_keywords)
or_(Song.search_title.like(u'%' + self.whitespace.sub(u' ',
search_keywords.lower()) + u'%'),
Song.search_lyrics.like(u'%' + search_keywords.lower() + u'%'),
Song.comments.like(u'%' + search_keywords.lower() + u'%')))
self.displayResultsSong(search_results) self.displayResultsSong(search_results)
elif search_type == SongSearch.Titles: elif search_type == SongSearch.Titles:
log.debug(u'Titles Search') log.debug(u'Titles Search')
@ -197,10 +192,18 @@ class SongMediaItem(MediaManagerItem):
elif search_type == SongSearch.Themes: elif search_type == SongSearch.Themes:
log.debug(u'Theme Search') log.debug(u'Theme Search')
search_results = self.parent.manager.get_all_objects(Song, search_results = self.parent.manager.get_all_objects(Song,
Song.theme_name == search_keywords) Song.theme_name.like(u'%' + self.whitespace.sub(u' ',
search_keywords) + u'%'))
self.displayResultsSong(search_results) self.displayResultsSong(search_results)
self.check_search_result() self.check_search_result()
def searchEntire(self, search_keywords):
return self.parent.manager.get_all_objects(Song,
or_(Song.search_title.like(u'%' + self.whitespace.sub(u' ',
search_keywords.lower()) + u'%'),
Song.search_lyrics.like(u'%' + search_keywords.lower() + u'%'),
Song.comments.like(u'%' + search_keywords.lower() + u'%')))
def onSongListLoad(self): def onSongListLoad(self):
""" """
Handle the exit from the edit dialog and trigger remote updates Handle the exit from the edit dialog and trigger remote updates
@ -234,6 +237,9 @@ class SongMediaItem(MediaManagerItem):
song_name = QtGui.QListWidgetItem(song_detail) song_name = QtGui.QListWidgetItem(song_detail)
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
if song.title == self.autoSelectItem :
self.listView.setCurrentItem(song_name)
def displayResultsAuthor(self, searchresults): def displayResultsAuthor(self, searchresults):
log.debug(u'display results Author') log.debug(u'display results Author')
@ -475,3 +481,13 @@ class SongMediaItem(MediaManagerItem):
""" """
return locale.strcoll(unicode(song_1.title.lower()), return locale.strcoll(unicode(song_1.title.lower()),
unicode(song_2.title.lower())) unicode(song_2.title.lower()))
def search(self, string):
"""
Search for some songs
"""
search_results = self.searchEntire(string)
results = []
for song in search_results:
results.append([song.id, song.title])
return results

View File

@ -110,6 +110,7 @@ class WowImport(SongImport):
self.title = file_name.rpartition(u'.')[0] self.title = file_name.rpartition(u'.')[0]
songData = open(file, 'rb') songData = open(file, 'rb')
if songData.read(19) != u'WoW File\nSong Words': if songData.read(19) != u'WoW File\nSong Words':
self.log_error(file)
continue continue
# Seek to byte which stores number of blocks in the song # Seek to byte which stores number of blocks in the song
songData.seek(56) songData.seek(56)

View File

@ -233,6 +233,7 @@ class OpenLyrics(object):
IMPLEMENTED_VERSION = u'0.7' IMPLEMENTED_VERSION = u'0.7'
def __init__(self, manager): def __init__(self, manager):
self.manager = manager self.manager = manager
self.chord_regex = re.compile(u'<chord name=".*?"/>')
def song_to_xml(self, song): def song_to_xml(self, song):
""" """
@ -317,7 +318,7 @@ class OpenLyrics(object):
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 = self.chord_regex.sub(u'', xml)
song_xml = objectify.fromstring(xml) song_xml = objectify.fromstring(xml)
if hasattr(song_xml, u'properties'): if hasattr(song_xml, u'properties'):
properties = song_xml.properties properties = song_xml.properties

View File

@ -268,4 +268,3 @@ class SongsPlugin(Plugin):
action_list.remove_action(self.songExportItem, UiStrings().Export) action_list.remove_action(self.songExportItem, UiStrings().Export)
action_list.remove_action(self.toolsReindexItem, UiStrings().Tools) action_list.remove_action(self.toolsReindexItem, UiStrings().Tools)
Plugin.finalise(self) Plugin.finalise(self)

View File

@ -16,7 +16,7 @@
; NOTE: The value of AppId uniquely identifies this application. ; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications. ; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{AA7699FA-B2D2-43F4-8A70-D497D03C9485} AppID={{AA7699FA-B2D2-43F4-8A70-D497D03C9485}
AppName={#AppName} AppName={#AppName}
AppVerName={#AppVerName} AppVerName={#AppVerName}
AppPublisher={#AppPublisher} AppPublisher={#AppPublisher}
@ -29,11 +29,12 @@ AllowNoIcons=true
LicenseFile=LICENSE.txt LicenseFile=LICENSE.txt
OutputDir=..\..\dist OutputDir=..\..\dist
OutputBaseFilename=OpenLP-{#RealVersion}-setup OutputBaseFilename=OpenLP-{#RealVersion}-setup
Compression=lzma Compression=lzma/Max
SolidCompression=true SolidCompression=true
SetupIconFile=OpenLP.ico SetupIconFile=OpenLP.ico
WizardImageFile=WizImageBig.bmp WizardImageFile=WizImageBig.bmp
WizardSmallImageFile=WizImageSmall.bmp WizardSmallImageFile=WizImageSmall.bmp
ChangesAssociations=true
[Languages] [Languages]
Name: english; MessagesFile: compiler:Default.isl Name: english; MessagesFile: compiler:Default.isl
@ -79,6 +80,10 @@ Name: {userappdata}\Microsoft\Internet Explorer\Quick Launch\{#AppName}; Filenam
Filename: {app}\{#AppExeName}; Description: {cm:LaunchProgram,{#AppName}}; Flags: nowait postinstall skipifsilent Filename: {app}\{#AppExeName}; Description: {cm:LaunchProgram,{#AppName}}; Flags: nowait postinstall skipifsilent
[Registry] [Registry]
Root: HKCR; Subkey: ".osz"; ValueType: string; ValueName: ""; ValueData: "OpenLP"; Flags: uninsdeletevalue
Root: HKCR; Subkey: "OpenLP"; ValueType: string; ValueName: ""; ValueData: "OpenLP Service"; Flags: uninsdeletekey
Root: HKCR; Subkey: "OpenLP\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\OpenLP.exe,0"
Root: HKCR; Subkey: "OpenLP\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\OpenLP.exe"" ""%1"""
[Code] [Code]
function GetUninstallString(): String; function GetUninstallString(): String;