For worshipassistant add a default verse-id for lyrics to use if none is given. Fixes bug 1458056.

Don't import setting keys that does not exists. Fixes bug 1458672.
When going from a theme-blanked item to item which doesn't support theme-blanking, switch to black-blank.
Only use transitions if we are changing slide. Fixes bug 1449064.
Make translation of 'Advanced' specific to the bible plugin.
Many PowerPoint fixes/improvements:
 * Make screenshots for main webview, even on single screen setup. Fixes bug 1449041.
 * Implement workaround for unblanking bug in PowerPoint 2010.
 * Open PowerPoint hidden so the main application window isn't visible.
 * Added support for odp for PowerPoint 2007 and newer.
 * Added support for Powerpoint events, which is used to update the slidecontroller if OpenLP is not in focus.
 * Minimized the flashing of the PowerPoint presentation window in the taskbar.

bzr-revno: 2539
This commit is contained in:
Tomas Groth 2015-05-29 17:22:22 +01:00 committed by Tim Bentley
commit 623a0974a5
13 changed files with 289 additions and 104 deletions

View File

@ -1 +1 @@
2.1.3 2.1.4

View File

@ -900,7 +900,10 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties):
for section_key in import_keys: for section_key in import_keys:
if 'eneral' in section_key: if 'eneral' in section_key:
section_key = section_key.lower() section_key = section_key.lower()
value = import_settings.value(section_key) try:
value = import_settings.value(section_key)
except KeyError:
log.warning('The key "%s" does not exist (anymore), so it will be skipped.' % section_key)
if value is not None: if value is not None:
settings.setValue('%s' % (section_key), value) settings.setValue('%s' % (section_key), value)
now = datetime.now() now = datetime.now()

View File

@ -828,8 +828,10 @@ class SlideController(DisplayController, RegistryProperties):
self.selected_row = 0 self.selected_row = 0
# take a copy not a link to the servicemanager copy. # take a copy not a link to the servicemanager copy.
self.service_item = copy.copy(service_item) self.service_item = copy.copy(service_item)
if old_item and self.is_live and old_item.is_capable(ItemCapabilities.ProvidesOwnDisplay): # Reset blanking if needed
self._reset_blank() if old_item and self.is_live and (old_item.is_capable(ItemCapabilities.ProvidesOwnDisplay) or
self.service_item.is_capable(ItemCapabilities.ProvidesOwnDisplay)):
self._reset_blank(self.service_item.is_capable(ItemCapabilities.ProvidesOwnDisplay))
if service_item.is_command(): if service_item.is_command():
Registry().execute( Registry().execute(
'%s_start' % service_item.name.lower(), [self.service_item, self.is_live, self.hide_mode(), slide_no]) '%s_start' % service_item.name.lower(), [self.service_item, self.is_live, self.hide_mode(), slide_no])
@ -1080,6 +1082,7 @@ class SlideController(DisplayController, RegistryProperties):
% timeout) % timeout)
return return
row = self.preview_widget.current_slide_number() row = self.preview_widget.current_slide_number()
old_selected_row = self.selected_row
self.selected_row = 0 self.selected_row = 0
if -1 < row < self.preview_widget.slide_count(): if -1 < row < self.preview_widget.slide_count():
if self.service_item.is_command(): if self.service_item.is_command():
@ -1089,7 +1092,7 @@ class SlideController(DisplayController, RegistryProperties):
else: else:
to_display = self.service_item.get_rendered_frame(row) to_display = self.service_item.get_rendered_frame(row)
if self.service_item.is_text(): if self.service_item.is_text():
self.display.text(to_display) self.display.text(to_display, row != old_selected_row)
else: else:
if start: if start:
self.display.build_html(self.service_item, to_display) self.display.build_html(self.service_item, to_display)
@ -1119,8 +1122,7 @@ class SlideController(DisplayController, RegistryProperties):
This updates the preview frame, for example after changing a slide or using *Blank to Theme*. This updates the preview frame, for example after changing a slide or using *Blank to Theme*.
""" """
self.log_debug('update_preview %s ' % self.screens.current['primary']) self.log_debug('update_preview %s ' % self.screens.current['primary'])
if not self.screens.current['primary'] and self.service_item and \ if self.service_item and self.service_item.is_capable(ItemCapabilities.ProvidesOwnDisplay):
self.service_item.is_capable(ItemCapabilities.ProvidesOwnDisplay):
# Grab now, but try again in a couple of seconds if slide change is slow # Grab now, but try again in a couple of seconds if slide change is slow
QtCore.QTimer.singleShot(0.5, self.grab_maindisplay) QtCore.QTimer.singleShot(0.5, self.grab_maindisplay)
QtCore.QTimer.singleShot(2.5, self.grab_maindisplay) QtCore.QTimer.singleShot(2.5, self.grab_maindisplay)
@ -1349,7 +1351,11 @@ class SlideController(DisplayController, RegistryProperties):
:param item: The service item to be processed :param item: The service item to be processed
""" """
self.media_controller.video(self.controller_type, item, self.hide_mode()) if self.is_live and self.hide_mode() == HideMode.Theme:
self.media_controller.video(self.controller_type, item, HideMode.Blank)
self.on_blank_display(True)
else:
self.media_controller.video(self.controller_type, item, self.hide_mode())
if not self.is_live: if not self.is_live:
self.preview_display.show() self.preview_display.show()
self.slide_preview.hide() self.slide_preview.hide()
@ -1362,16 +1368,22 @@ class SlideController(DisplayController, RegistryProperties):
self.preview_display.hide() self.preview_display.hide()
self.slide_preview.show() self.slide_preview.show()
def _reset_blank(self): def _reset_blank(self, no_theme):
""" """
Used by command items which provide their own displays to reset the Used by command items which provide their own displays to reset the
screen hide attributes screen hide attributes
:param no_theme: Does the new item support theme-blanking.
""" """
hide_mode = self.hide_mode() hide_mode = self.hide_mode()
if hide_mode == HideMode.Blank: if hide_mode == HideMode.Blank:
self.on_blank_display(True) self.on_blank_display(True)
elif hide_mode == HideMode.Theme: elif hide_mode == HideMode.Theme:
self.on_theme_display(True) # The new item-type doesn't support theme-blanking, so 'switch' to normal blanking.
if no_theme:
self.on_blank_display(True)
else:
self.on_theme_display(True)
elif hide_mode == HideMode.Screen: elif hide_mode == HideMode.Screen:
self.on_hide_display(True) self.on_hide_display(True)
else: else:

