Merge lp:~tomasgroth/openlp/presentation-beyond-last into lp:openlp

Proposed by Tomas Groth
Status: Superseded
Proposed branch: lp:~tomasgroth/openlp/presentation-beyond-last
Merge into: lp:openlp
Diff against target: 649 lines (+272/-55)
7 files modified
openlp/core/common/registry.py (+1/-1)
openlp/core/ui/servicemanager.py (+6/-0)
openlp/core/ui/slidecontroller.py (+22/-6)
openlp/plugins/presentations/lib/impresscontroller.py (+195/-21)
openlp/plugins/presentations/lib/messagelistener.py (+20/-20)
openlp/plugins/presentations/lib/powerpointcontroller.py (+23/-4)
openlp/plugins/presentations/lib/presentationcontroller.py (+5/-3)
To merge this branch: bzr merge lp:~tomasgroth/openlp/presentation-beyond-last
Reviewer Review Type Date Requested Status
OpenLP Core Pending
Review via email: mp+367863@code.launchpad.net

This proposal supersedes a proposal from 2019-05-21.

This proposal has been superseded by a proposal from 2019-05-24.

Commit message

Make it possible to go to next or previous service item when stepping through a presentation.
Disables impress and powerpoint presentation console.

To post a comment you must log in.
Revision history for this message
Raoul Snyman (raoul-snyman) wrote : Posted in a previous version of this proposal

Linux tests failed, please see https://ci.openlp.io/job/MP-02-Linux_Tests/158/ for more details

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Linux tests passed!

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

Linting failed, please see https://ci.openlp.io/job/MP-03-Linting/103/ for more details

2670. By Tomas Groth

pep8 fixes

Revision history for this message
Phill (phill-ridout) wrote :

Whats happrning with the commented out code?

2671. By Tomas Groth

Reenable setting slidecontroller index when openlp is not in focus.

2672. By Tomas Groth

Remove unused code

2673. By Tomas Groth

Fix traceback on Mac tests

2674. By Tomas Groth

pep8

2675. By Tomas Groth

trunk

2676. By Tomas Groth

Fix as suggested

2677. By Tomas Groth

