Merge lp:~tomasgroth/openlp/ppt-fixes into lp:openlp

Proposed by Tomas Groth
Status: Merged
Approved by: Raoul Snyman
Approved revision: 2529
Merged at revision: 2527
Proposed branch: lp:~tomasgroth/openlp/ppt-fixes
Merge into: lp:openlp
Diff against target: 553 lines (+180/-65)
6 files modified
openlp/plugins/presentations/lib/impresscontroller.py (+1/-1)
openlp/plugins/presentations/lib/messagelistener.py (+2/-2)
openlp/plugins/presentations/lib/powerpointcontroller.py (+123/-60)
openlp/plugins/presentations/lib/presentationtab.py (+25/-0)
openlp/plugins/presentations/presentationplugin.py (+2/-1)
tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py (+27/-1)
To merge this branch: bzr merge lp:~tomasgroth/openlp/ppt-fixes
Reviewer Review Type Date Requested Status
Raoul Snyman Approve
Tim Bentley Approve
Review via email: mp+255050@code.launchpad.net

Description of the change

Take focus back if Powerpoint steals it - fixes bug 1423913.
Optionally advance a Powerpoint slides animation when clicked in the slidecontroller - fixes bug 1194847.
Made OpenLP respect hidden slides. Improved logging in case of errors.
For Impress, go to previous effect instead of the previous slide.

To post a comment you must log in.
Revision history for this message
Tomas Groth (tomasgroth) wrote :

lp:~tomasgroth/openlp/ppt-fixes (revision 2529)
[SUCCESS] https//ci.openlp.io/job/Branch-01-Pull/999/
[SUCCESS] https//ci.openlp.io/job/Branch-02-Functional-Tests/922/
[SUCCESS] https//ci.openlp.io/job/Branch-03-Interface-Tests/864/
[SUCCESS] https//ci.openlp.io/job/Branch-04a-Windows_Functional_Tests/753/
[SUCCESS] https//ci.openlp.io/job/Branch-04b-Windows_Interface_Tests/352/
[SUCCESS] https//ci.openlp.io/job/Branch-05a-Code_Analysis/487/
[SUCCESS] https//ci.openlp.io/job/Branch-05b-Test_Coverage/358/

Revision history for this message
Tim Bentley (trb143) wrote :

