This commit is contained in:
Andreas Preikschat 2012-10-23 15:47:58 +02:00
commit 29ea9f6938
44 changed files with 435 additions and 205 deletions

View File

@ -85,6 +85,14 @@ class PluginManager(object):
log.debug(u'finding plugins in %s at depth %d', log.debug(u'finding plugins in %s at depth %d',
unicode(plugin_dir), startdepth) unicode(plugin_dir), startdepth)
for root, dirs, files in os.walk(plugin_dir): for root, dirs, files in os.walk(plugin_dir):
# TODO Presentation plugin is not yet working on Mac OS X.
# For now just ignore it. The following code will hide it
# in settings dialog.
if sys.platform == 'darwin':
present_plugin_dir = os.path.join(plugin_dir, 'presentations')
# Ignore files from the presentation plugin directory.
if root.startswith(present_plugin_dir):
continue
for name in files: for name in files:
if name.endswith(u'.py') and not name.startswith(u'__'): if name.endswith(u'.py') and not name.startswith(u'__'):
path = os.path.abspath(os.path.join(root, name)) path = os.path.abspath(os.path.join(root, name))

View File

@ -29,12 +29,12 @@
The :mod:`advancedtab` provides an advanced settings facility. The :mod:`advancedtab` provides an advanced settings facility.
""" """
from datetime import datetime, timedelta from datetime import datetime, timedelta
from PyQt4 import QtCore, QtGui
import logging import logging
import os import os
import sys import sys
from PyQt4 import QtCore, QtGui
from openlp.core.lib import SettingsTab, translate, build_icon, Receiver from openlp.core.lib import SettingsTab, translate, build_icon, Receiver
from openlp.core.lib.settings import Settings from openlp.core.lib.settings import Settings
from openlp.core.lib.ui import UiStrings from openlp.core.lib.ui import UiStrings
@ -432,8 +432,7 @@ class AdvancedTab(SettingsTab):
translate('OpenLP.AdvancedTab', translate('OpenLP.AdvancedTab',
'<strong>WARNING:</strong> New data directory location contains ' '<strong>WARNING:</strong> New data directory location contains '
'OpenLP data files. These files WILL be replaced during a copy.')) 'OpenLP data files. These files WILL be replaced during a copy.'))
self.x11GroupBox.setTitle(translate('OpenLP.AdvancedTab', self.x11GroupBox.setTitle(translate('OpenLP.AdvancedTab', 'X11'))
'X11'))
self.x11BypassCheckBox.setText(translate('OpenLP.AdvancedTab', self.x11BypassCheckBox.setText(translate('OpenLP.AdvancedTab',
'Bypass X11 Window Manager')) 'Bypass X11 Window Manager'))
# Slide Limits # Slide Limits
@ -493,8 +492,14 @@ class AdvancedTab(SettingsTab):
QtCore.QVariant(True)).toBool() QtCore.QVariant(True)).toBool()
self.serviceNameCheckBox.setChecked(default_service_enabled) self.serviceNameCheckBox.setChecked(default_service_enabled)
self.serviceNameCheckBoxToggled(default_service_enabled) self.serviceNameCheckBoxToggled(default_service_enabled)
self.x11BypassCheckBox.setChecked( # Fix for bug #1014422.
settings.value(u'x11 bypass wm', QtCore.QVariant(True)).toBool()) x11_bypass_default = True
if sys.platform.startswith(u'linux'):
# Default to False on Gnome.
x11_bypass_default = bool(not
os.environ.get(u'GNOME_DESKTOP_SESSION_ID'))
self.x11BypassCheckBox.setChecked(settings.value(
u'x11 bypass wm', QtCore.QVariant(x11_bypass_default)).toBool())
self.defaultColor = settings.value(u'default color', self.defaultColor = settings.value(u'default color',
QtCore.QVariant(u'#ffffff')).toString() QtCore.QVariant(u'#ffffff')).toString()
self.defaultFileEdit.setText(settings.value(u'default image', self.defaultFileEdit.setText(settings.value(u'default image',
@ -766,7 +771,7 @@ class AdvancedTab(SettingsTab):
self.dataExists = False self.dataExists = False
self.dataDirectoryCopyCheckBox.setChecked(True) self.dataDirectoryCopyCheckBox.setChecked(True)
self.newDataDirectoryHasFilesLabel.hide() self.newDataDirectoryHasFilesLabel.hide()
def onDataDirectoryCancelButtonClicked(self): def onDataDirectoryCancelButtonClicked(self):
""" """
Cancel the data directory location change Cancel the data directory location change

View File

@ -177,8 +177,10 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
return FirstTimePage.Progress return FirstTimePage.Progress
elif self.currentId() == FirstTimePage.Themes: elif self.currentId() == FirstTimePage.Themes:
Receiver.send_message(u'cursor_busy') Receiver.send_message(u'cursor_busy')
Receiver.send_message(u'openlp_process_events')
while not self.themeScreenshotThread.isFinished(): while not self.themeScreenshotThread.isFinished():
time.sleep(0.1) time.sleep(0.1)
Receiver.send_message(u'openlp_process_events')
# Build the screenshot icons, as this can not be done in the thread. # Build the screenshot icons, as this can not be done in the thread.
self._buildThemeScreenshots() self._buildThemeScreenshots()
Receiver.send_message(u'cursor_normal') Receiver.send_message(u'cursor_normal')
@ -188,10 +190,11 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
def onCurrentIdChanged(self, pageId): def onCurrentIdChanged(self, pageId):
""" """
Detects Page changes and updates as approprate. Detects Page changes and updates as appropriate.
""" """
# Keep track of the page we are at. Triggering "Cancel" causes pageId # Keep track of the page we are at. Triggering "Cancel" causes pageId
# to be a -1. # to be a -1.
Receiver.send_message(u'openlp_process_events')
if pageId != -1: if pageId != -1:
self.lastId = pageId self.lastId = pageId
if pageId == FirstTimePage.Plugins: if pageId == FirstTimePage.Plugins:
@ -227,10 +230,12 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
self.cancelButton.setVisible(False) self.cancelButton.setVisible(False)
elif pageId == FirstTimePage.Progress: elif pageId == FirstTimePage.Progress:
Receiver.send_message(u'cursor_busy') Receiver.send_message(u'cursor_busy')
self.repaint()
Receiver.send_message(u'openlp_process_events')
# Try to give the wizard a chance to redraw itself
time.sleep(0.2)
self._preWizard() self._preWizard()
Receiver.send_message(u'openlp_process_events')
self._performWizard() self._performWizard()
Receiver.send_message(u'openlp_process_events')
self._postWizard() self._postWizard()
Receiver.send_message(u'cursor_normal') Receiver.send_message(u'cursor_normal')
Receiver.send_message(u'openlp_process_events') Receiver.send_message(u'openlp_process_events')
@ -263,8 +268,8 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
""" """
Receiver.send_message(u'cursor_busy') Receiver.send_message(u'cursor_busy')
self._performWizard() self._performWizard()
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')
Settings().setValue(u'general/has run wizard', Settings().setValue(u'general/has run wizard',
QtCore.QVariant(True)) QtCore.QVariant(True))
self.close() self.close()
@ -344,6 +349,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
Receiver.send_message(u'openlp_process_events') Receiver.send_message(u'openlp_process_events')
# Loop through the songs list and increase for each selected item # Loop through the songs list and increase for each selected item
for i in xrange(self.songsListWidget.count()): for i in xrange(self.songsListWidget.count()):
Receiver.send_message(u'openlp_process_events')
item = self.songsListWidget.item(i) item = self.songsListWidget.item(i)
if item.checkState() == QtCore.Qt.Checked: if item.checkState() == QtCore.Qt.Checked:
filename = item.data(QtCore.Qt.UserRole).toString() filename = item.data(QtCore.Qt.UserRole).toString()
@ -352,6 +358,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
# Loop through the Bibles list and increase for each selected item # Loop through the Bibles list and increase for each selected item
iterator = QtGui.QTreeWidgetItemIterator(self.biblesTreeWidget) iterator = QtGui.QTreeWidgetItemIterator(self.biblesTreeWidget)
while iterator.value(): while iterator.value():
Receiver.send_message(u'openlp_process_events')
item = iterator.value() item = iterator.value()
if item.parent() and item.checkState(0) == QtCore.Qt.Checked: if item.parent() and item.checkState(0) == QtCore.Qt.Checked:
filename = item.data(0, QtCore.Qt.UserRole).toString() filename = item.data(0, QtCore.Qt.UserRole).toString()
@ -360,6 +367,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
iterator += 1 iterator += 1
# Loop through the themes list and increase for each selected item # Loop through the themes list and increase for each selected item
for i in xrange(self.themesListWidget.count()): for i in xrange(self.themesListWidget.count()):
Receiver.send_message(u'openlp_process_events')
item = self.themesListWidget.item(i) item = self.themesListWidget.item(i)
if item.checkState() == QtCore.Qt.Checked: if item.checkState() == QtCore.Qt.Checked:
filename = item.data(QtCore.Qt.UserRole).toString() filename = item.data(QtCore.Qt.UserRole).toString()
@ -381,6 +389,10 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
self.progressPage.setTitle(translate('OpenLP.FirstTimeWizard', self.progressPage.setTitle(translate('OpenLP.FirstTimeWizard',
'Setting Up')) 'Setting Up'))
self.progressPage.setSubTitle(u'Setup complete.') self.progressPage.setSubTitle(u'Setup complete.')
self.repaint()
Receiver.send_message(u'openlp_process_events')
# Try to give the wizard a chance to repaint itself
time.sleep(0.1)
def _postWizard(self): def _postWizard(self):
""" """
@ -418,8 +430,11 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard):
'Enabling selected plugins...')) 'Enabling selected plugins...'))
self._setPluginStatus(self.songsCheckBox, u'songs/status') self._setPluginStatus(self.songsCheckBox, u'songs/status')
self._setPluginStatus(self.bibleCheckBox, u'bibles/status') self._setPluginStatus(self.bibleCheckBox, u'bibles/status')
self._setPluginStatus(self.presentationCheckBox, # TODO Presentation plugin is not yet working on Mac OS X.
u'presentations/status') # For now just ignore it.
if sys.platform != 'darwin':
self._setPluginStatus(self.presentationCheckBox,
u'presentations/status')
self._setPluginStatus(self.imageCheckBox, u'images/status') self._setPluginStatus(self.imageCheckBox, u'images/status')
self._setPluginStatus(self.mediaCheckBox, u'media/status') self._setPluginStatus(self.mediaCheckBox, u'media/status')
self._setPluginStatus(self.remoteCheckBox, u'remotes/status') self._setPluginStatus(self.remoteCheckBox, u'remotes/status')

