diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index ab8228658..67b5c1b04 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -29,12 +29,12 @@ The :mod:`advancedtab` provides an advanced settings facility. """ from datetime import datetime, timedelta - -from PyQt4 import QtCore, QtGui - import logging import os import sys + +from PyQt4 import QtCore, QtGui + from openlp.core.lib import SettingsTab, translate, build_icon, Receiver from openlp.core.lib.settings import Settings from openlp.core.lib.ui import UiStrings @@ -432,8 +432,7 @@ class AdvancedTab(SettingsTab): translate('OpenLP.AdvancedTab', 'WARNING: New data directory location contains ' 'OpenLP data files. These files WILL be replaced during a copy.')) - self.x11GroupBox.setTitle(translate('OpenLP.AdvancedTab', - 'X11')) + self.x11GroupBox.setTitle(translate('OpenLP.AdvancedTab', 'X11')) self.x11BypassCheckBox.setText(translate('OpenLP.AdvancedTab', 'Bypass X11 Window Manager')) # Slide Limits @@ -493,8 +492,14 @@ class AdvancedTab(SettingsTab): QtCore.QVariant(True)).toBool() self.serviceNameCheckBox.setChecked(default_service_enabled) self.serviceNameCheckBoxToggled(default_service_enabled) - self.x11BypassCheckBox.setChecked( - settings.value(u'x11 bypass wm', QtCore.QVariant(True)).toBool()) + # 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')) + self.x11BypassCheckBox.setChecked(settings.value( + u'x11 bypass wm', QtCore.QVariant(x11_bypass_default)).toBool()) self.defaultColor = settings.value(u'default color', QtCore.QVariant(u'#ffffff')).toString() self.defaultFileEdit.setText(settings.value(u'default image', @@ -766,7 +771,7 @@ class AdvancedTab(SettingsTab): self.dataExists = False self.dataDirectoryCopyCheckBox.setChecked(True) self.newDataDirectoryHasFilesLabel.hide() - + def onDataDirectoryCancelButtonClicked(self): """ Cancel the data directory location change diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index 1bd746759..14f1299de 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -177,8 +177,10 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard): return FirstTimePage.Progress elif self.currentId() == FirstTimePage.Themes: Receiver.send_message(u'cursor_busy') + Receiver.send_message(u'openlp_process_events') while not self.themeScreenshotThread.isFinished(): time.sleep(0.1) + Receiver.send_message(u'openlp_process_events') # Build the screenshot icons, as this can not be done in the thread. self._buildThemeScreenshots() Receiver.send_message(u'cursor_normal') @@ -188,10 +190,11 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard): 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 # to be a -1. + Receiver.send_message(u'openlp_process_events') if pageId != -1: self.lastId = pageId if pageId == FirstTimePage.Plugins: @@ -227,10 +230,12 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard): self.cancelButton.setVisible(False) elif pageId == FirstTimePage.Progress: 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() - Receiver.send_message(u'openlp_process_events') self._performWizard() - Receiver.send_message(u'openlp_process_events') self._postWizard() Receiver.send_message(u'cursor_normal') Receiver.send_message(u'openlp_process_events') @@ -263,8 +268,8 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard): """ Receiver.send_message(u'cursor_busy') self._performWizard() - Receiver.send_message(u'openlp_process_events') Receiver.send_message(u'cursor_normal') + Receiver.send_message(u'openlp_process_events') Settings().setValue(u'general/has run wizard', QtCore.QVariant(True)) self.close() @@ -344,6 +349,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard): Receiver.send_message(u'openlp_process_events') # Loop through the songs list and increase for each selected item for i in xrange(self.songsListWidget.count()): + Receiver.send_message(u'openlp_process_events') item = self.songsListWidget.item(i) if item.checkState() == QtCore.Qt.Checked: 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 iterator = QtGui.QTreeWidgetItemIterator(self.biblesTreeWidget) while iterator.value(): + Receiver.send_message(u'openlp_process_events') item = iterator.value() if item.parent() and item.checkState(0) == QtCore.Qt.Checked: filename = item.data(0, QtCore.Qt.UserRole).toString() @@ -360,6 +367,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard): iterator += 1 # Loop through the themes list and increase for each selected item for i in xrange(self.themesListWidget.count()): + Receiver.send_message(u'openlp_process_events') item = self.themesListWidget.item(i) if item.checkState() == QtCore.Qt.Checked: filename = item.data(QtCore.Qt.UserRole).toString() @@ -381,6 +389,10 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard): self.progressPage.setTitle(translate('OpenLP.FirstTimeWizard', 'Setting Up')) 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): """ diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 306bb7511..a1cc0215d 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -31,6 +31,7 @@ and play multimedia within OpenLP. """ import cgi import logging +import os import sys from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL @@ -135,8 +136,14 @@ class MainDisplay(Display): self.setStyleSheet(u'border: 0px; margin: 0px; padding: 0px;') windowFlags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool | \ 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', - QtCore.QVariant(True)).toBool(): + QtCore.QVariant(x11_bypass_default)).toBool(): windowFlags |= QtCore.Qt.X11BypassWindowManagerHint # TODO: The following combination of windowFlags works correctly # on Mac OS X. For next OpenLP version we should test it on other diff --git a/openlp/core/ui/media/vlc.py b/openlp/core/ui/media/vlc.py index db041f658..9948fcd81 100644 --- a/openlp/core/ui/media/vlc.py +++ b/openlp/core/ui/media/vlc.py @@ -48,7 +48,7 @@ import sys from inspect import getargspec __version__ = "N/A" -build_date = "Fri Sep 28 22:48:50 2012" +build_date = "Fri Oct 5 21:35:59 2012" if sys.version_info[0] > 2: str = str @@ -680,6 +680,19 @@ class LogCb(ctypes.c_void_p): \note Log message handlers must be thread-safe. """ pass +class VideoLockCb(ctypes.c_void_p): + """Callback prototype to allocate and lock a picture buffer. +Whenever a new video frame needs to be decoded, the lock callback is +invoked. Depending on the video chroma, one or three pixel planes of +adequate dimensions must be returned via the second parameter. Those +planes must be aligned on 32-bytes boundaries. +\param opaque private pointer as passed to L{libvlc_video_set_callbacks}() [IN] +\param planes start address of the pixel planes (LibVLC allocates the array + of void pointers, this callback must initialize the array) [OUT] +\return a private pointer for the display and unlock callbacks to identify + the picture buffers + """ + pass class VideoUnlockCb(ctypes.c_void_p): """Callback prototype to unlock a picture buffer. When the video frame decoding is complete, the unlock callback is invoked. @@ -687,7 +700,7 @@ This callback might not be needed at all. It is only an indication that the application can now read the pixel values if it needs to. \warning A picture buffer is unlocked after the picture is decoded, but before the picture is displayed. -\param opaque private pointer as passed to libvlc_video_set_callbacks() [IN] +\param opaque private pointer as passed to L{libvlc_video_set_callbacks}() [IN] \param picture private pointer returned from the @ref libvlc_video_lock_cb callback [IN] \param planes pixel planes as defined by the @ref libvlc_video_lock_cb @@ -698,7 +711,7 @@ class VideoDisplayCb(ctypes.c_void_p): """Callback prototype to display a picture. When the video frame needs to be shown, as determined by the media playback clock, the display callback is invoked. -\param opaque private pointer as passed to libvlc_video_set_callbacks() [IN] +\param opaque private pointer as passed to L{libvlc_video_set_callbacks}() [IN] \param picture private pointer returned from the @ref libvlc_video_lock_cb callback [IN] """ @@ -710,7 +723,7 @@ and the chain of video filters (if any). It can opt to change any parameter as it needs. In that case, LibVLC will attempt to convert the video format (rescaling and chroma conversion) but these operations can be CPU intensive. \param opaque pointer to the private pointer passed to - libvlc_video_set_callbacks() [IN/OUT] + L{libvlc_video_set_callbacks}() [IN/OUT] \param chroma pointer to the 4 bytes video format identifier [IN/OUT] \param width pointer to the pixel width [IN/OUT] \param height pointer to the pixel height [IN/OUT] @@ -730,7 +743,7 @@ in the video decoders, video filters and/or video converters. pass class VideoCleanupCb(ctypes.c_void_p): """Callback prototype to configure picture buffers format. -\param opaque private pointer as passed to libvlc_video_set_callbacks() +\param opaque private pointer as passed to L{libvlc_video_set_callbacks}() (and possibly modified by @ref libvlc_video_format_cb) [IN] """ pass @@ -806,6 +819,18 @@ class CallbackDecorators(object): \param args variable argument list for the format \note Log message handlers must be thread-safe. ''' + VideoLockCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ListPOINTER(ctypes.c_void_p)) + VideoLockCb.__doc__ = '''Callback prototype to allocate and lock a picture buffer. +Whenever a new video frame needs to be decoded, the lock callback is +invoked. Depending on the video chroma, one or three pixel planes of +adequate dimensions must be returned via the second parameter. Those +planes must be aligned on 32-bytes boundaries. +\param opaque private pointer as passed to L{libvlc_video_set_callbacks}() [IN] +\param planes start address of the pixel planes (LibVLC allocates the array + of void pointers, this callback must initialize the array) [OUT] +\return a private pointer for the display and unlock callbacks to identify + the picture buffers + ''' VideoUnlockCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ListPOINTER(ctypes.c_void_p)) VideoUnlockCb.__doc__ = '''Callback prototype to unlock a picture buffer. When the video frame decoding is complete, the unlock callback is invoked. @@ -813,7 +838,7 @@ This callback might not be needed at all. It is only an indication that the application can now read the pixel values if it needs to. \warning A picture buffer is unlocked after the picture is decoded, but before the picture is displayed. -\param opaque private pointer as passed to libvlc_video_set_callbacks() [IN] +\param opaque private pointer as passed to L{libvlc_video_set_callbacks}() [IN] \param picture private pointer returned from the @ref libvlc_video_lock_cb callback [IN] \param planes pixel planes as defined by the @ref libvlc_video_lock_cb @@ -823,7 +848,7 @@ but before the picture is displayed. VideoDisplayCb.__doc__ = '''Callback prototype to display a picture. When the video frame needs to be shown, as determined by the media playback clock, the display callback is invoked. -\param opaque private pointer as passed to libvlc_video_set_callbacks() [IN] +\param opaque private pointer as passed to L{libvlc_video_set_callbacks}() [IN] \param picture private pointer returned from the @ref libvlc_video_lock_cb callback [IN] ''' @@ -834,7 +859,7 @@ and the chain of video filters (if any). It can opt to change any parameter as it needs. In that case, LibVLC will attempt to convert the video format (rescaling and chroma conversion) but these operations can be CPU intensive. \param opaque pointer to the private pointer passed to - libvlc_video_set_callbacks() [IN/OUT] + L{libvlc_video_set_callbacks}() [IN/OUT] \param chroma pointer to the 4 bytes video format identifier [IN/OUT] \param width pointer to the pixel width [IN/OUT] \param height pointer to the pixel height [IN/OUT] @@ -853,7 +878,7 @@ in the video decoders, video filters and/or video converters. ''' VideoCleanupCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p) VideoCleanupCb.__doc__ = '''Callback prototype to configure picture buffers format. -\param opaque private pointer as passed to libvlc_video_set_callbacks() +\param opaque private pointer as passed to L{libvlc_video_set_callbacks}() (and possibly modified by @ref libvlc_video_format_cb) [IN] ''' AudioPlayCb = ctypes.CFUNCTYPE(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_uint, ctypes.c_int64) @@ -2483,9 +2508,22 @@ class MediaPlayer(_Ctype): ''' return libvlc_media_player_stop(self) + def video_set_callbacks(self, lock, unlock, display, opaque): + '''Set callbacks and private data to render decoded video to a custom area + in memory. + Use L{video_set_format}() or L{video_set_format_callbacks}() + to configure the decoded format. + @param lock: callback to lock video memory (must not be NULL). + @param unlock: callback to unlock video memory (or NULL if not needed). + @param display: callback to display video (or NULL if not needed). + @param opaque: private pointer for the three callbacks (as first parameter). + @version: LibVLC 1.1.1 or later. + ''' + return libvlc_video_set_callbacks(self, lock, unlock, display, opaque) + def video_set_format(self, chroma, width, height, pitch): '''Set decoded video chroma and dimensions. - This only works in combination with libvlc_video_set_callbacks(), + This only works in combination with L{video_set_callbacks}(), and is mutually exclusive with L{video_set_format_callbacks}(). @param chroma: a four-characters string identifying the chroma (e.g. "RV32" or "YUYV"). @param width: pixel width. @@ -2498,7 +2536,7 @@ class MediaPlayer(_Ctype): def video_set_format_callbacks(self, setup, cleanup): '''Set decoded video chroma and dimensions. This only works in combination with - libvlc_video_set_callbacks(). + L{video_set_callbacks}(). @param setup: callback to select the video format (cannot be NULL). @param cleanup: callback to release any allocated resources (or NULL). @version: LibVLC 2.0.0 or later. @@ -4382,9 +4420,26 @@ def libvlc_media_player_stop(p_mi): None, MediaPlayer) return f(p_mi) +def libvlc_video_set_callbacks(mp, lock, unlock, display, opaque): + '''Set callbacks and private data to render decoded video to a custom area + in memory. + Use L{libvlc_video_set_format}() or L{libvlc_video_set_format_callbacks}() + to configure the decoded format. + @param mp: the media player. + @param lock: callback to lock video memory (must not be NULL). + @param unlock: callback to unlock video memory (or NULL if not needed). + @param display: callback to display video (or NULL if not needed). + @param opaque: private pointer for the three callbacks (as first parameter). + @version: LibVLC 1.1.1 or later. + ''' + f = _Cfunctions.get('libvlc_video_set_callbacks', None) or \ + _Cfunction('libvlc_video_set_callbacks', ((1,), (1,), (1,), (1,), (1,),), None, + None, MediaPlayer, VideoLockCb, VideoUnlockCb, VideoDisplayCb, ctypes.c_void_p) + return f(mp, lock, unlock, display, opaque) + def libvlc_video_set_format(mp, chroma, width, height, pitch): '''Set decoded video chroma and dimensions. - This only works in combination with libvlc_video_set_callbacks(), + This only works in combination with L{libvlc_video_set_callbacks}(), and is mutually exclusive with L{libvlc_video_set_format_callbacks}(). @param mp: the media player. @param chroma: a four-characters string identifying the chroma (e.g. "RV32" or "YUYV"). @@ -4401,7 +4456,7 @@ def libvlc_video_set_format(mp, chroma, width, height, pitch): def libvlc_video_set_format_callbacks(mp, setup, cleanup): '''Set decoded video chroma and dimensions. This only works in combination with - libvlc_video_set_callbacks(). + L{libvlc_video_set_callbacks}(). @param mp: the media player. @param setup: callback to select the video format (cannot be NULL). @param cleanup: callback to release any allocated resources (or NULL). @@ -5878,10 +5933,9 @@ def libvlc_vlm_get_event_manager(p_instance): return f(p_instance) -# 3 function(s) blacklisted: +# 2 function(s) blacklisted: # libvlc_printerr # libvlc_set_exit_handler -# libvlc_video_set_callbacks # 17 function(s) not wrapped as methods: # libvlc_audio_output_list_release diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 2d108e72c..0f0f15a3d 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -855,7 +855,11 @@ class SlideController(Controller): else: label = QtGui.QLabel() 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(): label.setPixmap(QtGui.QPixmap(frame[u'image'])) else: diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 844ffec8c..a5293e3a8 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -30,7 +30,6 @@ import os import zipfile import shutil import logging -import locale import re 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 from openlp.core.theme import Theme 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__) @@ -457,9 +457,8 @@ class ThemeManager(QtGui.QWidget): self.configUpdated() files = SettingsManager.get_files(self.settingsSection, u'.png') # Sort the themes by its name considering language specific characters. - # lower() is needed for windows! - files.sort(key=lambda file_name: unicode(file_name).lower(), - cmp=locale.strcoll) + files.sort(key=lambda file_name: unicode(file_name), + cmp=locale_compare) # now process the file list of png files for name in files: # check to see file is in theme root directory diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 41a097a15..742b1e4bc 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -488,10 +488,30 @@ def format_time(text, local_time): 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 actions import ActionList __all__ = [u'AppLocation', u'get_application_version', u'check_latest_version', u'add_actions', u'get_filesystem_encoding', u'LanguageManager', 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'] diff --git a/openlp/plugins/alerts/alertsplugin.py b/openlp/plugins/alerts/alertsplugin.py index 2d7fd8296..1720fa382 100644 --- a/openlp/plugins/alerts/alertsplugin.py +++ b/openlp/plugins/alerts/alertsplugin.py @@ -105,6 +105,7 @@ CSS = """ font-size: %spt; color: %s; background-color: %s; + word-wrap: break-word; } """ diff --git a/openlp/plugins/alerts/forms/alertdialog.py b/openlp/plugins/alerts/forms/alertdialog.py index df4209e9d..09c098cf4 100644 --- a/openlp/plugins/alerts/forms/alertdialog.py +++ b/openlp/plugins/alerts/forms/alertdialog.py @@ -35,7 +35,7 @@ class Ui_AlertDialog(object): def setupUi(self, alertDialog): alertDialog.setObjectName(u'alertDialog') 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.setObjectName(u'alertDialogLayout') self.alertTextLayout = QtGui.QFormLayout() diff --git a/openlp/plugins/alerts/forms/alertform.py b/openlp/plugins/alerts/forms/alertform.py index ab4b6583e..7832f0c7d 100644 --- a/openlp/plugins/alerts/forms/alertform.py +++ b/openlp/plugins/alerts/forms/alertform.py @@ -80,6 +80,10 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): item_name = QtGui.QListWidgetItem(alert.text) item_name.setData(QtCore.Qt.UserRole, QtCore.QVariant(alert.id)) 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): self.triggerAlert(unicode(self.alertTextEdit.text())) @@ -112,7 +116,6 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): alert = AlertItem() alert.text = unicode(self.alertTextEdit.text()) self.manager.save_object(alert) - self.alertTextEdit.setText(u'') self.loadList() def onSaveClick(self): @@ -125,6 +128,7 @@ class AlertForm(QtGui.QDialog, Ui_AlertDialog): self.manager.save_object(alert) self.item_id = None self.loadList() + self.saveButton.setEnabled(False) def onTextChanged(self): """ diff --git a/openlp/plugins/alerts/lib/alertsmanager.py b/openlp/plugins/alerts/lib/alertsmanager.py index 018b38af2..7b43c202a 100644 --- a/openlp/plugins/alerts/lib/alertsmanager.py +++ b/openlp/plugins/alerts/lib/alertsmanager.py @@ -57,8 +57,6 @@ class AlertsManager(QtCore.QObject): """ if message: self.displayAlert(message[0]) - else: - self.displayAlert(u'') def displayAlert(self, text=u''): """ @@ -68,14 +66,15 @@ class AlertsManager(QtCore.QObject): display text """ log.debug(u'display alert called %s' % text) - self.alertList.append(text) - if self.timer_id != 0: - Receiver.send_message(u'mainwindow_status_text', - translate('AlertsPlugin.AlertsManager', - 'Alert message created and displayed.')) - return - Receiver.send_message(u'mainwindow_status_text', u'') - self.generateAlert() + if text: + self.alertList.append(text) + if self.timer_id != 0: + Receiver.send_message(u'mainwindow_status_text', + translate('AlertsPlugin.AlertsManager', + 'Alert message created and displayed.')) + return + Receiver.send_message(u'mainwindow_status_text', u'') + self.generateAlert() def generateAlert(self): """ diff --git a/openlp/plugins/bibles/forms/bibleimportform.py b/openlp/plugins/bibles/forms/bibleimportform.py index 0a1d4cc23..d805ba34f 100644 --- a/openlp/plugins/bibles/forms/bibleimportform.py +++ b/openlp/plugins/bibles/forms/bibleimportform.py @@ -30,7 +30,6 @@ The bible import functions for OpenLP """ import logging import os -import locale 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.settings import Settings 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.db import BiblesResourcesDB, clean_filename @@ -523,7 +522,7 @@ class BibleImportForm(OpenLPWizard): """ self.webTranslationComboBox.clear() bibles = self.web_bible_list[index].keys() - bibles.sort(cmp=locale.strcoll) + bibles.sort(cmp=locale_compare) self.webTranslationComboBox.addItems(bibles) def onOsisBrowseButtonClicked(self): diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 73e5ebe8b..97e4ce72e 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -355,37 +355,8 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False): log.debug(u'Matched reference %s' % reference) book = match.group(u'book') if not book_ref_id: - book_names = BibleStrings().BookNames - # escape reserved characters - 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 + book_ref_id = bible.get_book_ref_id_by_localised_name( + book, language_selection) elif not bible.get_book_by_book_ref_id(book_ref_id): book_ref_id = False ranges = match.group(u'ranges') diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index acf7cf461..d4fd930ed 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -29,6 +29,7 @@ import chardet import logging import os +import re import sqlite3 from PyQt4 import QtCore @@ -44,6 +45,8 @@ import upgrade log = logging.getLogger(__name__) +RESERVED_CHARACTERS = u'\\.^$*+?{}[]()' + class BibleMeta(BaseModel): """ Bible Meta Data @@ -352,6 +355,53 @@ class BibleDB(QtCore.QObject, Manager): book, book_id, language_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): """ This is probably the most used function. It retrieves the list of diff --git a/openlp/plugins/bibles/lib/manager.py b/openlp/plugins/bibles/lib/manager.py index a56aac80b..467f7c90a 100644 --- a/openlp/plugins/bibles/lib/manager.py +++ b/openlp/plugins/bibles/lib/manager.py @@ -277,8 +277,9 @@ class BibleManager(object): """ log.debug(u'BibleManager.get_verse_count("%s", "%s", %s)', bible, book, chapter) - db_book = self.db_cache[bible].get_book(book) - book_ref_id = db_book.book_reference_id + language_selection = self.get_language_selection(bible) + 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) def get_verse_count_by_book_ref_id(self, bible, book_ref_id, chapter): diff --git a/openlp/plugins/bibles/lib/mediaitem.py b/openlp/plugins/bibles/lib/mediaitem.py index 5176e7d47..0647076c8 100644 --- a/openlp/plugins/bibles/lib/mediaitem.py +++ b/openlp/plugins/bibles/lib/mediaitem.py @@ -27,7 +27,6 @@ ############################################################################### import logging -import locale 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, \ create_horizontal_adjusting_combo_box, critical_error_message_box, \ 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.lib import LayoutStyle, DisplayStyle, \ VerseReferenceList, get_reference_separator, LanguageSelection, \ @@ -381,7 +381,7 @@ class BibleMediaItem(MediaManagerItem): # Get all bibles and sort the list. bibles = self.plugin.manager.get_bibles().keys() bibles = filter(None, bibles) - bibles.sort(cmp=locale.strcoll) + bibles.sort(cmp=locale_compare) # Load the bibles into the combo boxes. self.quickVersionComboBox.addItems(bibles) self.quickSecondComboBox.addItems(bibles) @@ -538,7 +538,7 @@ class BibleMediaItem(MediaManagerItem): data = BiblesResourcesDB.get_book_by_id( book.book_reference_id) books.append(data[u'name'] + u' ') - books.sort(cmp=locale.strcoll) + books.sort(cmp=locale_compare) set_case_insensitive_completer(books, self.quickSearchEdit) def onImportClick(self): diff --git a/openlp/plugins/custom/forms/editcustomdialog.py b/openlp/plugins/custom/forms/editcustomdialog.py index e29f4431a..fe20108e1 100644 --- a/openlp/plugins/custom/forms/editcustomdialog.py +++ b/openlp/plugins/custom/forms/editcustomdialog.py @@ -36,7 +36,7 @@ class Ui_CustomEditDialog(object): customEditDialog.setObjectName(u'customEditDialog') customEditDialog.resize(450, 350) 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.setObjectName(u'dialogLayout') self.titleLayout = QtGui.QHBoxLayout() diff --git a/openlp/plugins/custom/lib/db.py b/openlp/plugins/custom/lib/db.py index 6f3155f48..c885562f2 100644 --- a/openlp/plugins/custom/lib/db.py +++ b/openlp/plugins/custom/lib/db.py @@ -34,12 +34,21 @@ from sqlalchemy import Column, Table, types from sqlalchemy.orm import mapper from openlp.core.lib.db import BaseModel, init_db +from openlp.core.utils import locale_compare class CustomSlide(BaseModel): """ 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): """ diff --git a/openlp/plugins/custom/lib/mediaitem.py b/openlp/plugins/custom/lib/mediaitem.py index 663c5489d..10a3ec056 100644 --- a/openlp/plugins/custom/lib/mediaitem.py +++ b/openlp/plugins/custom/lib/mediaitem.py @@ -27,7 +27,6 @@ ############################################################################### import logging -import locale from PyQt4 import QtCore, QtGui 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. self.saveAutoSelectId() self.listView.clear() - # Sort the customs by its title considering language specific - # characters. lower() is needed for windows! - custom_slides.sort( - cmp=locale.strcoll, key=lambda custom: custom.title.lower()) + custom_slides.sort() for custom_slide in custom_slides: custom_name = QtGui.QListWidgetItem(custom_slide.title) custom_name.setData( diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 0fdb1537c..9239f8d72 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -28,7 +28,6 @@ import logging import os -import locale from PyQt4 import QtCore, QtGui @@ -37,7 +36,8 @@ from openlp.core.lib import MediaManagerItem, build_icon, ItemCapabilities, \ Receiver, create_thumb, validate_thumb from openlp.core.lib.ui import UiStrings, critical_error_message_box 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__) @@ -126,10 +126,10 @@ class ImageMediaItem(MediaManagerItem): if not initialLoad: Receiver.send_message(u'cursor_busy') self.plugin.formParent.displayProgressBar(len(images)) - # Sort the themes by its filename considering language specific - # characters. lower() is needed for windows! - images.sort(cmp=locale.strcoll, - key=lambda filename: os.path.split(unicode(filename))[1].lower()) + # Sort the images by its filename considering language specific + # characters. + images.sort(cmp=locale_compare, + key=lambda filename: os.path.split(unicode(filename))[1]) for imageFile in images: filename = os.path.split(unicode(imageFile))[1] thumb = os.path.join(self.servicePath, filename) diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index fe66b90d8..9f661149b 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -28,7 +28,6 @@ import logging import os -import locale 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 from openlp.core.ui import Controller, Display from openlp.core.ui.media import get_media_players, set_media_players +from openlp.core.utils import locale_compare log = logging.getLogger(__name__) -CLAPPERBOARD = QtGui.QImage(u':/media/media_video.png') -#TODO: Add an appropriate Icon for DVDs, CDs, ... +CLAPPERBOARD = u':/media/slidecontroller_multimedia.png' +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') class MediaMediaItem(MediaManagerItem): @@ -218,7 +219,7 @@ class MediaMediaItem(MediaManagerItem): service_item.add_capability(ItemCapabilities.RequiresMedia) # force a non-existent theme service_item.theme = -1 - frame = u':/media/image_clapperboard.png' + frame = CLAPPERBOARD (path, name) = os.path.split(filename) service_item.add_from_command(path, name, frame) return True @@ -284,16 +285,16 @@ class MediaMediaItem(MediaManagerItem): u'media', self.getFileList()) def loadList(self, media): - # Sort the themes by its filename considering language specific - # characters. lower() is needed for windows! - media.sort(cmp=locale.strcoll, - key=lambda filename: os.path.split(unicode(filename))[1].lower()) + # Sort the media by its filename considering language specific + # characters. + media.sort(cmp=locale_compare, + key=lambda filename: os.path.split(unicode(filename))[1]) for track in media: track_info = QtCore.QFileInfo(track) - if not track_info.isFile(): + if track_info.isFile(): filename = os.path.split(unicode(track))[1] 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)) else: filename = os.path.split(unicode(track))[1] @@ -306,8 +307,8 @@ class MediaMediaItem(MediaManagerItem): def getList(self, type=MediaType.Audio): media = SettingsManager.load_list(self.settingsSection, u'media') - media.sort(cmp=locale.strcoll, - key=lambda filename: os.path.split(unicode(filename))[1].lower()) + media.sort(cmp=locale_compare, + key=lambda filename: os.path.split(unicode(filename))[1]) ext = [] if type == MediaType.Audio: ext = self.plugin.audio_extensions_list diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index a85f43592..3d78f7bdb 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -153,7 +153,7 @@ class ImpressController(PresentationController): desktop = None try: 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') return desktop if desktop else None @@ -284,6 +284,8 @@ class ImpressDocument(PresentationDocument): props = tuple(props) doc = self.document pages = doc.getDrawPages() + if not pages: + return if not os.path.isdir(self.get_temp_folder()): os.makedirs(self.get_temp_folder()) for idx in range(pages.getCount()): @@ -359,7 +361,7 @@ class ImpressDocument(PresentationDocument): log.debug(u'is active OpenOffice') if not self.is_loaded(): return False - return self.control is not None + return self.control.isRunning() if self.control else False def unblank_screen(self): """ @@ -380,7 +382,7 @@ class ImpressDocument(PresentationDocument): Returns true if screen is blank """ log.debug(u'is blank OpenOffice') - if self.control: + if self.control and self.control.isRunning(): return self.control.isPaused() else: return False @@ -436,7 +438,11 @@ class ImpressDocument(PresentationDocument): """ Triggers the next effect of slide on the running presentation """ + is_paused = self.control.isPaused() self.control.gotoNextEffect() + time.sleep(0.1) + if not is_paused and self.control.isPaused(): + self.control.gotoPreviousEffect() def previous_step(self): """ diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 290882e26..9cfed177c 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -28,7 +28,6 @@ import logging import os -import locale 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, \ create_horizontal_adjusting_combo_box from openlp.core.lib.settings import Settings +from openlp.core.utils import locale_compare from openlp.plugins.presentations.lib import MessageListener log = logging.getLogger(__name__) @@ -169,10 +169,10 @@ class PresentationMediaItem(MediaManagerItem): if not initialLoad: Receiver.send_message(u'cursor_busy') self.plugin.formParent.displayProgressBar(len(files)) - # Sort the themes by its filename considering language specific - # characters. lower() is needed for windows! - files.sort(cmp=locale.strcoll, - key=lambda filename: os.path.split(unicode(filename))[1].lower()) + # Sort the presentations by its filename considering language specific + # characters. + files.sort(cmp=locale_compare, + key=lambda filename: os.path.split(unicode(filename))[1]) for file in files: if not initialLoad: self.plugin.formParent.incrementProgressBar() diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index cb8f7b7b8..7cc12c7f9 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -49,6 +49,7 @@ class Controller(object): """ self.is_live = live self.doc = None + self.hide_mode = None log.info(u'%s controller loaded' % live) def add_handler(self, controller, file, hide_mode, slide_no): @@ -67,6 +68,7 @@ class Controller(object): # Inform slidecontroller that the action failed? return self.doc.slidenumber = slide_no + self.hide_mode = hide_mode if self.is_live: if hide_mode == HideMode.Screen: Receiver.send_message(u'live_display_hide', HideMode.Screen) @@ -78,7 +80,7 @@ class Controller(object): else: self.doc.start_presentation() Receiver.send_message(u'live_display_hide', HideMode.Screen) - self.doc.slidenumber = 0 + self.doc.slidenumber = 1 if slide_no > 1: self.slide(slide_no) @@ -88,100 +90,134 @@ class Controller(object): Use the last slide number. """ log.debug(u'Live = %s, activate' % self.is_live) + if not self.doc: + return False if self.doc.is_active(): - return + return True if not self.doc.is_loaded(): if not self.doc.load_presentation(): - return + log.warn(u'Failed to activate %s' % self.doc.filepath) + return False if self.is_live: self.doc.start_presentation() if self.doc.slidenumber > 1: if self.doc.slidenumber > self.doc.get_slide_count(): self.doc.slidenumber = self.doc.get_slide_count() 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): """ Go to a specific slide """ log.debug(u'Live = %s, slide' % self.is_live) + if not self.doc: + return if not self.is_live: return - if self.doc.is_blank(): + if self.hide_mode: self.doc.slidenumber = int(slide) + 1 + self.poll() + return + if not self.activate(): return - self.activate() self.doc.goto_slide(int(slide) + 1) - self.doc.poll_slidenumber(self.is_live) + self.poll() def first(self): """ Based on the handler passed at startup triggers the first slide """ log.debug(u'Live = %s, first' % self.is_live) + if not self.doc: + return if not self.is_live: return - if self.doc.is_blank(): + if self.hide_mode: self.doc.slidenumber = 1 + self.poll() + return + if not self.activate(): return - self.activate() self.doc.start_presentation() - self.doc.poll_slidenumber(self.is_live) + self.poll() def last(self): """ Based on the handler passed at startup triggers the last slide """ log.debug(u'Live = %s, last' % self.is_live) + if not self.doc: + return if not self.is_live: return - if self.doc.is_blank(): + if self.hide_mode: self.doc.slidenumber = self.doc.get_slide_count() + self.poll() + return + if not self.activate(): return - self.activate() self.doc.goto_slide(self.doc.get_slide_count()) - self.doc.poll_slidenumber(self.is_live) + self.poll() def next(self): """ Based on the handler passed at startup triggers the next slide event """ log.debug(u'Live = %s, next' % self.is_live) + if not self.doc: + return if not self.is_live: 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(): self.doc.slidenumber = self.doc.slidenumber + 1 + self.poll() + return + if not self.activate(): return # The "End of slideshow" screen is after the last slide # Note, we can't just stop on the last slide, since it may # contain animations that need to be stepped through. if self.doc.slidenumber > self.doc.get_slide_count(): return - self.activate() self.doc.next_step() - self.doc.poll_slidenumber(self.is_live) + self.poll() def previous(self): """ Based on the handler passed at startup triggers the previous slide event """ log.debug(u'Live = %s, previous' % self.is_live) + if not self.doc: + return if not self.is_live: return - if self.doc.is_blank(): + if self.hide_mode: + if not self.doc.is_active(): + return if self.doc.slidenumber > 1: self.doc.slidenumber = self.doc.slidenumber - 1 + self.poll() + return + if not self.activate(): return - self.activate() self.doc.previous_step() - self.doc.poll_slidenumber(self.is_live) + self.poll() def shutdown(self): """ Based on the handler passed at startup triggers slide show to shut down """ log.debug(u'Live = %s, shutdown' % self.is_live) + if not self.doc: + return self.doc.close_presentation() self.doc = None @@ -190,21 +226,30 @@ class Controller(object): Instruct the controller to blank the presentation """ log.debug(u'Live = %s, blank' % self.is_live) + self.hide_mode = hide_mode + if not self.doc: + return if not self.is_live: return - if not self.doc.is_loaded(): - return - if not self.doc.is_active(): - return 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) - self.doc.blank_screen() + elif hide_mode == HideMode.Blank: + if not self.activate(): + return + self.doc.blank_screen() def stop(self): """ Instruct the controller to stop and hide the presentation """ log.debug(u'Live = %s, stop' % self.is_live) + self.hide_mode = HideMode.Screen + if not self.doc: + return if not self.is_live: return if not self.doc.is_loaded(): @@ -218,9 +263,13 @@ class Controller(object): Instruct the controller to unblank the presentation """ log.debug(u'Live = %s, unblank' % self.is_live) + self.hide_mode = None + if not self.doc: + return if not self.is_live: return - self.activate() + if not self.activate(): + return if self.doc.slidenumber and \ self.doc.slidenumber != self.doc.get_slide_number(): self.doc.goto_slide(self.doc.slidenumber) @@ -228,7 +277,9 @@ class Controller(object): Receiver.send_message(u'live_display_hide', HideMode.Screen) 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): diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index 54b9c2144..7da9a95d7 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -94,9 +94,9 @@ class PowerpointController(PresentationController): self.docs[0].close_presentation() if self.process is None: return - if self.process.Presentations.Count > 0: - return try: + if self.process.Presentations.Count > 0: + return self.process.Quit() except pywintypes.com_error: pass @@ -210,6 +210,13 @@ class PowerpointDocument(PresentationDocument): self.presentation.SlideShowSettings.Run() self.presentation.SlideShowWindow.View.State = 1 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): """ @@ -253,6 +260,8 @@ class PowerpointDocument(PresentationDocument): renderer = self.controller.plugin.renderer rect = renderer.screens.current[u'size'] ppt_window = self.presentation.SlideShowSettings.Run() + if not ppt_window: + return ppt_window.Top = rect.y() * 72 / dpi ppt_window.Height = rect.height() * 72 / dpi ppt_window.Left = rect.x() * 72 / dpi @@ -286,6 +295,8 @@ class PowerpointDocument(PresentationDocument): """ log.debug(u'next_step') self.presentation.SlideShowWindow.View.Next() + if self.get_slide_number() > self.get_slide_count(): + self.previous_step() def previous_step(self): """ diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index b72e1e9d4..4006cbc3e 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -260,16 +260,17 @@ class PresentationDocument(object): else: return None - def poll_slidenumber(self, is_live): + def poll_slidenumber(self, is_live, hide_mode): """ Check the current slide number """ if not self.is_active(): return - current = self.get_slide_number() - if current == self.slidenumber: - return - self.slidenumber = current + if not hide_mode: + current = self.get_slide_number() + if current == self.slidenumber: + return + self.slidenumber = current if is_live: prefix = u'live' else: diff --git a/openlp/plugins/remotes/html/index.html b/openlp/plugins/remotes/html/index.html index dad2f8351..148d444bf 100644 --- a/openlp/plugins/remotes/html/index.html +++ b/openlp/plugins/remotes/html/index.html @@ -66,10 +66,10 @@ ${refresh}
@@ -97,10 +97,10 @@ ${refresh} @@ -127,10 +127,10 @@