Looks OK

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

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'openlp/plugins/presentations/lib/impresscontroller.py'
--- openlp/plugins/presentations/lib/impresscontroller.py 2015-01-18 13:39:21 +0000
+++ openlp/plugins/presentations/lib/impresscontroller.py 2015-04-02 08:41:26 +0000
@@ -428,7 +428,7 @@
428 """428 """
429 Triggers the previous slide on the running presentation.429 Triggers the previous slide on the running presentation.
430 """430 """
431 self.control.gotoPreviousSlide()431 self.control.gotoPreviousEffect()
432432
433 def get_slide_text(self, slide_no):433 def get_slide_text(self, slide_no):
434 """434 """
435435
=== modified file 'openlp/plugins/presentations/lib/messagelistener.py'
--- openlp/plugins/presentations/lib/messagelistener.py 2015-01-18 13:39:21 +0000
+++ openlp/plugins/presentations/lib/messagelistener.py 2015-04-02 08:41:26 +0000
@@ -93,7 +93,7 @@
93 return True93 return True
94 if not self.doc.is_loaded():94 if not self.doc.is_loaded():
95 if not self.doc.load_presentation():95 if not self.doc.load_presentation():
96 log.warning('Failed to activate %s' % self.doc.filepath)96 log.warning('Failed to activate %s' % self.doc.file_path)
97 return False97 return False
98 if self.is_live:98 if self.is_live:
99 self.doc.start_presentation()99 self.doc.start_presentation()
@@ -104,7 +104,7 @@
104 if self.doc.is_active():104 if self.doc.is_active():
105 return True105 return True
106 else:106 else:
107 log.warning('Failed to activate %s' % self.doc.filepath)107 log.warning('Failed to activate %s' % self.doc.file_path)
108 return False108 return False
109109
110 def slide(self, slide):110 def slide(self, slide):
111111
=== modified file 'openlp/plugins/presentations/lib/powerpointcontroller.py'
--- openlp/plugins/presentations/lib/powerpointcontroller.py 2015-01-18 13:39:21 +0000
+++ openlp/plugins/presentations/lib/powerpointcontroller.py 2015-04-02 08:41:26 +0000
@@ -22,11 +22,14 @@
22"""22"""
23This module is for controlling powerpoint. PPT API documentation:23This module is for controlling powerpoint. PPT API documentation:
24`http://msdn.microsoft.com/en-us/library/aa269321(office.10).aspx`_24`http://msdn.microsoft.com/en-us/library/aa269321(office.10).aspx`_
252010: https://msdn.microsoft.com/en-us/library/office/ff743835%28v=office.14%29.aspx
262013: https://msdn.microsoft.com/en-us/library/office/ff743835.aspx
25"""27"""
26import os28import os
27import logging29import logging
30import time
2831
29from openlp.core.common import is_win32from openlp.core.common import is_win, Settings
3033
31if is_win():34if is_win():
32 from win32com.client import Dispatch35 from win32com.client import Dispatch
@@ -36,9 +39,8 @@
36 import pywintypes39 import pywintypes
3740
38from openlp.core.lib import ScreenList41from openlp.core.lib import ScreenList
39from openlp.core.common import Registry
40from openlp.core.lib.ui import UiStrings, critical_error_message_box, translate42from openlp.core.lib.ui import UiStrings, critical_error_message_box, translate
41from openlp.core.common import trace_error_handler43from openlp.core.common import trace_error_handler, Registry
42from .presentationcontroller import PresentationController, PresentationDocument44from .presentationcontroller import PresentationController, PresentationDocument
4345
44log = logging.getLogger(__name__)46log = logging.getLogger(__name__)
@@ -82,6 +84,7 @@
82 if not self.process:84 if not self.process:
83 self.process = Dispatch('PowerPoint.Application')85 self.process = Dispatch('PowerPoint.Application')
84 self.process.Visible = True86 self.process.Visible = True
87 # ppWindowMinimized = 2
85 self.process.WindowState = 288 self.process.WindowState = 2
8689
87 def kill(self):90 def kill(self):
@@ -97,8 +100,10 @@
97 if self.process.Presentations.Count > 0:100 if self.process.Presentations.Count > 0:
98 return101 return
99 self.process.Quit()102 self.process.Quit()
100 except (AttributeError, pywintypes.com_error):103 except (AttributeError, pywintypes.com_error) as e:
101 pass104 log.exception('Exception caught while killing powerpoint process')
105 log.exception(e)
106 trace_error_handler(log)
102 self.process = None107 self.process = None
103108
104109
@@ -117,6 +122,8 @@
117 log.debug('Init Presentation Powerpoint')122 log.debug('Init Presentation Powerpoint')
118 super(PowerpointDocument, self).__init__(controller, presentation)123 super(PowerpointDocument, self).__init__(controller, presentation)
119 self.presentation = None124 self.presentation = None
125 self.index_map = {}
126 self.slide_count = 0
120127
121 def load_presentation(self):128 def load_presentation(self):
122 """129 """
@@ -131,16 +138,21 @@
131 self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count)138 self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count)
132 self.create_thumbnails()139 self.create_thumbnails()
133 self.create_titles_and_notes()140 self.create_titles_and_notes()
134 # Powerpoint 2013 pops up when loading a file, so we minimize it again141 # Powerpoint 2010 and 2013 pops up when loading a file, so we minimize it again
135 if self.presentation.Application.Version == u'15.0':142 if float(self.presentation.Application.Version) >= 14.0:
136 try:143 try:
144 # ppWindowMinimized = 2
137 self.presentation.Application.WindowState = 2145 self.presentation.Application.WindowState = 2
138 except:146 except (AttributeError, pywintypes.com_error) as e:
139 log.error('Failed to minimize main powerpoint window')147 log.exception('Failed to minimize main powerpoint window')
148 log.exception(e)
140 trace_error_handler(log)149 trace_error_handler(log)
150 # Make sure powerpoint doesn't steal focus
151 Registry().get('main_window').activateWindow()
141 return True152 return True
142 except pywintypes.com_error:153 except (AttributeError, pywintypes.com_error) as e:
143 log.error('PPT open failed')154 log.exception('Exception caught while loading Powerpoint presentation')
155 log.exception(e)
144 trace_error_handler(log)156 trace_error_handler(log)
145 return False157 return False
146158
@@ -158,9 +170,14 @@
158 log.debug('create_thumbnails')170 log.debug('create_thumbnails')
159 if self.check_thumbnails():171 if self.check_thumbnails():
160 return172 return
173 key = 1
161 for num in range(self.presentation.Slides.Count):174 for num in range(self.presentation.Slides.Count):
162 self.presentation.Slides(num + 1).Export(175 if not self.presentation.Slides(num + 1).SlideShowTransition.Hidden:
163 os.path.join(self.get_thumbnail_folder(), 'slide%d.png' % (num + 1)), 'png', 320, 240)176 self.index_map[key] = num + 1
177 self.presentation.Slides(num + 1).Export(
178 os.path.join(self.get_thumbnail_folder(), 'slide%d.png' % (key)), 'png', 320, 240)
179 key += 1
180 self.slide_count = key - 1
164181
165 def close_presentation(self):182 def close_presentation(self):
166 """183 """
@@ -171,10 +188,14 @@
171 if self.presentation:188 if self.presentation:
172 try:189 try:
173 self.presentation.Close()190 self.presentation.Close()
174 except pywintypes.com_error:191 except (AttributeError, pywintypes.com_error) as e:
175 pass192 log.exception('Caught exception while closing powerpoint presentation')
193 log.exception(e)
194 trace_error_handler(log)
176 self.presentation = None195 self.presentation = None
177 self.controller.remove_doc(self)196 self.controller.remove_doc(self)
197 # Make sure powerpoint doesn't steal focus
198 Registry().get('main_window').activateWindow()
178199
179 def is_loaded(self):200 def is_loaded(self):
180 """201 """
@@ -188,7 +209,10 @@
188 return False209 return False
189 if self.controller.process.Presentations.Count == 0:210 if self.controller.process.Presentations.Count == 0:
190 return False211 return False
191 except (AttributeError, pywintypes.com_error):212 except (AttributeError, pywintypes.com_error) as e:
213 log.exception('Caught exception while in is_loaded')
214 log.exception(e)
215 trace_error_handler(log)
192 return False216 return False
193 return True217 return True
194218
@@ -204,7 +228,10 @@
204 return False228 return False
205 if self.presentation.SlideShowWindow.View is None:229 if self.presentation.SlideShowWindow.View is None:
206 return False230 return False
207 except (AttributeError, pywintypes.com_error):231 except (AttributeError, pywintypes.com_error) as e:
232 log.exception('Caught exception while in is_active')
233 log.exception(e)
234 trace_error_handler(log)
208 return False235 return False
209 return True236 return True
210237
@@ -215,19 +242,21 @@
215 log.debug('unblank_screen')242 log.debug('unblank_screen')
216 try:243 try:
217 self.presentation.SlideShowSettings.Run()244 self.presentation.SlideShowSettings.Run()
245 # ppSlideShowRunning = 1
218 self.presentation.SlideShowWindow.View.State = 1246 self.presentation.SlideShowWindow.View.State = 1
219 self.presentation.SlideShowWindow.Activate()247 self.presentation.SlideShowWindow.Activate()
220 if self.presentation.Application.Version == '14.0':248 # Unblanking is broken in PowerPoint 2010 and 2013, need to redisplay
221 # Unblanking is broken in PowerPoint 2010, need to redisplay249 if float(self.presentation.Application.Version) >= 14.0:
222 slide = self.presentation.SlideShowWindow.View.CurrentShowPosition250 self.presentation.SlideShowWindow.View.GotoSlide(self.blank_slide, False)
223 click = self.presentation.SlideShowWindow.View.GetClickIndex()251 if self.blank_click:
224 self.presentation.SlideShowWindow.View.GotoSlide(slide)252 self.presentation.SlideShowWindow.View.GotoClick(self.blank_click)
225 if click:253 except (AttributeError, pywintypes.com_error) as e:
226 self.presentation.SlideShowWindow.View.GotoClick(click)254 log.exception('Caught exception while in unblank_screen')
227 except pywintypes.com_error:255 log.exception(e)
228 log.error('COM error while in unblank_screen')
229 trace_error_handler(log)256 trace_error_handler(log)
230 self.show_error_msg()257 self.show_error_msg()
258 # Make sure powerpoint doesn't steal focus
259 Registry().get('main_window').activateWindow()
231260
232 def blank_screen(self):261 def blank_screen(self):
233 """262 """
@@ -235,9 +264,15 @@
235 """264 """
236 log.debug('blank_screen')265 log.debug('blank_screen')
237 try:266 try:
267 # Unblanking is broken in PowerPoint 2010 and 2013, need to save info for later
268 if float(self.presentation.Application.Version) >= 14.0:
269 self.blank_slide = self.get_slide_number()
270 self.blank_click = self.presentation.SlideShowWindow.View.GetClickIndex()
271 # ppSlideShowBlackScreen = 3
238 self.presentation.SlideShowWindow.View.State = 3272 self.presentation.SlideShowWindow.View.State = 3
239 except pywintypes.com_error:273 except (AttributeError, pywintypes.com_error) as e:
240 log.error('COM error while in blank_screen')274 log.exception('Caught exception while in blank_screen')
275 log.exception(e)
241 trace_error_handler(log)276 trace_error_handler(log)
242 self.show_error_msg()277 self.show_error_msg()
243278
@@ -248,9 +283,11 @@
248 log.debug('is_blank')283 log.debug('is_blank')
249 if self.is_active():284 if self.is_active():
250 try:285 try:
286 # ppSlideShowBlackScreen = 3
251 return self.presentation.SlideShowWindow.View.State == 3287 return self.presentation.SlideShowWindow.View.State == 3
252 except pywintypes.com_error:288 except (AttributeError, pywintypes.com_error) as e:
253 log.error('COM error while in is_blank')289 log.exception('Caught exception while in is_blank')
290 log.exception(e)
254 trace_error_handler(log)291 trace_error_handler(log)
255 self.show_error_msg()292 self.show_error_msg()
256 else:293 else:
@@ -263,8 +300,9 @@
263 log.debug('stop_presentation')300 log.debug('stop_presentation')
264 try:301 try:
265 self.presentation.SlideShowWindow.View.Exit()302 self.presentation.SlideShowWindow.View.Exit()
266 except pywintypes.com_error:303 except (AttributeError, pywintypes.com_error) as e:
267 log.error('COM error while in stop_presentation')304 log.exception('Caught exception while in stop_presentation')
305 log.exception(e)
268 trace_error_handler(log)306 trace_error_handler(log)
269 self.show_error_msg()307 self.show_error_msg()
270308
@@ -292,15 +330,19 @@
292 ppt_window.Left = size.x() * 72 / dpi330 ppt_window.Left = size.x() * 72 / dpi
293 ppt_window.Width = size.width() * 72 / dpi331 ppt_window.Width = size.width() * 72 / dpi
294 except AttributeError as e:332 except AttributeError as e:
295 log.error('AttributeError while in start_presentation')333 log.exception('AttributeError while in start_presentation')
296 log.error(e)334 log.exception(e)
297 # Powerpoint 2013 pops up when starting a file, so we minimize it again335 # Powerpoint 2010 and 2013 pops up when starting a file, so we minimize it again
298 if self.presentation.Application.Version == u'15.0':336 if float(self.presentation.Application.Version) >= 14.0:
299 try:337 try:
338 # ppWindowMinimized = 2
300 self.presentation.Application.WindowState = 2339 self.presentation.Application.WindowState = 2
301 except:340 except (AttributeError, pywintypes.com_error) as e:
302 log.error('Failed to minimize main powerpoint window')341 log.exception('Failed to minimize main powerpoint window')
342 log.exception(e)
303 trace_error_handler(log)343 trace_error_handler(log)
344 # Make sure powerpoint doesn't steal focus
345 Registry().get('main_window').activateWindow()
304346
305 def get_slide_number(self):347 def get_slide_number(self):
306 """348 """
@@ -309,9 +351,19 @@
309 log.debug('get_slide_number')351 log.debug('get_slide_number')
310 ret = 0352 ret = 0
311 try:353 try:
312 ret = self.presentation.SlideShowWindow.View.CurrentShowPosition354 # We need 2 approaches to getting the current slide number, because
313 except pywintypes.com_error:355 # SlideShowWindow.View.Slide.SlideIndex wont work on the end-slide where Slide isn't available, and
314 log.error('COM error while in get_slide_number')356 # SlideShowWindow.View.CurrentShowPosition returns 0 when called when a transistion is executing (in 2013)
357 # So we use SlideShowWindow.View.Slide.SlideIndex unless the state is done (ppSlideShowDone = 5)
358 if self.presentation.SlideShowWindow.View.State != 5:
359 ret = self.presentation.SlideShowWindow.View.Slide.SlideNumber
360 # Do reverse lookup in the index_map to find the slide number to return
361 ret = next((key for key, slidenum in self.index_map.items() if slidenum == ret), None)
362 else:
363 ret = self.presentation.SlideShowWindow.View.CurrentShowPosition
364 except (AttributeError, pywintypes.com_error) as e:
365 log.exception('Caught exception while in get_slide_number')
366 log.exception(e)
315 trace_error_handler(log)367 trace_error_handler(log)
316 self.show_error_msg()368 self.show_error_msg()
317 return ret369 return ret
@@ -321,14 +373,7 @@
321 Returns total number of slides.373 Returns total number of slides.
322 """374 """
323 log.debug('get_slide_count')375 log.debug('get_slide_count')
324 ret = 0376 return self.slide_count
325 try:
326 ret = self.presentation.Slides.Count
327 except pywintypes.com_error:
328 log.error('COM error while in get_slide_count')
329 trace_error_handler(log)
330 self.show_error_msg()
331 return ret
332377
333 def goto_slide(self, slide_no):378 def goto_slide(self, slide_no):
334 """379 """
@@ -338,9 +383,19 @@
338 """383 """
339 log.debug('goto_slide')384 log.debug('goto_slide')
340 try:385 try:
341 self.presentation.SlideShowWindow.View.GotoSlide(slide_no)386 if Settings().value('presentations/powerpoint slide click advance') \
342 except pywintypes.com_error:387 and self.get_slide_number() == self.index_map[slide_no]:
343 log.error('COM error while in goto_slide')388 click_index = self.presentation.SlideShowWindow.View.GetClickIndex()
389 click_count = self.presentation.SlideShowWindow.View.GetClickCount()
390 log.debug('We are already on this slide - go to next effect if any left, idx: %d, count: %d'
391 % (click_index, click_count))
392 if click_index < click_count:
393 self.next_step()
394 else:
395 self.presentation.SlideShowWindow.View.GotoSlide(self.index_map[slide_no])
396 except (AttributeError, pywintypes.com_error) as e:
397 log.exception('Caught exception while in goto_slide')
398 log.exception(e)
344 trace_error_handler(log)399 trace_error_handler(log)
345 self.show_error_msg()400 self.show_error_msg()
346401
@@ -351,12 +406,14 @@
351 log.debug('next_step')406 log.debug('next_step')
352 try:407 try:
353 self.presentation.SlideShowWindow.View.Next()408 self.presentation.SlideShowWindow.View.Next()
354 except pywintypes.com_error:409 except (AttributeError, pywintypes.com_error) as e:
355 log.error('COM error while in next_step')410 log.exception('Caught exception while in next_step')
411 log.exception(e)
356 trace_error_handler(log)412 trace_error_handler(log)
357 self.show_error_msg()413 self.show_error_msg()
358 return414 return
359 if self.get_slide_number() > self.get_slide_count():415 if self.get_slide_number() > self.get_slide_count():
416 log.debug('past end, stepping back to previous')
360 self.previous_step()417 self.previous_step()
361418
362 def previous_step(self):419 def previous_step(self):
@@ -366,8 +423,9 @@
366 log.debug('previous_step')423 log.debug('previous_step')
367 try:424 try:
368 self.presentation.SlideShowWindow.View.Previous()425 self.presentation.SlideShowWindow.View.Previous()
369 except pywintypes.com_error:426 except (AttributeError, pywintypes.com_error) as e:
370 log.error('COM error while in previous_step')427 log.exception('Caught exception while in previous_step')
428 log.exception(e)
371 trace_error_handler(log)429 trace_error_handler(log)
372 self.show_error_msg()430 self.show_error_msg()
373431
@@ -377,7 +435,7 @@
377435
378 :param slide_no: The slide the text is required for, starting at 1436 :param slide_no: The slide the text is required for, starting at 1
379 """437 """
380 return _get_text_from_shapes(self.presentation.Slides(slide_no).Shapes)438 return _get_text_from_shapes(self.presentation.Slides(self.index_map[slide_no]).Shapes)
381439
382 def get_slide_notes(self, slide_no):440 def get_slide_notes(self, slide_no):
383 """441 """
@@ -385,7 +443,7 @@
385443
386 :param slide_no: The slide the text is required for, starting at 1444 :param slide_no: The slide the text is required for, starting at 1
387 """445 """
388 return _get_text_from_shapes(self.presentation.Slides(slide_no).NotesPage.Shapes)446 return _get_text_from_shapes(self.presentation.Slides(self.index_map[slide_no]).NotesPage.Shapes)
389447
390 def create_titles_and_notes(self):448 def create_titles_and_notes(self):
391 """449 """
@@ -396,7 +454,8 @@
396 """454 """
397 titles = []455 titles = []
398 notes = []456 notes = []
399 for slide in self.presentation.Slides:457 for num in range(self.get_slide_count()):
458 slide = self.presentation.Slides(self.index_map[num + 1])
400 try:459 try:
401 text = slide.Shapes.Title.TextFrame.TextRange.Text460 text = slide.Shapes.Title.TextFrame.TextRange.Text
402 except Exception as e:461 except Exception as e:
@@ -413,7 +472,11 @@
413 """472 """
414 Stop presentation and display an error message.473 Stop presentation and display an error message.
415 """474 """
416 self.stop_presentation()475 try:
476 self.presentation.SlideShowWindow.View.Exit()
477 except (AttributeError, pywintypes.com_error) as e:
478 log.exception('Failed to exit Powerpoint presentation after error')
479 log.exception(e)
417 critical_error_message_box(UiStrings().Error, translate('PresentationPlugin.PowerpointDocument',480 critical_error_message_box(UiStrings().Error, translate('PresentationPlugin.PowerpointDocument',
418 'An error occurred in the Powerpoint integration '481 'An error occurred in the Powerpoint integration '
419 'and the presentation will be stopped. '482 'and the presentation will be stopped. '
420483
=== modified file 'openlp/plugins/presentations/lib/presentationtab.py'
--- openlp/plugins/presentations/lib/presentationtab.py 2015-01-18 13:39:21 +0000
+++ openlp/plugins/presentations/lib/presentationtab.py 2015-04-02 08:41:26 +0000
@@ -68,6 +68,15 @@
68 self.override_app_check_box.setObjectName('override_app_check_box')68 self.override_app_check_box.setObjectName('override_app_check_box')
69 self.advanced_layout.addWidget(self.override_app_check_box)69 self.advanced_layout.addWidget(self.override_app_check_box)
70 self.left_layout.addWidget(self.advanced_group_box)70 self.left_layout.addWidget(self.advanced_group_box)
71 # PowerPoint
72 self.powerpoint_group_box = QtGui.QGroupBox(self.left_column)
73 self.powerpoint_group_box.setObjectName('powerpoint_group_box')
74 self.powerpoint_layout = QtGui.QVBoxLayout(self.powerpoint_group_box)
75 self.powerpoint_layout.setObjectName('powerpoint_layout')
76 self.ppt_slide_click_check_box = QtGui.QCheckBox(self.powerpoint_group_box)
77 self.powerpoint_group_box.setObjectName('ppt_slide_click_check_box')
78 self.powerpoint_layout.addWidget(self.ppt_slide_click_check_box)
79 self.left_layout.addWidget(self.powerpoint_group_box)
71 # Pdf options80 # Pdf options
72 self.pdf_group_box = QtGui.QGroupBox(self.left_column)81 self.pdf_group_box = QtGui.QGroupBox(self.left_column)
73 self.pdf_group_box.setObjectName('pdf_group_box')82 self.pdf_group_box.setObjectName('pdf_group_box')
@@ -108,8 +117,12 @@
108 self.set_controller_text(checkbox, controller)117 self.set_controller_text(checkbox, controller)
109 self.advanced_group_box.setTitle(UiStrings().Advanced)118 self.advanced_group_box.setTitle(UiStrings().Advanced)
110 self.pdf_group_box.setTitle(translate('PresentationPlugin.PresentationTab', 'PDF options'))119 self.pdf_group_box.setTitle(translate('PresentationPlugin.PresentationTab', 'PDF options'))
120 self.powerpoint_group_box.setTitle(translate('PresentationPlugin.PresentationTab', 'PowerPoint options'))
111 self.override_app_check_box.setText(121 self.override_app_check_box.setText(
112 translate('PresentationPlugin.PresentationTab', 'Allow presentation application to be overridden'))122 translate('PresentationPlugin.PresentationTab', 'Allow presentation application to be overridden'))
123 self.ppt_slide_click_check_box.setText(
124 translate('PresentationPlugin.PresentationTab',
125 'Clicking on a selected slide in the slidecontroller advances to next effect.'))
113 self.pdf_program_check_box.setText(126 self.pdf_program_check_box.setText(
114 translate('PresentationPlugin.PresentationTab', 'Use given full path for mudraw or ghostscript binary:'))127 translate('PresentationPlugin.PresentationTab', 'Use given full path for mudraw or ghostscript binary:'))
115128
@@ -123,11 +136,18 @@
123 """136 """
124 Load the settings.137 Load the settings.
125 """138 """
139 powerpoint_available = False
126 for key in self.controllers:140 for key in self.controllers:
127 controller = self.controllers[key]141 controller = self.controllers[key]
128 checkbox = self.presenter_check_boxes[controller.name]142 checkbox = self.presenter_check_boxes[controller.name]
129 checkbox.setChecked(Settings().value(self.settings_section + '/' + controller.name))143 checkbox.setChecked(Settings().value(self.settings_section + '/' + controller.name))
144 if controller.name == 'Powerpoint' and controller.is_available():
145 powerpoint_available = True
130 self.override_app_check_box.setChecked(Settings().value(self.settings_section + '/override app'))146 self.override_app_check_box.setChecked(Settings().value(self.settings_section + '/override app'))
147 # Load Powerpoint settings
148 self.ppt_slide_click_check_box.setChecked(Settings().value(self.settings_section +
149 '/powerpoint slide click advance'))
150 self.ppt_slide_click_check_box.setEnabled(powerpoint_available)
131 # load pdf-program settings151 # load pdf-program settings
132 enable_pdf_program = Settings().value(self.settings_section + '/enable_pdf_program')152 enable_pdf_program = Settings().value(self.settings_section + '/enable_pdf_program')
133 self.pdf_program_check_box.setChecked(enable_pdf_program)153 self.pdf_program_check_box.setChecked(enable_pdf_program)
@@ -161,6 +181,11 @@
161 if Settings().value(setting_key) != self.override_app_check_box.checkState():181 if Settings().value(setting_key) != self.override_app_check_box.checkState():
162 Settings().setValue(setting_key, self.override_app_check_box.checkState())182 Settings().setValue(setting_key, self.override_app_check_box.checkState())
163 changed = True183 changed = True
184 # Save powerpoint settings
185 setting_key = self.settings_section + '/powerpoint slide click advance'
186 if Settings().value(setting_key) != self.ppt_slide_click_check_box.checkState():
187 Settings().setValue(setting_key, self.ppt_slide_click_check_box.checkState())
188 changed = True
164 # Save pdf-settings189 # Save pdf-settings
165 pdf_program = self.pdf_program_path.text()190 pdf_program = self.pdf_program_path.text()
166 enable_pdf_program = self.pdf_program_check_box.checkState()191 enable_pdf_program = self.pdf_program_check_box.checkState()
167192
=== modified file 'openlp/plugins/presentations/presentationplugin.py'
--- openlp/plugins/presentations/presentationplugin.py 2015-03-10 22:00:32 +0000
+++ openlp/plugins/presentations/presentationplugin.py 2015-04-02 08:41:26 +0000
@@ -44,7 +44,8 @@
44 'presentations/Powerpoint Viewer': QtCore.Qt.Checked,44 'presentations/Powerpoint Viewer': QtCore.Qt.Checked,
45 'presentations/Pdf': QtCore.Qt.Checked,45 'presentations/Pdf': QtCore.Qt.Checked,
46 'presentations/presentations files': [],46 'presentations/presentations files': [],
47 'presentations/thumbnail_scheme': ''47 'presentations/thumbnail_scheme': '',
48 'presentations/powerpoint slide click advance': QtCore.Qt.Unchecked
48 }49 }
4950
5051
5152
=== modified file 'tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py'
--- tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py 2015-01-18 13:39:21 +0000
+++ tests/functional/openlp_plugins/presentations/test_powerpointcontroller.py 2015-04-02 08:41:26 +0000
@@ -33,11 +33,15 @@
3333
34from openlp.plugins.presentations.lib.powerpointcontroller import PowerpointController, PowerpointDocument,\34from openlp.plugins.presentations.lib.powerpointcontroller import PowerpointController, PowerpointDocument,\
35 _get_text_from_shapes35 _get_text_from_shapes
36from openlp.core.common import is_win36from openlp.core.common import is_win, Settings
3737
38if is_win():38if is_win():
39 import pywintypes39 import pywintypes
4040
41__default_settings__ = {
42 'presentations/powerpoint slide click advance': True
43}
44
4145
42class TestPowerpointController(TestCase, TestMixin):46class TestPowerpointController(TestCase, TestMixin):
43 """47 """
@@ -104,6 +108,7 @@
104 self.mock_presentation_document_get_temp_folder.return_value = 'temp folder'108 self.mock_presentation_document_get_temp_folder.return_value = 'temp folder'
105 self.file_name = os.path.join(TEST_RESOURCES_PATH, 'presentations', 'test.pptx')109 self.file_name = os.path.join(TEST_RESOURCES_PATH, 'presentations', 'test.pptx')
106 self.real_controller = PowerpointController(self.mock_plugin)110 self.real_controller = PowerpointController(self.mock_plugin)
111 Settings().extend_default_settings(__default_settings__)
107112
108 def tearDown(self):113 def tearDown(self):
109 """114 """
@@ -126,6 +131,7 @@
126 instance = PowerpointDocument(self.mock_controller, self.mock_presentation)131 instance = PowerpointDocument(self.mock_controller, self.mock_presentation)
127 instance.presentation = MagicMock()132 instance.presentation = MagicMock()
128 instance.presentation.SlideShowWindow.View.GotoSlide = MagicMock(side_effect=pywintypes.com_error('1'))133 instance.presentation.SlideShowWindow.View.GotoSlide = MagicMock(side_effect=pywintypes.com_error('1'))
134 instance.index_map[42] = 42
129135
130 # WHEN: Calling goto_slide which will throw an exception136 # WHEN: Calling goto_slide which will throw an exception
131 instance.goto_slide(42)137 instance.goto_slide(42)
@@ -227,3 +233,23 @@
227233
228 # THEN: it should not fail but return empty string234 # THEN: it should not fail but return empty string
229 self.assertEqual(result, '', 'result should be empty')235 self.assertEqual(result, '', 'result should be empty')
236
237 def goto_slide_test(self):
238 """
239 Test that goto_slide goes to next effect if the slide is already displayed
240 """
241 # GIVEN: A Document with mocked controller, presentation, and mocked functions get_slide_number and next_step
242 doc = PowerpointDocument(self.mock_controller, self.mock_presentation)
243 doc.presentation = MagicMock()
244 doc.presentation.SlideShowWindow.View.GetClickIndex.return_value = 1
245 doc.presentation.SlideShowWindow.View.GetClickCount.return_value = 2
246 doc.get_slide_number = MagicMock()
247 doc.get_slide_number.return_value = 1
248 doc.next_step = MagicMock()
249 doc.index_map[1] = 1
250
251 # WHEN: Calling goto_slide
252 doc.goto_slide(1)
253
254 # THEN: next_step() should be call to try to advance to the next effect.
255 self.assertTrue(doc.next_step.called, 'next_step() should have been called!')