View File

@ -193,7 +193,7 @@ class BibleMediaItem(MediaManagerItem):
self.add_search_fields('quick', translate('BiblesPlugin.MediaItem', 'Quick')) self.add_search_fields('quick', translate('BiblesPlugin.MediaItem', 'Quick'))
self.quickTab.setVisible(True) self.quickTab.setVisible(True)
# Add the Advanced Search tab. # Add the Advanced Search tab.
self.add_search_tab('advanced', UiStrings().Advanced) self.add_search_tab('advanced', translate('BiblesPlugin.MediaItem', 'Advanced'))
self.advanced_book_label = QtGui.QLabel(self.advancedTab) self.advanced_book_label = QtGui.QLabel(self.advancedTab)
self.advanced_book_label.setObjectName('advanced_book_label') self.advanced_book_label.setObjectName('advanced_book_label')
self.advancedLayout.addWidget(self.advanced_book_label, 0, 0, QtCore.Qt.AlignRight) self.advancedLayout.addWidget(self.advanced_book_label, 0, 0, QtCore.Qt.AlignRight)

View File

@ -243,6 +243,9 @@ class Controller(object):
Instruct the controller to stop and hide the presentation. Instruct the controller to stop and hide the presentation.
""" """
log.debug('Live = %s, stop' % self.is_live) log.debug('Live = %s, stop' % self.is_live)
# Save the current slide number to be able to return to this slide if the presentation is activated again.
if self.doc.is_active():
self.doc.slidenumber = self.doc.get_slide_number()
self.hide_mode = HideMode.Screen self.hide_mode = HideMode.Screen
if not self.doc: if not self.doc:
return return
@ -266,8 +269,6 @@ class Controller(object):
return return
if not self.activate(): if not self.activate():
return return
if self.doc.slidenumber and self.doc.slidenumber != self.doc.get_slide_number():
self.doc.goto_slide(self.doc.slidenumber)
self.doc.unblank_screen() self.doc.unblank_screen()
Registry().execute('live_display_hide', HideMode.Screen) Registry().execute('live_display_hide', HideMode.Screen)

View File