View File

@ -82,13 +82,13 @@ class Ui_FirstTimeWizard(object):
self.imageCheckBox.setChecked(True) self.imageCheckBox.setChecked(True)
self.imageCheckBox.setObjectName(u'imageCheckBox') self.imageCheckBox.setObjectName(u'imageCheckBox')
self.pluginLayout.addWidget(self.imageCheckBox) self.pluginLayout.addWidget(self.imageCheckBox)
self.presentationCheckBox = QtGui.QCheckBox(self.pluginPage) # TODO Presentation plugin is not yet working on Mac OS X.
if sys.platform == "darwin": # For now just ignore it.
self.presentationCheckBox.setChecked(False) if sys.platform != 'darwin':
else: self.presentationCheckBox = QtGui.QCheckBox(self.pluginPage)
self.presentationCheckBox.setChecked(True) self.presentationCheckBox.setChecked(True)
self.presentationCheckBox.setObjectName(u'presentationCheckBox') self.presentationCheckBox.setObjectName(u'presentationCheckBox')
self.pluginLayout.addWidget(self.presentationCheckBox) self.pluginLayout.addWidget(self.presentationCheckBox)
self.mediaCheckBox = QtGui.QCheckBox(self.pluginPage) self.mediaCheckBox = QtGui.QCheckBox(self.pluginPage)
self.mediaCheckBox.setChecked(True) self.mediaCheckBox.setChecked(True)
self.mediaCheckBox.setObjectName(u'mediaCheckBox') self.mediaCheckBox.setObjectName(u'mediaCheckBox')
@ -214,10 +214,11 @@ class Ui_FirstTimeWizard(object):
self.bibleCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Bible')) self.bibleCheckBox.setText(translate('OpenLP.FirstTimeWizard', 'Bible'))
self.imageCheckBox.setText(translate('OpenLP.FirstTimeWizard', self.imageCheckBox.setText(translate('OpenLP.FirstTimeWizard',
'Images')) 'Images'))
self.presentationCheckBox.setText(translate('OpenLP.FirstTimeWizard', # TODO Presentation plugin is not yet working on Mac OS X.
'Presentations')) # For now just ignore it.
if sys.platform == "darwin": if sys.platform != 'darwin':
self.presentationCheckBox.setEnabled(False) self.presentationCheckBox.setText(translate('OpenLP.FirstTimeWizard',
'Presentations'))
self.mediaCheckBox.setText(translate('OpenLP.FirstTimeWizard', self.mediaCheckBox.setText(translate('OpenLP.FirstTimeWizard',
'Media (Audio and Video)')) 'Media (Audio and Video)'))
self.remoteCheckBox.setText(translate('OpenLP.FirstTimeWizard', self.remoteCheckBox.setText(translate('OpenLP.FirstTimeWizard',

View File

@ -31,6 +31,7 @@ and play multimedia within OpenLP.
""" """
import cgi import cgi
import logging import logging
import os
import sys import sys
from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL
@ -135,8 +136,14 @@ class MainDisplay(Display):
self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;') self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;')
windowFlags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool | \ windowFlags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool | \
QtCore.Qt.WindowStaysOnTopHint QtCore.Qt.WindowStaysOnTopHint
# Fix for bug #1014422.
x11_bypass_default = True
if sys.platform.startswith(u'linux'):
# Default to False on Gnome.
x11_bypass_default = bool(not
os.environ.get(u'GNOME_DESKTOP_SESSION_ID'))
if Settings().value(u'advanced/x11 bypass wm', if Settings().value(u'advanced/x11 bypass wm',
QtCore.QVariant(True)).toBool(): QtCore.QVariant(x11_bypass_default)).toBool():
windowFlags |= QtCore.Qt.X11BypassWindowManagerHint windowFlags |= QtCore.Qt.X11BypassWindowManagerHint
# TODO: The following combination of windowFlags works correctly # TODO: The following combination of windowFlags works correctly
# on Mac OS X. For next OpenLP version we should test it on other # on Mac OS X. For next OpenLP version we should test it on other

View File

@ -855,7 +855,11 @@ class SlideController(Controller):
else: else:
label = QtGui.QLabel() label = QtGui.QLabel()
label.setMargin(4) label.setMargin(4)
label.setScaledContents(True) if serviceItem.is_media():
label.setAlignment(QtCore.Qt.AlignHCenter |
QtCore.Qt.AlignVCenter)
else:
label.setScaledContents(True)
if self.serviceItem.is_command(): if self.serviceItem.is_command():
label.setPixmap(QtGui.QPixmap(frame[u'image'])) label.setPixmap(QtGui.QPixmap(frame[u'image']))
else: else:

View File

@ -30,7 +30,6 @@ import os
import zipfile import zipfile
import shutil import shutil
import logging import logging
import locale
import re import re
from xml.etree.ElementTree import ElementTree, XML from xml.etree.ElementTree import ElementTree, XML
@ -46,7 +45,8 @@ from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
create_widget_action create_widget_action
from openlp.core.theme import Theme from openlp.core.theme import Theme
from openlp.core.ui import FileRenameForm, ThemeForm from openlp.core.ui import FileRenameForm, ThemeForm
from openlp.core.utils import AppLocation, delete_file, get_filesystem_encoding from openlp.core.utils import AppLocation, delete_file, locale_compare, \
get_filesystem_encoding
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -284,6 +284,7 @@ class ThemeManager(QtGui.QWidget):
plugin.renameTheme(old_theme_name, new_theme_name) plugin.renameTheme(old_theme_name, new_theme_name)
self.mainwindow.renderer.update_theme( self.mainwindow.renderer.update_theme(
new_theme_name, old_theme_name) new_theme_name, old_theme_name)
self.loadThemes()
def onCopyTheme(self): def onCopyTheme(self):
""" """
@ -457,9 +458,8 @@ class ThemeManager(QtGui.QWidget):
self.configUpdated() self.configUpdated()
files = SettingsManager.get_files(self.settingsSection, u'.png') files = SettingsManager.get_files(self.settingsSection, u'.png')
# Sort the themes by its name considering language specific characters. # Sort the themes by its name considering language specific characters.
# lower() is needed for windows! files.sort(key=lambda file_name: unicode(file_name),
files.sort(key=lambda file_name: unicode(file_name).lower(), cmp=locale_compare)
cmp=locale.strcoll)
# now process the file list of png files # now process the file list of png files
for name in files: for name in files:
# check to see file is in theme root directory # check to see file is in theme root directory

View File

@ -488,10 +488,30 @@ def format_time(text, local_time):
return re.sub('\%[a-zA-Z]', match_formatting, text) return re.sub('\%[a-zA-Z]', match_formatting, text)
def locale_compare(string1, string2):
"""
Compares two strings according to the current locale settings.
As any other compare function, returns a negative, or a positive value,
or 0, depending on whether string1 collates before or after string2 or
is equal to it. Comparison is case insensitive.
"""
# Function locale.strcol() from standard Python library does not work
# properly on Windows and probably somewhere else.
return QtCore.QString.localeAwareCompare(string1.lower(), string2.lower())
# For performance reasons provide direct reference to compare function
# without wrapping it in another function making te string lowercase.
# This is needed for sorting songs.
locale_direct_compare = QtCore.QString.localeAwareCompare
from languagemanager import LanguageManager from languagemanager import LanguageManager
from actions import ActionList from actions import ActionList
__all__ = [u'AppLocation', u'get_application_version', u'check_latest_version', __all__ = [u'AppLocation', u'get_application_version', u'check_latest_version',
u'add_actions', u'get_filesystem_encoding', u'LanguageManager', u'add_actions', u'get_filesystem_encoding', u'LanguageManager',
u'ActionList', u'get_web_page', u'get_uno_command', u'get_uno_instance', u'ActionList', u'get_web_page', u'get_uno_command', u'get_uno_instance',
u'delete_file', u'clean_filename', u'format_time'] u'delete_file', u'clean_filename', u'format_time', u'locale_compare',
u'locale_direct_compare']

View File

@ -105,6 +105,7 @@ CSS = """
font-size: %spt; font-size: %spt;
color: %s; color: %s;
background-color: %s; background-color: %s;
word-wrap: break-word;
} }
""" """

View File

@ -35,7 +35,7 @@ class Ui_AlertDialog(object):
def setupUi(self, alertDialog): def setupUi(self, alertDialog):
alertDialog.setObjectName(u'alertDialog') alertDialog.setObjectName(u'alertDialog')
alertDialog.resize(400, 300) alertDialog.resize(400, 300)
alertDialog.setWindowIcon(build_icon(u':/icon/openlp.org-icon-32.bmp')) alertDialog.setWindowIcon(build_icon(u':/icon/openlp-logo-16x16.png'))
self.alertDialogLayout = QtGui.QGridLayout(alertDialog) self.alertDialogLayout = QtGui.QGridLayout(alertDialog)
self.alertDialogLayout.setObjectName(u'alertDialogLayout') self.alertDialogLayout.setObjectName(u'alertDialogLayout')
self.alertTextLayout = QtGui.QFormLayout() self.alertTextLayout = QtGui.QFormLayout()

View File

@ -80,6 +80,10 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
item_name = QtGui.QListWidgetItem(alert.text) item_name = QtGui.QListWidgetItem(alert.text)
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(alert.id)) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(alert.id))
self.alertListWidget.addItem(item_name) self.alertListWidget.addItem(item_name)
if alert.text == unicode(self.alertTextEdit.text()):
self.item_id = alert.id
self.alertListWidget.setCurrentRow(
self.alertListWidget.row(item_name))
def onDisplayClicked(self): def onDisplayClicked(self):
self.triggerAlert(unicode(self.alertTextEdit.text())) self.triggerAlert(unicode(self.alertTextEdit.text()))
@ -112,7 +116,6 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
alert = AlertItem() alert = AlertItem()
alert.text = unicode(self.alertTextEdit.text()) alert.text = unicode(self.alertTextEdit.text())
self.manager.save_object(alert) self.manager.save_object(alert)
self.alertTextEdit.setText(u'')
self.loadList() self.loadList()
def onSaveClick(self): def onSaveClick(self):
@ -125,6 +128,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog):
self.manager.save_object(alert) self.manager.save_object(alert)
self.item_id = None self.item_id = None
self.loadList() self.loadList()
self.saveButton.setEnabled(False)
def onTextChanged(self): def onTextChanged(self):
""" """

View File

@ -57,8 +57,6 @@ class AlertsManager(QtCore.QObject):
""" """
if message: if message:
self.displayAlert(message[0]) self.displayAlert(message[0])
else:
self.displayAlert(u'')
def displayAlert(self, text=u''): def displayAlert(self, text=u''):
""" """
@ -68,14 +66,15 @@ class AlertsManager(QtCore.QObject):
display text display text
""" """
log.debug(u'display alert called %s' % text) log.debug(u'display alert called %s' % text)
self.alertList.append(text) if text:
if self.timer_id != 0: self.alertList.append(text)
Receiver.send_message(u'mainwindow_status_text', if self.timer_id != 0:
translate('AlertsPlugin.AlertsManager', Receiver.send_message(u'mainwindow_status_text',
'Alert message created and displayed.')) translate('AlertsPlugin.AlertsManager',
return 'Alert message created and displayed.'))
Receiver.send_message(u'mainwindow_status_text', u'') return
self.generateAlert() Receiver.send_message(u'mainwindow_status_text', u'')
self.generateAlert()
def generateAlert(self): def generateAlert(self):
""" """

View File

@ -30,7 +30,6 @@ The bible import functions for OpenLP
""" """
import logging import logging
import os import os
import locale
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
@ -39,7 +38,7 @@ from openlp.core.lib.db import delete_database
from openlp.core.lib.ui import UiStrings, critical_error_message_box from openlp.core.lib.ui import UiStrings, critical_error_message_box
from openlp.core.lib.settings import Settings from openlp.core.lib.settings import Settings
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
from openlp.core.utils import AppLocation from openlp.core.utils import AppLocation, locale_compare
from openlp.plugins.bibles.lib.manager import BibleFormat from openlp.plugins.bibles.lib.manager import BibleFormat
from openlp.plugins.bibles.lib.db import BiblesResourcesDB, clean_filename from openlp.plugins.bibles.lib.db import BiblesResourcesDB, clean_filename
@ -523,7 +522,7 @@ class BibleImportForm(OpenLPWizard):
""" """
self.webTranslationComboBox.clear() self.webTranslationComboBox.clear()
bibles = self.web_bible_list[index].keys() bibles = self.web_bible_list[index].keys()
bibles.sort(cmp=locale.strcoll) bibles.sort(cmp=locale_compare)
self.webTranslationComboBox.addItems(bibles) self.webTranslationComboBox.addItems(bibles)
def onOsisBrowseButtonClicked(self): def onOsisBrowseButtonClicked(self):

View File

@ -355,37 +355,8 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
log.debug(u'Matched reference %s' % reference) log.debug(u'Matched reference %s' % reference)
book = match.group(u'book') book = match.group(u'book')
if not book_ref_id: if not book_ref_id:
book_names = BibleStrings().BookNames book_ref_id = bible.get_book_ref_id_by_localised_name(
# escape reserved characters book, language_selection)
book_escaped = book
for character in u'\\.^$*+?{}[]()':
book_escaped = book_escaped.replace(
character, u'\\' + character)
regex_book = re.compile(u'\s*%s\s*' % u'\s*'.join(
book_escaped.split()), re.UNICODE | re.IGNORECASE)
if language_selection == LanguageSelection.Bible:
db_book = bible.get_book(book)
if db_book:
book_ref_id = db_book.book_reference_id
elif language_selection == LanguageSelection.Application:
books = filter(lambda key:
regex_book.match(unicode(book_names[key])), book_names.keys())
books = filter(None, map(BiblesResourcesDB.get_book, books))
for value in books:
if bible.get_book_by_book_ref_id(value[u'id']):
book_ref_id = value[u'id']
break
elif language_selection == LanguageSelection.English:
books = BiblesResourcesDB.get_books_like(book)
if books:
book_list = filter(
lambda value: regex_book.match(value[u'name']), books)
if not book_list:
book_list = books
for value in book_list:
if bible.get_book_by_book_ref_id(value[u'id']):
book_ref_id = value[u'id']
break
elif not bible.get_book_by_book_ref_id(book_ref_id): elif not bible.get_book_by_book_ref_id(book_ref_id):
book_ref_id = False book_ref_id = False
ranges = match.group(u'ranges') ranges = match.group(u'ranges')

View File

@ -29,6 +29,7 @@
import chardet import chardet
import logging import logging
import os import os
import re
import sqlite3 import sqlite3
from PyQt4 import QtCore from PyQt4 import QtCore
@ -44,6 +45,8 @@ import upgrade
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
RESERVED_CHARACTERS = u'\\.^$*+?{}[]()'
class BibleMeta(BaseModel): class BibleMeta(BaseModel):
""" """
Bible Meta Data Bible Meta Data
@ -352,6 +355,53 @@ class BibleDB(QtCore.QObject, Manager):
book, book_id, language_id) book, book_id, language_id)
return book_id return book_id
def get_book_ref_id_by_localised_name(self, book,
language_selection):
"""
Return the id of a named book.
``book``
The name of the book, according to the selected language.
``language_selection``
The language selection the user has chosen in the settings
section of the Bible.
"""
log.debug(u'get_book_ref_id_by_localised_name("%s", "%s")',
book, language_selection)
from openlp.plugins.bibles.lib import LanguageSelection, \
BibleStrings
book_names = BibleStrings().BookNames
# escape reserved characters
book_escaped = book
for character in RESERVED_CHARACTERS:
book_escaped = book_escaped.replace(
character, u'\\' + character)
regex_book = re.compile(u'\s*%s\s*' % u'\s*'.join(
book_escaped.split()), re.UNICODE | re.IGNORECASE)
if language_selection == LanguageSelection.Bible:
db_book = self.get_book(book)
if db_book:
return db_book.book_reference_id
elif language_selection == LanguageSelection.Application:
books = filter(lambda key:
regex_book.match(unicode(book_names[key])), book_names.keys())
books = filter(None, map(BiblesResourcesDB.get_book, books))
for value in books:
if self.get_book_by_book_ref_id(value[u'id']):
return value[u'id']
elif language_selection == LanguageSelection.English:
books = BiblesResourcesDB.get_books_like(book)
if books:
book_list = filter(
lambda value: regex_book.match(value[u'name']), books)
if not book_list:
book_list = books
for value in book_list:
if self.get_book_by_book_ref_id(value[u'id']):
return value[u'id']
return False
def get_verses(self, reference_list, show_error=True): 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

View File

@ -277,8 +277,9 @@ class BibleManager(object):
""" """
log.debug(u'BibleManager.get_verse_count("%s", "%s", %s)', log.debug(u'BibleManager.get_verse_count("%s", "%s", %s)',
bible, book, chapter) bible, book, chapter)
db_book = self.db_cache[bible].get_book(book) language_selection = self.get_language_selection(bible)
book_ref_id = db_book.book_reference_id book_ref_id = self.db_cache[bible].get_book_ref_id_by_localised_name(
book, language_selection)
return self.db_cache[bible].get_verse_count(book_ref_id, chapter) return self.db_cache[bible].get_verse_count(book_ref_id, chapter)
def get_verse_count_by_book_ref_id(self, bible, book_ref_id, chapter): def get_verse_count_by_book_ref_id(self, bible, book_ref_id, chapter):

View File

@ -27,7 +27,6 @@
############################################################################### ###############################################################################
import logging import logging
import locale
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
@ -38,6 +37,7 @@ from openlp.core.lib.settings import Settings
from openlp.core.lib.ui import UiStrings, set_case_insensitive_completer, \ from openlp.core.lib.ui import UiStrings, set_case_insensitive_completer, \
create_horizontal_adjusting_combo_box, critical_error_message_box, \ create_horizontal_adjusting_combo_box, critical_error_message_box, \
find_and_set_in_combo_box, build_icon find_and_set_in_combo_box, build_icon
from openlp.core.utils import locale_compare
from openlp.plugins.bibles.forms import BibleImportForm, EditBibleForm from openlp.plugins.bibles.forms import BibleImportForm, EditBibleForm
from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, \ from openlp.plugins.bibles.lib import LayoutStyle, DisplayStyle, \
VerseReferenceList, get_reference_separator, LanguageSelection, \ VerseReferenceList, get_reference_separator, LanguageSelection, \
@ -381,7 +381,7 @@ class BibleMediaItem(MediaManagerItem):
# Get all bibles and sort the list. # Get all bibles and sort the list.
bibles = self.plugin.manager.get_bibles().keys() bibles = self.plugin.manager.get_bibles().keys()
bibles = filter(None, bibles) bibles = filter(None, bibles)
bibles.sort(cmp=locale.strcoll) bibles.sort(cmp=locale_compare)
# Load the bibles into the combo boxes. # Load the bibles into the combo boxes.
self.quickVersionComboBox.addItems(bibles) self.quickVersionComboBox.addItems(bibles)
self.quickSecondComboBox.addItems(bibles) self.quickSecondComboBox.addItems(bibles)
@ -538,7 +538,7 @@ class BibleMediaItem(MediaManagerItem):
data = BiblesResourcesDB.get_book_by_id( data = BiblesResourcesDB.get_book_by_id(
book.book_reference_id) book.book_reference_id)
books.append(data[u'name'] + u' ') books.append(data[u'name'] + u' ')
books.sort(cmp=locale.strcoll) books.sort(cmp=locale_compare)
set_case_insensitive_completer(books, self.quickSearchEdit) set_case_insensitive_completer(books, self.quickSearchEdit)
def onImportClick(self): def onImportClick(self):

View File

@ -36,7 +36,7 @@ class Ui_CustomEditDialog(object):
customEditDialog.setObjectName(u'customEditDialog') customEditDialog.setObjectName(u'customEditDialog')
customEditDialog.resize(450, 350) customEditDialog.resize(450, 350)
customEditDialog.setWindowIcon( customEditDialog.setWindowIcon(
build_icon(u':/icon/openlp.org-icon-32.bmp')) build_icon(u':/icon/openlp-logo-16x16.png'))
self.dialogLayout = QtGui.QVBoxLayout(customEditDialog) self.dialogLayout = QtGui.QVBoxLayout(customEditDialog)
self.dialogLayout.setObjectName(u'dialogLayout') self.dialogLayout.setObjectName(u'dialogLayout')
self.titleLayout = QtGui.QHBoxLayout() self.titleLayout = QtGui.QHBoxLayout()

View File

@ -34,12 +34,21 @@ from sqlalchemy import Column, Table, types
from sqlalchemy.orm import mapper from sqlalchemy.orm import mapper
from openlp.core.lib.db import BaseModel, init_db from openlp.core.lib.db import BaseModel, init_db
from openlp.core.utils import locale_compare
class CustomSlide(BaseModel): class CustomSlide(BaseModel):
""" """
CustomSlide model CustomSlide model
""" """
pass # By default sort the customs by its title considering language specific
# characters.
def __lt__(self, other):
r = locale_compare(self.title, other.title)
return True if r < 0 else False
def __eq__(self, other):
return 0 == locale_compare(self.title, other.title)
def init_schema(url): def init_schema(url):
""" """

View File

@ -27,7 +27,6 @@
############################################################################### ###############################################################################
import logging import logging
import locale
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from sqlalchemy.sql import or_, func from sqlalchemy.sql import or_, func
@ -109,10 +108,7 @@ class CustomMediaItem(MediaManagerItem):
# Sort out what custom we want to select after loading the list. # Sort out what custom we want to select after loading the list.
self.saveAutoSelectId() self.saveAutoSelectId()
self.listView.clear() self.listView.clear()
# Sort the customs by its title considering language specific custom_slides.sort()
# characters. lower() is needed for windows!
custom_slides.sort(
cmp=locale.strcoll, key=lambda custom: custom.title.lower())
for custom_slide in custom_slides: for custom_slide in custom_slides:
custom_name = QtGui.QListWidgetItem(custom_slide.title) custom_name = QtGui.QListWidgetItem(custom_slide.title)
custom_name.setData( custom_name.setData(

View File

@ -28,7 +28,6 @@
import logging import logging
import os import os
import locale
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
@ -37,7 +36,8 @@ from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \
Receiver, create_thumb, validate_thumb Receiver, create_thumb, validate_thumb
from openlp.core.lib.ui import UiStrings, critical_error_message_box from openlp.core.lib.ui import UiStrings, critical_error_message_box
from openlp.core.lib.settings import Settings from openlp.core.lib.settings import Settings
from openlp.core.utils import AppLocation, delete_file, get_images_filter from openlp.core.utils import AppLocation, delete_file, locale_compare, \
get_images_filter
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -126,10 +126,10 @@ class ImageMediaItem(MediaManagerItem):
if not initialLoad: if not initialLoad:
Receiver.send_message(u'cursor_busy') Receiver.send_message(u'cursor_busy')
self.plugin.formParent.displayProgressBar(len(images)) self.plugin.formParent.displayProgressBar(len(images))
# Sort the themes by its filename considering language specific # Sort the images by its filename considering language specific
# characters. lower() is needed for windows! # characters.
images.sort(cmp=locale.strcoll, images.sort(cmp=locale_compare,
key=lambda filename: os.path.split(unicode(filename))[1].lower()) key=lambda filename: os.path.split(unicode(filename))[1])
for imageFile in images: for imageFile in images:
filename = os.path.split(unicode(imageFile))[1] filename = os.path.split(unicode(imageFile))[1]
thumb = os.path.join(self.servicePath, filename) thumb = os.path.join(self.servicePath, filename)

View File

@ -28,7 +28,6 @@
import logging import logging
import os import os
import locale
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
@ -39,11 +38,13 @@ from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
create_horizontal_adjusting_combo_box create_horizontal_adjusting_combo_box
from openlp.core.ui import Controller, Display from openlp.core.ui import Controller, Display
from openlp.core.ui.media import get_media_players, set_media_players from openlp.core.ui.media import get_media_players, set_media_players
from openlp.core.utils import locale_compare
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
CLAPPERBOARD = QtGui.QImage(u':/media/media_video.png') CLAPPERBOARD = u':/media/slidecontroller_multimedia.png'
#TODO: Add an appropriate Icon for DVDs, CDs, ... VIDEO = QtGui.QImage(u':/media/media_video.png')
AUDIO = QtGui.QImage(u':/media/media_audio.png')
DVD_ICON = QtGui.QImage(u':/media/media_video.png') DVD_ICON = QtGui.QImage(u':/media/media_video.png')
class MediaMediaItem(MediaManagerItem): class MediaMediaItem(MediaManagerItem):
@ -218,7 +219,7 @@ class MediaMediaItem(MediaManagerItem):
service_item.add_capability(ItemCapabilities.RequiresMedia) service_item.add_capability(ItemCapabilities.RequiresMedia)
# force a non-existent theme # force a non-existent theme
service_item.theme = -1 service_item.theme = -1
frame = u':/media/image_clapperboard.png' frame = CLAPPERBOARD
(path, name) = os.path.split(filename) (path, name) = os.path.split(filename)
service_item.add_from_command(path, name, frame) service_item.add_from_command(path, name, frame)
return True return True
@ -284,16 +285,16 @@ class MediaMediaItem(MediaManagerItem):
u'media', self.getFileList()) u'media', self.getFileList())
def loadList(self, media): def loadList(self, media):
# Sort the themes by its filename considering language specific # Sort the media by its filename considering language specific
# characters. lower() is needed for windows! # characters.
media.sort(cmp=locale.strcoll, media.sort(cmp=locale_compare,
key=lambda filename: os.path.split(unicode(filename))[1].lower()) key=lambda filename: os.path.split(unicode(filename))[1])
for track in media: for track in media:
track_info = QtCore.QFileInfo(track) track_info = QtCore.QFileInfo(track)
if not track_info.isFile(): if track_info.isFile():
filename = os.path.split(unicode(track))[1] filename = os.path.split(unicode(track))[1]
item_name = QtGui.QListWidgetItem(filename) item_name = QtGui.QListWidgetItem(filename)
item_name.setIcon(build_icon(CLAPPERBOARD)) item_name.setIcon(build_icon(VIDEO))
item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track)) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(track))
else: else:
filename = os.path.split(unicode(track))[1] filename = os.path.split(unicode(track))[1]
@ -306,8 +307,8 @@ class MediaMediaItem(MediaManagerItem):
def getList(self, type=MediaType.Audio): def getList(self, type=MediaType.Audio):
media = SettingsManager.load_list(self.settingsSection, u'media') media = SettingsManager.load_list(self.settingsSection, u'media')
media.sort(cmp=locale.strcoll, media.sort(cmp=locale_compare,
key=lambda filename: os.path.split(unicode(filename))[1].lower()) key=lambda filename: os.path.split(unicode(filename))[1])
ext = [] ext = []
if type == MediaType.Audio: if type == MediaType.Audio:
ext = self.plugin.audio_extensions_list ext = self.plugin.audio_extensions_list

View File

@ -153,7 +153,7 @@ class ImpressController(PresentationController):
desktop = None desktop = None
try: try:
desktop = self.manager.createInstance(u'com.sun.star.frame.Desktop') desktop = self.manager.createInstance(u'com.sun.star.frame.Desktop')
except AttributeError: except (AttributeError, pywintypes.com_error):
log.warn(u'Failure to find desktop - Impress may have closed') log.warn(u'Failure to find desktop - Impress may have closed')
return desktop if desktop else None return desktop if desktop else None
@ -284,6 +284,8 @@ class ImpressDocument(PresentationDocument):
props = tuple(props) props = tuple(props)
doc = self.document doc = self.document
pages = doc.getDrawPages() pages = doc.getDrawPages()
if not pages:
return
if not os.path.isdir(self.get_temp_folder()): if not os.path.isdir(self.get_temp_folder()):
os.makedirs(self.get_temp_folder()) os.makedirs(self.get_temp_folder())
for idx in range(pages.getCount()): for idx in range(pages.getCount()):
@ -359,7 +361,7 @@ class ImpressDocument(PresentationDocument):
log.debug(u'is active OpenOffice') log.debug(u'is active OpenOffice')
if not self.is_loaded(): if not self.is_loaded():
return False return False
return self.control is not None return self.control.isRunning() if self.control else False
def unblank_screen(self): def unblank_screen(self):
""" """
@ -380,7 +382,7 @@ class ImpressDocument(PresentationDocument):
Returns true if screen is blank Returns true if screen is blank
""" """
log.debug(u'is blank OpenOffice') log.debug(u'is blank OpenOffice')
if self.control: if self.control and self.control.isRunning():
return self.control.isPaused() return self.control.isPaused()
else: else:
return False return False
@ -436,7 +438,11 @@ class ImpressDocument(PresentationDocument):
""" """
Triggers the next effect of slide on the running presentation Triggers the next effect of slide on the running presentation
""" """
is_paused = self.control.isPaused()
self.control.gotoNextEffect() self.control.gotoNextEffect()
time.sleep(0.1)
if not is_paused and self.control.isPaused():
self.control.gotoPreviousEffect()
def previous_step(self): def previous_step(self):
""" """

View File

@ -28,7 +28,6 @@
import logging import logging
import os import os
import locale
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
@ -38,6 +37,7 @@ from openlp.core.lib import MediaManagerItem, build_icon, SettingsManager, \
from openlp.core.lib.ui import UiStrings, critical_error_message_box, \ from openlp.core.lib.ui import UiStrings, critical_error_message_box, \
create_horizontal_adjusting_combo_box create_horizontal_adjusting_combo_box
from openlp.core.lib.settings import Settings from openlp.core.lib.settings import Settings
from openlp.core.utils import locale_compare
from openlp.plugins.presentations.lib import MessageListener from openlp.plugins.presentations.lib import MessageListener
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -169,10 +169,10 @@ class PresentationMediaItem(MediaManagerItem):
if not initialLoad: if not initialLoad:
Receiver.send_message(u'cursor_busy') Receiver.send_message(u'cursor_busy')
self.plugin.formParent.displayProgressBar(len(files)) self.plugin.formParent.displayProgressBar(len(files))
# Sort the themes by its filename considering language specific # Sort the presentations by its filename considering language specific
# characters. lower() is needed for windows! # characters.
files.sort(cmp=locale.strcoll, files.sort(cmp=locale_compare,
key=lambda filename: os.path.split(unicode(filename))[1].lower()) key=lambda filename: os.path.split(unicode(filename))[1])
for file in files: for file in files:
if not initialLoad: if not initialLoad:
self.plugin.formParent.incrementProgressBar() self.plugin.formParent.incrementProgressBar()

View File

@ -49,6 +49,7 @@ class Controller(object):
""" """
self.is_live = live self.is_live = live
self.doc = None self.doc = None
self.hide_mode = None
log.info(u'%s controller loaded' % live) log.info(u'%s controller loaded' % live)
def add_handler(self, controller, file, hide_mode, slide_no): def add_handler(self, controller, file, hide_mode, slide_no):
@ -67,6 +68,7 @@ class Controller(object):
# Inform slidecontroller that the action failed? # Inform slidecontroller that the action failed?
return return
self.doc.slidenumber = slide_no self.doc.slidenumber = slide_no
self.hide_mode = hide_mode
if self.is_live: if self.is_live:
if hide_mode == HideMode.Screen: if hide_mode == HideMode.Screen:
Receiver.send_message(u'live_display_hide', HideMode.Screen) Receiver.send_message(u'live_display_hide', HideMode.Screen)
@ -78,7 +80,7 @@ class Controller(object):
else: else:
self.doc.start_presentation() self.doc.start_presentation()
Receiver.send_message(u'live_display_hide', HideMode.Screen) Receiver.send_message(u'live_display_hide', HideMode.Screen)
self.doc.slidenumber = 0 self.doc.slidenumber = 1
if slide_no > 1: if slide_no > 1:
self.slide(slide_no) self.slide(slide_no)
@ -88,100 +90,134 @@ class Controller(object):
Use the last slide number. Use the last slide number.
""" """
log.debug(u'Live = %s, activate' % self.is_live) log.debug(u'Live = %s, activate' % self.is_live)
if not self.doc:
return False
if self.doc.is_active(): if self.doc.is_active():
return return True
if not self.doc.is_loaded(): if not self.doc.is_loaded():
if not self.doc.load_presentation(): if not self.doc.load_presentation():
return log.warn(u'Failed to activate %s' % self.doc.filepath)
return False
if self.is_live: if self.is_live:
self.doc.start_presentation() self.doc.start_presentation()
if self.doc.slidenumber > 1: if self.doc.slidenumber > 1:
if self.doc.slidenumber > self.doc.get_slide_count(): if self.doc.slidenumber > self.doc.get_slide_count():
self.doc.slidenumber = self.doc.get_slide_count() self.doc.slidenumber = self.doc.get_slide_count()
self.doc.goto_slide(self.doc.slidenumber) self.doc.goto_slide(self.doc.slidenumber)
if self.doc.is_active():
return True
else:
log.warn(u'Failed to activate %s' % self.doc.filepath)
return False
def slide(self, slide): def slide(self, slide):
""" """
Go to a specific slide Go to a specific slide
""" """
log.debug(u'Live = %s, slide' % self.is_live) log.debug(u'Live = %s, slide' % self.is_live)
if not self.doc:
return
if not self.is_live: if not self.is_live:
return return
if self.doc.is_blank(): if self.hide_mode:
self.doc.slidenumber = int(slide) + 1 self.doc.slidenumber = int(slide) + 1
self.poll()
return
if not self.activate():
return return
self.activate()
self.doc.goto_slide(int(slide) + 1) self.doc.goto_slide(int(slide) + 1)
self.doc.poll_slidenumber(self.is_live) self.poll()
def first(self): def first(self):
""" """
Based on the handler passed at startup triggers the first slide Based on the handler passed at startup triggers the first slide
""" """
log.debug(u'Live = %s, first' % self.is_live) log.debug(u'Live = %s, first' % self.is_live)
if not self.doc:
return
if not self.is_live: if not self.is_live:
return return
if self.doc.is_blank(): if self.hide_mode:
self.doc.slidenumber = 1 self.doc.slidenumber = 1
self.poll()
return
if not self.activate():
return return
self.activate()
self.doc.start_presentation() self.doc.start_presentation()
self.doc.poll_slidenumber(self.is_live) self.poll()
def last(self): def last(self):
""" """
Based on the handler passed at startup triggers the last slide Based on the handler passed at startup triggers the last slide
""" """
log.debug(u'Live = %s, last' % self.is_live) log.debug(u'Live = %s, last' % self.is_live)
if not self.doc:
return
if not self.is_live: if not self.is_live:
return return
if self.doc.is_blank(): if self.hide_mode:
self.doc.slidenumber = self.doc.get_slide_count() self.doc.slidenumber = self.doc.get_slide_count()
self.poll()
return
if not self.activate():
return return
self.activate()
self.doc.goto_slide(self.doc.get_slide_count()) self.doc.goto_slide(self.doc.get_slide_count())
self.doc.poll_slidenumber(self.is_live) self.poll()
def next(self): def next(self):
""" """
Based on the handler passed at startup triggers the next slide event Based on the handler passed at startup triggers the next slide event
""" """
log.debug(u'Live = %s, next' % self.is_live) log.debug(u'Live = %s, next' % self.is_live)
if not self.doc:
return
if not self.is_live: if not self.is_live:
return return
if self.doc.is_blank(): if self.hide_mode:
if not self.doc.is_active():
return
if self.doc.slidenumber < self.doc.get_slide_count(): if self.doc.slidenumber < self.doc.get_slide_count():
self.doc.slidenumber = self.doc.slidenumber + 1 self.doc.slidenumber = self.doc.slidenumber + 1
self.poll()
return
if not self.activate():
return return
# The "End of slideshow" screen is after the last slide # The "End of slideshow" screen is after the last slide
# Note, we can't just stop on the last slide, since it may # Note, we can't just stop on the last slide, since it may
# contain animations that need to be stepped through. # contain animations that need to be stepped through.
if self.doc.slidenumber > self.doc.get_slide_count(): if self.doc.slidenumber > self.doc.get_slide_count():
return return
self.activate()
self.doc.next_step() self.doc.next_step()
self.doc.poll_slidenumber(self.is_live) self.poll()
def previous(self): def previous(self):
""" """
Based on the handler passed at startup triggers the previous slide event Based on the handler passed at startup triggers the previous slide event
""" """
log.debug(u'Live = %s, previous' % self.is_live) log.debug(u'Live = %s, previous' % self.is_live)
if not self.doc:
return
if not self.is_live: if not self.is_live:
return return
if self.doc.is_blank(): if self.hide_mode:
if not self.doc.is_active():
return
if self.doc.slidenumber > 1: if self.doc.slidenumber > 1:
self.doc.slidenumber = self.doc.slidenumber - 1 self.doc.slidenumber = self.doc.slidenumber - 1
self.poll()
return
if not self.activate():
return return
self.activate()
self.doc.previous_step() self.doc.previous_step()
self.doc.poll_slidenumber(self.is_live) self.poll()
def shutdown(self): def shutdown(self):
""" """
Based on the handler passed at startup triggers slide show to shut down Based on the handler passed at startup triggers slide show to shut down
""" """
log.debug(u'Live = %s, shutdown' % self.is_live) log.debug(u'Live = %s, shutdown' % self.is_live)
if not self.doc:
return
self.doc.close_presentation() self.doc.close_presentation()
self.doc = None self.doc = None
@ -190,21 +226,30 @@ class Controller(object):
Instruct the controller to blank the presentation Instruct the controller to blank the presentation
""" """
log.debug(u'Live = %s, blank' % self.is_live) log.debug(u'Live = %s, blank' % self.is_live)
self.hide_mode = hide_mode
if not self.doc:
return
if not self.is_live: if not self.is_live:
return return
if not self.doc.is_loaded():
return
if not self.doc.is_active():
return
if hide_mode == HideMode.Theme: if hide_mode == HideMode.Theme:
if not self.doc.is_loaded():
return
if not self.doc.is_active():
return
Receiver.send_message(u'live_display_hide', HideMode.Theme) Receiver.send_message(u'live_display_hide', HideMode.Theme)
self.doc.blank_screen() elif hide_mode == HideMode.Blank:
if not self.activate():
return
self.doc.blank_screen()
def stop(self): def stop(self):
""" """
Instruct the controller to stop and hide the presentation Instruct the controller to stop and hide the presentation
""" """
log.debug(u'Live = %s, stop' % self.is_live) log.debug(u'Live = %s, stop' % self.is_live)
self.hide_mode = HideMode.Screen
if not self.doc:
return
if not self.is_live: if not self.is_live:
return return
if not self.doc.is_loaded(): if not self.doc.is_loaded():
@ -218,9 +263,13 @@ class Controller(object):
Instruct the controller to unblank the presentation Instruct the controller to unblank the presentation
""" """
log.debug(u'Live = %s, unblank' % self.is_live) log.debug(u'Live = %s, unblank' % self.is_live)
self.hide_mode = None
if not self.doc:
return
if not self.is_live: if not self.is_live:
return return
self.activate() if not self.activate():
return
if self.doc.slidenumber and \ if self.doc.slidenumber and \
self.doc.slidenumber != self.doc.get_slide_number(): self.doc.slidenumber != self.doc.get_slide_number():
self.doc.goto_slide(self.doc.slidenumber) self.doc.goto_slide(self.doc.slidenumber)
@ -228,7 +277,9 @@ class Controller(object):
Receiver.send_message(u'live_display_hide', HideMode.Screen) Receiver.send_message(u'live_display_hide', HideMode.Screen)
def poll(self): def poll(self):
self.doc.poll_slidenumber(self.is_live) if not self.doc:
return
self.doc.poll_slidenumber(self.is_live, self.hide_mode)
class MessageListener(object): class MessageListener(object):

View File

@ -94,9 +94,9 @@ class PowerpointController(PresentationController):
self.docs[0].close_presentation() self.docs[0].close_presentation()
if self.process is None: if self.process is None:
return return
if self.process.Presentations.Count > 0:
return
try: try:
if self.process.Presentations.Count > 0:
return
self.process.Quit() self.process.Quit()
except pywintypes.com_error: except pywintypes.com_error:
pass pass
@ -210,6 +210,13 @@ class PowerpointDocument(PresentationDocument):
self.presentation.SlideShowSettings.Run() self.presentation.SlideShowSettings.Run()
self.presentation.SlideShowWindow.View.State = 1 self.presentation.SlideShowWindow.View.State = 1
self.presentation.SlideShowWindow.Activate() self.presentation.SlideShowWindow.Activate()
if self.presentation.Application.Version == u'14.0':
# Unblanking is broken in PowerPoint 2010, need to redisplay
slide = self.presentation.SlideShowWindow.View.CurrentShowPosition
click = self.presentation.SlideShowWindow.View.GetClickIndex()
self.presentation.SlideShowWindow.View.GotoSlide(slide)
if click:
self.presentation.SlideShowWindow.View.GotoClick(click)
def blank_screen(self): def blank_screen(self):
""" """
@ -253,6 +260,8 @@ class PowerpointDocument(PresentationDocument):
renderer = self.controller.plugin.renderer renderer = self.controller.plugin.renderer
rect = renderer.screens.current[u'size'] rect = renderer.screens.current[u'size']
ppt_window = self.presentation.SlideShowSettings.Run() ppt_window = self.presentation.SlideShowSettings.Run()
if not ppt_window:
return
ppt_window.Top = rect.y() * 72 / dpi ppt_window.Top = rect.y() * 72 / dpi
ppt_window.Height = rect.height() * 72 / dpi ppt_window.Height = rect.height() * 72 / dpi
ppt_window.Left = rect.x() * 72 / dpi ppt_window.Left = rect.x() * 72 / dpi
@ -286,6 +295,8 @@ class PowerpointDocument(PresentationDocument):
""" """
log.debug(u'next_step') log.debug(u'next_step')
self.presentation.SlideShowWindow.View.Next() self.presentation.SlideShowWindow.View.Next()
if self.get_slide_number() > self.get_slide_count():
self.previous_step()
def previous_step(self): def previous_step(self):
""" """

View File

@ -260,16 +260,17 @@ class PresentationDocument(object):
else: else:
return None return None
def poll_slidenumber(self, is_live): def poll_slidenumber(self, is_live, hide_mode):
""" """
Check the current slide number Check the current slide number
""" """
if not self.is_active(): if not self.is_active():
return return
current = self.get_slide_number() if not hide_mode:
if current == self.slidenumber: current = self.get_slide_number()
return if current == self.slidenumber:
self.slidenumber = current return
self.slidenumber = current
if is_live: if is_live:
prefix = u'live' prefix = u'live'
else: else:

View File

@ -66,10 +66,10 @@
<a href="#" id="service-refresh" data-role="button" data-icon="refresh">${refresh}</a> <a href="#" id="service-refresh" data-role="button" data-icon="refresh">${refresh}</a>
<div data-role="navbar"> <div data-role="navbar">
<ul> <ul>
<li><a href="#service-manager" data-theme="e">Service</a></li> <li><a href="#service-manager" data-theme="e">${service}</a></li>
<li><a href="#slide-controller">Slides</a></li> <li><a href="#slide-controller">${slides}</a></li>
<li><a href="#alerts">Alerts</a></li> <li><a href="#alerts">${alerts}</a></li>
<li><a href="#search">Search</a></li> <li><a href="#search">${search}</a></li>
</ul> </ul>
</div> </div>
</div> </div>
@ -97,10 +97,10 @@
<a href="#" id="controller-refresh" data-role="button" data-icon="refresh">${refresh}</a> <a href="#" id="controller-refresh" data-role="button" data-icon="refresh">${refresh}</a>
<div data-role="navbar"> <div data-role="navbar">
<ul> <ul>
<li><a href="#service-manager">Service</a></li> <li><a href="#service-manager">${service}</a></li>
<li><a href="#slide-controller" data-theme="e">Slides</a></li> <li><a href="#slide-controller" data-theme="e">${slides}</a></li>
<li><a href="#alerts">Alerts</a></li> <li><a href="#alerts">${alerts}</a></li>
<li><a href="#search">Search</a></li> <li><a href="#search">${search}</a></li>
</ul> </ul>
</div> </div>
</div> </div>
@ -127,10 +127,10 @@
<h1>${alerts}</h1> <h1>${alerts}</h1>
<div data-role="navbar"> <div data-role="navbar">
<ul> <ul>
<li><a href="#service-manager">Service</a></li> <li><a href="#service-manager">${service}</a></li>
<li><a href="#slide-controller">Slides</a></li> <li><a href="#slide-controller">${slides}</a></li>
<li><a href="#alerts" data-theme="e">Alerts</a></li> <li><a href="#alerts" data-theme="e">${alerts}</a></li>
<li><a href="#search">Search</a></li> <li><a href="#search">${search}</a></li>
</ul> </ul>
</div> </div>
</div> </div>
@ -148,10 +148,10 @@
<h1>${search}</h1> <h1>${search}</h1>
<div data-role="navbar"> <div data-role="navbar">
<ul> <ul>
<li><a href="#service-manager">Service</a></li> <li><a href="#service-manager">${service}</a></li>
<li><a href="#slide-controller">Slides</a></li> <li><a href="#slide-controller">${slides}</a></li>
<li><a href="#alerts">Alerts</a></li> <li><a href="#alerts">${alerts}</a></li>
<li><a href="#search" data-theme="e">Search</a></li> <li><a href="#search" data-theme="e">${search}</a></li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -55,7 +55,9 @@ window.OpenLP = {
); );
}, },
loadService: function (event) { loadService: function (event) {
event.preventDefault(); if (event) {
event.preventDefault();
}
$.getJSON( $.getJSON(
"/api/service/list", "/api/service/list",
function (data, status) { function (data, status) {
@ -150,6 +152,10 @@ window.OpenLP = {
OpenLP.currentSlide = data.results.slide; OpenLP.currentSlide = data.results.slide;
OpenLP.currentItem = data.results.item; OpenLP.currentItem = data.results.item;
if ($("#service-manager").is(":visible")) { if ($("#service-manager").is(":visible")) {
if (OpenLP.currentService != data.results.service) {
OpenLP.currentService = data.results.service;
OpenLP.loadService();
}
$("#service-manager div[data-role=content] ul[data-role=listview] li").attr("data-theme", "c").removeClass("ui-btn-up-e").addClass("ui-btn-up-c"); $("#service-manager div[data-role=content] ul[data-role=listview] li").attr("data-theme", "c").removeClass("ui-btn-up-e").addClass("ui-btn-up-c");
$("#service-manager div[data-role=content] ul[data-role=listview] li a").each(function () { $("#service-manager div[data-role=content] ul[data-role=listview] li a").each(function () {
var item = $(this); var item = $(this);
@ -307,7 +313,7 @@ window.OpenLP = {
} }
); );
}, },
escapeString: function (string) { escapeString: function (string) {
return string.replace(/\\/g, "\\\\").replace(/"/g, "\\\"") return string.replace(/\\/g, "\\\\").replace(/"/g, "\\\"")
} }
} }

View File

@ -139,8 +139,10 @@ window.OpenLP = {
"/api/poll", "/api/poll",
function (data, status) { function (data, status) {
OpenLP.updateClock(data); OpenLP.updateClock(data);
if (OpenLP.currentItem != data.results.item) { if (OpenLP.currentItem != data.results.item ||
OpenLP.currentService != data.results.service) {
OpenLP.currentItem = data.results.item; OpenLP.currentItem = data.results.item;
OpenLP.currentService = data.results.service;
OpenLP.loadSlides(); OpenLP.loadSlides();
} }
else if (OpenLP.currentSlide != data.results.slide) { else if (OpenLP.currentSlide != data.results.slide) {

View File

@ -308,7 +308,9 @@ class HttpConnection(object):
'add_and_go_to_service': translate('RemotePlugin.Mobile', 'add_and_go_to_service': translate('RemotePlugin.Mobile',
'Add &amp; Go to Service'), 'Add &amp; Go to Service'),
'no_results': translate('RemotePlugin.Mobile', 'No Results'), 'no_results': translate('RemotePlugin.Mobile', 'No Results'),
'options': translate('RemotePlugin.Mobile', 'Options') 'options': translate('RemotePlugin.Mobile', 'Options'),
'service': translate('RemotePlugin.Mobile', 'Service'),
'slides': translate('RemotePlugin.Mobile', 'Slides')
} }
def ready_read(self): def ready_read(self):

View File

@ -29,7 +29,6 @@
The :mod:`songexportform` module provides the wizard for exporting songs to the The :mod:`songexportform` module provides the wizard for exporting songs to the
OpenLyrics format. OpenLyrics format.
""" """
import locale
import logging import logging
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
@ -38,6 +37,7 @@ from openlp.core.lib import build_icon, Receiver, SettingsManager, translate, \
create_separated_list create_separated_list
from openlp.core.lib.ui import UiStrings, critical_error_message_box from openlp.core.lib.ui import UiStrings, critical_error_message_box
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
from openlp.core.utils import locale_direct_compare
from openlp.plugins.songs.lib.db import Song from openlp.plugins.songs.lib.db import Song
from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport
@ -252,7 +252,8 @@ class SongExportForm(OpenLPWizard):
# Load the list of songs. # Load the list of songs.
Receiver.send_message(u'cursor_busy') Receiver.send_message(u'cursor_busy')
songs = self.plugin.manager.get_all_objects(Song) songs = self.plugin.manager.get_all_objects(Song)
songs.sort(cmp=locale.strcoll, key=lambda song: song.title.lower()) songs.sort(
cmp=locale_direct_compare, key=lambda song: song.sort_string)
for song in songs: for song in songs:
# No need to export temporary songs. # No need to export temporary songs.
if song.temporary: if song.temporary:

View File

@ -31,8 +31,9 @@ the Songs plugin
""" """
from sqlalchemy import Column, ForeignKey, Table, types from sqlalchemy import Column, ForeignKey, Table, types
from sqlalchemy.orm import mapper, relation from sqlalchemy.orm import mapper, relation, reconstructor
from sqlalchemy.sql.expression import func from sqlalchemy.sql.expression import func
from PyQt4 import QtCore
from openlp.core.lib.db import BaseModel, init_db from openlp.core.lib.db import BaseModel, init_db
@ -63,7 +64,22 @@ class Song(BaseModel):
""" """
Song model Song model
""" """
pass def __init__(self):
self.sort_string = ''
# This decorator tells sqlalchemy to call this method everytime
# any data on this object are updated.
@reconstructor
def init_on_load(self):
"""
Precompute string to be used for sorting.
Song sorting is performance sensitive operation.
To get maximum speed lets precompute the string
used for comparison.
"""
# Avoid the overhead of converting string to lowercase and to QString
self.sort_string = QtCore.QString(self.title.lower())
class Topic(BaseModel): class Topic(BaseModel):

View File

@ -27,7 +27,6 @@
############################################################################### ###############################################################################
import logging import logging
import locale
import re import re
import os import os
import shutil import shutil
@ -40,7 +39,7 @@ from openlp.core.lib import MediaManagerItem, Receiver, ItemCapabilities, \
check_directory_exists check_directory_exists
from openlp.core.lib.ui import UiStrings, create_widget_action from openlp.core.lib.ui import UiStrings, create_widget_action
from openlp.core.lib.settings import Settings from openlp.core.lib.settings import Settings
from openlp.core.utils import AppLocation from openlp.core.utils import AppLocation, locale_direct_compare
from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \ from openlp.plugins.songs.forms import EditSongForm, SongMaintenanceForm, \
SongImportForm, SongExportForm SongImportForm, SongExportForm
from openlp.plugins.songs.lib import OpenLyrics, SongXML, VerseType, \ from openlp.plugins.songs.lib import OpenLyrics, SongXML, VerseType, \
@ -260,10 +259,8 @@ class SongMediaItem(MediaManagerItem):
log.debug(u'display results Song') log.debug(u'display results Song')
self.saveAutoSelectId() self.saveAutoSelectId()
self.listView.clear() self.listView.clear()
# Sort the songs by its title considering language specific characters.
# lower() is needed for windows!
searchresults.sort( searchresults.sort(
cmp=locale.strcoll, key=lambda song: song.title.lower()) cmp=locale_direct_compare, key=lambda song: song.sort_string)
for song in searchresults: for song in searchresults:
# Do not display temporary songs # Do not display temporary songs
if song.temporary: if song.temporary:

View File

@ -63,10 +63,14 @@ class OpenLPSongImport(SongImport):
SongImport.__init__(self, manager, **kwargs) SongImport.__init__(self, manager, **kwargs)
self.sourceSession = None self.sourceSession = None
def doImport(self): def doImport(self, progressDialog=None):
""" """
Run the import for an OpenLP version 2 song database. Run the import for an OpenLP version 2 song database.
``progressDialog``
The QProgressDialog used when importing songs from the FRW.
""" """
class OldAuthor(BaseModel): class OldAuthor(BaseModel):
""" """
Author model Author model
@ -101,13 +105,14 @@ class OpenLPSongImport(SongImport):
""" """
pass pass
# Check the file type
if not self.importSource.endswith(u'.sqlite'): if not self.importSource.endswith(u'.sqlite'):
self.logError(self.importSource, self.logError(self.importSource,
translate('SongsPlugin.OpenLPSongImport', translate('SongsPlugin.OpenLPSongImport',
'Not a valid OpenLP 2.0 song database.')) 'Not a valid OpenLP 2.0 song database.'))
return return
self.importSource = u'sqlite:///%s' % self.importSource self.importSource = u'sqlite:///%s' % self.importSource
# Load the db file
engine = create_engine(self.importSource) engine = create_engine(self.importSource)
source_meta = MetaData() source_meta = MetaData()
source_meta.reflect(engine) source_meta.reflect(engine)
@ -224,7 +229,11 @@ class OpenLPSongImport(SongImport):
file_name=media_file.file_name)) file_name=media_file.file_name))
clean_song(self.manager, new_song) clean_song(self.manager, new_song)
self.manager.save_object(new_song) self.manager.save_object(new_song)
if self.importWizard: if progressDialog:
progressDialog.setValue(progressDialog.value() + 1)
progressDialog.setLabelText(
WizardStrings.ImportingType % new_song.title)
else:
self.importWizard.incrementProgressBar( self.importWizard.incrementProgressBar(
WizardStrings.ImportingType % new_song.title) WizardStrings.ImportingType % new_song.title)
if self.stopImportFlag: if self.stopImportFlag:

View File

@ -27,6 +27,7 @@
############################################################################### ###############################################################################
import logging import logging
import os import os
import time
from PyQt4 import QtCore from PyQt4 import QtCore
@ -123,6 +124,7 @@ class OooImport(SongImport):
try: try:
uno_instance = get_uno_instance(resolver) uno_instance = get_uno_instance(resolver)
except NoConnectException: except NoConnectException:
time.sleep(0.1)
log.exception("Failed to resolve uno connection") log.exception("Failed to resolve uno connection")
self.startOooProcess() self.startOooProcess()
loop += 1 loop += 1

View File

@ -154,18 +154,20 @@ class SundayPlusImport(SongImport):
# If any line inside any verse contains CCLI or # If any line inside any verse contains CCLI or
# only Public Domain, we treat this as special data: # only Public Domain, we treat this as special data:
# we remove that line and add data to specific field. # we remove that line and add data to specific field.
processed_lines = []
for i in xrange(len(lines)): for i in xrange(len(lines)):
lines[i] = lines[i].strip() line = lines[i].strip()
line = lines[i] if line[:3].lower() == u'ccl':
if line[:4].lower() == u'ccli':
m = re.search(r'[0-9]+', line) m = re.search(r'[0-9]+', line)
if m: if m:
self.ccliNumber = int(m.group(0)) self.ccliNumber = int(m.group(0))
lines.pop(i) continue
elif line.lower() == u'public domain': elif line.lower() == u'public domain':
self.copyright = u'Public Domain' self.copyright = u'Public Domain'
lines.pop(i) continue
self.addVerse('\n'.join(lines).strip(), verse_type) processed_lines.append(line)
self.addVerse('\n'.join(processed_lines).strip(),
verse_type)
if end == -1: if end == -1:
break break
i = end + 1 i = end + 1

View File

@ -260,7 +260,7 @@ class OpenLyrics(object):
IMPLEMENTED_VERSION = u'0.8' IMPLEMENTED_VERSION = u'0.8'
START_TAGS_REGEX = re.compile(r'\{(\w+)\}') START_TAGS_REGEX = re.compile(r'\{(\w+)\}')
END_TAGS_REGEX = re.compile(r'\{\/(\w+)\}') END_TAGS_REGEX = re.compile(r'\{\/(\w+)\}')
VERSE_NUMBER_REGEX = re.compile(u'[a-zA-Z]*') VERSE_TAG_SPLITTER = re.compile(u'([a-zA-Z]+)([0-9]*)([a-zA-Z]?)')
def __init__(self, manager): def __init__(self, manager):
self.manager = manager self.manager = manager
@ -325,10 +325,22 @@ class OpenLyrics(object):
# Process the song's lyrics. # Process the song's lyrics.
lyrics = etree.SubElement(song_xml, u'lyrics') lyrics = etree.SubElement(song_xml, u'lyrics')
verse_list = sxml.get_verses(song.lyrics) verse_list = sxml.get_verses(song.lyrics)
# Add a suffix letter to each verse
verse_tags = []
for verse in verse_list: for verse in verse_list:
verse_tag = verse[0][u'type'][0].lower() verse_tag = verse[0][u'type'][0].lower()
verse_number = verse[0][u'label'] verse_number = verse[0][u'label']
verse_def = verse_tag + verse_number verse_def = verse_tag + verse_number
verse_tags.append(verse_def)
# Create the letter from the number of duplicates
verse[0][u'suffix'] = chr(96 + verse_tags.count(verse_def))
# If the verse tag is a duplicate use the suffix letter
for verse in verse_list:
verse_tag = verse[0][u'type'][0].lower()
verse_number = verse[0][u'label']
verse_def = verse_tag + verse_number
if verse_tags.count(verse_def) > 1:
verse_def += verse[0][u'suffix']
verse_element = \ verse_element = \
self._add_text_to_element(u'verse', lyrics, None, verse_def) self._add_text_to_element(u'verse', lyrics, None, verse_def)
if u'lang' in verse[0]: if u'lang' in verse[0]:
@ -742,28 +754,28 @@ class OpenLyrics(object):
if lines.get(u'break') is not None: if lines.get(u'break') is not None:
text += u'\n[---]' text += u'\n[---]'
verse_def = verse.get(u'name', u' ').lower() verse_def = verse.get(u'name', u' ').lower()
if verse_def[0] in VerseType.Tags: verse_tag, verse_number, verse_part = \
verse_tag = verse_def[0] OpenLyrics.VERSE_TAG_SPLITTER.search(verse_def).groups()
else: if verse_tag not in VerseType.Tags:
verse_tag = VerseType.Tags[VerseType.Other] verse_tag = VerseType.Tags[VerseType.Other]
verse_number = OpenLyrics.VERSE_NUMBER_REGEX.sub(u'', verse_def)
# OpenLyrics allows e. g. "c", but we need "c1". However, this does # OpenLyrics allows e. g. "c", but we need "c1". However, this does
# not correct the verse order. # not correct the verse order.
if not verse_number: if not verse_number:
verse_number = u'1' verse_number = u'1'
lang = verse.get(u'lang') lang = verse.get(u'lang')
translit = verse.get(u'translit')
# In OpenLP 1.9.6 we used v1a, v1b ... to represent visual slide # In OpenLP 1.9.6 we used v1a, v1b ... to represent visual slide
# breaks. In OpenLyrics 0.7 an attribute has been added. # breaks. In OpenLyrics 0.7 an attribute has been added.
if song_xml.get(u'modifiedIn') in (u'1.9.6', u'OpenLP 1.9.6') and \ if song_xml.get(u'modifiedIn') in (u'1.9.6', u'OpenLP 1.9.6') and \
song_xml.get(u'version') == u'0.7' and \ song_xml.get(u'version') == u'0.7' and \
(verse_tag, verse_number, lang) in verses: (verse_tag, verse_number, lang, translit) in verses:
verses[(verse_tag, verse_number, lang)] += u'\n[---]\n' + text verses[(verse_tag, verse_number, lang, translit, None)] += u'\n[---]\n' + text
# Merge v1a, v1b, .... to v1. # Merge v1a, v1b, .... to v1.
elif (verse_tag, verse_number, lang) in verses: elif (verse_tag, verse_number, lang, translit, verse_part) in verses:
verses[(verse_tag, verse_number, lang)] += u'\n' + text verses[(verse_tag, verse_number, lang, translit, verse_part)] += u'\n' + text
else: else:
verses[(verse_tag, verse_number, lang)] = text verses[(verse_tag, verse_number, lang, translit, verse_part)] = text
verse_def_list.append((verse_tag, verse_number, lang)) verse_def_list.append((verse_tag, verse_number, lang, translit, verse_part))
# We have to use a list to keep the order, as dicts are not sorted. # We have to use a list to keep the order, as dicts are not sorted.
for verse in verse_def_list: for verse in verse_def_list:
sxml.add_verse_to_lyrics( sxml.add_verse_to_lyrics(

View File

@ -29,6 +29,7 @@
import logging import logging
import os import os
from tempfile import gettempdir from tempfile import gettempdir
import sqlite3
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
@ -82,7 +83,7 @@ class SongsPlugin(Plugin):
unicode(UiStrings().Tools)) unicode(UiStrings().Tools))
QtCore.QObject.connect(Receiver.get_receiver(), QtCore.QObject.connect(Receiver.get_receiver(),
QtCore.SIGNAL(u'servicemanager_new_service'), QtCore.SIGNAL(u'servicemanager_new_service'),
self.clearTemporarySongs) self.clearTemporarySongs)
def addImportMenuItem(self, import_menu): def addImportMenuItem(self, import_menu):
@ -235,31 +236,37 @@ class SongsPlugin(Plugin):
If the first time wizard has run, this function is run to import all the If the first time wizard has run, this function is run to import all the
new songs into the database. new songs into the database.
""" """
Receiver.send_message(u'openlp_process_events')
self.onToolsReindexItemTriggered() self.onToolsReindexItemTriggered()
Receiver.send_message(u'openlp_process_events')
db_dir = unicode(os.path.join( db_dir = unicode(os.path.join(
unicode(gettempdir(), get_filesystem_encoding()), u'openlp')) unicode(gettempdir(), get_filesystem_encoding()), u'openlp'))
if not os.path.exists(db_dir): if not os.path.exists(db_dir):
return return
song_dbs = [] song_dbs = []
song_count = 0
for sfile in os.listdir(db_dir): for sfile in os.listdir(db_dir):
if sfile.startswith(u'songs_') and sfile.endswith(u'.sqlite'): if sfile.startswith(u'songs_') and sfile.endswith(u'.sqlite'):
Receiver.send_message(u'openlp_process_events')
song_dbs.append(os.path.join(db_dir, sfile)) song_dbs.append(os.path.join(db_dir, sfile))
song_count += self._countSongs(os.path.join(db_dir, sfile))
if not song_dbs: if not song_dbs:
return return
Receiver.send_message(u'openlp_process_events')
progress = QtGui.QProgressDialog(self.formParent) progress = QtGui.QProgressDialog(self.formParent)
progress.setWindowModality(QtCore.Qt.WindowModal) progress.setWindowModality(QtCore.Qt.WindowModal)
progress.setWindowTitle(translate('OpenLP.Ui', 'Importing Songs')) progress.setWindowTitle(translate('OpenLP.Ui', 'Importing Songs'))
progress.setLabelText(translate('OpenLP.Ui', 'Starting import...')) progress.setLabelText(translate('OpenLP.Ui', 'Starting import...'))
progress.setCancelButton(None) progress.setCancelButton(None)
progress.setRange(0, len(song_dbs)) progress.setRange(0, song_count)
progress.setMinimumDuration(0) progress.setMinimumDuration(0)
progress.forceShow() progress.forceShow()
for idx, db in enumerate(song_dbs): Receiver.send_message(u'openlp_process_events')
progress.setValue(idx) for db in song_dbs:
Receiver.send_message(u'openlp_process_events')
importer = OpenLPSongImport(self.manager, filename=db) importer = OpenLPSongImport(self.manager, filename=db)
importer.doImport() importer.doImport(progress)
progress.setValue(len(song_dbs)) Receiver.send_message(u'openlp_process_events')
progress.setValue(song_count)
self.mediaItem.onSearchTextButtonClicked() self.mediaItem.onSearchTextButtonClicked()
def finalise(self): def finalise(self):
@ -287,3 +294,15 @@ class SongsPlugin(Plugin):
songs = self.manager.get_all_objects(Song, Song.temporary == True) songs = self.manager.get_all_objects(Song, Song.temporary == True)
for song in songs: for song in songs:
self.manager.delete_object(Song, song.id) self.manager.delete_object(Song, song.id)
def _countSongs(self, db_file):
connection = sqlite3.connect(db_file)
cursor = connection.cursor()
cursor.execute(u'SELECT COUNT(id) AS song_count FROM songs')
song_count = cursor.fetchone()[0]
connection.close()
try:
song_count = int(song_count)
except (TypeError, ValueError):
song_count = 0
return song_count

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -130,7 +130,9 @@
<qresource prefix="media"> <qresource prefix="media">
<file>media_time.png</file> <file>media_time.png</file>
<file>media_stop.png</file> <file>media_stop.png</file>
<file>image_clapperboard.png</file> <file>media_audio.png</file>
<file>media_video.png</file>
<file>slidecontroller_multimedia.png</file>
</qresource> </qresource>
<qresource prefix="messagebox"> <qresource prefix="messagebox">
<file>messagebox_critical.png</file> <file>messagebox_critical.png</file>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB