Merge lp:~tomasgroth/openlp/presentation-beyond-last into lp:openlp
- presentation-beyond-last
- Merge into trunk
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 |
Related bugs: |
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.
Description of the change
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 | # |
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:/
- 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
1 | === modified file 'openlp/core/common/registry.py' | |||
2 | --- openlp/core/common/registry.py 2019-04-13 13:00:22 +0000 | |||
3 | +++ openlp/core/common/registry.py 2019-05-24 19:21:37 +0000 | |||
4 | @@ -146,7 +146,7 @@ | |||
5 | 146 | try: | 146 | try: |
6 | 147 | log.debug('Running function {} for {}'.format(function, event)) | 147 | log.debug('Running function {} for {}'.format(function, event)) |
7 | 148 | result = function(*args, **kwargs) | 148 | result = function(*args, **kwargs) |
9 | 149 | if result: | 149 | if result is not None: |
10 | 150 | results.append(result) | 150 | results.append(result) |
11 | 151 | except TypeError: | 151 | except TypeError: |
12 | 152 | # Who has called me can help in debugging | 152 | # Who has called me can help in debugging |
13 | 153 | 153 | ||
14 | === modified file 'openlp/core/ui/servicemanager.py' | |||
15 | --- openlp/core/ui/servicemanager.py 2019-05-24 18:50:51 +0000 | |||
16 | +++ openlp/core/ui/servicemanager.py 2019-05-24 19:21:37 +0000 | |||
17 | @@ -976,8 +976,10 @@ | |||
18 | 976 | prev_item_last_slide = None | 976 | prev_item_last_slide = None |
19 | 977 | service_iterator = QtWidgets.QTreeWidgetItemIterator(self.service_manager_list) | 977 | service_iterator = QtWidgets.QTreeWidgetItemIterator(self.service_manager_list) |
20 | 978 | while service_iterator.value(): | 978 | while service_iterator.value(): |
21 | 979 | # Found the selected/current service item | ||
22 | 979 | if service_iterator.value() == selected: | 980 | if service_iterator.value() == selected: |
23 | 980 | if last_slide and prev_item_last_slide: | 981 | if last_slide and prev_item_last_slide: |
24 | 982 | # Go to the last slide of the previous service item | ||
25 | 981 | pos = prev_item.data(0, QtCore.Qt.UserRole) | 983 | pos = prev_item.data(0, QtCore.Qt.UserRole) |
26 | 982 | check_expanded = self.service_items[pos - 1]['expanded'] | 984 | check_expanded = self.service_items[pos - 1]['expanded'] |
27 | 983 | self.service_manager_list.setCurrentItem(prev_item_last_slide) | 985 | self.service_manager_list.setCurrentItem(prev_item_last_slide) |
28 | @@ -986,13 +988,17 @@ | |||
29 | 986 | self.make_live() | 988 | self.make_live() |
30 | 987 | self.service_manager_list.setCurrentItem(prev_item) | 989 | self.service_manager_list.setCurrentItem(prev_item) |
31 | 988 | elif prev_item: | 990 | elif prev_item: |
32 | 991 | # Go to the first slide of the previous service item | ||
33 | 989 | self.service_manager_list.setCurrentItem(prev_item) | 992 | self.service_manager_list.setCurrentItem(prev_item) |
34 | 990 | self.make_live() | 993 | self.make_live() |
35 | 991 | return | 994 | return |
36 | 995 | # Found the previous service item root | ||
37 | 992 | if service_iterator.value().parent() is None: | 996 | if service_iterator.value().parent() is None: |
38 | 993 | prev_item = service_iterator.value() | 997 | prev_item = service_iterator.value() |
39 | 998 | # Found the last slide of the previous item | ||
40 | 994 | if service_iterator.value().parent() is prev_item: | 999 | if service_iterator.value().parent() is prev_item: |
41 | 995 | prev_item_last_slide = service_iterator.value() | 1000 | prev_item_last_slide = service_iterator.value() |
42 | 1001 | # Go to next item in the tree | ||
43 | 996 | service_iterator += 1 | 1002 | service_iterator += 1 |
44 | 997 | 1003 | ||
45 | 998 | def on_set_item(self, message): | 1004 | def on_set_item(self, message): |
46 | 999 | 1005 | ||
47 | === modified file 'openlp/core/ui/slidecontroller.py' | |||
48 | --- openlp/core/ui/slidecontroller.py 2019-05-22 06:47:00 +0000 | |||
49 | +++ openlp/core/ui/slidecontroller.py 2019-05-24 19:21:37 +0000 | |||
50 | @@ -1261,9 +1261,18 @@ | |||
51 | 1261 | if not self.service_item: | 1261 | if not self.service_item: |
52 | 1262 | return | 1262 | return |
53 | 1263 | if self.service_item.is_command(): | 1263 | if self.service_item.is_command(): |
57 | 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()), |
58 | 1265 | [self.service_item, self.is_live]) | 1265 | [self.service_item, self.is_live]) |
59 | 1266 | if self.is_live: | 1266 | # Check if we have gone past the end of the last slide |
60 | 1267 | if self.is_live and past_end and past_end[0]: | ||
61 | 1268 | if wrap is None: | ||
62 | 1269 | if self.slide_limits == SlideLimits.Wrap: | ||
63 | 1270 | self.on_slide_selected_index([0]) | ||
64 | 1271 | elif self.is_live and self.slide_limits == SlideLimits.Next: | ||
65 | 1272 | self.service_next() | ||
66 | 1273 | elif wrap: | ||
67 | 1274 | self.on_slide_selected_index([0]) | ||
68 | 1275 | elif self.is_live: | ||
69 | 1267 | self.update_preview() | 1276 | self.update_preview() |
70 | 1268 | else: | 1277 | else: |
71 | 1269 | row = self.preview_widget.current_slide_number() + 1 | 1278 | row = self.preview_widget.current_slide_number() + 1 |
72 | @@ -1290,9 +1299,16 @@ | |||
73 | 1290 | if not self.service_item: | 1299 | if not self.service_item: |
74 | 1291 | return | 1300 | return |
75 | 1292 | if self.service_item.is_command(): | 1301 | if self.service_item.is_command(): |
79 | 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()), |
80 | 1294 | [self.service_item, self.is_live]) | 1303 | [self.service_item, self.is_live]) |
81 | 1295 | if self.is_live: | 1304 | # Check id we have tried to go before that start slide |
82 | 1305 | if self.is_live and before_start and before_start[0]: | ||
83 | 1306 | if self.slide_limits == SlideLimits.Wrap: | ||
84 | 1307 | self.on_slide_selected_index([self.preview_widget.slide_count() - 1]) | ||
85 | 1308 | elif self.is_live and self.slide_limits == SlideLimits.Next: | ||
86 | 1309 | self.keypress_queue.append(ServiceItemAction.PreviousLastSlide) | ||
87 | 1310 | self._process_queue() | ||
88 | 1311 | elif self.is_live: | ||
89 | 1296 | self.update_preview() | 1312 | self.update_preview() |
90 | 1297 | else: | 1313 | else: |
91 | 1298 | row = self.preview_widget.current_slide_number() - 1 | 1314 | row = self.preview_widget.current_slide_number() - 1 |
92 | 1299 | 1315 | ||
93 | === modified file 'openlp/plugins/presentations/lib/impresscontroller.py' | |||
94 | --- openlp/plugins/presentations/lib/impresscontroller.py 2019-05-22 06:47:00 +0000 | |||
95 | +++ openlp/plugins/presentations/lib/impresscontroller.py 2019-05-24 19:21:37 +0000 | |||
96 | @@ -36,7 +36,7 @@ | |||
97 | 36 | 36 | ||
98 | 37 | from PyQt5 import QtCore | 37 | from PyQt5 import QtCore |
99 | 38 | 38 | ||
101 | 39 | from openlp.core.common import delete_file, get_uno_command, get_uno_instance, is_win | 39 | from openlp.core.common import delete_file, get_uno_command, get_uno_instance, is_win, trace_error_handler |
102 | 40 | from openlp.core.common.registry import Registry | 40 | from openlp.core.common.registry import Registry |
103 | 41 | from openlp.core.display.screens import ScreenList | 41 | from openlp.core.display.screens import ScreenList |
104 | 42 | from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument, \ | 42 | from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument, \ |
105 | @@ -47,15 +47,30 @@ | |||
106 | 47 | from win32com.client import Dispatch | 47 | from win32com.client import Dispatch |
107 | 48 | import pywintypes | 48 | import pywintypes |
108 | 49 | uno_available = False | 49 | uno_available = False |
109 | 50 | try: | ||
110 | 51 | service_manager = Dispatch('com.sun.star.ServiceManager') | ||
111 | 52 | service_manager._FlagAsMethod('Bridge_GetStruct') | ||
112 | 53 | XSlideShowListenerObj = service_manager.Bridge_GetStruct('com.sun.star.presentation.XSlideShowListener') | ||
113 | 54 | |||
114 | 55 | class SlideShowListenerImport(XSlideShowListenerObj.__class__): | ||
115 | 56 | pass | ||
116 | 57 | except (AttributeError, pywintypes.com_error): | ||
117 | 58 | class SlideShowListenerImport(): | ||
118 | 59 | pass | ||
119 | 60 | |||
120 | 50 | # Declare an empty exception to match the exception imported from UNO | 61 | # Declare an empty exception to match the exception imported from UNO |
121 | 51 | |||
122 | 52 | class ErrorCodeIOException(Exception): | 62 | class ErrorCodeIOException(Exception): |
123 | 53 | pass | 63 | pass |
124 | 54 | else: | 64 | else: |
125 | 55 | try: | 65 | try: |
126 | 56 | import uno | 66 | import uno |
127 | 67 | import unohelper | ||
128 | 57 | from com.sun.star.beans import PropertyValue | 68 | from com.sun.star.beans import PropertyValue |
129 | 58 | from com.sun.star.task import ErrorCodeIOException | 69 | from com.sun.star.task import ErrorCodeIOException |
130 | 70 | from com.sun.star.presentation import XSlideShowListener | ||
131 | 71 | |||
132 | 72 | class SlideShowListenerImport(unohelper.Base, XSlideShowListener): | ||
133 | 73 | pass | ||
134 | 59 | 74 | ||
135 | 60 | uno_available = True | 75 | uno_available = True |
136 | 61 | except ImportError: | 76 | except ImportError: |
137 | @@ -82,6 +97,8 @@ | |||
138 | 82 | self.process = None | 97 | self.process = None |
139 | 83 | self.desktop = None | 98 | self.desktop = None |
140 | 84 | self.manager = None | 99 | self.manager = None |
141 | 100 | self.conf_provider = None | ||
142 | 101 | self.presenter_screen_disabled_by_openlp = False | ||
143 | 85 | 102 | ||
144 | 86 | def check_available(self): | 103 | def check_available(self): |
145 | 87 | """ | 104 | """ |
146 | @@ -90,8 +107,7 @@ | |||
147 | 90 | log.debug('check_available') | 107 | log.debug('check_available') |
148 | 91 | if is_win(): | 108 | if is_win(): |
149 | 92 | return self.get_com_servicemanager() is not None | 109 | return self.get_com_servicemanager() is not None |
152 | 93 | else: | 110 | return uno_available |
151 | 94 | return uno_available | ||
153 | 95 | 111 | ||
154 | 96 | def start_process(self): | 112 | def start_process(self): |
155 | 97 | """ | 113 | """ |
156 | @@ -131,6 +147,7 @@ | |||
157 | 131 | self.manager = uno_instance.ServiceManager | 147 | self.manager = uno_instance.ServiceManager |
158 | 132 | log.debug('get UNO Desktop Openoffice - createInstanceWithContext - Desktop') | 148 | log.debug('get UNO Desktop Openoffice - createInstanceWithContext - Desktop') |
159 | 133 | desktop = self.manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance) | 149 | desktop = self.manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance) |
160 | 150 | self.toggle_presentation_screen(False) | ||
161 | 134 | return desktop | 151 | return desktop |
162 | 135 | except Exception: | 152 | except Exception: |
163 | 136 | log.warning('Failed to get UNO desktop') | 153 | log.warning('Failed to get UNO desktop') |
164 | @@ -148,6 +165,7 @@ | |||
165 | 148 | desktop = self.manager.createInstance('com.sun.star.frame.Desktop') | 165 | desktop = self.manager.createInstance('com.sun.star.frame.Desktop') |
166 | 149 | except (AttributeError, pywintypes.com_error): | 166 | except (AttributeError, pywintypes.com_error): |
167 | 150 | log.warning('Failure to find desktop - Impress may have closed') | 167 | log.warning('Failure to find desktop - Impress may have closed') |
168 | 168 | self.toggle_presentation_screen(False) | ||
169 | 151 | return desktop if desktop else None | 169 | return desktop if desktop else None |
170 | 152 | 170 | ||
171 | 153 | def get_com_servicemanager(self): | 171 | def get_com_servicemanager(self): |
172 | @@ -166,6 +184,8 @@ | |||
173 | 166 | Called at system exit to clean up any running presentations. | 184 | Called at system exit to clean up any running presentations. |
174 | 167 | """ | 185 | """ |
175 | 168 | log.debug('Kill OpenOffice') | 186 | log.debug('Kill OpenOffice') |
176 | 187 | if self.presenter_screen_disabled_by_openlp: | ||
177 | 188 | self._toggle_presentation_screen(True) | ||
178 | 169 | while self.docs: | 189 | while self.docs: |
179 | 170 | self.docs[0].close_presentation() | 190 | self.docs[0].close_presentation() |
180 | 171 | desktop = None | 191 | desktop = None |
181 | @@ -195,6 +215,54 @@ | |||
182 | 195 | except Exception: | 215 | except Exception: |
183 | 196 | log.warning('Failed to terminate OpenOffice') | 216 | log.warning('Failed to terminate OpenOffice') |
184 | 197 | 217 | ||
185 | 218 | def toggle_presentation_screen(self, target_value): | ||
186 | 219 | """ | ||
187 | 220 | Enable or disable the Presentation Screen/Console | ||
188 | 221 | """ | ||
189 | 222 | # Create Instance of ConfigurationProvider | ||
190 | 223 | if not self.conf_provider: | ||
191 | 224 | if is_win(): | ||
192 | 225 | self.conf_provider = self.manager.createInstance('com.sun.star.configuration.ConfigurationProvider') | ||
193 | 226 | else: | ||
194 | 227 | self.conf_provider = self.manager.createInstanceWithContext( | ||
195 | 228 | 'com.sun.star.configuration.ConfigurationProvider', uno.getComponentContext()) | ||
196 | 229 | # Setup lookup properties to get Impress settings | ||
197 | 230 | properties = [] | ||
198 | 231 | properties.append(self.create_property('nodepath', 'org.openoffice.Office.Impress')) | ||
199 | 232 | properties = tuple(properties) | ||
200 | 233 | try: | ||
201 | 234 | # Get an updateable configuration view | ||
202 | 235 | impress_conf_props = self.conf_provider.createInstanceWithArguments( | ||
203 | 236 | 'com.sun.star.configuration.ConfigurationUpdateAccess', properties) | ||
204 | 237 | # Get the specific setting for presentation screen | ||
205 | 238 | presenter_screen_enabled = impress_conf_props.getHierarchicalPropertyValue( | ||
206 | 239 | 'Misc/Start/EnablePresenterScreen') | ||
207 | 240 | # If the presentation screen is enabled we disable it | ||
208 | 241 | if presenter_screen_enabled != target_value: | ||
209 | 242 | impress_conf_props.setHierarchicalPropertyValue('Misc/Start/EnablePresenterScreen', target_value) | ||
210 | 243 | impress_conf_props.commitChanges() | ||
211 | 244 | # if target_value is False this is an attempt to disable the Presenter Screen | ||
212 | 245 | # so we make a note that it has been disabled, so it can be enabled again on close. | ||
213 | 246 | if target_value is False: | ||
214 | 247 | self.presenter_screen_disabled_by_openlp = True | ||
215 | 248 | except Exception as e: | ||
216 | 249 | log.exception(e) | ||
217 | 250 | trace_error_handler(log) | ||
218 | 251 | return | ||
219 | 252 | |||
220 | 253 | def create_property(self, name, value): | ||
221 | 254 | """ | ||
222 | 255 | Create an OOo style property object which are passed into some Uno methods. | ||
223 | 256 | """ | ||
224 | 257 | log.debug('create property OpenOffice') | ||
225 | 258 | if is_win(): | ||
226 | 259 | property_object = self.manager.Bridge_GetStruct('com.sun.star.beans.PropertyValue') | ||
227 | 260 | else: | ||
228 | 261 | property_object = PropertyValue() | ||
229 | 262 | property_object.Name = name | ||
230 | 263 | property_object.Value = value | ||
231 | 264 | return property_object | ||
232 | 265 | |||
233 | 198 | 266 | ||
234 | 199 | class ImpressDocument(PresentationDocument): | 267 | class ImpressDocument(PresentationDocument): |
235 | 200 | """ | 268 | """ |
236 | @@ -213,6 +281,8 @@ | |||
237 | 213 | self.document = None | 281 | self.document = None |
238 | 214 | self.presentation = None | 282 | self.presentation = None |
239 | 215 | self.control = None | 283 | self.control = None |
240 | 284 | self.slide_ended = False | ||
241 | 285 | self.slide_ended_reverse = False | ||
242 | 216 | 286 | ||
243 | 217 | def load_presentation(self): | 287 | def load_presentation(self): |
244 | 218 | """ | 288 | """ |
245 | @@ -233,13 +303,16 @@ | |||
246 | 233 | return False | 303 | return False |
247 | 234 | self.desktop = desktop | 304 | self.desktop = desktop |
248 | 235 | properties = [] | 305 | properties = [] |
250 | 236 | properties.append(self.create_property('Hidden', True)) | 306 | properties.append(self.controller.create_property('Hidden', True)) |
251 | 237 | properties = tuple(properties) | 307 | properties = tuple(properties) |
252 | 238 | try: | 308 | try: |
253 | 239 | self.document = desktop.loadComponentFromURL(url, '_blank', 0, properties) | 309 | self.document = desktop.loadComponentFromURL(url, '_blank', 0, properties) |
254 | 240 | except Exception: | 310 | except Exception: |
255 | 241 | log.warning('Failed to load presentation {url}'.format(url=url)) | 311 | log.warning('Failed to load presentation {url}'.format(url=url)) |
256 | 242 | return False | 312 | return False |
257 | 313 | if self.document is None: | ||
258 | 314 | log.warning('Presentation {url} could not be loaded'.format(url=url)) | ||
259 | 315 | return False | ||
260 | 243 | self.presentation = self.document.getPresentation() | 316 | self.presentation = self.document.getPresentation() |
261 | 244 | self.presentation.Display = ScreenList().current.number + 1 | 317 | self.presentation.Display = ScreenList().current.number + 1 |
262 | 245 | self.control = None | 318 | self.control = None |
263 | @@ -257,7 +330,7 @@ | |||
264 | 257 | temp_folder_path = self.get_temp_folder() | 330 | temp_folder_path = self.get_temp_folder() |
265 | 258 | thumb_dir_url = temp_folder_path.as_uri() | 331 | thumb_dir_url = temp_folder_path.as_uri() |
266 | 259 | properties = [] | 332 | properties = [] |
268 | 260 | properties.append(self.create_property('FilterName', 'impress_png_Export')) | 333 | properties.append(self.controller.create_property('FilterName', 'impress_png_Export')) |
269 | 261 | properties = tuple(properties) | 334 | properties = tuple(properties) |
270 | 262 | doc = self.document | 335 | doc = self.document |
271 | 263 | pages = doc.getDrawPages() | 336 | pages = doc.getDrawPages() |
272 | @@ -279,19 +352,6 @@ | |||
273 | 279 | except Exception: | 352 | except Exception: |
274 | 280 | log.exception('{path} - Unable to store openoffice preview'.format(path=path)) | 353 | log.exception('{path} - Unable to store openoffice preview'.format(path=path)) |
275 | 281 | 354 | ||
276 | 282 | def create_property(self, name, value): | ||
277 | 283 | """ | ||
278 | 284 | Create an OOo style property object which are passed into some Uno methods. | ||
279 | 285 | """ | ||
280 | 286 | log.debug('create property OpenOffice') | ||
281 | 287 | if is_win(): | ||
282 | 288 | property_object = self.controller.manager.Bridge_GetStruct('com.sun.star.beans.PropertyValue') | ||
283 | 289 | else: | ||
284 | 290 | property_object = PropertyValue() | ||
285 | 291 | property_object.Name = name | ||
286 | 292 | property_object.Value = value | ||
287 | 293 | return property_object | ||
288 | 294 | |||
289 | 295 | def close_presentation(self): | 355 | def close_presentation(self): |
290 | 296 | """ | 356 | """ |
291 | 297 | Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being | 357 | Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being |
292 | @@ -356,8 +416,7 @@ | |||
293 | 356 | log.debug('is blank OpenOffice') | 416 | log.debug('is blank OpenOffice') |
294 | 357 | if self.control and self.control.isRunning(): | 417 | if self.control and self.control.isRunning(): |
295 | 358 | return self.control.isPaused() | 418 | return self.control.isPaused() |
298 | 359 | else: | 419 | return False |
297 | 360 | return False | ||
299 | 361 | 420 | ||
300 | 362 | def stop_presentation(self): | 421 | def stop_presentation(self): |
301 | 363 | """ | 422 | """ |
302 | @@ -384,6 +443,8 @@ | |||
303 | 384 | sleep_count += 1 | 443 | sleep_count += 1 |
304 | 385 | self.control = self.presentation.getController() | 444 | self.control = self.presentation.getController() |
305 | 386 | window.setVisible(False) | 445 | window.setVisible(False) |
306 | 446 | listener = SlideShowListener(self) | ||
307 | 447 | self.control.getSlideShow().addSlideShowListener(listener) | ||
308 | 387 | else: | 448 | else: |
309 | 388 | self.control.activate() | 449 | self.control.activate() |
310 | 389 | self.goto_slide(1) | 450 | self.goto_slide(1) |
311 | @@ -415,17 +476,33 @@ | |||
312 | 415 | """ | 476 | """ |
313 | 416 | Triggers the next effect of slide on the running presentation. | 477 | Triggers the next effect of slide on the running presentation. |
314 | 417 | """ | 478 | """ |
315 | 479 | # if we are at the presentations end don't go further, just return True | ||
316 | 480 | if self.slide_ended and self.get_slide_count() == self.get_slide_number(): | ||
317 | 481 | return True | ||
318 | 482 | self.slide_ended = False | ||
319 | 483 | self.slide_ended_reverse = False | ||
320 | 484 | past_end = False | ||
321 | 418 | is_paused = self.control.isPaused() | 485 | is_paused = self.control.isPaused() |
322 | 419 | self.control.gotoNextEffect() | 486 | self.control.gotoNextEffect() |
323 | 420 | time.sleep(0.1) | 487 | time.sleep(0.1) |
324 | 488 | # If for some reason the presentation end was not detected above, this will catch it. | ||
325 | 489 | # The presentation is set to paused when going past the end. | ||
326 | 421 | if not is_paused and self.control.isPaused(): | 490 | if not is_paused and self.control.isPaused(): |
327 | 422 | self.control.gotoPreviousEffect() | 491 | self.control.gotoPreviousEffect() |
328 | 492 | past_end = True | ||
329 | 493 | return past_end | ||
330 | 423 | 494 | ||
331 | 424 | def previous_step(self): | 495 | def previous_step(self): |
332 | 425 | """ | 496 | """ |
333 | 426 | Triggers the previous slide on the running presentation. | 497 | Triggers the previous slide on the running presentation. |
334 | 427 | """ | 498 | """ |
335 | 499 | # if we are at the presentations start don't go further back, just return True | ||
336 | 500 | if self.slide_ended_reverse and self.get_slide_number() == 1: | ||
337 | 501 | return True | ||
338 | 502 | self.slide_ended = False | ||
339 | 503 | self.slide_ended_reverse = False | ||
340 | 428 | self.control.gotoPreviousEffect() | 504 | self.control.gotoPreviousEffect() |
341 | 505 | return False | ||
342 | 429 | 506 | ||
343 | 430 | def get_slide_text(self, slide_no): | 507 | def get_slide_text(self, slide_no): |
344 | 431 | """ | 508 | """ |
345 | @@ -483,3 +560,100 @@ | |||
346 | 483 | note = ' ' | 560 | note = ' ' |
347 | 484 | notes.append(note) | 561 | notes.append(note) |
348 | 485 | self.save_titles_and_notes(titles, notes) | 562 | self.save_titles_and_notes(titles, notes) |
349 | 563 | |||
350 | 564 | if is_win(): | ||
351 | 565 | property_object = self.controller.manager.Bridge_GetStruct('com.sun.star.beans.PropertyValue') | ||
352 | 566 | |||
353 | 567 | |||
354 | 568 | class SlideShowListener(SlideShowListenerImport): | ||
355 | 569 | """ | ||
356 | 570 | Listener interface to receive global slide show events. | ||
357 | 571 | """ | ||
358 | 572 | |||
359 | 573 | def __init__(self, document): | ||
360 | 574 | """ | ||
361 | 575 | Constructor | ||
362 | 576 | |||
363 | 577 | :param document: The ImpressDocument being presented | ||
364 | 578 | """ | ||
365 | 579 | self.document = document | ||
366 | 580 | |||
367 | 581 | def paused(self): | ||
368 | 582 | """ | ||
369 | 583 | Notify that the slide show is paused | ||
370 | 584 | """ | ||
371 | 585 | log.debug('LibreOffice SlideShowListener event: paused') | ||
372 | 586 | |||
373 | 587 | def resumed(self): | ||
374 | 588 | """ | ||
375 | 589 | Notify that the slide show is resumed from a paused state | ||
376 | 590 | """ | ||
377 | 591 | log.debug('LibreOffice SlideShowListener event: resumed') | ||
378 | 592 | |||
379 | 593 | def slideTransitionStarted(self): | ||
380 | 594 | """ | ||
381 | 595 | Notify that a new slide starts to become visible. | ||
382 | 596 | """ | ||
383 | 597 | log.debug('LibreOffice SlideShowListener event: slideTransitionStarted') | ||
384 | 598 | |||
385 | 599 | def slideTransitionEnded(self): | ||
386 | 600 | """ | ||
387 | 601 | Notify that the slide transtion of the current slide ended. | ||
388 | 602 | """ | ||
389 | 603 | log.debug('LibreOffice SlideShowListener event: slideTransitionEnded') | ||
390 | 604 | |||
391 | 605 | def slideAnimationsEnded(self): | ||
392 | 606 | """ | ||
393 | 607 | Notify that the last animation from the main sequence of the current slide has ended. | ||
394 | 608 | """ | ||
395 | 609 | log.debug('LibreOffice SlideShowListener event: slideAnimationsEnded') | ||
396 | 610 | if not Registry().get('main_window').isActiveWindow(): | ||
397 | 611 | log.debug('main window is not in focus - should update slidecontroller') | ||
398 | 612 | Registry().execute('slidecontroller_live_change', self.document.control.getCurrentSlideIndex() + 1) | ||
399 | 613 | |||
400 | 614 | def slideEnded(self, reverse): | ||
401 | 615 | """ | ||
402 | 616 | Notify that the current slide has ended, e.g. the user has clicked on the slide. Calling displaySlide() | ||
403 | 617 | twice will not issue this event. | ||
404 | 618 | """ | ||
405 | 619 | log.debug('LibreOffice SlideShowListener event: slideEnded %d' % reverse) | ||
406 | 620 | if reverse: | ||
407 | 621 | self.document.slide_ended = False | ||
408 | 622 | self.document.slide_ended_reverse = True | ||
409 | 623 | else: | ||
410 | 624 | self.document.slide_ended = True | ||
411 | 625 | self.document.slide_ended_reverse = False | ||
412 | 626 | |||
413 | 627 | def hyperLinkClicked(self, hyperLink): | ||
414 | 628 | """ | ||
415 | 629 | Notifies that a hyperlink has been clicked. | ||
416 | 630 | """ | ||
417 | 631 | log.debug('LibreOffice SlideShowListener event: hyperLinkClicked %s' % hyperLink) | ||
418 | 632 | |||
419 | 633 | def disposing(self, source): | ||
420 | 634 | """ | ||
421 | 635 | gets called when the broadcaster is about to be disposed. | ||
422 | 636 | :param source: | ||
423 | 637 | """ | ||
424 | 638 | log.debug('LibreOffice SlideShowListener event: disposing') | ||
425 | 639 | |||
426 | 640 | def beginEvent(self, node): | ||
427 | 641 | """ | ||
428 | 642 | This event is raised when the element local timeline begins to play. | ||
429 | 643 | :param node: | ||
430 | 644 | """ | ||
431 | 645 | log.debug('LibreOffice SlideShowListener event: beginEvent') | ||
432 | 646 | |||
433 | 647 | def endEvent(self, node): | ||
434 | 648 | """ | ||
435 | 649 | This event is raised at the active end of the element. | ||
436 | 650 | :param node: | ||
437 | 651 | """ | ||
438 | 652 | log.debug('LibreOffice SlideShowListener event: endEvent') | ||
439 | 653 | |||
440 | 654 | def repeat(self, node): | ||
441 | 655 | """ | ||
442 | 656 | This event is raised when the element local timeline repeats. | ||
443 | 657 | :param node: | ||
444 | 658 | """ | ||
445 | 659 | log.debug('LibreOffice SlideShowListener event: repeat') | ||
446 | 486 | 660 | ||
447 | === modified file 'openlp/plugins/presentations/lib/messagelistener.py' | |||
448 | --- openlp/plugins/presentations/lib/messagelistener.py 2019-05-22 06:47:00 +0000 | |||
449 | +++ openlp/plugins/presentations/lib/messagelistener.py 2019-05-24 19:21:37 +0000 | |||
450 | @@ -169,24 +169,21 @@ | |||
451 | 169 | """ | 169 | """ |
452 | 170 | log.debug('Live = {live}, next'.format(live=self.is_live)) | 170 | log.debug('Live = {live}, next'.format(live=self.is_live)) |
453 | 171 | if not self.doc: | 171 | if not self.doc: |
455 | 172 | return | 172 | return False |
456 | 173 | if not self.is_live: | 173 | if not self.is_live: |
458 | 174 | return | 174 | return False |
459 | 175 | if self.hide_mode: | 175 | if self.hide_mode: |
460 | 176 | if not self.doc.is_active(): | 176 | if not self.doc.is_active(): |
462 | 177 | return | 177 | return False |
463 | 178 | if self.doc.slidenumber < self.doc.get_slide_count(): | 178 | if self.doc.slidenumber < self.doc.get_slide_count(): |
464 | 179 | self.doc.slidenumber += 1 | 179 | self.doc.slidenumber += 1 |
465 | 180 | self.poll() | 180 | self.poll() |
467 | 181 | return | 181 | return False |
468 | 182 | if not self.activate(): | 182 | if not self.activate(): |
475 | 183 | return | 183 | return False |
476 | 184 | # The "End of slideshow" screen is after the last slide. Note, we can't just stop on the last slide, since it | 184 | ret = self.doc.next_step() |
471 | 185 | # may contain animations that need to be stepped through. | ||
472 | 186 | if self.doc.slidenumber > self.doc.get_slide_count(): | ||
473 | 187 | return | ||
474 | 188 | self.doc.next_step() | ||
477 | 189 | self.poll() | 185 | self.poll() |
478 | 186 | return ret | ||
479 | 190 | 187 | ||
480 | 191 | def previous(self): | 188 | def previous(self): |
481 | 192 | """ | 189 | """ |
482 | @@ -194,20 +191,21 @@ | |||
483 | 194 | """ | 191 | """ |
484 | 195 | log.debug('Live = {live}, previous'.format(live=self.is_live)) | 192 | log.debug('Live = {live}, previous'.format(live=self.is_live)) |
485 | 196 | if not self.doc: | 193 | if not self.doc: |
487 | 197 | return | 194 | return False |
488 | 198 | if not self.is_live: | 195 | if not self.is_live: |
490 | 199 | return | 196 | return False |
491 | 200 | if self.hide_mode: | 197 | if self.hide_mode: |
492 | 201 | if not self.doc.is_active(): | 198 | if not self.doc.is_active(): |
494 | 202 | return | 199 | return False |
495 | 203 | if self.doc.slidenumber > 1: | 200 | if self.doc.slidenumber > 1: |
496 | 204 | self.doc.slidenumber -= 1 | 201 | self.doc.slidenumber -= 1 |
497 | 205 | self.poll() | 202 | self.poll() |
499 | 206 | return | 203 | return False |
500 | 207 | if not self.activate(): | 204 | if not self.activate(): |
503 | 208 | return | 205 | return False |
504 | 209 | self.doc.previous_step() | 206 | ret = self.doc.previous_step() |
505 | 210 | self.poll() | 207 | self.poll() |
506 | 208 | return ret | ||
507 | 211 | 209 | ||
508 | 212 | def shutdown(self): | 210 | def shutdown(self): |
509 | 213 | """ | 211 | """ |
510 | @@ -418,11 +416,12 @@ | |||
511 | 418 | """ | 416 | """ |
512 | 419 | is_live = message[1] | 417 | is_live = message[1] |
513 | 420 | if is_live: | 418 | if is_live: |
515 | 421 | self.live_handler.next() | 419 | ret = self.live_handler.next() |
516 | 422 | if Settings().value('core/click live slide to unblank'): | 420 | if Settings().value('core/click live slide to unblank'): |
517 | 423 | Registry().execute('slidecontroller_live_unblank') | 421 | Registry().execute('slidecontroller_live_unblank') |
518 | 422 | return ret | ||
519 | 424 | else: | 423 | else: |
521 | 425 | self.preview_handler.next() | 424 | return self.preview_handler.next() |
522 | 426 | 425 | ||
523 | 427 | def previous(self, message): | 426 | def previous(self, message): |
524 | 428 | """ | 427 | """ |
525 | @@ -432,11 +431,12 @@ | |||
526 | 432 | """ | 431 | """ |
527 | 433 | is_live = message[1] | 432 | is_live = message[1] |
528 | 434 | if is_live: | 433 | if is_live: |
530 | 435 | self.live_handler.previous() | 434 | ret = self.live_handler.previous() |
531 | 436 | if Settings().value('core/click live slide to unblank'): | 435 | if Settings().value('core/click live slide to unblank'): |
532 | 437 | Registry().execute('slidecontroller_live_unblank') | 436 | Registry().execute('slidecontroller_live_unblank') |
533 | 437 | return ret | ||
534 | 438 | else: | 438 | else: |
536 | 439 | self.preview_handler.previous() | 439 | return self.preview_handler.previous() |
537 | 440 | 440 | ||
538 | 441 | def shutdown(self, message): | 441 | def shutdown(self, message): |
539 | 442 | """ | 442 | """ |
540 | 443 | 443 | ||
541 | === modified file 'openlp/plugins/presentations/lib/powerpointcontroller.py' | |||
542 | --- openlp/plugins/presentations/lib/powerpointcontroller.py 2019-05-22 06:47:00 +0000 | |||
543 | +++ openlp/plugins/presentations/lib/powerpointcontroller.py 2019-05-24 19:21:37 +0000 | |||
544 | @@ -170,14 +170,17 @@ | |||
545 | 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. |
546 | 171 | """ | 171 | """ |
547 | 172 | log.debug('create_thumbnails') | 172 | log.debug('create_thumbnails') |
548 | 173 | generate_thumbs = True | ||
549 | 173 | if self.check_thumbnails(): | 174 | if self.check_thumbnails(): |
551 | 174 | return | 175 | # No need for thumbnails but we still need the index |
552 | 176 | generate_thumbs = False | ||
553 | 175 | key = 1 | 177 | key = 1 |
554 | 176 | for num in range(self.presentation.Slides.Count): | 178 | for num in range(self.presentation.Slides.Count): |
555 | 177 | if not self.presentation.Slides(num + 1).SlideShowTransition.Hidden: | 179 | if not self.presentation.Slides(num + 1).SlideShowTransition.Hidden: |
556 | 178 | self.index_map[key] = num + 1 | 180 | self.index_map[key] = num + 1 |
559 | 179 | self.presentation.Slides(num + 1).Export( | 181 | if generate_thumbs: |
560 | 180 | str(self.get_thumbnail_folder() / 'slide{key:d}.png'.format(key=key)), 'png', 320, 240) | 182 | self.presentation.Slides(num + 1).Export( |
561 | 183 | str(self.get_thumbnail_folder() / 'slide{key:d}.png'.format(key=key)), 'png', 320, 240) | ||
562 | 181 | key += 1 | 184 | key += 1 |
563 | 182 | self.slide_count = key - 1 | 185 | self.slide_count = key - 1 |
564 | 183 | 186 | ||
565 | @@ -318,6 +321,9 @@ | |||
566 | 318 | size = ScreenList().current.display_geometry | 321 | size = ScreenList().current.display_geometry |
567 | 319 | ppt_window = None | 322 | ppt_window = None |
568 | 320 | try: | 323 | try: |
569 | 324 | # Disable the presentation console | ||
570 | 325 | self.presentation.SlideShowSettings.ShowPresenterView = 0 | ||
571 | 326 | # Start the presentation | ||
572 | 321 | ppt_window = self.presentation.SlideShowSettings.Run() | 327 | ppt_window = self.presentation.SlideShowSettings.Run() |
573 | 322 | except (AttributeError, pywintypes.com_error): | 328 | except (AttributeError, pywintypes.com_error): |
574 | 323 | log.exception('Caught exception while in start_presentation') | 329 | log.exception('Caught exception while in start_presentation') |
575 | @@ -437,6 +443,12 @@ | |||
576 | 437 | Triggers the next effect of slide on the running presentation. | 443 | Triggers the next effect of slide on the running presentation. |
577 | 438 | """ | 444 | """ |
578 | 439 | log.debug('next_step') | 445 | log.debug('next_step') |
579 | 446 | # if we are at the presentations end don't go further, just return True | ||
580 | 447 | if self.presentation.SlideShowWindow.View.GetClickCount() == \ | ||
581 | 448 | self.presentation.SlideShowWindow.View.GetClickIndex() \ | ||
582 | 449 | and self.get_slide_number() == self.get_slide_count(): | ||
583 | 450 | return True | ||
584 | 451 | past_end = False | ||
585 | 440 | try: | 452 | try: |
586 | 441 | self.presentation.SlideShowWindow.Activate() | 453 | self.presentation.SlideShowWindow.Activate() |
587 | 442 | self.presentation.SlideShowWindow.View.Next() | 454 | self.presentation.SlideShowWindow.View.Next() |
588 | @@ -444,28 +456,35 @@ | |||
589 | 444 | log.exception('Caught exception while in next_step') | 456 | log.exception('Caught exception while in next_step') |
590 | 445 | trace_error_handler(log) | 457 | trace_error_handler(log) |
591 | 446 | self.show_error_msg() | 458 | self.show_error_msg() |
593 | 447 | return | 459 | return past_end |
594 | 460 | # If for some reason the presentation end was not detected above, this will catch it. | ||
595 | 448 | if self.get_slide_number() > self.get_slide_count(): | 461 | if self.get_slide_number() > self.get_slide_count(): |
596 | 449 | log.debug('past end, stepping back to previous') | 462 | log.debug('past end, stepping back to previous') |
597 | 450 | self.previous_step() | 463 | self.previous_step() |
598 | 464 | past_end = True | ||
599 | 451 | # Stop powerpoint from flashing in the taskbar | 465 | # Stop powerpoint from flashing in the taskbar |
600 | 452 | if self.presentation_hwnd: | 466 | if self.presentation_hwnd: |
601 | 453 | win32gui.FlashWindowEx(self.presentation_hwnd, win32con.FLASHW_STOP, 0, 0) | 467 | win32gui.FlashWindowEx(self.presentation_hwnd, win32con.FLASHW_STOP, 0, 0) |
602 | 454 | # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup | 468 | # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup |
603 | 455 | if len(ScreenList()) > 1: | 469 | if len(ScreenList()) > 1: |
604 | 456 | Registry().get('main_window').activateWindow() | 470 | Registry().get('main_window').activateWindow() |
605 | 471 | return past_end | ||
606 | 457 | 472 | ||
607 | 458 | def previous_step(self): | 473 | def previous_step(self): |
608 | 459 | """ | 474 | """ |
609 | 460 | Triggers the previous slide on the running presentation. | 475 | Triggers the previous slide on the running presentation. |
610 | 461 | """ | 476 | """ |
611 | 462 | log.debug('previous_step') | 477 | log.debug('previous_step') |
612 | 478 | # if we are at the presentations start we can't go further back, just return True | ||
613 | 479 | if self.presentation.SlideShowWindow.View.GetClickIndex() == 0 and self.get_slide_number() == 1: | ||
614 | 480 | return True | ||
615 | 463 | try: | 481 | try: |
616 | 464 | self.presentation.SlideShowWindow.View.Previous() | 482 | self.presentation.SlideShowWindow.View.Previous() |
617 | 465 | except (AttributeError, pywintypes.com_error): | 483 | except (AttributeError, pywintypes.com_error): |
618 | 466 | log.exception('Caught exception while in previous_step') | 484 | log.exception('Caught exception while in previous_step') |
619 | 467 | trace_error_handler(log) | 485 | trace_error_handler(log) |
620 | 468 | self.show_error_msg() | 486 | self.show_error_msg() |
621 | 487 | return False | ||
622 | 469 | 488 | ||
623 | 470 | def get_slide_text(self, slide_no): | 489 | def get_slide_text(self, slide_no): |
624 | 471 | """ | 490 | """ |
625 | 472 | 491 | ||
626 | === modified file 'openlp/plugins/presentations/lib/presentationcontroller.py' | |||
627 | --- openlp/plugins/presentations/lib/presentationcontroller.py 2019-05-22 06:47:00 +0000 | |||
628 | +++ openlp/plugins/presentations/lib/presentationcontroller.py 2019-05-24 19:21:37 +0000 | |||
629 | @@ -248,15 +248,17 @@ | |||
630 | 248 | def next_step(self): | 248 | def next_step(self): |
631 | 249 | """ | 249 | """ |
632 | 250 | Triggers the next effect of slide on the running presentation. This might be the next animation on the current | 250 | Triggers the next effect of slide on the running presentation. This might be the next animation on the current |
634 | 251 | slide, or the next slide | 251 | slide, or the next slide. |
635 | 252 | Returns True if we stepped beyond the slides of the presentation | ||
636 | 252 | """ | 253 | """ |
638 | 253 | pass | 254 | return False |
639 | 254 | 255 | ||
640 | 255 | def previous_step(self): | 256 | def previous_step(self): |
641 | 256 | """ | 257 | """ |
642 | 257 | Triggers the previous slide on the running presentation | 258 | Triggers the previous slide on the running presentation |
643 | 259 | Returns True if we stepped beyond the slides of the presentation | ||
644 | 258 | """ | 260 | """ |
646 | 259 | pass | 261 | return False |
647 | 260 | 262 | ||
648 | 261 | def convert_thumbnail(self, image_path, index): | 263 | def convert_thumbnail(self, image_path, index): |
649 | 262 | """ | 264 | """ |
Linux tests failed, please see https:/ /ci.openlp. io/job/ MP-02-Linux_ Tests/158/ for more details