@ -32,12 +32,15 @@ import time
from openlp.core.common import is_win, Settings from openlp.core.common import is_win, Settings
if is_win(): if is_win():
from win32com.client import Dispatch from win32com.client import DispatchWithEvents
import win32com import win32com
import win32con
import winreg import winreg
import win32ui import win32ui
import win32gui
import pywintypes import pywintypes
from openlp.core.lib import ScreenList from openlp.core.lib import ScreenList
from openlp.core.lib.ui import UiStrings, critical_error_message_box, translate from openlp.core.lib.ui import UiStrings, critical_error_message_box, translate
from openlp.core.common import trace_error_handler, Registry from openlp.core.common import trace_error_handler, Registry
@ -70,8 +73,18 @@ class PowerpointController(PresentationController):
if is_win(): if is_win():
try: try:
winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, 'PowerPoint.Application').Close() winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, 'PowerPoint.Application').Close()
try:
# Try to detect if the version is 12 (2007) or above, and if so add 'odp' as a support filetype
version_key = winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, 'PowerPoint.Application\\CurVer')
tmp1, app_version_string, tmp2 = winreg.EnumValue(version_key, 0)
version_key.Close()
app_version = int(app_version_string[-2:])
if app_version >= 12:
self.also_supports = ['odp']
except (OSError, ValueError):
log.warning('Detection of powerpoint version using registry failed.')
return True return True
except WindowsError: except OSError:
pass pass
return False return False
@ -80,12 +93,22 @@ class PowerpointController(PresentationController):
""" """
Loads PowerPoint process. Loads PowerPoint process.
""" """
class PowerPointEvents:
"""
Class to catch events from PowerPoint.
"""
def OnSlideShowNextClick(self, slideshow_window, effect):
"""
Occurs on the next click of the slide.
If the main OpenLP window is not in focus force update of the slidecontroller.
"""
if not Registry().get('main_window').isActiveWindow():
log.debug('main window is not in focus - should update slidecontroller')
Registry().execute('slidecontroller_live_change', slideshow_window.View.CurrentShowPosition)
log.debug('start_process') log.debug('start_process')
if not self.process: if not self.process:
self.process = Dispatch('PowerPoint.Application') self.process = DispatchWithEvents('PowerPoint.Application', PowerPointEvents)
self.process.Visible = True
# ppWindowMinimized = 2
self.process.WindowState = 2
def kill(self): def kill(self):
""" """
@ -124,6 +147,9 @@ class PowerpointDocument(PresentationDocument):
self.presentation = None self.presentation = None
self.index_map = {} self.index_map = {}
self.slide_count = 0 self.slide_count = 0
self.blank_slide = 1
self.blank_click = None
self.presentation_hwnd = None
def load_presentation(self): def load_presentation(self):
""" """
@ -132,23 +158,15 @@ class PowerpointDocument(PresentationDocument):
""" """
log.debug('load_presentation') log.debug('load_presentation')
try: try:
if not self.controller.process or not self.controller.process.Visible: if not self.controller.process:
self.controller.start_process() self.controller.start_process()
self.controller.process.Presentations.Open(self.file_path, False, False, True) self.controller.process.Presentations.Open(os.path.normpath(self.file_path), False, False, False)
self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count) self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count)
self.create_thumbnails() self.create_thumbnails()
self.create_titles_and_notes() self.create_titles_and_notes()
# Powerpoint 2010 and 2013 pops up when loading a file, so we minimize it again # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup
if float(self.presentation.Application.Version) >= 14.0: if len(ScreenList().screen_list) > 1:
try: Registry().get('main_window').activateWindow()
# ppWindowMinimized = 2
self.presentation.Application.WindowState = 2
except (AttributeError, pywintypes.com_error) as e:
log.exception('Failed to minimize main powerpoint window')
log.exception(e)
trace_error_handler(log)
# Make sure powerpoint doesn't steal focus
Registry().get('main_window').activateWindow()
return True return True
except (AttributeError, pywintypes.com_error) as e: except (AttributeError, pywintypes.com_error) as e:
log.exception('Exception caught while loading Powerpoint presentation') log.exception('Exception caught while loading Powerpoint presentation')
@ -194,8 +212,9 @@ class PowerpointDocument(PresentationDocument):
trace_error_handler(log) trace_error_handler(log)
self.presentation = None self.presentation = None
self.controller.remove_doc(self) self.controller.remove_doc(self)
# Make sure powerpoint doesn't steal focus # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup
Registry().get('main_window').activateWindow() if len(ScreenList().screen_list) > 1:
Registry().get('main_window').activateWindow()
def is_loaded(self): def is_loaded(self):
""" """
@ -203,10 +222,6 @@ class PowerpointDocument(PresentationDocument):
""" """
log.debug('is_loaded') log.debug('is_loaded')
try: try:
if not self.controller.process.Visible:
return False
if self.controller.process.Windows.Count == 0:
return False
if self.controller.process.Presentations.Count == 0: if self.controller.process.Presentations.Count == 0:
return False return False
except (AttributeError, pywintypes.com_error) as e: except (AttributeError, pywintypes.com_error) as e:
@ -241,13 +256,11 @@ class PowerpointDocument(PresentationDocument):
""" """
log.debug('unblank_screen') log.debug('unblank_screen')
try: try:
self.presentation.SlideShowSettings.Run()
# ppSlideShowRunning = 1
self.presentation.SlideShowWindow.View.State = 1
self.presentation.SlideShowWindow.Activate() self.presentation.SlideShowWindow.Activate()
# Unblanking is broken in PowerPoint 2010 and 2013, need to redisplay self.presentation.SlideShowWindow.View.State = 1
if float(self.presentation.Application.Version) >= 14.0: # Unblanking is broken in PowerPoint 2010 (14.0), need to redisplay
self.presentation.SlideShowWindow.View.GotoSlide(self.blank_slide, False) if 15.0 > float(self.presentation.Application.Version) >= 14.0:
self.presentation.SlideShowWindow.View.GotoSlide(self.index_map[self.blank_slide], False)
if self.blank_click: if self.blank_click:
self.presentation.SlideShowWindow.View.GotoClick(self.blank_click) self.presentation.SlideShowWindow.View.GotoClick(self.blank_click)
except (AttributeError, pywintypes.com_error) as e: except (AttributeError, pywintypes.com_error) as e:
@ -255,8 +268,12 @@ class PowerpointDocument(PresentationDocument):
log.exception(e) log.exception(e)
trace_error_handler(log) trace_error_handler(log)
self.show_error_msg() self.show_error_msg()
# Make sure powerpoint doesn't steal focus # Stop powerpoint from flashing in the taskbar
Registry().get('main_window').activateWindow() if self.presentation_hwnd:
win32gui.FlashWindowEx(self.presentation_hwnd, win32con.FLASHW_STOP, 0, 0)
# Make sure powerpoint doesn't steal focus, unless we're on a single screen setup
if len(ScreenList().screen_list) > 1:
Registry().get('main_window').activateWindow()
def blank_screen(self): def blank_screen(self):
""" """
@ -264,8 +281,8 @@ class PowerpointDocument(PresentationDocument):
""" """
log.debug('blank_screen') log.debug('blank_screen')
try: try:
# Unblanking is broken in PowerPoint 2010 and 2013, need to save info for later # Unblanking is broken in PowerPoint 2010 (14.0), need to save info for later
if float(self.presentation.Application.Version) >= 14.0: if 15.0 > float(self.presentation.Application.Version) >= 14.0:
self.blank_slide = self.get_slide_number() self.blank_slide = self.get_slide_number()
self.blank_click = self.presentation.SlideShowWindow.View.GetClickIndex() self.blank_click = self.presentation.SlideShowWindow.View.GetClickIndex()
# ppSlideShowBlackScreen = 3 # ppSlideShowBlackScreen = 3
@ -295,7 +312,7 @@ class PowerpointDocument(PresentationDocument):
def stop_presentation(self): def stop_presentation(self):
""" """
Stops the current presentation and hides the output. Stops the current presentation and hides the output. Used when blanking to desktop.
""" """
log.debug('stop_presentation') log.debug('stop_presentation')
try: try:
@ -321,28 +338,52 @@ class PowerpointDocument(PresentationDocument):
except win32ui.error: except win32ui.error:
dpi = 96 dpi = 96
size = ScreenList().current['size'] size = ScreenList().current['size']
ppt_window = self.presentation.SlideShowSettings.Run() ppt_window = None
if not ppt_window:
return
try: try:
ppt_window.Top = size.y() * 72 / dpi ppt_window = self.presentation.SlideShowSettings.Run()
ppt_window.Height = size.height() * 72 / dpi except (AttributeError, pywintypes.com_error) as e:
ppt_window.Left = size.x() * 72 / dpi log.exception('Caught exception while in start_presentation')
ppt_window.Width = size.width() * 72 / dpi
except AttributeError as e:
log.exception('AttributeError while in start_presentation')
log.exception(e) log.exception(e)
# Powerpoint 2010 and 2013 pops up when starting a file, so we minimize it again trace_error_handler(log)
if float(self.presentation.Application.Version) >= 14.0: self.show_error_msg()
if ppt_window and not Settings().value('presentations/powerpoint control window'):
try: try:
# ppWindowMinimized = 2 ppt_window.Top = size.y() * 72 / dpi
self.presentation.Application.WindowState = 2 ppt_window.Height = size.height() * 72 / dpi
except (AttributeError, pywintypes.com_error) as e: ppt_window.Left = size.x() * 72 / dpi
log.exception('Failed to minimize main powerpoint window') ppt_window.Width = size.width() * 72 / dpi
except AttributeError as e:
log.exception('AttributeError while in start_presentation')
log.exception(e) log.exception(e)
trace_error_handler(log) # Find the presentation window and save the handle for later
# Make sure powerpoint doesn't steal focus self.presentation_hwnd = None
Registry().get('main_window').activateWindow() if ppt_window:
log.debug('main display size: y=%d, height=%d, x=%d, width=%d'
% (size.y(), size.height(), size.x(), size.width()))
win32gui.EnumWindows(self._window_enum_callback, size)
# Make sure powerpoint doesn't steal focus, unless we're on a single screen setup
if len(ScreenList().screen_list) > 1:
Registry().get('main_window').activateWindow()
def _window_enum_callback(self, hwnd, size):
"""
Method for callback from win32gui.EnumWindows.
Used to find the powerpoint presentation window and stop it flashing in the taskbar.
"""
# Get the size of the current window and if it matches the size of our main display we assume
# it is the powerpoint presentation window.
(left, top, right, bottom) = win32gui.GetWindowRect(hwnd)
window_title = win32gui.GetWindowText(hwnd)
log.debug('window size: left=%d, top=%d, right=%d, width=%d' % (left, top, right, bottom))
log.debug('compare size: %d and %d, %d and %d, %d and %d, %d and %d'
% (size.y(), top, size.height(), (bottom - top), size.x(), left, size.width(), (right - left)))
log.debug('window title: %s' % window_title)
if size.y() == top and size.height() == (bottom - top) and size.x() == left and \
size.width() == (right - left) and os.path.basename(self.file_path) in window_title:
log.debug('Found a match and will save the handle')
self.presentation_hwnd = hwnd
# Stop powerpoint from flashing in the taskbar
win32gui.FlashWindowEx(self.presentation_hwnd, win32con.FLASHW_STOP, 0, 0)
def get_slide_number(self): def get_slide_number(self):
""" """
@ -384,7 +425,7 @@ class PowerpointDocument(PresentationDocument):
log.debug('goto_slide') log.debug('goto_slide')
try: try:
if Settings().value('presentations/powerpoint slide click advance') \ if Settings().value('presentations/powerpoint slide click advance') \
and self.get_slide_number() == self.index_map[slide_no]: and self.get_slide_number() == slide_no:
click_index = self.presentation.SlideShowWindow.View.GetClickIndex() click_index = self.presentation.SlideShowWindow.View.GetClickIndex()
click_count = self.presentation.SlideShowWindow.View.GetClickCount() click_count = self.presentation.SlideShowWindow.View.GetClickCount()
log.debug('We are already on this slide - go to next effect if any left, idx: %d, count: %d' log.debug('We are already on this slide - go to next effect if any left, idx: %d, count: %d'
@ -405,6 +446,7 @@ class PowerpointDocument(PresentationDocument):
""" """
log.debug('next_step') log.debug('next_step')
try: try:
self.presentation.SlideShowWindow.Activate()
self.presentation.SlideShowWindow.View.Next() self.presentation.SlideShowWindow.View.Next()
except (AttributeError, pywintypes.com_error) as e: except (AttributeError, pywintypes.com_error) as e:
log.exception('Caught exception while in next_step') log.exception('Caught exception while in next_step')
@ -415,6 +457,12 @@ class PowerpointDocument(PresentationDocument):
if self.get_slide_number() > self.get_slide_count(): if self.get_slide_number() > self.get_slide_count():
log.debug('past end, stepping back to previous') log.debug('past end, stepping back to previous')
self.previous_step() self.previous_step()
# Stop powerpoint from flashing in the taskbar
if self.presentation_hwnd:
win32gui.FlashWindowEx(self.presentation_hwnd, win32con.FLASHW_STOP, 0, 0)
# Make sure powerpoint doesn't steal focus, unless we're on a single screen setup
if len(ScreenList().screen_list) > 1:
Registry().get('main_window').activateWindow()
def previous_step(self): def previous_step(self):
""" """
@ -490,8 +538,12 @@ def _get_text_from_shapes(shapes):
:param shapes: A set of shapes to search for text. :param shapes: A set of shapes to search for text.
""" """
text = '' text = ''
for shape in shapes: try:
if shape.PlaceholderFormat.Type == 2: # 2 from is enum PpPlaceholderType.ppPlaceholderBody for shape in shapes:
if shape.HasTextFrame and shape.TextFrame.HasText: if shape.PlaceholderFormat.Type == 2: # 2 from is enum PpPlaceholderType.ppPlaceholderBody
text += shape.TextFrame.TextRange.Text + '\n' if shape.HasTextFrame and shape.TextFrame.HasText:
text += shape.TextFrame.TextRange.Text + '\n'
except pywintypes.com_error as e:
log.warning('Failed to extract text from powerpoint slide')
log.warning(e)
return text return text

View File

@ -74,8 +74,11 @@ class PresentationTab(SettingsTab):
self.powerpoint_layout = QtGui.QVBoxLayout(self.powerpoint_group_box) self.powerpoint_layout = QtGui.QVBoxLayout(self.powerpoint_group_box)
self.powerpoint_layout.setObjectName('powerpoint_layout') self.powerpoint_layout.setObjectName('powerpoint_layout')
self.ppt_slide_click_check_box = QtGui.QCheckBox(self.powerpoint_group_box) self.ppt_slide_click_check_box = QtGui.QCheckBox(self.powerpoint_group_box)
self.powerpoint_group_box.setObjectName('ppt_slide_click_check_box') self.ppt_slide_click_check_box.setObjectName('ppt_slide_click_check_box')
self.powerpoint_layout.addWidget(self.ppt_slide_click_check_box) self.powerpoint_layout.addWidget(self.ppt_slide_click_check_box)
self.ppt_window_check_box = QtGui.QCheckBox(self.powerpoint_group_box)
self.ppt_window_check_box.setObjectName('ppt_window_check_box')
self.powerpoint_layout.addWidget(self.ppt_window_check_box)
self.left_layout.addWidget(self.powerpoint_group_box) self.left_layout.addWidget(self.powerpoint_group_box)
# Pdf options # Pdf options
self.pdf_group_box = QtGui.QGroupBox(self.left_column) self.pdf_group_box = QtGui.QGroupBox(self.left_column)
@ -123,6 +126,9 @@ class PresentationTab(SettingsTab):
self.ppt_slide_click_check_box.setText( self.ppt_slide_click_check_box.setText(
translate('PresentationPlugin.PresentationTab', translate('PresentationPlugin.PresentationTab',
'Clicking on a selected slide in the slidecontroller advances to next effect.')) 'Clicking on a selected slide in the slidecontroller advances to next effect.'))
self.ppt_window_check_box.setText(
translate('PresentationPlugin.PresentationTab',
'Let PowerPoint control the size and position of the presentation window.'))
self.pdf_program_check_box.setText( self.pdf_program_check_box.setText(
translate('PresentationPlugin.PresentationTab', 'Use given full path for mudraw or ghostscript binary:')) translate('PresentationPlugin.PresentationTab', 'Use given full path for mudraw or ghostscript binary:'))
@ -148,6 +154,8 @@ class PresentationTab(SettingsTab):
self.ppt_slide_click_check_box.setChecked(Settings().value(self.settings_section + self.ppt_slide_click_check_box.setChecked(Settings().value(self.settings_section +
'/powerpoint slide click advance')) '/powerpoint slide click advance'))
self.ppt_slide_click_check_box.setEnabled(powerpoint_available) self.ppt_slide_click_check_box.setEnabled(powerpoint_available)
self.ppt_window_check_box.setChecked(Settings().value(self.settings_section + '/powerpoint control window'))
self.ppt_window_check_box.setEnabled(powerpoint_available)
# load pdf-program settings # load pdf-program settings
enable_pdf_program = Settings().value(self.settings_section + '/enable_pdf_program') enable_pdf_program = Settings().value(self.settings_section + '/enable_pdf_program')
self.pdf_program_check_box.setChecked(enable_pdf_program) self.pdf_program_check_box.setChecked(enable_pdf_program)
@ -186,6 +194,10 @@ class PresentationTab(SettingsTab):
if Settings().value(setting_key) != self.ppt_slide_click_check_box.checkState(): if Settings().value(setting_key) != self.ppt_slide_click_check_box.checkState():
Settings().setValue(setting_key, self.ppt_slide_click_check_box.checkState()) Settings().setValue(setting_key, self.ppt_slide_click_check_box.checkState())
changed = True changed = True
setting_key = self.settings_section + '/powerpoint control window'
if Settings().value(setting_key) != self.ppt_window_check_box.checkState():
Settings().setValue(setting_key, self.ppt_window_check_box.checkState())
changed = True
# Save pdf-settings # Save pdf-settings
pdf_program = self.pdf_program_path.text() pdf_program = self.pdf_program_path.text()
enable_pdf_program = self.pdf_program_check_box.checkState() enable_pdf_program = self.pdf_program_check_box.checkState()

View File

@ -45,7 +45,8 @@ __default_settings__ = {'presentations/override app': QtCore.Qt.Unchecked,
'presentations/Pdf': QtCore.Qt.Checked, 'presentations/Pdf': QtCore.Qt.Checked,
'presentations/presentations files': [], 'presentations/presentations files': [],
'presentations/thumbnail_scheme': '', 'presentations/thumbnail_scheme': '',
'presentations/powerpoint slide click advance': QtCore.Qt.Unchecked 'presentations/powerpoint slide click advance': QtCore.Qt.Unchecked,
'presentations/powerpoint control window': QtCore.Qt.Unchecked
} }

View File

@ -131,6 +131,7 @@ class WorshipAssistantImport(SongImport):
return return
verse = '' verse = ''
used_verses = [] used_verses = []
verse_id = VerseType.tags[VerseType.Verse] + '1'
for line in lyrics.splitlines(): for line in lyrics.splitlines():
if line.startswith('['): # verse marker if line.startswith('['): # verse marker
# Add previous verse # Add previous verse

View File

@ -164,45 +164,42 @@ class TestPowerpointDocument(TestCase, TestMixin):
""" """
Test creating the titles from PowerPoint Test creating the titles from PowerPoint
""" """
if is_win() and self.real_controller.check_available(): # GIVEN: mocked save_titles_and_notes, _get_text_from_shapes and two mocked slides
# GIVEN: mocked save_titles_and_notes, _get_text_from_shapes and two mocked slides self.doc = PowerpointDocument(self.mock_controller, self.file_name)
self.doc = PowerpointDocument(self.real_controller, self.file_name) self.doc.get_slide_count = MagicMock()
self.doc.save_titles_and_notes = MagicMock() self.doc.get_slide_count.return_value = 2
self.doc._PowerpointDocument__get_text_from_shapes = MagicMock() self.doc.index_map = {1: 1, 2: 2}
slide = MagicMock() self.doc.save_titles_and_notes = MagicMock()
slide.Shapes.Title.TextFrame.TextRange.Text = 'SlideText' self.doc._PowerpointDocument__get_text_from_shapes = MagicMock()
pres = MagicMock() slide = MagicMock()
pres.Slides = [slide, slide] slide.Shapes.Title.TextFrame.TextRange.Text = 'SlideText'
self.doc.presentation = pres pres = MagicMock()
pres.Slides = MagicMock(side_effect=[slide, slide])
self.doc.presentation = pres
# WHEN reading the titles and notes # WHEN reading the titles and notes
self.doc.create_titles_and_notes() self.doc.create_titles_and_notes()
# THEN the save should have been called exactly once with 2 titles and 2 notes # THEN the save should have been called exactly once with 2 titles and 2 notes
self.doc.save_titles_and_notes.assert_called_once_with(['SlideText\n', 'SlideText\n'], [' ', ' ']) self.doc.save_titles_and_notes.assert_called_once_with(['SlideText\n', 'SlideText\n'], [' ', ' '])
else:
self.skipTest('Powerpoint not available, skipping test.')
def create_titles_and_notes_with_no_slides_test(self): def create_titles_and_notes_with_no_slides_test(self):
""" """
Test creating the titles from PowerPoint when it returns no slides Test creating the titles from PowerPoint when it returns no slides
""" """
if is_win() and self.real_controller.check_available(): # GIVEN: mocked save_titles_and_notes, _get_text_from_shapes and two mocked slides
# GIVEN: mocked save_titles_and_notes, _get_text_from_shapes and two mocked slides doc = PowerpointDocument(self.mock_controller, self.file_name)
doc = PowerpointDocument(self.real_controller, self.file_name) doc.save_titles_and_notes = MagicMock()
doc.save_titles_and_notes = MagicMock() doc._PowerpointDocument__get_text_from_shapes = MagicMock()
doc._PowerpointDocument__get_text_from_shapes = MagicMock() pres = MagicMock()
pres = MagicMock() pres.Slides = []
pres.Slides = [] doc.presentation = pres
doc.presentation = pres
# WHEN reading the titles and notes # WHEN reading the titles and notes
doc.create_titles_and_notes() doc.create_titles_and_notes()
# THEN the save should have been called exactly once with empty titles and notes # THEN the save should have been called exactly once with empty titles and notes
doc.save_titles_and_notes.assert_called_once_with([], []) doc.save_titles_and_notes.assert_called_once_with([], [])
else:
self.skipTest('Powerpoint not available, skipping test.')
def get_text_from_shapes_test(self): def get_text_from_shapes_test(self):
""" """
@ -253,3 +250,54 @@ class TestPowerpointDocument(TestCase, TestMixin):
# THEN: next_step() should be call to try to advance to the next effect. # THEN: next_step() should be call to try to advance to the next effect.
self.assertTrue(doc.next_step.called, 'next_step() should have been called!') self.assertTrue(doc.next_step.called, 'next_step() should have been called!')
def blank_screen_test(self):
"""
Test that blank_screen works as expected
"""
# GIVEN: A Document with mocked controller, presentation, and mocked function get_slide_number
doc = PowerpointDocument(self.mock_controller, self.mock_presentation)
doc.presentation = MagicMock()
doc.presentation.SlideShowWindow.View.GetClickIndex.return_value = 3
doc.presentation.Application.Version = 14.0
doc.get_slide_number = MagicMock()
doc.get_slide_number.return_value = 2
# WHEN: Calling goto_slide
doc.blank_screen()
# THEN: The view state, doc.blank_slide and doc.blank_click should have new values
self.assertEquals(doc.presentation.SlideShowWindow.View.State, 3, 'The View State should be 3')
self.assertEquals(doc.blank_slide, 2, 'doc.blank_slide should be 2 because of the PowerPoint version')
self.assertEquals(doc.blank_click, 3, 'doc.blank_click should be 3 because of the PowerPoint version')
def unblank_screen_test(self):
"""
Test that unblank_screen works as expected
"""
# GIVEN: A Document with mocked controller, presentation, ScreenList, and mocked function get_slide_number
with patch('openlp.plugins.presentations.lib.powerpointcontroller.ScreenList') as mocked_screen_list:
mocked_screen_list_ret = MagicMock()
mocked_screen_list_ret.screen_list = [1]
mocked_screen_list.return_value = mocked_screen_list_ret
doc = PowerpointDocument(self.mock_controller, self.mock_presentation)
doc.presentation = MagicMock()
doc.presentation.SlideShowWindow.View.GetClickIndex.return_value = 3
doc.presentation.Application.Version = 14.0
doc.get_slide_number = MagicMock()
doc.get_slide_number.return_value = 2
doc.index_map[1] = 1
doc.blank_slide = 1
doc.blank_click = 1
# WHEN: Calling goto_slide
doc.unblank_screen()
# THEN: The view state have new value, and several function should have been called
self.assertEquals(doc.presentation.SlideShowWindow.View.State, 1, 'The View State should be 1')
self.assertEquals(doc.presentation.SlideShowWindow.Activate.called, True,
'SlideShowWindow.Activate should have been called')
self.assertEquals(doc.presentation.SlideShowWindow.View.GotoSlide.called, True,
'View.GotoSlide should have been called because of the PowerPoint version')
self.assertEquals(doc.presentation.SlideShowWindow.View.GotoClick.called, True,
'View.GotoClick should have been called because of the PowerPoint version')

View File

@ -49,3 +49,5 @@ class TestWorshipAssistantFileImport(SongImportTestHelper):
self.load_external_result_data(os.path.join(TEST_PATH, 'would_you_be_free.json'))) self.load_external_result_data(os.path.join(TEST_PATH, 'would_you_be_free.json')))
self.file_import(os.path.join(TEST_PATH, 'would_you_be_free2.csv'), self.file_import(os.path.join(TEST_PATH, 'would_you_be_free2.csv'),
self.load_external_result_data(os.path.join(TEST_PATH, 'would_you_be_free.json'))) self.load_external_result_data(os.path.join(TEST_PATH, 'would_you_be_free.json')))
self.file_import(os.path.join(TEST_PATH, 'lift_up_your_heads.csv'),
self.load_external_result_data(os.path.join(TEST_PATH, 'lift_up_your_heads.json')))