merge trunk

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'openlp/core/common/registry.py'
--- openlp/core/common/registry.py 2019-04-13 13:00:22 +0000
+++ openlp/core/common/registry.py 2019-05-24 19:21:37 +0000
@@ -146,7 +146,7 @@
146 try:146 try:
147 log.debug('Running function {} for {}'.format(function, event))147 log.debug('Running function {} for {}'.format(function, event))
148 result = function(*args, **kwargs)148 result = function(*args, **kwargs)
149 if result:149 if result is not None:
150 results.append(result)150 results.append(result)
151 except TypeError:151 except TypeError:
152 # Who has called me can help in debugging152 # Who has called me can help in debugging
153153
=== modified file 'openlp/core/ui/servicemanager.py'
--- openlp/core/ui/servicemanager.py 2019-05-24 18:50:51 +0000
+++ openlp/core/ui/servicemanager.py 2019-05-24 19:21:37 +0000
@@ -976,8 +976,10 @@
976 prev_item_last_slide = None976 prev_item_last_slide = None
977 service_iterator = QtWidgets.QTreeWidgetItemIterator(self.service_manager_list)977 service_iterator = QtWidgets.QTreeWidgetItemIterator(self.service_manager_list)
978 while service_iterator.value():978 while service_iterator.value():
979 # Found the selected/current service item
979 if service_iterator.value() == selected:980 if service_iterator.value() == selected:
980 if last_slide and prev_item_last_slide:981 if last_slide and prev_item_last_slide:
982 # Go to the last slide of the previous service item
981 pos = prev_item.data(0, QtCore.Qt.UserRole)983 pos = prev_item.data(0, QtCore.Qt.UserRole)
982 check_expanded = self.service_items[pos - 1]['expanded']984 check_expanded = self.service_items[pos - 1]['expanded']
983 self.service_manager_list.setCurrentItem(prev_item_last_slide)985 self.service_manager_list.setCurrentItem(prev_item_last_slide)
@@ -986,13 +988,17 @@
986 self.make_live()988 self.make_live()
987 self.service_manager_list.setCurrentItem(prev_item)989 self.service_manager_list.setCurrentItem(prev_item)
988 elif prev_item:990 elif prev_item:
991 # Go to the first slide of the previous service item
989 self.service_manager_list.setCurrentItem(prev_item)992 self.service_manager_list.setCurrentItem(prev_item)
990 self.make_live()993 self.make_live()
991 return994 return
995 # Found the previous service item root
992 if service_iterator.value().parent() is None:996 if service_iterator.value().parent() is None:
993 prev_item = service_iterator.value()997 prev_item = service_iterator.value()
998 # Found the last slide of the previous item
994 if service_iterator.value().parent() is prev_item:999 if service_iterator.value().parent() is prev_item:
995 prev_item_last_slide = service_iterator.value()1000 prev_item_last_slide = service_iterator.value()
1001 # Go to next item in the tree
996 service_iterator += 11002 service_iterator += 1
9971003
998 def on_set_item(self, message):1004 def on_set_item(self, message):
9991005
=== modified file 'openlp/core/ui/slidecontroller.py'
--- openlp/core/ui/slidecontroller.py 2019-05-22 06:47:00 +0000
+++ openlp/core/ui/slidecontroller.py 2019-05-24 19:21:37 +0000
@@ -1261,9 +1261,18 @@
1261 if not self.service_item:1261 if not self.service_item:
1262 return1262 return
1263 if self.service_item.is_command():1263 if self.service_item.is_command():
1264 Registry().execute('{text}_next'.format(text=self.service_item.name.lower()),1264 past_end = Registry().execute('{text}_next'.format(text=self.service_item.name.lower()),
1265 [self.service_item, self.is_live])1265 [self.service_item, self.is_live])
1266 if self.is_live:1266 # Check if we have gone past the end of the last slide
1267 if self.is_live and past_end and past_end[0]:
1268 if wrap is None:
1269 if self.slide_limits == SlideLimits.Wrap:
1270 self.on_slide_selected_index([0])
1271 elif self.is_live and self.slide_limits == SlideLimits.Next:
1272 self.service_next()
1273 elif wrap:
1274 self.on_slide_selected_index([0])
1275 elif self.is_live:
1267 self.update_preview()1276 self.update_preview()
1268 else:1277 else:
1269 row = self.preview_widget.current_slide_number() + 11278 row = self.preview_widget.current_slide_number() + 1
@@ -1290,9 +1299,16 @@
1290 if not self.service_item:1299 if not self.service_item:
1291 return1300 return
1292 if self.service_item.is_command():1301 if self.service_item.is_command():
1293 Registry().execute('{text}_previous'.format(text=self.service_item.name.lower()),1302 before_start = Registry().execute('{text}_previous'.format(text=self.service_item.name.lower()),
1294 [self.service_item, self.is_live])1303 [self.service_item, self.is_live])
1295 if self.is_live:1304 # Check id we have tried to go before that start slide
1305 if self.is_live and before_start and before_start[0]:
1306 if self.slide_limits == SlideLimits.Wrap:
1307 self.on_slide_selected_index([self.preview_widget.slide_count() - 1])
1308 elif self.is_live and self.slide_limits == SlideLimits.Next:
1309 self.keypress_queue.append(ServiceItemAction.PreviousLastSlide)
1310 self._process_queue()
1311 elif self.is_live:
1296 self.update_preview()1312 self.update_preview()
1297 else:1313 else:
1298 row = self.preview_widget.current_slide_number() - 11314 row = self.preview_widget.current_slide_number() - 1
12991315
=== modified file 'openlp/plugins/presentations/lib/impresscontroller.py'
--- openlp/plugins/presentations/lib/impresscontroller.py 2019-05-22 06:47:00 +0000
+++ openlp/plugins/presentations/lib/impresscontroller.py 2019-05-24 19:21:37 +0000
@@ -36,7 +36,7 @@
3636
37from PyQt5 import QtCore37from PyQt5 import QtCore
3838
39from openlp.core.common import delete_file, get_uno_command, get_uno_instance, is_win39from openlp.core.common import delete_file, get_uno_command, get_uno_instance, is_win, trace_error_handler
40from openlp.core.common.registry import Registry40from openlp.core.common.registry import Registry
41from openlp.core.display.screens import ScreenList41from openlp.core.display.screens import ScreenList
42from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument, \42from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument, \
@@ -47,15 +47,30 @@
47 from win32com.client import Dispatch47 from win32com.client import Dispatch
48 import pywintypes48 import pywintypes
49 uno_available = False49 uno_available = False
50 try:
51 service_manager = Dispatch('com.sun.star.ServiceManager')
52 service_manager._FlagAsMethod('Bridge_GetStruct')
53 XSlideShowListenerObj = service_manager.Bridge_GetStruct('com.sun.star.presentation.XSlideShowListener')
54
55 class SlideShowListenerImport(XSlideShowListenerObj.__class__):
56 pass
57 except (AttributeError, pywintypes.com_error):
58 class SlideShowListenerImport():
59 pass
60
50 # Declare an empty exception to match the exception imported from UNO61 # Declare an empty exception to match the exception imported from UNO
51
52 class ErrorCodeIOException(Exception):62 class ErrorCodeIOException(Exception):
53 pass63 pass
54else:64else:
55 try:65 try:
56 import uno66 import uno
67 import unohelper
57 from com.sun.star.beans import PropertyValue68 from com.sun.star.beans import PropertyValue
58 from com.sun.star.task import ErrorCodeIOException69 from com.sun.star.task import ErrorCodeIOException
70 from com.sun.star.presentation import XSlideShowListener
71
72 class SlideShowListenerImport(unohelper.Base, XSlideShowListener):
73 pass
5974
60 uno_available = True75 uno_available = True
61 except ImportError:76 except ImportError:
@@ -82,6 +97,8 @@
82 self.process = None97 self.process = None
83 self.desktop = None98 self.desktop = None
84 self.manager = None99 self.manager = None
100 self.conf_provider = None
101 self.presenter_screen_disabled_by_openlp = False
85102
86 def check_available(self):103 def check_available(self):
87 """104 """
@@ -90,8 +107,7 @@
90 log.debug('check_available')107 log.debug('check_available')
91 if is_win():108 if is_win():
92 return self.get_com_servicemanager() is not None109 return self.get_com_servicemanager() is not None
93 else:110 return uno_available
94 return uno_available
95111
96 def start_process(self):112 def start_process(self):
97 """113 """
@@ -131,6 +147,7 @@
131 self.manager = uno_instance.ServiceManager147 self.manager = uno_instance.ServiceManager
132 log.debug('get UNO Desktop Openoffice - createInstanceWithContext - Desktop')148 log.debug('get UNO Desktop Openoffice - createInstanceWithContext - Desktop')
133 desktop = self.manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance)149 desktop = self.manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance)
150 self.toggle_presentation_screen(False)
134 return desktop151 return desktop
135 except Exception:152 except Exception:
136 log.warning('Failed to get UNO desktop')153 log.warning('Failed to get UNO desktop')
@@ -148,6 +165,7 @@
148 desktop = self.manager.createInstance('com.sun.star.frame.Desktop')165 desktop = self.manager.createInstance('com.sun.star.frame.Desktop')
149 except (AttributeError, pywintypes.com_error):166 except (AttributeError, pywintypes.com_error):
150 log.warning('Failure to find desktop - Impress may have closed')167 log.warning('Failure to find desktop - Impress may have closed')
168 self.toggle_presentation_screen(False)
151 return desktop if desktop else None169 return desktop if desktop else None
152170
153 def get_com_servicemanager(self):171 def get_com_servicemanager(self):
@@ -166,6 +184,8 @@
166 Called at system exit to clean up any running presentations.184 Called at system exit to clean up any running presentations.
167 """185 """
168 log.debug('Kill OpenOffice')186 log.debug('Kill OpenOffice')
187 if self.presenter_screen_disabled_by_openlp:
188 self._toggle_presentation_screen(True)
169 while self.docs:189 while self.docs:
170 self.docs[0].close_presentation()190 self.docs[0].close_presentation()
171 desktop = None191 desktop = None
@@ -195,6 +215,54 @@
195 except Exception:215 except Exception:
196 log.warning('Failed to terminate OpenOffice')216 log.warning('Failed to terminate OpenOffice')
197217
218 def toggle_presentation_screen(self, target_value):
219 """
220 Enable or disable the Presentation Screen/Console
221 """
222 # Create Instance of ConfigurationProvider
223 if not self.conf_provider:
224 if is_win():
225 self.conf_provider = self.manager.createInstance('com.sun.star.configuration.ConfigurationProvider')
226 else:
227 self.conf_provider = self.manager.createInstanceWithContext(
228 'com.sun.star.configuration.ConfigurationProvider', uno.getComponentContext())
229 # Setup lookup properties to get Impress settings
230 properties = []
231 properties.append(self.create_property('nodepath', 'org.openoffice.Office.Impress'))
232 properties = tuple(properties)
233 try:
234 # Get an updateable configuration view
235 impress_conf_props = self.conf_provider.createInstanceWithArguments(
236 'com.sun.star.configuration.ConfigurationUpdateAccess', properties)
237 # Get the specific setting for presentation screen
238 presenter_screen_enabled = impress_conf_props.getHierarchicalPropertyValue(
239 'Misc/Start/EnablePresenterScreen')
240 # If the presentation screen is enabled we disable it
241 if presenter_screen_enabled != target_value:
242 impress_conf_props.setHierarchicalPropertyValue('Misc/Start/EnablePresenterScreen', target_value)
243 impress_conf_props.commitChanges()
244 # if target_value is False this is an attempt to disable the Presenter Screen
245 # so we make a note that it has been disabled, so it can be enabled again on close.
246 if target_value is False:
247 self.presenter_screen_disabled_by_openlp = True
248 except Exception as e:
249 log.exception(e)
250 trace_error_handler(log)
251 return
252
253 def create_property(self, name, value):
254 """
255 Create an OOo style property object which are passed into some Uno methods.
256 """
257 log.debug('create property OpenOffice')
258 if is_win():
259 property_object = self.manager.Bridge_GetStruct('com.sun.star.beans.PropertyValue')
260 else:
261 property_object = PropertyValue()
262 property_object.Name = name
263 property_object.Value = value
264 return property_object
265
198266
199class ImpressDocument(PresentationDocument):267class ImpressDocument(PresentationDocument):
200 """268 """
@@ -213,6 +281,8 @@
213 self.document = None281 self.document = None
214 self.presentation = None282 self.presentation = None
215 self.control = None283 self.control = None
284 self.slide_ended = False
285 self.slide_ended_reverse = False
216286
217 def load_presentation(self):287 def load_presentation(self):
218 """288 """
@@ -233,13 +303,16 @@
233 return False303 return False
234 self.desktop = desktop304 self.desktop = desktop
235 properties = []305 properties = []
236 properties.append(self.create_property('Hidden', True))306 properties.append(self.controller.create_property('Hidden', True))
237 properties = tuple(properties)307 properties = tuple(properties)
238 try:308 try:
239 self.document = desktop.loadComponentFromURL(url, '_blank', 0, properties)309 self.document = desktop.loadComponentFromURL(url, '_blank', 0, properties)
240 except Exception:310 except Exception:
241 log.warning('Failed to load presentation {url}'.format(url=url))311 log.warning('Failed to load presentation {url}'.format(url=url))
242 return False312 return False
313 if self.document is None:
314 log.warning('Presentation {url} could not be loaded'.format(url=url))
315 return False
243 self.presentation = self.document.getPresentation()316 self.presentation = self.document.getPresentation()
244 self.presentation.Display = ScreenList().current.number + 1317 self.presentation.Display = ScreenList().current.number + 1
245 self.control = None318 self.control = None
@@ -257,7 +330,7 @@
257 temp_folder_path = self.get_temp_folder()330 temp_folder_path = self.get_temp_folder()
258 thumb_dir_url = temp_folder_path.as_uri()331 thumb_dir_url = temp_folder_path.as_uri()
259 properties = []332 properties = []
260 properties.append(self.create_property('FilterName', 'impress_png_Export'))333 properties.append(self.controller.create_property('FilterName', 'impress_png_Export'))
261 properties = tuple(properties)334 properties = tuple(properties)
262 doc = self.document335 doc = self.document
263 pages = doc.getDrawPages()336 pages = doc.getDrawPages()
@@ -279,19 +352,6 @@
279 except Exception:352 except Exception:
280 log.exception('{path} - Unable to store openoffice preview'.format(path=path))353 log.exception('{path} - Unable to store openoffice preview'.format(path=path))
281354
282 def create_property(self, name, value):
283 """
284 Create an OOo style property object which are passed into some Uno methods.
285 """
286 log.debug('create property OpenOffice')
287 if is_win():
288 property_object = self.controller.manager.Bridge_GetStruct('com.sun.star.beans.PropertyValue')
289 else:
290 property_object = PropertyValue()
291 property_object.Name = name
292 property_object.Value = value
293 return property_object
294
295 def close_presentation(self):355 def close_presentation(self):
296 """356 """
297 Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being357 Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being
@@ -356,8 +416,7 @@
356 log.debug('is blank OpenOffice')416 log.debug('is blank OpenOffice')
357 if self.control and self.control.isRunning():417 if self.control and self.control.isRunning():
358 return self.control.isPaused()418 return self.control.isPaused()
359 else:419 return False
360 return False
361420
362 def stop_presentation(self):421 def stop_presentation(self):
363 """422 """
@@ -384,6 +443,8 @@
384 sleep_count += 1443 sleep_count += 1
385 self.control = self.presentation.getController()444 self.control = self.presentation.getController()
386 window.setVisible(False)445 window.setVisible(False)
446 listener = SlideShowListener(self)
447 self.control.getSlideShow().addSlideShowListener(listener)
387 else:448 else:
388 self.control.activate()449 self.control.activate()
389 self.goto_slide(1)450 self.goto_slide(1)
@@ -415,17 +476,33 @@
415 """476 """
416 Triggers the next effect of slide on the running presentation.477 Triggers the next effect of slide on the running presentation.
417 """478 """
479 # if we are at the presentations end don't go further, just return True
480 if self.slide_ended and self.get_slide_count() == self.get_slide_number():
481 return True
482 self.slide_ended = False
483 self.slide_ended_reverse = False
484 past_end = False
418 is_paused = self.control.isPaused()485 is_paused = self.control.isPaused()
419 self.control.gotoNextEffect()486 self.control.gotoNextEffect()
420 time.sleep(0.1)487 time.sleep(0.1)
488 # If for some reason the presentation end was not detected above, this will catch it.
489 # The presentation is set to paused when going past the end.
421 if not is_paused and self.control.isPaused():490 if not is_paused and self.control.isPaused():
422 self.control.gotoPreviousEffect()491 self.control.gotoPreviousEffect()
492 past_end = True
493 return past_end
423494
424 def previous_step(self):495 def previous_step(self):
425 """496 """
426 Triggers the previous slide on the running presentation.497 Triggers the previous slide on the running presentation.
427 """498 """
499 # if we are at the presentations start don't go further back, just return True
500 if self.slide_ended_reverse and self.get_slide_number() == 1:
501 return True
502 self.slide_ended = False
503 self.slide_ended_reverse = False
428 self.control.gotoPreviousEffect()504 self.control.gotoPreviousEffect()
505 return False
429506
430 def get_slide_text(self, slide_no):507 def get_slide_text(self, slide_no):
431 """508 """
@@ -483,3 +560,100 @@
483 note = ' '560 note = ' '
484 notes.append(note)561 notes.append(note)
485 self.save_titles_and_notes(titles, notes)562 self.save_titles_and_notes(titles, notes)
563
564 if is_win():
565 property_object = self.controller.manager.Bridge_GetStruct('com.sun.star.beans.PropertyValue')
566
567
568class SlideShowListener(SlideShowListenerImport):
569 """
570 Listener interface to receive global slide show events.
571 """
572
573 def __init__(self, document):
574 """
575 Constructor
576
577 :param document: The ImpressDocument being presented
578 """
579 self.document = document
580
581 def paused(self):
582 """
583 Notify that the slide show is paused
584 """
585 log.debug('LibreOffice SlideShowListener event: paused')
586
587 def resumed(self):
588 """
589 Notify that the slide show is resumed from a paused state
590 """
591 log.debug('LibreOffice SlideShowListener event: resumed')
592
593 def slideTransitionStarted(self):
594 """
595 Notify that a new slide starts to become visible.
596 """
597 log.debug('LibreOffice SlideShowListener event: slideTransitionStarted')
598
599 def slideTransitionEnded(self):
600 """
601 Notify that the slide transtion of the current slide ended.
602 """
603 log.debug('LibreOffice SlideShowListener event: slideTransitionEnded')
604
605 def slideAnimationsEnded(self):
606 """
607 Notify that the last animation from the main sequence of the current slide has ended.
608 """
609 log.debug('LibreOffice SlideShowListener event: slideAnimationsEnded')
610 if not Registry().get('main_window').isActiveWindow():
611 log.debug('main window is not in focus - should update slidecontroller')
612 Registry().execute('slidecontroller_live_change', self.document.control.getCurrentSlideIndex() + 1)
613
614 def slideEnded(self, reverse):
615 """
616 Notify that the current slide has ended, e.g. the user has clicked on the slide. Calling displaySlide()
617 twice will not issue this event.
618 """
619 log.debug('LibreOffice SlideShowListener event: slideEnded %d' % reverse)
620 if reverse:
621 self.document.slide_ended = False
622 self.document.slide_ended_reverse = True
623 else:
624 self.document.slide_ended = True
625 self.document.slide_ended_reverse = False
626
627 def hyperLinkClicked(self, hyperLink):
628 """
629 Notifies that a hyperlink has been clicked.
630 """
631 log.debug('LibreOffice SlideShowListener event: hyperLinkClicked %s' % hyperLink)
632
633 def disposing(self, source):
634 """
635 gets called when the broadcaster is about to be disposed.
636 :param source:
637 """
638 log.debug('LibreOffice SlideShowListener event: disposing')
639
640 def beginEvent(self, node):
641 """
642 This event is raised when the element local timeline begins to play.
643 :param node:
644 """
645 log.debug('LibreOffice SlideShowListener event: beginEvent')
646
647 def endEvent(self, node):
648 """
649 This event is raised at the active end of the element.
650 :param node:
651 """
652 log.debug('LibreOffice SlideShowListener event: endEvent')
653
654 def repeat(self, node):
655 """
656 This event is raised when the element local timeline repeats.
657 :param node:
658 """
659 log.debug('LibreOffice SlideShowListener event: repeat')
486660
=== modified file 'openlp/plugins/presentations/lib/messagelistener.py'
--- openlp/plugins/presentations/lib/messagelistener.py 2019-05-22 06:47:00 +0000
+++ openlp/plugins/presentations/lib/messagelistener.py 2019-05-24 19:21:37 +0000
@@ -169,24 +169,21 @@
169 """169 """
170 log.debug('Live = {live}, next'.format(live=self.is_live))170 log.debug('Live = {live}, next'.format(live=self.is_live))
171 if not self.doc:171 if not self.doc:
172 return172 return False
173 if not self.is_live:173 if not self.is_live:
174 return174 return False
175 if self.hide_mode:175 if self.hide_mode:
176 if not self.doc.is_active():176 if not self.doc.is_active():
177 return177 return False
178 if self.doc.slidenumber < self.doc.get_slide_count():178 if self.doc.slidenumber < self.doc.get_slide_count():
179 self.doc.slidenumber += 1179 self.doc.slidenumber += 1
180 self.poll()180 self.poll()
181 return181 return False
182 if not self.activate():182 if not self.activate():
183 return183 return False
184 # The "End of slideshow" screen is after the last slide. Note, we can't just stop on the last slide, since it184 ret = self.doc.next_step()
185 # may contain animations that need to be stepped through.
186 if self.doc.slidenumber > self.doc.get_slide_count():
187 return
188 self.doc.next_step()
189 self.poll()185 self.poll()
186 return ret
190187
191 def previous(self):188 def previous(self):
192 """189 """
@@ -194,20 +191,21 @@
194 """191 """
195 log.debug('Live = {live}, previous'.format(live=self.is_live))192 log.debug('Live = {live}, previous'.format(live=self.is_live))
196 if not self.doc:193 if not self.doc:
197 return194 return False
198 if not self.is_live:195 if not self.is_live:
199 return196 return False
200 if self.hide_mode:197 if self.hide_mode:
201 if not self.doc.is_active():198 if not self.doc.is_active():
202 return199 return False
203 if self.doc.slidenumber > 1:200 if self.doc.slidenumber > 1:
204 self.doc.slidenumber -= 1201 self.doc.slidenumber -= 1
205 self.poll()202 self.poll()
206 return203 return False
207 if not self.activate():204 if not self.activate():
208 return205 return False
209 self.doc.previous_step()206 ret = self.doc.previous_step()
210 self.poll()207 self.poll()
208 return ret
211209
212 def shutdown(self):210 def shutdown(self):
213 """211 """
@@ -418,11 +416,12 @@
418 """416 """
419 is_live = message[1]417 is_live = message[1]
420 if is_live:418 if is_live:
421 self.live_handler.next()419 ret = self.live_handler.next()
422 if Settings().value('core/click live slide to unblank'):420 if Settings().value('core/click live slide to unblank'):
423 Registry().execute('slidecontroller_live_unblank')421 Registry().execute('slidecontroller_live_unblank')
422 return ret
424 else:423 else:
425 self.preview_handler.next()424 return self.preview_handler.next()
426425
427 def previous(self, message):426 def previous(self, message):
428 """427 """
@@ -432,11 +431,12 @@
432 """431 """
433 is_live = message[1]432 is_live = message[1]
434 if is_live:433 if is_live:
435 self.live_handler.previous()434 ret = self.live_handler.previous()
436 if Settings().value('core/click live slide to unblank'):435 if Settings().value('core/click live slide to unblank'):
437 Registry().execute('slidecontroller_live_unblank')436 Registry().execute('slidecontroller_live_unblank')
437 return ret
438 else:438 else:
439 self.preview_handler.previous()439 return self.preview_handler.previous()
440440
441 def shutdown(self, message):441 def shutdown(self, message):
442 """442 """
443443
=== modified file 'openlp/plugins/presentations/lib/powerpointcontroller.py'
--- openlp/plugins/presentations/lib/powerpointcontroller.py 2019-05-22 06:47:00 +0000
+++ openlp/plugins/presentations/lib/powerpointcontroller.py 2019-05-24 19:21:37 +0000
@@ -170,14 +170,17 @@
170 However, for the moment, we want a physical file since it makes life easier elsewhere.170 However, for the moment, we want a physical file since it makes life easier elsewhere.
171 """171 """
172 log.debug('create_thumbnails')172 log.debug('create_thumbnails')
173 generate_thumbs = True
173 if self.check_thumbnails():174 if self.check_thumbnails():
174 return175 # No need for thumbnails but we still need the index
176 generate_thumbs = False
175 key = 1177 key = 1
176 for num in range(self.presentation.Slides.Count):178 for num in range(self.presentation.Slides.Count):
177 if not self.presentation.Slides(num + 1).SlideShowTransition.Hidden:179 if not self.presentation.Slides(num + 1).SlideShowTransition.Hidden:
178 self.index_map[key] = num + 1180 self.index_map[key] = num + 1
179 self.presentation.Slides(num + 1).Export(181 if generate_thumbs:
180 str(self.get_thumbnail_folder() / 'slide{key:d}.png'.format(key=key)), 'png', 320, 240)182 self.presentation.Slides(num + 1).Export(
183 str(self.get_thumbnail_folder() / 'slide{key:d}.png'.format(key=key)), 'png', 320, 240)
181 key += 1184 key += 1
182 self.slide_count = key - 1185 self.slide_count = key - 1
183186
@@ -318,6 +321,9 @@
318 size = ScreenList().current.display_geometry321 size = ScreenList().current.display_geometry
319 ppt_window = None322 ppt_window = None
320 try:323 try:
324 # Disable the presentation console
325 self.presentation.SlideShowSettings.ShowPresenterView = 0
326 # Start the presentation
321 ppt_window = self.presentation.SlideShowSettings.Run()327 ppt_window = self.presentation.SlideShowSettings.Run()
322 except (AttributeError, pywintypes.com_error):328 except (AttributeError, pywintypes.com_error):
323 log.exception('Caught exception while in start_presentation')329 log.exception('Caught exception while in start_presentation')
@@ -437,6 +443,12 @@
437 Triggers the next effect of slide on the running presentation.443 Triggers the next effect of slide on the running presentation.
438 """444 """
439 log.debug('next_step')445 log.debug('next_step')
446 # if we are at the presentations end don't go further, just return True
447 if self.presentation.SlideShowWindow.View.GetClickCount() == \
448 self.presentation.SlideShowWindow.View.GetClickIndex() \
449 and self.get_slide_number() == self.get_slide_count():
450 return True
451 past_end = False
440 try:452 try:
441 self.presentation.SlideShowWindow.Activate()453 self.presentation.SlideShowWindow.Activate()
442 self.presentation.SlideShowWindow.View.Next()454 self.presentation.SlideShowWindow.View.Next()
@@ -444,28 +456,35 @@
444 log.exception('Caught exception while in next_step')456 log.exception('Caught exception while in next_step')
445 trace_error_handler(log)457 trace_error_handler(log)
446 self.show_error_msg()458 self.show_error_msg()
447 return459 return past_end
460 # If for some reason the presentation end was not detected above, this will catch it.
448 if self.get_slide_number() > self.get_slide_count():461 if self.get_slide_number() > self.get_slide_count():
449 log.debug('past end, stepping back to previous')462 log.debug('past end, stepping back to previous')
450 self.previous_step()463 self.previous_step()
464 past_end = True
451 # Stop powerpoint from flashing in the taskbar465 # Stop powerpoint from flashing in the taskbar
452 if self.presentation_hwnd:466 if self.presentation_hwnd:
453 win32gui.FlashWindowEx(self.presentation_hwnd, win32con.FLASHW_STOP, 0, 0)467 win32gui.FlashWindowEx(self.presentation_hwnd, win32con.FLASHW_STOP, 0, 0)
454 # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup468 # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup
455 if len(ScreenList()) > 1:469 if len(ScreenList()) > 1:
456 Registry().get('main_window').activateWindow()470 Registry().get('main_window').activateWindow()
471 return past_end
457472
458 def previous_step(self):473 def previous_step(self):
459 """474 """
460 Triggers the previous slide on the running presentation.475 Triggers the previous slide on the running presentation.
461 """476 """
462 log.debug('previous_step')477 log.debug('previous_step')
478 # if we are at the presentations start we can't go further back, just return True
479 if self.presentation.SlideShowWindow.View.GetClickIndex() == 0 and self.get_slide_number() == 1:
480 return True
463 try:481 try:
464 self.presentation.SlideShowWindow.View.Previous()482 self.presentation.SlideShowWindow.View.Previous()
465 except (AttributeError, pywintypes.com_error):483 except (AttributeError, pywintypes.com_error):
466 log.exception('Caught exception while in previous_step')484 log.exception('Caught exception while in previous_step')
467 trace_error_handler(log)485 trace_error_handler(log)
468 self.show_error_msg()486 self.show_error_msg()
487 return False
469488
470 def get_slide_text(self, slide_no):489 def get_slide_text(self, slide_no):
471 """490 """
472491
=== modified file 'openlp/plugins/presentations/lib/presentationcontroller.py'
--- openlp/plugins/presentations/lib/presentationcontroller.py 2019-05-22 06:47:00 +0000
+++ openlp/plugins/presentations/lib/presentationcontroller.py 2019-05-24 19:21:37 +0000
@@ -248,15 +248,17 @@
248 def next_step(self):248 def next_step(self):
249 """249 """
250 Triggers the next effect of slide on the running presentation. This might be the next animation on the current250 Triggers the next effect of slide on the running presentation. This might be the next animation on the current
251 slide, or the next slide251 slide, or the next slide.
252 Returns True if we stepped beyond the slides of the presentation
252 """253 """
253 pass254 return False
254255
255 def previous_step(self):256 def previous_step(self):
256 """257 """
257 Triggers the previous slide on the running presentation258 Triggers the previous slide on the running presentation
259 Returns True if we stepped beyond the slides of the presentation
258 """260 """
259 pass261 return False
260262
261 def convert_thumbnail(self, image_path, index):263 def convert_thumbnail(self, image_path, index):
262 """264 """