View File

@ -0,0 +1,40 @@
"SongID","SongNr","Title","Author","Copyright","FirstLine","PriKey","AltKey","Tempo","Focus","Theme","Scripture","Active","Songbook","TimeSig","Introduced","LastUsed","TimesUsed","CCLINr","User1","User2","User3","User4","User5","Roadmap","Overmap","FileLink1","FileLink2","Updated","Lyrics","Info","Lyrics2","Background"
"000013ab-0000-0000-0000-000000000000","0","Lift Up Your Heads"," Bryan Mierau","Public Domain","Lift up your heads and the doors","Em","NULL","NULL","NULL","NULL","NULL","1","1","NULL","NULL","NULL","0","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","NULL","2004-04-07 06:36:18.952",".Em D C D
Lift up your heads and the doors of your heart
. Am B7 Em
And the King of glory will come in
(Repeat)
.G Am D
Who is this King of Glory?
. B7 Em
The Lord strong and mighty!
.G Am D
Who is this King of Glory?
. B7
The Lord, mighty in battle!
.G Am D
Who is this King of Glory?
.B7 Em
Jesus our Messiah!
.G Am D
Who is this King of Glory?
.B7 Em
Jesus, Lord of Lords!
","NULL","Lift up your heads and the doors of your heart
And the King of glory will come in
(Repeat)
Who is this King of Glory?
The Lord strong and mighty!
Who is this King of Glory?
The Lord, mighty in battle!
Who is this King of Glory?
Jesus our Messiah!
Who is this King of Glory?
Jesus, Lord of Lords!
","NULL"
1 SongID SongNr Title Author Copyright FirstLine PriKey AltKey Tempo Focus Theme Scripture Active Songbook TimeSig Introduced LastUsed TimesUsed CCLINr User1 User2 User3 User4 User5 Roadmap Overmap FileLink1 FileLink2 Updated Lyrics Info Lyrics2 Background
2 000013ab-0000-0000-0000-000000000000 0 Lift Up Your Heads Bryan Mierau Public Domain Lift up your heads and the doors Em NULL NULL NULL NULL NULL 1 1 NULL NULL NULL 0 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL 2004-04-07 06:36:18.952 .Em D C D Lift up your heads and the doors of your heart . Am B7 Em And the King of glory will come in (Repeat) .G Am D Who is this King of Glory? . B7 Em The Lord strong and mighty! .G Am D Who is this King of Glory? . B7 The Lord, mighty in battle! .G Am D Who is this King of Glory? .B7 Em Jesus our Messiah! .G Am D Who is this King of Glory? .B7 Em Jesus, Lord of Lords! NULL Lift up your heads and the doors of your heart And the King of glory will come in (Repeat) Who is this King of Glory? The Lord strong and mighty! Who is this King of Glory? The Lord, mighty in battle! Who is this King of Glory? Jesus our Messiah! Who is this King of Glory? Jesus, Lord of Lords! NULL

View File

@ -0,0 +1,13 @@
{
"authors": [
"Bryan Mierau"
],
"title": "Lift Up Your Heads",
"verse_order_list": [],
"verses": [
[
"Lift up your heads and the doors of your heart\nAnd the King of glory will come in\n(Repeat)\n\nWho is this King of Glory?\nThe Lord strong and mighty!\nWho is this King of Glory?\nThe Lord, mighty in battle!\n\nWho is this King of Glory?\nJesus our Messiah!\nWho is this King of Glory?\nJesus, Lord of Lords!\n",
"v1"
]
]
}