forked from openlp/openlp
Remove Powerpoint Viewer
Clean up the logging framework and add to MainWindow Fix bug in remote API various minor text fixes. lp:~trb143/openlp/cleanups2018 (revision 2827) https://ci.openlp.io/job/Branch-01-Pull/2441/ [SUCCESS] https://ci.openlp.io/job/Branch-02a-Linux-Tests/2342/ [SUCCESS] https://ci.openlp.io/job/Branch-02b-macOS-Tests/136/ [SUCCESS] https://ci.openlp.io/job/Branch-03a-Build-Source/58/ [SUCCES... bzr-revno: 2812
This commit is contained in:
commit
0d2bf9926d
@ -69,6 +69,7 @@ def _make_response(view_result):
|
||||
"""
|
||||
Create a Response object from response
|
||||
"""
|
||||
log.debug("in Make response")
|
||||
if isinstance(view_result, Response):
|
||||
return view_result
|
||||
elif isinstance(view_result, tuple):
|
||||
@ -88,6 +89,9 @@ def _make_response(view_result):
|
||||
elif isinstance(view_result, str):
|
||||
return Response(body=view_result, status=200,
|
||||
content_type='text/html', charset='utf8')
|
||||
else:
|
||||
return Response(body=view_result, status=200,
|
||||
content_type='text/plain', charset='utf8')
|
||||
|
||||
|
||||
def _handle_exception(error):
|
||||
|
@ -120,7 +120,8 @@ class OpenLP(QtWidgets.QApplication, LogMixin):
|
||||
self.main_window.show()
|
||||
if can_show_splash:
|
||||
# now kill the splashscreen
|
||||
self.splash.finish(self.main_window)
|
||||
log.debug('Splashscreen closing')
|
||||
self.splash.close()
|
||||
log.debug('Splashscreen closed')
|
||||
# make sure Qt really display the splash screen
|
||||
self.processEvents()
|
||||
|
@ -56,7 +56,10 @@ class LogMixin(object):
|
||||
def wrapped(*args, **kwargs):
|
||||
parent.logger.debug("Entering {function}".format(function=func.__name__))
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
if len(inspect.signature(func).parameters.values()):
|
||||
return func(*args, **kwargs)
|
||||
else:
|
||||
return func(*args)
|
||||
except Exception as e:
|
||||
if parent.logger.getEffectiveLevel() <= logging.ERROR:
|
||||
parent.logger.error('Exception in {function} : {error}'.format(function=func.__name__,
|
||||
@ -89,6 +92,13 @@ class LogMixin(object):
|
||||
trace_error_handler(self.logger)
|
||||
self.logger.error(message)
|
||||
|
||||
def log_critical(self, message):
|
||||
"""
|
||||
Common log critical handler which prints the calling path
|
||||
"""
|
||||
trace_error_handler(self.logger)
|
||||
self.logger.critical(message)
|
||||
|
||||
def log_exception(self, message):
|
||||
"""
|
||||
Common log exception handler which prints the calling path
|
||||
|
@ -259,7 +259,8 @@ class Settings(QtCore.QSettings):
|
||||
('media/last directory', 'media/last directory', [(str_to_path, None)]),
|
||||
('songuasge/db password', 'songusage/db password', []),
|
||||
('songuasge/db hostname', 'songusage/db hostname', []),
|
||||
('songuasge/db database', 'songusage/db database', [])
|
||||
('songuasge/db database', 'songusage/db database', []),
|
||||
('presentations / Powerpoint Viewer', '', [])
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
|
@ -98,7 +98,7 @@ class UiAboutDialog(object):
|
||||
'OpenLP is free church presentation software, or lyrics '
|
||||
'projection software, used to display slides of songs, Bible '
|
||||
'verses, videos, images, and even presentations (if '
|
||||
'Impress, PowerPoint or PowerPoint Viewer is installed) '
|
||||
'Impress or PowerPoint is installed) '
|
||||
'for church worship using a computer and a data projector.\n'
|
||||
'\n'
|
||||
'Find out more about OpenLP: http://openlp.org/\n'
|
||||
|
@ -37,7 +37,7 @@ from openlp.core.common import is_win, is_macosx, add_actions
|
||||
from openlp.core.common.actions import ActionList, CategoryOrder
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.common.i18n import LanguageManager, UiStrings, translate
|
||||
from openlp.core.common.mixins import RegistryProperties
|
||||
from openlp.core.common.mixins import LogMixin, RegistryProperties
|
||||
from openlp.core.common.path import Path, copyfile, create_paths
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.common.settings import Settings
|
||||
@ -56,8 +56,6 @@ from openlp.core.version import get_version
|
||||
from openlp.core.widgets.dialogs import FileDialog
|
||||
from openlp.core.widgets.docks import OpenLPDockWidget, MediaDockManager
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Ui_MainWindow(object):
|
||||
"""
|
||||
@ -465,12 +463,10 @@ class Ui_MainWindow(object):
|
||||
self.mode_live_item.setStatusTip(translate('OpenLP.MainWindow', 'Use layout that focuses on Live.'))
|
||||
|
||||
|
||||
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
|
||||
class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryProperties):
|
||||
"""
|
||||
The main window.
|
||||
"""
|
||||
log.info('MainWindow loaded')
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
This constructor sets up the interface, the various managers, and the plugins.
|
||||
@ -557,7 +553,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
|
||||
wait_dialog.setCancelButton(None)
|
||||
wait_dialog.show()
|
||||
for thread_name in self.application.worker_threads.keys():
|
||||
log.debug('Waiting for thread %s', thread_name)
|
||||
self.log_debug('Waiting for thread %s' % thread_name)
|
||||
self.application.processEvents()
|
||||
thread = self.application.worker_threads[thread_name]['thread']
|
||||
worker = self.application.worker_threads[thread_name]['worker']
|
||||
@ -595,7 +591,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
|
||||
"""
|
||||
Called on start up to restore the last active media plugin.
|
||||
"""
|
||||
log.info('Load data from Settings')
|
||||
self.log_info('Load data from Settings')
|
||||
if Settings().value('advanced/save current plugin'):
|
||||
saved_plugin_id = Settings().value('advanced/current media plugin')
|
||||
if saved_plugin_id != -1:
|
||||
@ -627,7 +623,6 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
|
||||
|
||||
:param version: The Version to be displayed.
|
||||
"""
|
||||
log.debug('version_notice')
|
||||
version_text = translate('OpenLP.MainWindow', 'Version {new} of OpenLP is now available for download (you are '
|
||||
'currently running version {current}). \n\nYou can download the latest version from '
|
||||
'http://openlp.org/.').format(new=version, current=get_version()[u'full'])
|
||||
@ -774,7 +769,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
|
||||
self.application.splash.close()
|
||||
QtWidgets.QMessageBox.information(self, title, message)
|
||||
|
||||
def on_help_web_site_clicked(self):
|
||||
@staticmethod
|
||||
def on_help_web_site_clicked():
|
||||
"""
|
||||
Load the OpenLP website
|
||||
"""
|
||||
@ -891,7 +887,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
|
||||
settings = Settings()
|
||||
import_settings = Settings(str(temp_config_path), Settings.IniFormat)
|
||||
|
||||
log.info('hook upgrade_plugin_settings')
|
||||
self.log_info('hook upgrade_plugin_settings')
|
||||
self.plugin_manager.hook_upgrade_plugin_settings(import_settings)
|
||||
# Upgrade settings to prepare the import.
|
||||
if import_settings.can_upgrade():
|
||||
@ -929,7 +925,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
|
||||
value = import_settings.value(section_key)
|
||||
except KeyError:
|
||||
value = None
|
||||
log.warning('The key "{key}" does not exist (anymore), so it will be skipped.'.format(key=section_key))
|
||||
self.log_warning('The key "{key}" does not exist (anymore), so it will be skipped.'.
|
||||
format(key=section_key))
|
||||
if value is not None:
|
||||
settings.setValue('{key}'.format(key=section_key), value)
|
||||
now = datetime.now()
|
||||
@ -1013,7 +1010,6 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
|
||||
"""
|
||||
The screen has changed so we have to update components such as the renderer.
|
||||
"""
|
||||
log.debug('screen_changed')
|
||||
self.application.set_busy_cursor()
|
||||
self.image_manager.update_display()
|
||||
self.renderer.update_display()
|
||||
@ -1076,7 +1072,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
|
||||
if Settings().value('advanced/save current plugin'):
|
||||
Settings().setValue('advanced/current media plugin', self.media_tool_box.currentIndex())
|
||||
# Call the cleanup method to shutdown plugins.
|
||||
log.info('cleanup plugins')
|
||||
self.log_info('cleanup plugins')
|
||||
self.plugin_manager.finalise_plugins()
|
||||
if save_settings:
|
||||
# Save settings
|
||||
@ -1216,7 +1212,6 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
|
||||
"""
|
||||
Load the main window settings.
|
||||
"""
|
||||
log.debug('Loading QSettings')
|
||||
settings = Settings()
|
||||
# Remove obsolete entries.
|
||||
settings.remove('custom slide')
|
||||
@ -1243,7 +1238,6 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
|
||||
# Exit if we just did a settings import.
|
||||
if self.settings_imported:
|
||||
return
|
||||
log.debug('Saving QSettings')
|
||||
settings = Settings()
|
||||
settings.beginGroup(self.general_settings_section)
|
||||
settings.setValue('recent files', self.recent_files)
|
||||
@ -1268,7 +1262,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
|
||||
if not recent_path.is_file():
|
||||
continue
|
||||
count += 1
|
||||
log.debug('Recent file name: {name}'.format(name=recent_path))
|
||||
self.log_debug('Recent file name: {name}'.format(name=recent_path))
|
||||
action = create_action(self, '',
|
||||
text='&{n} {name}'.format(n=count, name=recent_path.name),
|
||||
data=recent_path, triggers=self.service_manager_contents.on_recent_service_clicked)
|
||||
@ -1350,21 +1344,21 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
|
||||
"""
|
||||
Change the data directory.
|
||||
"""
|
||||
log.info('Changing data path to {newpath}'.format(newpath=self.new_data_path))
|
||||
self.log_info('Changing data path to {newpath}'.format(newpath=self.new_data_path))
|
||||
old_data_path = AppLocation.get_data_path()
|
||||
# Copy OpenLP data to new location if requested.
|
||||
self.application.set_busy_cursor()
|
||||
if self.copy_data:
|
||||
log.info('Copying data to new path')
|
||||
self.log_info('Copying data to new path')
|
||||
try:
|
||||
self.show_status_message(
|
||||
translate('OpenLP.MainWindow', 'Copying OpenLP data to new data directory location - {path} '
|
||||
'- Please wait for copy to finish').format(path=self.new_data_path))
|
||||
dir_util.copy_tree(str(old_data_path), str(self.new_data_path))
|
||||
log.info('Copy successful')
|
||||
self.log_info('Copy successful')
|
||||
except (OSError, DistutilsFileError) as why:
|
||||
self.application.set_normal_cursor()
|
||||
log.exception('Data copy failed {err}'.format(err=str(why)))
|
||||
self.log_exception('Data copy failed {err}'.format(err=str(why)))
|
||||
err_text = translate('OpenLP.MainWindow',
|
||||
'OpenLP Data directory copy failed\n\n{err}').format(err=str(why)),
|
||||
QtWidgets.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'New Data Directory Error'),
|
||||
@ -1372,7 +1366,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
|
||||
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
|
||||
return False
|
||||
else:
|
||||
log.info('No data copy requested')
|
||||
self.log_info('No data copy requested')
|
||||
# Change the location of data directory in config file.
|
||||
settings = QtCore.QSettings()
|
||||
settings.setValue('advanced/data path', self.new_data_path)
|
||||
|
@ -302,7 +302,7 @@ class Ui_ServiceManager(object):
|
||||
class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixin, RegistryProperties):
|
||||
"""
|
||||
Manages the services. This involves taking text strings from plugins and adding them to the service. This service
|
||||
can then be zipped up with all the resources used into one OSZ or oszl file for use on any OpenLP v2 installation.
|
||||
can then be zipped up with all the resources used into one OSZ or OSZL file for use on any OpenLP installation.
|
||||
Also handles the UI tasks of moving things up and down etc.
|
||||
"""
|
||||
servicemanager_set_item = QtCore.pyqtSignal(int)
|
||||
@ -415,10 +415,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
if suffix not in self.suffixes:
|
||||
self.suffixes.append(suffix)
|
||||
|
||||
def on_new_service_clicked(self, field=None):
|
||||
def on_new_service_clicked(self):
|
||||
"""
|
||||
Create a new service.
|
||||
:param field:
|
||||
"""
|
||||
if self.is_modified():
|
||||
result = self.save_modified_service()
|
||||
@ -466,10 +465,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
QtWidgets.QMessageBox.Save | QtWidgets.QMessageBox.Discard |
|
||||
QtWidgets.QMessageBox.Cancel, QtWidgets.QMessageBox.Save)
|
||||
|
||||
def on_recent_service_clicked(self, field=None):
|
||||
def on_recent_service_clicked(self):
|
||||
"""
|
||||
Load a recent file as the service triggered by mainwindow recent service list.
|
||||
:param field:
|
||||
"""
|
||||
if self.is_modified():
|
||||
result = self.save_modified_service()
|
||||
@ -608,7 +606,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
self.set_modified(False)
|
||||
return True
|
||||
|
||||
def save_file_as(self, field=None):
|
||||
def save_file_as(self):
|
||||
"""
|
||||
Get a file name and then call :func:`ServiceManager.save_file` to save the file.
|
||||
"""
|
||||
@ -660,10 +658,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
self.set_file_name(file_path)
|
||||
self.decide_save_method()
|
||||
|
||||
def decide_save_method(self, field=None):
|
||||
def decide_save_method(self):
|
||||
"""
|
||||
Determine which type of save method to use.
|
||||
:param field:
|
||||
"""
|
||||
if not self.file_name():
|
||||
return self.save_file_as()
|
||||
@ -824,10 +821,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
theme_action.setChecked(True)
|
||||
self.menu.exec(self.service_manager_list.mapToGlobal(point))
|
||||
|
||||
def on_service_item_note_form(self, field=None):
|
||||
def on_service_item_note_form(self):
|
||||
"""
|
||||
Allow the service note to be edited
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
self.service_note_form.text_edit.setPlainText(self.service_items[item]['service_item'].notes)
|
||||
@ -836,21 +832,18 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
self.repaint_service_list(item, -1)
|
||||
self.set_modified()
|
||||
|
||||
def on_start_time_form(self, field=None):
|
||||
def on_start_time_form(self):
|
||||
"""
|
||||
Opens a dialog to type in service item notes.
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
self.start_time_form.item = self.service_items[item]
|
||||
if self.start_time_form.exec():
|
||||
self.repaint_service_list(item, -1)
|
||||
|
||||
def toggle_auto_play_slides_once(self, field=None):
|
||||
def toggle_auto_play_slides_once(self):
|
||||
"""
|
||||
Toggle Auto play slide once. Inverts auto play once option for the item
|
||||
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
service_item = self.service_items[item]['service_item']
|
||||
@ -863,11 +856,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
self.main_window.general_settings_section + '/loop delay')
|
||||
self.set_modified()
|
||||
|
||||
def toggle_auto_play_slides_loop(self, field=None):
|
||||
def toggle_auto_play_slides_loop(self):
|
||||
"""
|
||||
Toggle Auto play slide loop.
|
||||
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
service_item = self.service_items[item]['service_item']
|
||||
@ -880,10 +871,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
self.main_window.general_settings_section + '/loop delay')
|
||||
self.set_modified()
|
||||
|
||||
def on_timed_slide_interval(self, field=None):
|
||||
def on_timed_slide_interval(self):
|
||||
"""
|
||||
Shows input dialog for enter interval in seconds for delay
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
service_item = self.service_items[item]['service_item']
|
||||
@ -906,7 +896,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
service_item.auto_play_slides_once = False
|
||||
self.set_modified()
|
||||
|
||||
def on_auto_start(self, field=None):
|
||||
def on_auto_start(self):
|
||||
"""
|
||||
Toggles to Auto Start Setting.
|
||||
"""
|
||||
@ -914,10 +904,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
self.service_items[item]['service_item'].will_auto_start = \
|
||||
not self.service_items[item]['service_item'].will_auto_start
|
||||
|
||||
def on_service_item_edit_form(self, field=None):
|
||||
def on_service_item_edit_form(self):
|
||||
"""
|
||||
Opens a dialog to edit the service item and update the service display if changes are saved.
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
self.service_item_edit_form.set_service_item(self.service_items[item]['service_item'])
|
||||
@ -990,11 +979,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
prev_item_last_slide = service_iterator.value()
|
||||
service_iterator += 1
|
||||
|
||||
def on_set_item(self, message, field=None):
|
||||
def on_set_item(self, message):
|
||||
"""
|
||||
Called by a signal to select a specific item and make it live usually from remote.
|
||||
|
||||
:param field:
|
||||
:param message: The data passed in from a remove message
|
||||
"""
|
||||
self.log_debug(message)
|
||||
@ -1060,10 +1047,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
self.service_manager.collapsed(item.parent())
|
||||
self.service_manager_list.setCurrentItem(item.parent())
|
||||
|
||||
def on_collapse_all(self, field=None):
|
||||
def on_collapse_all(self):
|
||||
"""
|
||||
Collapse all the service items.
|
||||
:param field:
|
||||
"""
|
||||
for item in self.service_items:
|
||||
item['expanded'] = False
|
||||
@ -1080,10 +1066,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
if item.childCount():
|
||||
self.service_items[pos - 1]['expanded'] = False
|
||||
|
||||
def on_expand_all(self, field=None):
|
||||
def on_expand_all(self):
|
||||
"""
|
||||
Collapse all the service items.
|
||||
:param field:
|
||||
"""
|
||||
for item in self.service_items:
|
||||
item['expanded'] = True
|
||||
@ -1100,10 +1085,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
if item.childCount():
|
||||
self.service_items[pos - 1]['expanded'] = True
|
||||
|
||||
def on_service_top(self, field=None):
|
||||
def on_service_top(self):
|
||||
"""
|
||||
Move the current ServiceItem to the top of the list.
|
||||
:param field:
|
||||
"""
|
||||
item, child = self.find_service_item()
|
||||
if item < len(self.service_items) and item != -1:
|
||||
@ -1113,10 +1097,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
self.repaint_service_list(0, child)
|
||||
self.set_modified()
|
||||
|
||||
def on_service_up(self, field=None):
|
||||
def on_service_up(self):
|
||||
"""
|
||||
Move the current ServiceItem one position up in the list.
|
||||
:param field:
|
||||
"""
|
||||
item, child = self.find_service_item()
|
||||
if item > 0:
|
||||
@ -1126,10 +1109,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
self.repaint_service_list(item - 1, child)
|
||||
self.set_modified()
|
||||
|
||||
def on_service_down(self, field=None):
|
||||
def on_service_down(self):
|
||||
"""
|
||||
Move the current ServiceItem one position down in the list.
|
||||
:param field:
|
||||
"""
|
||||
item, child = self.find_service_item()
|
||||
if item < len(self.service_items) and item != -1:
|
||||
@ -1139,10 +1121,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
self.repaint_service_list(item + 1, child)
|
||||
self.set_modified()
|
||||
|
||||
def on_service_end(self, field=None):
|
||||
def on_service_end(self):
|
||||
"""
|
||||
Move the current ServiceItem to the bottom of the list.
|
||||
:param field:
|
||||
"""
|
||||
item, child = self.find_service_item()
|
||||
if item < len(self.service_items) and item != -1:
|
||||
@ -1152,7 +1133,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
self.repaint_service_list(len(self.service_items) - 1, child)
|
||||
self.set_modified()
|
||||
|
||||
def on_delete_from_service(self, field=None):
|
||||
def on_delete_from_service(self):
|
||||
"""
|
||||
Remove the current ServiceItem from the list.
|
||||
:param field:
|
||||
@ -1378,7 +1359,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
self.drop_position = -1
|
||||
self.set_modified()
|
||||
|
||||
def make_preview(self, field=None):
|
||||
def make_preview(self):
|
||||
"""
|
||||
Send the current item to the Preview slide controller
|
||||
:param field:
|
||||
@ -1403,7 +1384,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
else:
|
||||
return self.service_items[item]['service_item']
|
||||
|
||||
def on_double_click_live(self, field=None):
|
||||
def on_double_click_live(self):
|
||||
"""
|
||||
Send the current item to the Live slide controller but triggered by a tablewidget click event.
|
||||
:param field:
|
||||
@ -1411,7 +1392,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
self.list_double_clicked = True
|
||||
self.make_live()
|
||||
|
||||
def on_single_click_preview(self, field=None):
|
||||
def on_single_click_preview(self):
|
||||
"""
|
||||
If single click previewing is enabled, and triggered by a tablewidget click event,
|
||||
start a timeout to verify a double-click hasn't triggered.
|
||||
@ -1463,7 +1444,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
'is missing or inactive'))
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
def remote_edit(self, field=None):
|
||||
def remote_edit(self):
|
||||
"""
|
||||
Triggers a remote edit to a plugin to allow item to be edited.
|
||||
:param field:
|
||||
@ -1475,7 +1456,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
if new_item:
|
||||
self.add_service_item(new_item, replace=True)
|
||||
|
||||
def on_service_item_rename(self, field=None):
|
||||
def on_service_item_rename(self):
|
||||
"""
|
||||
Opens a dialog to rename the service item.
|
||||
|
||||
@ -1493,7 +1474,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
self.repaint_service_list(item, -1)
|
||||
self.set_modified()
|
||||
|
||||
def create_custom(self, field=None):
|
||||
def create_custom(self):
|
||||
"""
|
||||
Saves the current text item as a custom slide
|
||||
:param field:
|
||||
@ -1613,7 +1594,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
|
||||
self.renderer.set_service_theme(self.service_theme)
|
||||
self.regenerate_service_items()
|
||||
|
||||
def on_theme_change_action(self, field=None):
|
||||
def on_theme_change_action(self):
|
||||
"""
|
||||
Handles theme change events
|
||||
|
||||
|
@ -545,14 +545,14 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
|
||||
self.on_theme_display(False)
|
||||
self.on_hide_display(False)
|
||||
|
||||
def service_previous(self, field=None):
|
||||
def service_previous(self):
|
||||
"""
|
||||
Live event to select the previous service item from the service manager.
|
||||
"""
|
||||
self.keypress_queue.append(ServiceItemAction.Previous)
|
||||
self._process_queue()
|
||||
|
||||
def service_next(self, field=None):
|
||||
def service_next(self):
|
||||
"""
|
||||
Live event to select the next service item from the service manager.
|
||||
"""
|
||||
@ -1103,7 +1103,7 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
|
||||
else:
|
||||
Registry().execute('live_display_show')
|
||||
|
||||
def on_slide_selected(self, field=None):
|
||||
def on_slide_selected(self):
|
||||
"""
|
||||
Slide selected in controller
|
||||
Note for some reason a dummy field is required. Nothing is passed!
|
||||
@ -1244,7 +1244,7 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
|
||||
self.preview_widget.change_slide(row)
|
||||
self.slide_selected()
|
||||
|
||||
def on_slide_selected_previous(self, field=None):
|
||||
def on_slide_selected_previous(self):
|
||||
"""
|
||||
Go to the previous slide.
|
||||
"""
|
||||
@ -1372,7 +1372,7 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
|
||||
if event.timerId() == self.timer_id:
|
||||
self.on_slide_selected_next(self.play_slides_loop.isChecked())
|
||||
|
||||
def on_edit_song(self, field=None):
|
||||
def on_edit_song(self):
|
||||
"""
|
||||
From the preview display requires the service Item to be editied
|
||||
"""
|
||||
@ -1381,16 +1381,16 @@ class SlideController(DisplayController, LogMixin, RegistryProperties):
|
||||
if new_item:
|
||||
self.add_service_item(new_item)
|
||||
|
||||
def on_preview_add_to_service(self, field=None):
|
||||
def on_preview_add_to_service(self):
|
||||
"""
|
||||
From the preview display request the Item to be added to service
|
||||
"""
|
||||
if self.service_item:
|
||||
self.service_manager.add_service_item(self.service_item)
|
||||
|
||||
def on_preview_double_click(self, field=None):
|
||||
def on_preview_double_click(self):
|
||||
"""
|
||||
Triggered when a preview slide item is doubleclicked
|
||||
Triggered when a preview slide item is double clicked
|
||||
"""
|
||||
if self.service_item:
|
||||
if Settings().value('advanced/double click live') and Settings().value('core/auto unblank'):
|
||||
|
@ -384,7 +384,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
This initialises the given bible, which means that its book names and their chapter numbers is added to the
|
||||
combo boxes on the 'Select' Tab. This is not of any importance of the 'Search' Tab.
|
||||
|
||||
:param last_book_id: The "book reference id" of the book which is chosen at the moment. (int)
|
||||
:param last_book: The "book reference id" of the book which is chosen at the moment. (int)
|
||||
:return: None
|
||||
"""
|
||||
log.debug('initialise_advanced_bible {bible}, {ref}'.format(bible=self.bible, ref=last_book))
|
||||
@ -574,6 +574,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
Update the second bible. If changing from single to dual bible modes as if the user wants to clear the search
|
||||
results, if not revert to the previously selected bible
|
||||
|
||||
:param: selection not required by part of the signature
|
||||
:return: None
|
||||
"""
|
||||
new_selection = self.second_combo_box.currentData()
|
||||
@ -1005,14 +1006,17 @@ class BibleMediaItem(MediaManagerItem):
|
||||
}[self.settings.display_style]
|
||||
return '{{su}}{bracket[0]}{verse_text}{bracket[1]}{{/su}} '.format(verse_text=verse_text, bracket=bracket)
|
||||
|
||||
def search(self, string, showError):
|
||||
def search(self, string, show_error=True):
|
||||
"""
|
||||
Search for some Bible verses (by reference).
|
||||
:param string: search string
|
||||
:param show_error: do we show the error
|
||||
:return: the results of the search
|
||||
"""
|
||||
if self.bible is None:
|
||||
return []
|
||||
reference = self.plugin.manager.parse_ref(self.bible.name, string)
|
||||
search_results = self.plugin.manager.get_verses(self.bible.name, reference, showError)
|
||||
search_results = self.plugin.manager.get_verses(self.bible.name, reference, show_error)
|
||||
if search_results:
|
||||
verse_text = ' '.join([verse.text for verse in search_results])
|
||||
return [[string, verse_text]]
|
||||
|
@ -1,307 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2018 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
import logging
|
||||
import re
|
||||
import zipfile
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from openlp.core.common import is_win
|
||||
from openlp.core.common.applocation import AppLocation
|
||||
from openlp.core.display.screens import ScreenList
|
||||
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument
|
||||
|
||||
if is_win():
|
||||
from ctypes import cdll
|
||||
from ctypes.wintypes import RECT
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PptviewController(PresentationController):
|
||||
"""
|
||||
Class to control interactions with PowerPoint Viewer Presentations. It creates the runtime Environment , Loads the
|
||||
and Closes the Presentation. As well as triggering the correct activities based on the users input
|
||||
"""
|
||||
log.info('PPTViewController loaded')
|
||||
|
||||
def __init__(self, plugin):
|
||||
"""
|
||||
Initialise the class
|
||||
"""
|
||||
log.debug('Initialising')
|
||||
self.process = None
|
||||
super(PptviewController, self).__init__(plugin, 'Powerpoint Viewer', PptviewDocument)
|
||||
self.supports = ['ppt', 'pps', 'pptx', 'ppsx', 'pptm']
|
||||
|
||||
def check_available(self):
|
||||
"""
|
||||
PPT Viewer is able to run on this machine.
|
||||
"""
|
||||
log.debug('check_available')
|
||||
if not is_win():
|
||||
return False
|
||||
return self.check_installed()
|
||||
|
||||
if is_win():
|
||||
def check_installed(self):
|
||||
"""
|
||||
Check the viewer is installed.
|
||||
"""
|
||||
log.debug('Check installed')
|
||||
try:
|
||||
self.start_process()
|
||||
return self.process.CheckInstalled()
|
||||
except OSError:
|
||||
return False
|
||||
|
||||
def start_process(self):
|
||||
"""
|
||||
Loads the PPTVIEWLIB library.
|
||||
"""
|
||||
if self.process:
|
||||
return
|
||||
log.debug('start PPTView')
|
||||
dll_path = AppLocation.get_directory(AppLocation.AppDir) \
|
||||
/ 'plugins' / 'presentations' / 'lib' / 'pptviewlib' / 'pptviewlib.dll'
|
||||
self.process = cdll.LoadLibrary(str(dll_path))
|
||||
if log.isEnabledFor(logging.DEBUG):
|
||||
self.process.SetDebug(1)
|
||||
|
||||
def kill(self):
|
||||
"""
|
||||
Called at system exit to clean up any running presentations
|
||||
"""
|
||||
log.debug('Kill pptviewer')
|
||||
while self.docs:
|
||||
self.docs[0].close_presentation()
|
||||
|
||||
|
||||
class PptviewDocument(PresentationDocument):
|
||||
"""
|
||||
Class which holds information and controls a single presentation.
|
||||
"""
|
||||
def __init__(self, controller, document_path):
|
||||
"""
|
||||
Constructor, store information about the file and initialise.
|
||||
|
||||
:param openlp.core.common.path.Path document_path: File path to the document to load
|
||||
:rtype: None
|
||||
"""
|
||||
log.debug('Init Presentation PowerPoint')
|
||||
super().__init__(controller, document_path)
|
||||
self.presentation = None
|
||||
self.ppt_id = None
|
||||
self.blanked = False
|
||||
self.hidden = False
|
||||
|
||||
def load_presentation(self):
|
||||
"""
|
||||
Called when a presentation is added to the SlideController. It builds the environment, starts communication with
|
||||
the background PptView task started earlier.
|
||||
"""
|
||||
log.debug('LoadPresentation')
|
||||
temp_path = self.get_temp_folder()
|
||||
size = ScreenList().current['size']
|
||||
rect = RECT(size.x(), size.y(), size.right(), size.bottom())
|
||||
preview_path = temp_path / 'slide'
|
||||
# Ensure that the paths are null terminated
|
||||
file_path_utf16 = str(self.file_path).encode('utf-16-le') + b'\0'
|
||||
preview_path_utf16 = str(preview_path).encode('utf-16-le') + b'\0'
|
||||
if not temp_path.is_dir():
|
||||
temp_path.mkdir(parents=True)
|
||||
self.ppt_id = self.controller.process.OpenPPT(file_path_utf16, None, rect, preview_path_utf16)
|
||||
if self.ppt_id >= 0:
|
||||
self.create_thumbnails()
|
||||
self.stop_presentation()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def create_thumbnails(self):
|
||||
"""
|
||||
PPTviewLib creates large BMP's, but we want small PNG's for consistency. Convert them here.
|
||||
"""
|
||||
log.debug('create_thumbnails')
|
||||
if self.check_thumbnails():
|
||||
return
|
||||
log.debug('create_thumbnails proceeding')
|
||||
for idx in range(self.get_slide_count()):
|
||||
path = self.get_temp_folder() / 'slide{index:d}.bmp'.format(index=idx + 1)
|
||||
self.convert_thumbnail(path, idx + 1)
|
||||
|
||||
def create_titles_and_notes(self):
|
||||
"""
|
||||
Extracts the titles and notes from the zipped file
|
||||
and writes the list of titles (one per slide)
|
||||
to 'titles.txt'
|
||||
and the notes to 'slideNotes[x].txt'
|
||||
in the thumbnails directory
|
||||
"""
|
||||
titles = None
|
||||
notes = None
|
||||
# let's make sure we have a valid zipped presentation
|
||||
if self.file_path.exists() and zipfile.is_zipfile(str(self.file_path)):
|
||||
namespaces = {"p": "http://schemas.openxmlformats.org/presentationml/2006/main",
|
||||
"a": "http://schemas.openxmlformats.org/drawingml/2006/main"}
|
||||
# open the file
|
||||
with zipfile.ZipFile(str(self.file_path)) as zip_file:
|
||||
# find the presentation.xml to get the slide count
|
||||
with zip_file.open('ppt/presentation.xml') as pres:
|
||||
tree = ElementTree.parse(pres)
|
||||
nodes = tree.getroot().findall(".//p:sldIdLst/p:sldId", namespaces=namespaces)
|
||||
# initialize the lists
|
||||
titles = ['' for i in range(len(nodes))]
|
||||
notes = ['' for i in range(len(nodes))]
|
||||
# loop thru the file list to find slides and notes
|
||||
for zip_info in zip_file.infolist():
|
||||
node_type = ''
|
||||
index = -1
|
||||
list_to_add = None
|
||||
# check if it is a slide
|
||||
match = re.search(r'slides/slide(.+)\.xml', zip_info.filename)
|
||||
if match:
|
||||
index = int(match.group(1)) - 1
|
||||
node_type = 'ctrTitle'
|
||||
list_to_add = titles
|
||||
# or a note
|
||||
match = re.search(r'notesSlides/notesSlide(.+)\.xml', zip_info.filename)
|
||||
if match:
|
||||
index = int(match.group(1)) - 1
|
||||
node_type = 'body'
|
||||
list_to_add = notes
|
||||
# if it is one of our files, index shouldn't be -1
|
||||
if index >= 0:
|
||||
with zip_file.open(zip_info) as zipped_file:
|
||||
tree = ElementTree.parse(zipped_file)
|
||||
text = ''
|
||||
nodes = tree.getroot().findall(".//p:ph[@type='" + node_type + "']../../..//p:txBody//a:t",
|
||||
namespaces=namespaces)
|
||||
# if we found any content
|
||||
if nodes and len(nodes) > 0:
|
||||
for node in nodes:
|
||||
if len(text) > 0:
|
||||
text += '\n'
|
||||
text += node.text
|
||||
# Let's remove the \n from the titles and
|
||||
# just add one at the end
|
||||
if node_type == 'ctrTitle':
|
||||
text = text.replace('\n', ' ').replace('\x0b', ' ') + '\n'
|
||||
list_to_add[index] = text
|
||||
# now let's write the files
|
||||
self.save_titles_and_notes(titles, notes)
|
||||
|
||||
def close_presentation(self):
|
||||
"""
|
||||
Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being
|
||||
shut down.
|
||||
"""
|
||||
log.debug('ClosePresentation')
|
||||
if self.controller.process:
|
||||
self.controller.process.ClosePPT(self.ppt_id)
|
||||
self.ppt_id = -1
|
||||
self.controller.remove_doc(self)
|
||||
|
||||
def is_loaded(self):
|
||||
"""
|
||||
Returns true if a presentation is loaded.
|
||||
"""
|
||||
if self.ppt_id < 0:
|
||||
return False
|
||||
if self.get_slide_count() < 0:
|
||||
return False
|
||||
return True
|
||||
|
||||
def is_active(self):
|
||||
"""
|
||||
Returns true if a presentation is currently active.
|
||||
"""
|
||||
return self.is_loaded() and not self.hidden
|
||||
|
||||
def blank_screen(self):
|
||||
"""
|
||||
Blanks the screen.
|
||||
"""
|
||||
self.controller.process.Blank(self.ppt_id)
|
||||
self.blanked = True
|
||||
|
||||
def unblank_screen(self):
|
||||
"""
|
||||
Unblanks (restores) the presentation.
|
||||
"""
|
||||
self.controller.process.Unblank(self.ppt_id)
|
||||
self.blanked = False
|
||||
|
||||
def is_blank(self):
|
||||
"""
|
||||
Returns true if screen is blank.
|
||||
"""
|
||||
log.debug('is blank OpenOffice')
|
||||
return self.blanked
|
||||
|
||||
def stop_presentation(self):
|
||||
"""
|
||||
Stops the current presentation and hides the output.
|
||||
"""
|
||||
self.hidden = True
|
||||
self.controller.process.Stop(self.ppt_id)
|
||||
|
||||
def start_presentation(self):
|
||||
"""
|
||||
Starts a presentation from the beginning.
|
||||
"""
|
||||
if self.hidden:
|
||||
self.hidden = False
|
||||
self.controller.process.Resume(self.ppt_id)
|
||||
else:
|
||||
self.controller.process.RestartShow(self.ppt_id)
|
||||
|
||||
def get_slide_number(self):
|
||||
"""
|
||||
Returns the current slide number.
|
||||
"""
|
||||
return self.controller.process.GetCurrentSlide(self.ppt_id)
|
||||
|
||||
def get_slide_count(self):
|
||||
"""
|
||||
Returns total number of slides.
|
||||
"""
|
||||
return self.controller.process.GetSlideCount(self.ppt_id)
|
||||
|
||||
def goto_slide(self, slide_no):
|
||||
"""
|
||||
Moves to a specific slide in the presentation.
|
||||
|
||||
:param slide_no: The slide the text is required for, starting at 1
|
||||
"""
|
||||
self.controller.process.GotoSlide(self.ppt_id, slide_no)
|
||||
|
||||
def next_step(self):
|
||||
"""
|
||||
Triggers the next effect of slide on the running presentation.
|
||||
"""
|
||||
self.controller.process.NextStep(self.ppt_id)
|
||||
|
||||
def previous_step(self):
|
||||
"""
|
||||
Triggers the previous slide on the running presentation.
|
||||
"""
|
||||
self.controller.process.PrevStep(self.ppt_id)
|
@ -1,121 +0,0 @@
|
||||
|
||||
PPTVIEWLIB - Control PowerPoint Viewer 2003/2007 (for openlp.org)
|
||||
Copyright (C) 2008-2018 Jonathan Corwin (j@corwin.co.uk)
|
||||
|
||||
This library wrappers the free Microsoft PowerPoint Viewer (2003/2007) program,
|
||||
allowing it to be more easily controlled from another program.
|
||||
|
||||
The PowerPoint Viewer must already be installed on the destination machine, and is
|
||||
freely available at microsoft.com.
|
||||
|
||||
The full Microsoft Office PowerPoint and PowerPoint Viewer 97 have a COM interface allowing
|
||||
automation. This ability was removed from the 2003+ viewer offerings.
|
||||
|
||||
To developers: I am not a C/C++ or Win32 API programmer as you can probably tell.
|
||||
The code and API of this DLL could certainly do with some tidying up, and the
|
||||
error trapping, where it exists, is very basic. I'll happily accept patches!
|
||||
|
||||
This library is covered by the GPL (http://www.gnu.org/licenses/)
|
||||
It is NOT covered by the LGPL, so can only be used in GPL compatable programs.
|
||||
(http://www.gnu.org/licenses/why-not-lgpl.html)
|
||||
|
||||
This README.TXT must be distributed with the pptviewlib.dll
|
||||
|
||||
This library has a limit of 50 PowerPoints which can be opened simultaneously.
|
||||
|
||||
This project can be built with the free Microsoft Visual C++ 2008 Express Edition.
|
||||
|
||||
USAGE
|
||||
-----
|
||||
BOOL CheckInstalled(void);
|
||||
Returns TRUE if PowerPointViewer is installed. FALSE if not.
|
||||
|
||||
int OpenPPT(char *filename, HWND hParentWnd, RECT rect, char *previewpath);
|
||||
|
||||
Opens the PowerPoint file, counts the number of slides, sizes and positions accordingly
|
||||
and creates preview images of each slide. Note PowerPoint Viewer only allows the
|
||||
slideshow to be resized whilst it is being loaded. It can be moved at any time however.
|
||||
|
||||
The only way to count the number of slides is to step through the entire show. Therefore
|
||||
there will be a delay whilst opening large presentations for the first time.
|
||||
For pre XP/2003 systems, the slideshow will flicker as the screen snapshots are taken.
|
||||
|
||||
filename: The PowerPoint file to be opened. Full path
|
||||
hParentWnd: The window which will become the parent of the slideshow window.
|
||||
Can be NULL.
|
||||
rect: The location/dimensions of the slideshow window.
|
||||
If all properties of this structure are zero, the dimensions of the hParentWnd
|
||||
are used.
|
||||
previewpath If specified, the prefix to use for snapshot images of each slide, in the
|
||||
form: previewpath + n + ".bmp", where n is the slide number.
|
||||
A file called previewpath + "info.txt" will also be created containing information
|
||||
about the PPT file, to speed up future openings of the unmodified file.
|
||||
Note it is up the calling program to directly access these images if they
|
||||
are required.
|
||||
|
||||
RETURNS: An unique identifier to pass to other methods in this library.
|
||||
If < 0, then the PPT failed to open.
|
||||
If >=0, ClosePPT must be called when the PPT is no longer being used
|
||||
or when the calling program is closed to release resources/hooks.
|
||||
|
||||
void ClosePPT(int id);
|
||||
Closes the presentation, releasing any resources and hooks.
|
||||
|
||||
id: The value returned from OpenPPT.
|
||||
|
||||
int GetCurrentSlide(int id);
|
||||
Returns the current slide number (from 1)
|
||||
|
||||
id: The value returned from OpenPPT.
|
||||
|
||||
int GetSlideCount(int id);
|
||||
Returns the total number of slides.
|
||||
|
||||
id: The value returned from OpenPPT.
|
||||
|
||||
void NextStep(int id);
|
||||
Advances one step (animation) through the slideshow.
|
||||
|
||||
id: The value returned from OpenPPT.
|
||||
|
||||
void PrevStep(int id);
|
||||
Goes backwards one step (animation) through the slideshow.
|
||||
|
||||
id: The value returned from OpenPPT.
|
||||
|
||||
void GotoSlide(int id, int slideno);
|
||||
Goes directly to a specific slide in the slideshow
|
||||
|
||||
id: The value returned from OpenPPT.
|
||||
slideno: The number of the slide (from 1) to go directly to.
|
||||
|
||||
If the slide has already been displayed, then the completed slide with animations performed
|
||||
will be shown. This is how the PowerPoint Viewer works so have no control over this.
|
||||
|
||||
void RestartShow(int id);
|
||||
Restarts the show from the beginning. To reset animations, behind the scenes it
|
||||
has to travel to the end and step backwards though the entire show. Therefore
|
||||
for large presentations there might be a delay.
|
||||
|
||||
id: The value returned from OpenPPT.
|
||||
|
||||
void Blank(int id);
|
||||
Blanks the screen, colour black.
|
||||
|
||||
id: The value returned from OpenPPT.
|
||||
|
||||
void Unblank(int id)
|
||||
Unblanks the screen, restoring it to it's pre-blank state.
|
||||
|
||||
id: The value returned from OpenPPT.
|
||||
|
||||
void Stop(int id)
|
||||
Moves the slideshow off the screen. (There is no concept of stop show in the PowerPoint Viewer)
|
||||
|
||||
id: The value returned from OpenPPT.
|
||||
|
||||
void Resume(int id)
|
||||
Moves the slideshow display back onto the screen following a Stop()
|
||||
|
||||
id: The value returned from OpenPPT.
|
||||
|
@ -1,210 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2018 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
import sys
|
||||
from ctypes import *
|
||||
from ctypes.wintypes import RECT
|
||||
|
||||
from PyQt5 import QtWidgets
|
||||
|
||||
|
||||
class PPTViewer(QtWidgets.QWidget):
|
||||
"""
|
||||
Standalone Test Harness for the pptviewlib library
|
||||
"""
|
||||
def __init__(self, parent=None):
|
||||
super(PPTViewer, self).__init__(parent)
|
||||
self.pptid = -1
|
||||
self.setWindowTitle('PowerPoint Viewer Test')
|
||||
|
||||
ppt_label = QtWidgets.QLabel('Open PowerPoint file')
|
||||
slide_label = QtWidgets.QLabel('Go to slide #')
|
||||
self.pptEdit = QtWidgets.QLineEdit()
|
||||
self.slideEdit = QtWidgets.QLineEdit()
|
||||
x_label = QtWidgets.QLabel('X pos')
|
||||
y_label = QtWidgets.QLabel('Y pos')
|
||||
width_label = QtWidgets.QLabel('Width')
|
||||
height_label = QtWidgets.QLabel('Height')
|
||||
self.xEdit = QtWidgets.QLineEdit('100')
|
||||
self.yEdit = QtWidgets.QLineEdit('100')
|
||||
self.widthEdit = QtWidgets.QLineEdit('900')
|
||||
self.heightEdit = QtWidgets.QLineEdit('700')
|
||||
self.total = QtWidgets.QLabel()
|
||||
ppt_btn = QtWidgets.QPushButton('Open')
|
||||
ppt_dlg_btn = QtWidgets.QPushButton('...')
|
||||
folder_label = QtWidgets.QLabel('Slide .bmp path')
|
||||
self.folderEdit = QtWidgets.QLineEdit('slide')
|
||||
slide_btn = QtWidgets.QPushButton('Go')
|
||||
prev = QtWidgets.QPushButton('Prev')
|
||||
next = QtWidgets.QPushButton('Next')
|
||||
blank = QtWidgets.QPushButton('Blank')
|
||||
unblank = QtWidgets.QPushButton('Unblank')
|
||||
restart = QtWidgets.QPushButton('Restart')
|
||||
close = QtWidgets.QPushButton('Close')
|
||||
resume = QtWidgets.QPushButton('Resume')
|
||||
stop = QtWidgets.QPushButton('Stop')
|
||||
grid = QtWidgets.QGridLayout()
|
||||
row = 0
|
||||
grid.addWidget(folder_label, 0, 0)
|
||||
grid.addWidget(self.folderEdit, 0, 1)
|
||||
row += 1
|
||||
grid.addWidget(x_label, row, 0)
|
||||
grid.addWidget(self.xEdit, row, 1)
|
||||
grid.addWidget(y_label, row, 2)
|
||||
grid.addWidget(self.yEdit, row, 3)
|
||||
row += 1
|
||||
grid.addWidget(width_label, row, 0)
|
||||
grid.addWidget(self.widthEdit, row, 1)
|
||||
grid.addWidget(height_label, row, 2)
|
||||
grid.addWidget(self.heightEdit, row, 3)
|
||||
row += 1
|
||||
grid.addWidget(ppt_label, row, 0)
|
||||
grid.addWidget(self.pptEdit, row, 1)
|
||||
grid.addWidget(ppt_dlg_btn, row, 2)
|
||||
grid.addWidget(ppt_btn, row, 3)
|
||||
row += 1
|
||||
grid.addWidget(slide_label, row, 0)
|
||||
grid.addWidget(self.slideEdit, row, 1)
|
||||
grid.addWidget(slide_btn, row, 2)
|
||||
row += 1
|
||||
grid.addWidget(prev, row, 0)
|
||||
grid.addWidget(next, row, 1)
|
||||
row += 1
|
||||
grid.addWidget(blank, row, 0)
|
||||
grid.addWidget(unblank, row, 1)
|
||||
row += 1
|
||||
grid.addWidget(restart, row, 0)
|
||||
grid.addWidget(close, row, 1)
|
||||
row += 1
|
||||
grid.addWidget(stop, row, 0)
|
||||
grid.addWidget(resume, row, 1)
|
||||
ppt_btn.clicked.connect(self.openClick)
|
||||
ppt_dlg_btn.clicked.connect(self.openDialog)
|
||||
slide_btn.clicked.connect(self.gotoClick)
|
||||
prev.clicked.connect(self.prevClick)
|
||||
next.clicked.connect(self.nextClick)
|
||||
blank.clicked.connect(self.blankClick)
|
||||
unblank.clicked.connect(self.unblankClick)
|
||||
restart.clicked.connect(self.restartClick)
|
||||
close.clicked.connect(self.closeClick)
|
||||
stop.clicked.connect(self.stopClick)
|
||||
resume.clicked.connect(self.resumeClick)
|
||||
self.setLayout(grid)
|
||||
self.resize(300, 150)
|
||||
|
||||
def prevClick(self):
|
||||
if self.pptid < 0:
|
||||
return
|
||||
self.pptdll.PrevStep(self.pptid)
|
||||
self.updateCurrSlide()
|
||||
app.processEvents()
|
||||
|
||||
def nextClick(self):
|
||||
if self.pptid < 0:
|
||||
return
|
||||
self.pptdll.NextStep(self.pptid)
|
||||
self.updateCurrSlide()
|
||||
app.processEvents()
|
||||
|
||||
def blankClick(self):
|
||||
if self.pptid < 0:
|
||||
return
|
||||
self.pptdll.Blank(self.pptid)
|
||||
app.processEvents()
|
||||
|
||||
def unblankClick(self):
|
||||
if self.pptid < 0:
|
||||
return
|
||||
self.pptdll.Unblank(self.pptid)
|
||||
app.processEvents()
|
||||
|
||||
def restartClick(self):
|
||||
if self.pptid < 0:
|
||||
return
|
||||
self.pptdll.RestartShow(self.pptid)
|
||||
self.updateCurrSlide()
|
||||
app.processEvents()
|
||||
|
||||
def stopClick(self):
|
||||
if self.pptid < 0:
|
||||
return
|
||||
self.pptdll.Stop(self.pptid)
|
||||
app.processEvents()
|
||||
|
||||
def resumeClick(self):
|
||||
if self.pptid < 0:
|
||||
return
|
||||
self.pptdll.Resume(self.pptid)
|
||||
app.processEvents()
|
||||
|
||||
def closeClick(self):
|
||||
if self.pptid < 0:
|
||||
return
|
||||
self.pptdll.ClosePPT(self.pptid)
|
||||
self.pptid = -1
|
||||
app.processEvents()
|
||||
|
||||
def openClick(self):
|
||||
oldid = self.pptid
|
||||
rect = RECT(int(self.xEdit.text()), int(self.yEdit.text()),
|
||||
int(self.widthEdit.text()), int(self.heightEdit.text()))
|
||||
filename = str(self.pptEdit.text().replace('/', '\\'))
|
||||
folder = str(self.folderEdit.text().replace('/', '\\'))
|
||||
print(filename, folder)
|
||||
self.pptid = self.pptdll.OpenPPT(filename, None, rect, folder)
|
||||
print('id: ' + str(self.pptid))
|
||||
if oldid >= 0:
|
||||
self.pptdll.ClosePPT(oldid)
|
||||
slides = self.pptdll.GetSlideCount(self.pptid)
|
||||
print('slidecount: ' + str(slides))
|
||||
self.total.setNum(self.pptdll.GetSlideCount(self.pptid))
|
||||
self.updateCurrSlide()
|
||||
|
||||
def updateCurrSlide(self):
|
||||
if self.pptid < 0:
|
||||
return
|
||||
slide = str(self.pptdll.GetCurrentSlide(self.pptid))
|
||||
print('currslide: ' + slide)
|
||||
self.slideEdit.setText(slide)
|
||||
app.processEvents()
|
||||
|
||||
def gotoClick(self):
|
||||
if self.pptid < 0:
|
||||
return
|
||||
print(self.slideEdit.text())
|
||||
self.pptdll.GotoSlide(self.pptid, int(self.slideEdit.text()))
|
||||
self.updateCurrSlide()
|
||||
app.processEvents()
|
||||
|
||||
def openDialog(self):
|
||||
self.pptEdit.setText(QtWidgets.QFileDialog.getOpenFileName(self, 'Open file')[0])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pptdll = cdll.LoadLibrary(r'pptviewlib.dll')
|
||||
pptdll.SetDebug(1)
|
||||
print('Begin...')
|
||||
app = QtWidgets.QApplication(sys.argv)
|
||||
window = PPTViewer()
|
||||
window.pptdll = pptdll
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
@ -1,920 +0,0 @@
|
||||
/******************************************************************************
|
||||
* OpenLP - Open Source Lyrics Projection *
|
||||
* --------------------------------------------------------------------------- *
|
||||
* Copyright (c) 2008-2018 OpenLP Developers *
|
||||
* --------------------------------------------------------------------------- *
|
||||
* This program is free software; you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU General Public License as published by the Free *
|
||||
* Software Foundation; version 2 of the License. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT *
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for *
|
||||
* more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License along *
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59 *
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
||||
******************************************************************************/
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <io.h>
|
||||
#include <direct.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include "pptviewlib.h"
|
||||
|
||||
// Because of the callbacks used by SetWindowsHookEx, the memory used needs to
|
||||
// be sharable across processes (the callbacks are done from a different
|
||||
// process) Therefore use data_seg with RWS memory.
|
||||
//
|
||||
// See http://msdn.microsoft.com/en-us/library/aa366551(VS.85).aspx for
|
||||
// alternative method of holding memory, removing fixed limits which would allow
|
||||
// dynamic number of items, rather than a fixed number. Use a Local\ mapping,
|
||||
// since global has UAC issues in Vista.
|
||||
|
||||
#pragma data_seg(".PPTVIEWLIB")
|
||||
PPTVIEW pptView[MAX_PPTS] = {NULL};
|
||||
HHOOK globalHook = NULL;
|
||||
BOOL debug = FALSE;
|
||||
#pragma data_seg()
|
||||
#pragma comment(linker, "/SECTION:.PPTVIEWLIB,RWS")
|
||||
|
||||
HINSTANCE hInstance = NULL;
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ulReasonForCall,
|
||||
LPVOID lpReserved)
|
||||
{
|
||||
hInstance = (HINSTANCE)hModule;
|
||||
switch(ulReasonForCall)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
DEBUG(L"PROCESS_ATTACH\n");
|
||||
break;
|
||||
case DLL_THREAD_ATTACH:
|
||||
//DEBUG(L"THREAD_ATTACH\n");
|
||||
break;
|
||||
case DLL_THREAD_DETACH:
|
||||
//DEBUG(L"THREAD_DETACH\n");
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
// Clean up... hopefully there is only the one process attached?
|
||||
// We'll find out soon enough during tests!
|
||||
DEBUG(L"PROCESS_DETACH\n");
|
||||
for (int i = 0; i < MAX_PPTS; i++)
|
||||
ClosePPT(i);
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
DllExport void SetDebug(BOOL onOff)
|
||||
{
|
||||
printf("SetDebug\n");
|
||||
debug = onOff;
|
||||
DEBUG(L"enabled\n");
|
||||
}
|
||||
|
||||
DllExport BOOL CheckInstalled()
|
||||
{
|
||||
wchar_t cmdLine[MAX_PATH * 2];
|
||||
|
||||
DEBUG(L"CheckInstalled\n");
|
||||
BOOL found = GetPPTViewerPath(cmdLine, sizeof(cmdLine));
|
||||
if(found)
|
||||
{
|
||||
DEBUG(L"Exe: %s\n", cmdLine);
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
// Open the PointPoint, count the slides and take a snapshot of each slide
|
||||
// for use in previews
|
||||
// previewpath is a prefix for the location to put preview images of each slide.
|
||||
// "<n>.bmp" will be appended to complete the path. E.g. "c:\temp\slide" would
|
||||
// create "c:\temp\slide1.bmp" slide2.bmp, slide3.bmp etc.
|
||||
// It will also create a *info.txt containing information about the ppt
|
||||
DllExport int OpenPPT(wchar_t *filename, HWND hParentWnd, RECT rect,
|
||||
wchar_t *previewPath)
|
||||
{
|
||||
STARTUPINFO si;
|
||||
PROCESS_INFORMATION pi;
|
||||
wchar_t cmdLine[MAX_PATH * 2];
|
||||
int id;
|
||||
|
||||
DEBUG(L"OpenPPT start: %s; %s\n", filename, previewPath);
|
||||
DEBUG(L"OpenPPT start: %u; %i, %i, %i, %i\n", hParentWnd, rect.top,
|
||||
rect.left, rect.bottom, rect.right);
|
||||
if (GetPPTViewerPath(cmdLine, sizeof(cmdLine)) == FALSE)
|
||||
{
|
||||
DEBUG(L"OpenPPT: GetPPTViewerPath failed\n");
|
||||
return -1;
|
||||
}
|
||||
id = -1;
|
||||
for (int i = 0; i < MAX_PPTS; i++)
|
||||
{
|
||||
if (pptView[i].state == PPT_CLOSED)
|
||||
{
|
||||
id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (id < 0)
|
||||
{
|
||||
DEBUG(L"OpenPPT: Too many PPTs\n");
|
||||
return -1;
|
||||
}
|
||||
memset(&pptView[id], 0, sizeof(PPTVIEW));
|
||||
wcscpy_s(pptView[id].filename, MAX_PATH, filename);
|
||||
wcscpy_s(pptView[id].previewPath, MAX_PATH, previewPath);
|
||||
pptView[id].state = PPT_CLOSED;
|
||||
pptView[id].slideCount = 0;
|
||||
pptView[id].currentSlide = 0;
|
||||
pptView[id].firstSlideSteps = 0;
|
||||
pptView[id].lastSlideSteps = 0;
|
||||
pptView[id].guess = 0;
|
||||
pptView[id].hParentWnd = hParentWnd;
|
||||
pptView[id].hWnd = NULL;
|
||||
pptView[id].hWnd2 = NULL;
|
||||
for (int i = 0; i < MAX_SLIDES; i++)
|
||||
{
|
||||
pptView[id].slideNos[i] = 0;
|
||||
}
|
||||
if (hParentWnd != NULL && rect.top == 0 && rect.bottom == 0
|
||||
&& rect.left == 0 && rect.right == 0)
|
||||
{
|
||||
LPRECT windowRect = NULL;
|
||||
GetWindowRect(hParentWnd, windowRect);
|
||||
pptView[id].rect.top = 0;
|
||||
pptView[id].rect.left = 0;
|
||||
pptView[id].rect.bottom = windowRect->bottom - windowRect->top;
|
||||
pptView[id].rect.right = windowRect->right - windowRect->left;
|
||||
}
|
||||
else
|
||||
{
|
||||
pptView[id].rect.top = rect.top;
|
||||
pptView[id].rect.left = rect.left;
|
||||
pptView[id].rect.bottom = rect.bottom;
|
||||
pptView[id].rect.right = rect.right;
|
||||
}
|
||||
wcscat_s(cmdLine, MAX_PATH * 2, L" /F /S \"");
|
||||
wcscat_s(cmdLine, MAX_PATH * 2, filename);
|
||||
wcscat_s(cmdLine, MAX_PATH * 2, L"\"");
|
||||
memset(&si, 0, sizeof(si));
|
||||
memset(&pi, 0, sizeof(pi));
|
||||
BOOL gotInfo = GetPPTInfo(id);
|
||||
/*
|
||||
* I'd really like to just hook on the new threadid. However this always
|
||||
* gives error 87. Perhaps I'm hooking to soon? No idea... however can't
|
||||
* wait since I need to ensure I pick up the WM_CREATE as this is the only
|
||||
* time the window can be resized in such away the content scales correctly
|
||||
*
|
||||
* hook = SetWindowsHookEx(WH_CBT,CbtProc,hInstance,pi.dwThreadId);
|
||||
*/
|
||||
if (globalHook != NULL)
|
||||
{
|
||||
UnhookWindowsHookEx(globalHook);
|
||||
}
|
||||
globalHook = SetWindowsHookEx(WH_CBT, CbtProc, hInstance, NULL);
|
||||
if (globalHook == 0)
|
||||
{
|
||||
DEBUG(L"OpenPPT: SetWindowsHookEx failed\n");
|
||||
ClosePPT(id);
|
||||
return -1;
|
||||
}
|
||||
pptView[id].state = PPT_STARTED;
|
||||
Sleep(10);
|
||||
if (!CreateProcess(NULL, cmdLine, NULL, NULL, FALSE, 0, 0, NULL, &si, &pi))
|
||||
{
|
||||
DEBUG(L"OpenPPT: CreateProcess failed: %s\n", cmdLine);
|
||||
ClosePPT(id);
|
||||
return -1;
|
||||
}
|
||||
pptView[id].dwProcessId = pi.dwProcessId;
|
||||
pptView[id].dwThreadId = pi.dwThreadId;
|
||||
pptView[id].hThread = pi.hThread;
|
||||
pptView[id].hProcess = pi.hProcess;
|
||||
while (pptView[id].state == PPT_STARTED)
|
||||
Sleep(10);
|
||||
if (gotInfo)
|
||||
{
|
||||
DEBUG(L"OpenPPT: Info loaded, no refresh\n");
|
||||
pptView[id].state = PPT_LOADED;
|
||||
Resume(id);
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG(L"OpenPPT: Get info\n");
|
||||
pptView[id].steps = 0;
|
||||
int steps = 0;
|
||||
while (pptView[id].state == PPT_OPENED)
|
||||
{
|
||||
if (steps <= pptView[id].steps)
|
||||
{
|
||||
Sleep(100);
|
||||
DEBUG(L"OpenPPT: Step %d/%d\n", steps, pptView[id].steps);
|
||||
steps++;
|
||||
NextStep(id);
|
||||
}
|
||||
Sleep(10);
|
||||
}
|
||||
DEBUG(L"OpenPPT: Slides %d, Steps %d, first slide steps %d\n",
|
||||
pptView[id].slideCount, pptView[id].steps,
|
||||
pptView[id].firstSlideSteps);
|
||||
for(int i = 1; i <= pptView[id].slideCount; i++)
|
||||
{
|
||||
DEBUG(L"OpenPPT: Slide %d = %d\n", i, pptView[id].slideNos[i]);
|
||||
}
|
||||
SavePPTInfo(id);
|
||||
if (pptView[id].state == PPT_CLOSING
|
||||
|| pptView[id].slideCount <= 0)
|
||||
{
|
||||
ClosePPT(id);
|
||||
id=-1;
|
||||
}
|
||||
else
|
||||
{
|
||||
RestartShow(id);
|
||||
}
|
||||
}
|
||||
if (id >= 0)
|
||||
{
|
||||
if (pptView[id].msgHook != NULL)
|
||||
{
|
||||
UnhookWindowsHookEx(pptView[id].msgHook);
|
||||
}
|
||||
pptView[id].msgHook = NULL;
|
||||
}
|
||||
DEBUG(L"OpenPPT: Exit: id=%i\n", id);
|
||||
return id;
|
||||
}
|
||||
// Load information about the ppt from an info.txt file.
|
||||
// Format:
|
||||
// version
|
||||
// filedate
|
||||
// filesize
|
||||
// slidecount
|
||||
// first slide steps
|
||||
BOOL GetPPTInfo(int id)
|
||||
{
|
||||
struct _stat fileStats;
|
||||
wchar_t info[MAX_PATH];
|
||||
FILE* pFile;
|
||||
wchar_t buf[100];
|
||||
|
||||
DEBUG(L"GetPPTInfo: start\n");
|
||||
if (_wstat(pptView[id].filename, &fileStats) != 0)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
swprintf_s(info, MAX_PATH, L"%sinfo.txt", pptView[id].previewPath);
|
||||
int err = _wfopen_s(&pFile, info, L"r");
|
||||
if (err != 0)
|
||||
{
|
||||
DEBUG(L"GetPPTInfo: file open failed - %d\n", err);
|
||||
return FALSE;
|
||||
}
|
||||
fgetws(buf, 100, pFile); // version == 1
|
||||
fgetws(buf, 100, pFile);
|
||||
if (fileStats.st_mtime != _wtoi(buf))
|
||||
{
|
||||
DEBUG(L"GetPPTInfo: date changed\n");
|
||||
fclose (pFile);
|
||||
return FALSE;
|
||||
}
|
||||
fgetws(buf, 100, pFile);
|
||||
if (fileStats.st_size != _wtoi(buf))
|
||||
{
|
||||
DEBUG(L"GetPPTInfo: size changed\n");
|
||||
fclose (pFile);
|
||||
return FALSE;
|
||||
}
|
||||
fgetws(buf, 100, pFile); // slidecount
|
||||
int slideCount = _wtoi(buf);
|
||||
fgetws(buf, 100, pFile); // first slide steps
|
||||
int firstSlideSteps = _wtoi(buf);
|
||||
// check all the preview images still exist
|
||||
for (int i = 1; i <= slideCount; i++)
|
||||
{
|
||||
swprintf_s(info, MAX_PATH, L"%s%i.bmp", pptView[id].previewPath, i);
|
||||
if (GetFileAttributes(info) == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
DEBUG(L"GetPPTInfo: bmp not found\n");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
fclose(pFile);
|
||||
pptView[id].slideCount = slideCount;
|
||||
pptView[id].firstSlideSteps = firstSlideSteps;
|
||||
DEBUG(L"GetPPTInfo: exit ok\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL SavePPTInfo(int id)
|
||||
{
|
||||
struct _stat fileStats;
|
||||
wchar_t info[MAX_PATH];
|
||||
FILE* pFile;
|
||||
|
||||
DEBUG(L"SavePPTInfo: start\n");
|
||||
if (_wstat(pptView[id].filename, &fileStats) != 0)
|
||||
{
|
||||
DEBUG(L"SavePPTInfo: stat of %s failed\n", pptView[id].filename);
|
||||
return FALSE;
|
||||
}
|
||||
swprintf_s(info, MAX_PATH, L"%sinfo.txt", pptView[id].previewPath);
|
||||
int err = _wfopen_s(&pFile, info, L"w");
|
||||
if (err != 0)
|
||||
{
|
||||
DEBUG(L"SavePPTInfo: fopen of %s failed%i\n", info, err);
|
||||
return FALSE;
|
||||
}
|
||||
fprintf(pFile, "1\n");
|
||||
fprintf(pFile, "%u\n", fileStats.st_mtime);
|
||||
fprintf(pFile, "%u\n", fileStats.st_size);
|
||||
fprintf(pFile, "%u\n", pptView[id].slideCount);
|
||||
fprintf(pFile, "%u\n", pptView[id].firstSlideSteps);
|
||||
fclose(pFile);
|
||||
DEBUG(L"SavePPTInfo: exit ok\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Get the path of the PowerPoint viewer from the registry
|
||||
BOOL GetPPTViewerPath(wchar_t *pptViewerPath, int stringSize)
|
||||
{
|
||||
wchar_t cwd[MAX_PATH];
|
||||
|
||||
DEBUG(L"GetPPTViewerPath: start\n");
|
||||
if(GetPPTViewerPathFromReg(pptViewerPath, stringSize))
|
||||
{
|
||||
if(_waccess(pptViewerPath, 0) != -1)
|
||||
{
|
||||
DEBUG(L"GetPPTViewerPath: exit registry\n");
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
// This is where it gets ugly. PPT2007 it seems no longer stores its
|
||||
// location in the registry. So we have to use the defaults which will
|
||||
// upset those who like to put things somewhere else
|
||||
|
||||
// Viewer 2007 in 64bit Windows:
|
||||
if(_waccess(L"C:\\Program Files (x86)\\Microsoft Office\\Office12\\PPTVIEW.EXE",
|
||||
0) != -1)
|
||||
{
|
||||
wcscpy_s(
|
||||
L"C:\\Program Files (x86)\\Microsoft Office\\Office12\\PPTVIEW.EXE",
|
||||
stringSize, pptViewerPath);
|
||||
DEBUG(L"GetPPTViewerPath: exit 64bit 2007\n");
|
||||
return TRUE;
|
||||
}
|
||||
// Viewer 2007 in 32bit Windows:
|
||||
if(_waccess(L"C:\\Program Files\\Microsoft Office\\Office12\\PPTVIEW.EXE", 0)
|
||||
!= -1)
|
||||
{
|
||||
wcscpy_s(L"C:\\Program Files\\Microsoft Office\\Office12\\PPTVIEW.EXE",
|
||||
stringSize, pptViewerPath);
|
||||
DEBUG(L"GetPPTViewerPath: exit 32bit 2007\n");
|
||||
return TRUE;
|
||||
}
|
||||
// Give them the opportunity to place it in the same folder as the app
|
||||
_wgetcwd(cwd, MAX_PATH);
|
||||
wcscat_s(cwd, MAX_PATH, L"\\PPTVIEW.EXE");
|
||||
if(_waccess(cwd, 0) != -1)
|
||||
{
|
||||
wcscpy_s(pptViewerPath, stringSize, cwd);
|
||||
DEBUG(L"GetPPTViewerPath: exit local\n");
|
||||
return TRUE;
|
||||
}
|
||||
DEBUG(L"GetPPTViewerPath: exit fail\n");
|
||||
return FALSE;
|
||||
}
|
||||
BOOL GetPPTViewerPathFromReg(wchar_t *pptViewerPath, int stringSize)
|
||||
{
|
||||
HKEY hKey;
|
||||
DWORD dwType, dwSize;
|
||||
LRESULT lResult;
|
||||
|
||||
// The following registry settings are for, respectively, (I think)
|
||||
// PPT Viewer 2007 (older versions. Latest not in registry) & PPT Viewer 2010
|
||||
// PPT Viewer 2003 (recent versions)
|
||||
// PPT Viewer 2003 (older versions)
|
||||
// PPT Viewer 97
|
||||
if ((RegOpenKeyExW(HKEY_CLASSES_ROOT,
|
||||
L"PowerPointViewer.Show.12\\shell\\Show\\command", 0, KEY_READ, &hKey)
|
||||
!= ERROR_SUCCESS)
|
||||
&& (RegOpenKeyExW(HKEY_CLASSES_ROOT,
|
||||
L"PowerPointViewer.Show.11\\shell\\Show\\command", 0, KEY_READ, &hKey)
|
||||
!= ERROR_SUCCESS)
|
||||
&& (RegOpenKeyExW(HKEY_CLASSES_ROOT,
|
||||
L"Applications\\PPTVIEW.EXE\\shell\\open\\command", 0, KEY_READ, &hKey)
|
||||
!= ERROR_SUCCESS)
|
||||
&& (RegOpenKeyExW(HKEY_CLASSES_ROOT,
|
||||
L"Applications\\PPTVIEW.EXE\\shell\\Show\\command", 0, KEY_READ, &hKey)
|
||||
!= ERROR_SUCCESS))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
dwType = REG_SZ;
|
||||
dwSize = (DWORD)stringSize;
|
||||
lResult = RegQueryValueEx(hKey, NULL, NULL, &dwType, (LPBYTE)pptViewerPath,
|
||||
&dwSize);
|
||||
RegCloseKey(hKey);
|
||||
if (lResult != ERROR_SUCCESS)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
// remove "%1" from end of key value
|
||||
pptViewerPath[wcslen(pptViewerPath) - 4] = '\0';
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Unhook the Windows hook
|
||||
void Unhook(int id)
|
||||
{
|
||||
DEBUG(L"Unhook: start %d\n", id);
|
||||
if (pptView[id].hook != NULL)
|
||||
{
|
||||
UnhookWindowsHookEx(pptView[id].hook);
|
||||
}
|
||||
if (pptView[id].msgHook != NULL)
|
||||
{
|
||||
UnhookWindowsHookEx(pptView[id].msgHook);
|
||||
}
|
||||
pptView[id].hook = NULL;
|
||||
pptView[id].msgHook = NULL;
|
||||
DEBUG(L"Unhook: exit ok\n");
|
||||
}
|
||||
|
||||
// Close the PowerPoint viewer, release resources
|
||||
DllExport void ClosePPT(int id)
|
||||
{
|
||||
DEBUG(L"ClosePPT: start%d\n", id);
|
||||
pptView[id].state = PPT_CLOSED;
|
||||
Unhook(id);
|
||||
if (pptView[id].hWnd == 0)
|
||||
{
|
||||
TerminateThread(pptView[id].hThread, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
PostMessage(pptView[id].hWnd, WM_CLOSE, 0, 0);
|
||||
}
|
||||
CloseHandle(pptView[id].hThread);
|
||||
CloseHandle(pptView[id].hProcess);
|
||||
memset(&pptView[id], 0, sizeof(PPTVIEW));
|
||||
DEBUG(L"ClosePPT: exit ok\n");
|
||||
return;
|
||||
}
|
||||
// Moves the show back onto the display
|
||||
DllExport void Resume(int id)
|
||||
{
|
||||
DEBUG(L"Resume: %d\n", id);
|
||||
MoveWindow(pptView[id].hWnd, pptView[id].rect.left,
|
||||
pptView[id].rect.top,
|
||||
pptView[id].rect.right - pptView[id].rect.left,
|
||||
pptView[id].rect.bottom - pptView[id].rect.top, TRUE);
|
||||
Unblank(id);
|
||||
}
|
||||
// Moves the show off the screen so it can't be seen
|
||||
DllExport void Stop(int id)
|
||||
{
|
||||
DEBUG(L"Stop:%d\n", id);
|
||||
MoveWindow(pptView[id].hWnd, -32000, -32000,
|
||||
pptView[id].rect.right - pptView[id].rect.left,
|
||||
pptView[id].rect.bottom - pptView[id].rect.top, TRUE);
|
||||
}
|
||||
|
||||
// Return the total number of slides
|
||||
DllExport int GetSlideCount(int id)
|
||||
{
|
||||
DEBUG(L"GetSlideCount:%d\n", id);
|
||||
if (pptView[id].state == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return pptView[id].slideCount;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the number of the slide currently viewing
|
||||
DllExport int GetCurrentSlide(int id)
|
||||
{
|
||||
DEBUG(L"GetCurrentSlide:%d\n", id);
|
||||
if (pptView[id].state == 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return pptView[id].currentSlide;
|
||||
}
|
||||
}
|
||||
|
||||
// Take a step forwards through the show
|
||||
DllExport void NextStep(int id)
|
||||
{
|
||||
DEBUG(L"NextStep:%d (%d)\n", id, pptView[id].currentSlide);
|
||||
if (pptView[id].currentSlide > pptView[id].slideCount) return;
|
||||
if (pptView[id].currentSlide < pptView[id].slideCount)
|
||||
{
|
||||
pptView[id].guess = pptView[id].currentSlide + 1;
|
||||
}
|
||||
PostMessage(pptView[id].hWnd2, WM_MOUSEWHEEL, MAKEWPARAM(0, -WHEEL_DELTA),
|
||||
0);
|
||||
}
|
||||
|
||||
// Take a step backwards through the show
|
||||
DllExport void PrevStep(int id)
|
||||
{
|
||||
DEBUG(L"PrevStep:%d (%d)\n", id, pptView[id].currentSlide);
|
||||
if (pptView[id].currentSlide > 1)
|
||||
{
|
||||
pptView[id].guess = pptView[id].currentSlide - 1;
|
||||
}
|
||||
PostMessage(pptView[id].hWnd2, WM_MOUSEWHEEL, MAKEWPARAM(0, WHEEL_DELTA),
|
||||
0);
|
||||
}
|
||||
|
||||
// Blank the show (black screen)
|
||||
DllExport void Blank(int id)
|
||||
{
|
||||
// B just toggles blank on/off. However pressing any key unblanks.
|
||||
// So send random unmapped letter first (say 'A'), then we can
|
||||
// better guarantee B will blank instead of trying to guess
|
||||
// whether it was already blank or not.
|
||||
DEBUG(L"Blank:%d\n", id);
|
||||
HWND h1 = GetForegroundWindow();
|
||||
HWND h2 = GetFocus();
|
||||
SetForegroundWindow(pptView[id].hWnd);
|
||||
SetFocus(pptView[id].hWnd);
|
||||
// slight pause, otherwise event triggering this call may grab focus back!
|
||||
Sleep(50);
|
||||
keybd_event((int)'A', 0, 0, 0);
|
||||
keybd_event((int)'A', 0, KEYEVENTF_KEYUP, 0);
|
||||
keybd_event((int)'B', 0, 0, 0);
|
||||
keybd_event((int)'B', 0, KEYEVENTF_KEYUP, 0);
|
||||
SetForegroundWindow(h1);
|
||||
SetFocus(h2);
|
||||
}
|
||||
// Unblank the show
|
||||
DllExport void Unblank(int id)
|
||||
{
|
||||
DEBUG(L"Unblank:%d\n", id);
|
||||
// Pressing any key resumes.
|
||||
// For some reason SendMessage works for unblanking, but not blanking.
|
||||
SendMessage(pptView[id].hWnd2, WM_CHAR, 'A', 0);
|
||||
}
|
||||
|
||||
// Go directly to a slide
|
||||
DllExport void GotoSlide(int id, int slideNo)
|
||||
{
|
||||
DEBUG(L"GotoSlide %i %i:\n", id, slideNo);
|
||||
// Did try WM_KEYDOWN/WM_CHAR/WM_KEYUP with SendMessage but didn't work
|
||||
// perhaps I was sending to the wrong window? No idea.
|
||||
// Anyway fall back to keybd_event, which is OK as long we makesure
|
||||
// the slideshow has focus first
|
||||
char ch[10];
|
||||
|
||||
if (slideNo < 0) return;
|
||||
pptView[id].guess = slideNo;
|
||||
_itoa_s(slideNo, ch, 10, 10);
|
||||
HWND h1 = GetForegroundWindow();
|
||||
HWND h2 = GetFocus();
|
||||
SetForegroundWindow(pptView[id].hWnd);
|
||||
SetFocus(pptView[id].hWnd);
|
||||
// slight pause, otherwise event triggering this call may grab focus back!
|
||||
Sleep(50);
|
||||
for (int i=0; i<10; i++)
|
||||
{
|
||||
if (ch[i] == '\0') break;
|
||||
keybd_event((BYTE)ch[i], 0, 0, 0);
|
||||
keybd_event((BYTE)ch[i], 0, KEYEVENTF_KEYUP, 0);
|
||||
}
|
||||
keybd_event(VK_RETURN, 0, 0, 0);
|
||||
keybd_event(VK_RETURN, 0, KEYEVENTF_KEYUP, 0);
|
||||
SetForegroundWindow(h1);
|
||||
SetFocus(h2);
|
||||
}
|
||||
|
||||
// Restart the show from the beginning
|
||||
DllExport void RestartShow(int id)
|
||||
{
|
||||
// If we just go direct to slide one, then it remembers that all other
|
||||
// slides have been animated, so ends up just showing the completed slides
|
||||
// of those slides that have been animated next time we advance.
|
||||
// Only way I've found to get around this is to step backwards all the way
|
||||
// through. Lets move the window out of the way first so the audience
|
||||
// doesn't see this.
|
||||
DEBUG(L"RestartShow:%d\n", id);
|
||||
Stop(id);
|
||||
GotoSlide(id, pptView[id].slideCount);
|
||||
for (int i=0; i <= pptView[id].steps - pptView[id].lastSlideSteps; i++)
|
||||
{
|
||||
PrevStep(id);
|
||||
Sleep(10);
|
||||
}
|
||||
int i = 0;
|
||||
while ((pptView[id].currentSlide > 1) && (i++ < 30000))
|
||||
{
|
||||
Sleep(10);
|
||||
}
|
||||
Resume(id);
|
||||
}
|
||||
|
||||
// This hook is started with the PPTVIEW.EXE process and waits for the
|
||||
// WM_CREATEWND message. At this point (and only this point) can the
|
||||
// window be resized to the correct size.
|
||||
// Release the hook as soon as we're complete to free up resources
|
||||
LRESULT CALLBACK CbtProc(int nCode, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
HHOOK hook = globalHook;
|
||||
if (nCode == HCBT_CREATEWND)
|
||||
{
|
||||
wchar_t csClassName[32];
|
||||
HWND hCurrWnd = (HWND)wParam;
|
||||
DWORD retProcId = NULL;
|
||||
GetClassName(hCurrWnd, csClassName, sizeof(csClassName));
|
||||
if ((wcscmp(csClassName, L"paneClassDC") == 0)
|
||||
||(wcscmp(csClassName, L"screenClass") == 0))
|
||||
{
|
||||
int id = -1;
|
||||
DWORD windowThread = GetWindowThreadProcessId(hCurrWnd, NULL);
|
||||
for (int i=0; i < MAX_PPTS; i++)
|
||||
{
|
||||
if (pptView[i].dwThreadId == windowThread)
|
||||
{
|
||||
id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (id >= 0)
|
||||
{
|
||||
if (wcscmp(csClassName, L"paneClassDC") == 0)
|
||||
{
|
||||
pptView[id].hWnd2 = hCurrWnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
pptView[id].hWnd = hCurrWnd;
|
||||
CBT_CREATEWND* cw = (CBT_CREATEWND*)lParam;
|
||||
if (pptView[id].hParentWnd != NULL)
|
||||
{
|
||||
cw->lpcs->hwndParent = pptView[id].hParentWnd;
|
||||
}
|
||||
cw->lpcs->cy = pptView[id].rect.bottom
|
||||
- pptView[id].rect.top;
|
||||
cw->lpcs->cx = pptView[id].rect.right
|
||||
- pptView[id].rect.left;
|
||||
cw->lpcs->y = -32000;
|
||||
cw->lpcs->x = -32000;
|
||||
}
|
||||
if ((pptView[id].hWnd != NULL) && (pptView[id].hWnd2 != NULL))
|
||||
{
|
||||
UnhookWindowsHookEx(globalHook);
|
||||
globalHook = NULL;
|
||||
pptView[id].hook = SetWindowsHookEx(WH_CALLWNDPROC,
|
||||
CwpProc, hInstance, pptView[id].dwThreadId);
|
||||
pptView[id].msgHook = SetWindowsHookEx(WH_GETMESSAGE,
|
||||
GetMsgProc, hInstance, pptView[id].dwThreadId);
|
||||
Sleep(10);
|
||||
pptView[id].state = PPT_OPENED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return CallNextHookEx(hook, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
// This hook exists whilst the slideshow is loading but only listens on the
|
||||
// slideshows thread. It listens out for mousewheel events
|
||||
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
HHOOK hook = NULL;
|
||||
MSG *pMSG = (MSG *)lParam;
|
||||
DWORD windowThread = GetWindowThreadProcessId(pMSG->hwnd, NULL);
|
||||
int id = -1;
|
||||
for (int i = 0; i < MAX_PPTS; i++)
|
||||
{
|
||||
if (pptView[i].dwThreadId == windowThread)
|
||||
{
|
||||
id = i;
|
||||
hook = pptView[id].msgHook;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (id >= 0 && nCode == HC_ACTION && wParam == PM_REMOVE
|
||||
&& pMSG->message == WM_MOUSEWHEEL)
|
||||
{
|
||||
if (pptView[id].state != PPT_LOADED)
|
||||
{
|
||||
if (pptView[id].currentSlide == 1)
|
||||
{
|
||||
pptView[id].firstSlideSteps++;
|
||||
}
|
||||
pptView[id].steps++;
|
||||
pptView[id].lastSlideSteps++;
|
||||
}
|
||||
}
|
||||
return CallNextHookEx(hook, nCode, wParam, lParam);
|
||||
}
|
||||
// This hook exists whilst the slideshow is running but only listens on the
|
||||
// slideshows thread. It listens out for slide changes, message WM_USER+22.
|
||||
LRESULT CALLBACK CwpProc(int nCode, WPARAM wParam, LPARAM lParam){
|
||||
CWPSTRUCT *cwp;
|
||||
cwp = (CWPSTRUCT *)lParam;
|
||||
HHOOK hook = NULL;
|
||||
wchar_t filename[MAX_PATH];
|
||||
|
||||
DWORD windowThread = GetWindowThreadProcessId(cwp->hwnd, NULL);
|
||||
int id = -1;
|
||||
for (int i = 0; i < MAX_PPTS; i++)
|
||||
{
|
||||
if (pptView[i].dwThreadId == windowThread)
|
||||
{
|
||||
id = i;
|
||||
hook = pptView[id].hook;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((id >= 0) && (nCode == HC_ACTION))
|
||||
{
|
||||
if (cwp->message == WM_USER + 22)
|
||||
{
|
||||
if (pptView[id].state != PPT_LOADED)
|
||||
{
|
||||
if ((pptView[id].currentSlide > 0)
|
||||
&& (pptView[id].previewPath != NULL
|
||||
&& wcslen(pptView[id].previewPath) > 0))
|
||||
{
|
||||
swprintf_s(filename, MAX_PATH, L"%s%i.bmp",
|
||||
pptView[id].previewPath,
|
||||
pptView[id].currentSlide);
|
||||
CaptureAndSaveWindow(cwp->hwnd, filename);
|
||||
}
|
||||
if (((cwp->wParam == 0)
|
||||
|| (pptView[id].slideNos[1] == cwp->wParam))
|
||||
&& (pptView[id].currentSlide > 0))
|
||||
{
|
||||
pptView[id].state = PPT_LOADED;
|
||||
pptView[id].currentSlide = pptView[id].slideCount + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cwp->wParam > 0)
|
||||
{
|
||||
pptView[id].currentSlide = pptView[id].currentSlide + 1;
|
||||
pptView[id].slideNos[pptView[id].currentSlide]
|
||||
= cwp->wParam;
|
||||
pptView[id].slideCount = pptView[id].currentSlide;
|
||||
pptView[id].lastSlideSteps = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cwp->wParam > 0)
|
||||
{
|
||||
if(pptView[id].guess > 0
|
||||
&& pptView[id].slideNos[pptView[id].guess] == 0)
|
||||
{
|
||||
pptView[id].currentSlide = 0;
|
||||
}
|
||||
for(int i = 1; i <= pptView[id].slideCount; i++)
|
||||
{
|
||||
if(pptView[id].slideNos[i] == cwp->wParam)
|
||||
{
|
||||
pptView[id].currentSlide = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(pptView[id].currentSlide == 0)
|
||||
{
|
||||
pptView[id].slideNos[pptView[id].guess] = cwp->wParam;
|
||||
pptView[id].currentSlide = pptView[id].guess;
|
||||
}
|
||||
pptView[id].guess = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((pptView[id].state != PPT_CLOSED)
|
||||
|
||||
&&(cwp->message == WM_CLOSE || cwp->message == WM_QUIT))
|
||||
{
|
||||
pptView[id].state = PPT_CLOSING;
|
||||
}
|
||||
}
|
||||
return CallNextHookEx(hook, nCode, wParam, lParam);
|
||||
}
|
||||
|
||||
VOID CaptureAndSaveWindow(HWND hWnd, wchar_t* filename)
|
||||
{
|
||||
HBITMAP hBmp;
|
||||
if ((hBmp = CaptureWindow(hWnd)) == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
RECT client;
|
||||
GetClientRect(hWnd, &client);
|
||||
UINT uiBytesPerRow = 3 * client.right; // RGB takes 24 bits
|
||||
UINT uiRemainderForPadding;
|
||||
|
||||
if ((uiRemainderForPadding = uiBytesPerRow % sizeof(DWORD)) > 0)
|
||||
uiBytesPerRow += (sizeof(DWORD) - uiRemainderForPadding);
|
||||
|
||||
UINT uiBytesPerAllRows = uiBytesPerRow * client.bottom;
|
||||
PBYTE pDataBits;
|
||||
|
||||
if ((pDataBits = new BYTE[uiBytesPerAllRows]) != NULL)
|
||||
{
|
||||
BITMAPINFOHEADER bmi = {0};
|
||||
BITMAPFILEHEADER bmf = {0};
|
||||
|
||||
// Prepare to get the data out of HBITMAP:
|
||||
bmi.biSize = sizeof(bmi);
|
||||
bmi.biPlanes = 1;
|
||||
bmi.biBitCount = 24;
|
||||
bmi.biHeight = client.bottom;
|
||||
bmi.biWidth = client.right;
|
||||
|
||||
// Get it:
|
||||
HDC hDC = GetDC(hWnd);
|
||||
GetDIBits(hDC, hBmp, 0, client.bottom, pDataBits, (BITMAPINFO*) &bmi,
|
||||
DIB_RGB_COLORS);
|
||||
ReleaseDC(hWnd, hDC);
|
||||
|
||||
// Fill the file header:
|
||||
bmf.bfOffBits = sizeof(bmf) + sizeof(bmi);
|
||||
bmf.bfSize = bmf.bfOffBits + uiBytesPerAllRows;
|
||||
bmf.bfType = 0x4D42;
|
||||
|
||||
// Writing:
|
||||
FILE* pFile;
|
||||
int err = _wfopen_s(&pFile, filename, L"wb");
|
||||
if (err == 0)
|
||||
{
|
||||
fwrite(&bmf, sizeof(bmf), 1, pFile);
|
||||
fwrite(&bmi, sizeof(bmi), 1, pFile);
|
||||
fwrite(pDataBits, sizeof(BYTE), uiBytesPerAllRows, pFile);
|
||||
fclose(pFile);
|
||||
}
|
||||
delete [] pDataBits;
|
||||
}
|
||||
DeleteObject(hBmp);
|
||||
}
|
||||
HBITMAP CaptureWindow(HWND hWnd)
|
||||
{
|
||||
HDC hDC;
|
||||
BOOL bOk = FALSE;
|
||||
HBITMAP hImage = NULL;
|
||||
|
||||
hDC = GetDC(hWnd);
|
||||
RECT rcClient;
|
||||
GetClientRect(hWnd, &rcClient);
|
||||
if ((hImage = CreateCompatibleBitmap(hDC, rcClient.right, rcClient.bottom))
|
||||
!= NULL)
|
||||
{
|
||||
HDC hMemDC;
|
||||
HBITMAP hDCBmp;
|
||||
|
||||
if ((hMemDC = CreateCompatibleDC(hDC)) != NULL)
|
||||
{
|
||||
hDCBmp = (HBITMAP)SelectObject(hMemDC, hImage);
|
||||
HMODULE hLib = LoadLibrary(L"User32");
|
||||
// PrintWindow works for windows outside displayable area
|
||||
// but was only introduced in WinXP. BitBlt requires the window to
|
||||
// be topmost and within the viewable area of the display
|
||||
if (GetProcAddress(hLib, "PrintWindow") == NULL)
|
||||
{
|
||||
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE);
|
||||
BitBlt(hMemDC, 0, 0, rcClient.right, rcClient.bottom, hDC, 0,
|
||||
0, SRCCOPY);
|
||||
SetWindowPos(hWnd, HWND_NOTOPMOST, -32000, -32000, 0, 0,
|
||||
SWP_NOSIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintWindow(hWnd, hMemDC, 0);
|
||||
}
|
||||
SelectObject(hMemDC, hDCBmp);
|
||||
DeleteDC(hMemDC);
|
||||
bOk = TRUE;
|
||||
}
|
||||
}
|
||||
ReleaseDC(hWnd, hDC);
|
||||
if (!bOk)
|
||||
{
|
||||
if (hImage)
|
||||
{
|
||||
DeleteObject(hImage);
|
||||
hImage = NULL;
|
||||
}
|
||||
}
|
||||
return hImage;
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
/******************************************************************************
|
||||
* OpenLP - Open Source Lyrics Projection *
|
||||
* --------------------------------------------------------------------------- *
|
||||
* Copyright (c) 2008-2018 OpenLP Developers *
|
||||
* --------------------------------------------------------------------------- *
|
||||
* This program is free software; you can redistribute it and/or modify it *
|
||||
* under the terms of the GNU General Public License as published by the Free *
|
||||
* Software Foundation; version 2 of the License. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT *
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or *
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for *
|
||||
* more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License along *
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., 59 *
|
||||
* Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
||||
******************************************************************************/
|
||||
|
||||
#define DllExport extern "C" __declspec( dllexport )
|
||||
|
||||
#define DEBUG(...) if (debug) wprintf(__VA_ARGS__)
|
||||
|
||||
enum PPTVIEWSTATE {PPT_CLOSED, PPT_STARTED, PPT_OPENED, PPT_LOADED,
|
||||
PPT_CLOSING};
|
||||
|
||||
DllExport int OpenPPT(wchar_t *filename, HWND hParentWnd, RECT rect,
|
||||
wchar_t *previewPath);
|
||||
DllExport BOOL CheckInstalled();
|
||||
DllExport void ClosePPT(int id);
|
||||
DllExport int GetCurrentSlide(int id);
|
||||
DllExport int GetSlideCount(int id);
|
||||
DllExport void NextStep(int id);
|
||||
DllExport void PrevStep(int id);
|
||||
DllExport void GotoSlide(int id, int slide_no);
|
||||
DllExport void RestartShow(int id);
|
||||
DllExport void Blank(int id);
|
||||
DllExport void Unblank(int id);
|
||||
DllExport void Stop(int id);
|
||||
DllExport void Resume(int id);
|
||||
DllExport void SetDebug(BOOL onOff);
|
||||
|
||||
LRESULT CALLBACK CbtProc(int nCode, WPARAM wParam, LPARAM lParam);
|
||||
LRESULT CALLBACK CwpProc(int nCode, WPARAM wParam, LPARAM lParam);
|
||||
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam);
|
||||
BOOL GetPPTViewerPath(wchar_t *pptViewerPath, int stringSize);
|
||||
BOOL GetPPTViewerPathFromReg(wchar_t *pptViewerPath, int stringSize);
|
||||
HBITMAP CaptureWindow(HWND hWnd);
|
||||
VOID SaveBitmap(wchar_t* filename, HBITMAP hBmp) ;
|
||||
VOID CaptureAndSaveWindow(HWND hWnd, wchar_t* filename);
|
||||
BOOL GetPPTInfo(int id);
|
||||
BOOL SavePPTInfo(int id);
|
||||
void Unhook(int id);
|
||||
|
||||
#define MAX_PPTS 16
|
||||
#define MAX_SLIDES 256
|
||||
|
||||
struct PPTVIEW
|
||||
{
|
||||
HHOOK hook;
|
||||
HHOOK msgHook;
|
||||
HWND hWnd;
|
||||
HWND hWnd2;
|
||||
HWND hParentWnd;
|
||||
HANDLE hProcess;
|
||||
HANDLE hThread;
|
||||
DWORD dwProcessId;
|
||||
DWORD dwThreadId;
|
||||
RECT rect;
|
||||
int slideCount;
|
||||
int currentSlide;
|
||||
int firstSlideSteps;
|
||||
int lastSlideSteps;
|
||||
int steps;
|
||||
int guess;
|
||||
wchar_t filename[MAX_PATH];
|
||||
wchar_t previewPath[MAX_PATH];
|
||||
int slideNos[MAX_SLIDES];
|
||||
PPTVIEWSTATE state;
|
||||
};
|
@ -1,202 +0,0 @@
|
||||
<?xml version="1.0" encoding="Windows-1252"?>
|
||||
<VisualStudioProject
|
||||
ProjectType="Visual C++"
|
||||
Version="9.00"
|
||||
Name="pptviewlib"
|
||||
ProjectGUID="{04CC20D1-DC5A-4189-8181-4011E3C21DCF}"
|
||||
RootNamespace="pptviewlib"
|
||||
Keyword="Win32Proj"
|
||||
TargetFrameworkVersion="196613"
|
||||
>
|
||||
<Platforms>
|
||||
<Platform
|
||||
Name="Win32"
|
||||
/>
|
||||
</Platforms>
|
||||
<ToolFiles>
|
||||
</ToolFiles>
|
||||
<Configurations>
|
||||
<Configuration
|
||||
Name="Debug|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="2"
|
||||
CharacterSet="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;PPTVIEWLIB_EXPORTS"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="3"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
DebugInformationFormat="4"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
LinkIncremental="2"
|
||||
ModuleDefinitionFile=""
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release|Win32"
|
||||
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
|
||||
IntermediateDirectory="$(ConfigurationName)"
|
||||
ConfigurationType="2"
|
||||
CharacterSet="1"
|
||||
WholeProgramOptimization="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="2"
|
||||
EnableIntrinsicFunctions="true"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;PPTVIEWLIB_EXPORTS"
|
||||
RuntimeLibrary="2"
|
||||
EnableFunctionLevelLinking="true"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
LinkIncremental="1"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="2"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
</Configurations>
|
||||
<References>
|
||||
</References>
|
||||
<Files>
|
||||
<Filter
|
||||
Name="Source Files"
|
||||
Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
|
||||
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\pptviewlib.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\README.TXT"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Header Files"
|
||||
Filter="h;hpp;hxx;hm;inl;inc;xsd"
|
||||
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
|
||||
>
|
||||
<File
|
||||
RelativePath=".\pptviewlib.h"
|
||||
>
|
||||
</File>
|
||||
</Filter>
|
||||
<Filter
|
||||
Name="Resource Files"
|
||||
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
|
||||
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
|
||||
>
|
||||
</Filter>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
</VisualStudioProject>
|
Binary file not shown.
Binary file not shown.
@ -44,7 +44,6 @@ __default_settings__ = {'presentations/override app': QtCore.Qt.Unchecked,
|
||||
'presentations/pdf_program': None,
|
||||
'presentations/Impress': QtCore.Qt.Checked,
|
||||
'presentations/Powerpoint': QtCore.Qt.Checked,
|
||||
'presentations/Powerpoint Viewer': QtCore.Qt.Checked,
|
||||
'presentations/Pdf': QtCore.Qt.Checked,
|
||||
'presentations/presentations files': [],
|
||||
'presentations/thumbnail_scheme': '',
|
||||
@ -57,7 +56,7 @@ __default_settings__ = {'presentations/override app': QtCore.Qt.Unchecked,
|
||||
class PresentationPlugin(Plugin):
|
||||
"""
|
||||
This plugin allowed a Presentation to be opened, controlled and displayed on the output display. The plugin controls
|
||||
third party applications such as OpenOffice.org Impress, Microsoft PowerPoint and the PowerPoint viewer.
|
||||
third party applications such as OpenOffice.org Impress, and Microsoft PowerPoint.
|
||||
"""
|
||||
log = logging.getLogger('PresentationPlugin')
|
||||
|
||||
|
@ -321,9 +321,12 @@ class SongMediaItem(MediaManagerItem):
|
||||
:param search_results: A tuple containing (songbook entry, book name, song title, song id)
|
||||
:return: None
|
||||
"""
|
||||
def get_songbook_key(result):
|
||||
"""Get the key to sort by"""
|
||||
return (get_natural_key(result[1]), get_natural_key(result[0]), get_natural_key(result[2]))
|
||||
def get_songbook_key(text_array):
|
||||
"""
|
||||
Get the key to sort by
|
||||
:param text_array: the result text to be processed.
|
||||
"""
|
||||
return get_natural_key(text_array[1]), get_natural_key(text_array[0]), get_natural_key(text_array[2])
|
||||
|
||||
log.debug('display results Book')
|
||||
self.list_view.clear()
|
||||
@ -373,7 +376,7 @@ class SongMediaItem(MediaManagerItem):
|
||||
"""
|
||||
def get_theme_key(song):
|
||||
"""Get the key to sort by"""
|
||||
return (get_natural_key(song.theme_name), song.sort_key)
|
||||
return get_natural_key(song.theme_name), song.sort_key
|
||||
|
||||
log.debug('display results Themes')
|
||||
self.list_view.clear()
|
||||
@ -396,7 +399,7 @@ class SongMediaItem(MediaManagerItem):
|
||||
"""
|
||||
def get_cclinumber_key(song):
|
||||
"""Get the key to sort by"""
|
||||
return (get_natural_key(song.ccli_number), song.sort_key)
|
||||
return get_natural_key(song.ccli_number), song.sort_key
|
||||
|
||||
log.debug('display results CCLI number')
|
||||
self.list_view.clear()
|
||||
@ -460,6 +463,8 @@ class SongMediaItem(MediaManagerItem):
|
||||
"""
|
||||
Called by ServiceManager or SlideController by event passing the Song Id in the payload along with an indicator
|
||||
to say which type of display is required.
|
||||
:param song_id: the id of the song
|
||||
:param preview: show we preview after the update
|
||||
"""
|
||||
log.debug('on_remote_edit for song {song}'.format(song=song_id))
|
||||
song_id = int(song_id)
|
||||
@ -721,7 +726,8 @@ class SongMediaItem(MediaManagerItem):
|
||||
self.generate_footer(item, song)
|
||||
return item
|
||||
|
||||
def _authors_match(self, song, authors):
|
||||
@staticmethod
|
||||
def _authors_match(song, authors):
|
||||
"""
|
||||
Checks whether authors from a song in the database match the authors of the song to be imported.
|
||||
|
||||
@ -738,11 +744,12 @@ class SongMediaItem(MediaManagerItem):
|
||||
# List must be empty at the end
|
||||
return not author_list
|
||||
|
||||
def search(self, string, show_error):
|
||||
def search(self, string, show_error=True):
|
||||
"""
|
||||
Search for some songs
|
||||
:param string: The string to show
|
||||
:param show_error: Is this an error?
|
||||
:return: the results of the search
|
||||
"""
|
||||
search_results = self.search_entire(string)
|
||||
return [[song.id, song.title, song.alternate_title] for song in search_results]
|
||||
|
54
tests/functional/openlp_core/api/endpoint/test_controller.py
Normal file
54
tests/functional/openlp_core/api/endpoint/test_controller.py
Normal file
@ -0,0 +1,54 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2018 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
from unittest import TestCase
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.api.endpoint.controller import controller_text
|
||||
|
||||
|
||||
class TestController(TestCase):
|
||||
"""
|
||||
Test the Remote plugin deploy functions
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Setup for tests
|
||||
"""
|
||||
Registry.create()
|
||||
self.registry = Registry()
|
||||
self.mocked_live_controller = MagicMock()
|
||||
Registry().register('live_controller', self.mocked_live_controller)
|
||||
|
||||
def test_controller_text(self):
|
||||
"""
|
||||
Remote Deploy tests - test the dummy zip file is processed correctly
|
||||
"""
|
||||
# GIVEN: A mocked service with a dummy service item
|
||||
self.mocked_live_controller.service_item = MagicMock()
|
||||
# WHEN: I trigger the method
|
||||
ret = controller_text("SomeText")
|
||||
# THEN: I get a basic set of results
|
||||
results = ret['results']
|
||||
assert isinstance(results['item'], MagicMock)
|
||||
assert len(results['slides']) == 0
|
@ -167,7 +167,7 @@ class TestMediaItem(TestCase, TestMixin):
|
||||
# THEN: It should be a subclass of :class:`MediaManagerItem`
|
||||
assert isinstance(self.media_item, MediaManagerItem)
|
||||
|
||||
def test_steup_item(self):
|
||||
def test_setup_item(self):
|
||||
"""
|
||||
Test the setup_item method
|
||||
"""
|
||||
|
@ -1,226 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2018 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
This module contains tests for the pptviewcontroller module of the Presentations plugin.
|
||||
"""
|
||||
import shutil
|
||||
from tempfile import mkdtemp
|
||||
from unittest import TestCase, skipIf
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from openlp.core.common import is_win
|
||||
from openlp.core.common.path import Path
|
||||
from openlp.plugins.presentations.lib.pptviewcontroller import PptviewDocument, PptviewController
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
from tests.utils.constants import RESOURCE_PATH
|
||||
|
||||
|
||||
class TestPptviewController(TestCase, TestMixin):
|
||||
"""
|
||||
Test the PptviewController Class
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up the patches and mocks need for all tests.
|
||||
"""
|
||||
self.setup_application()
|
||||
self.build_settings()
|
||||
self.mock_plugin = MagicMock()
|
||||
self.temp_folder = mkdtemp()
|
||||
self.mock_plugin.settings_section = self.temp_folder
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Stop the patches
|
||||
"""
|
||||
self.destroy_settings()
|
||||
shutil.rmtree(self.temp_folder)
|
||||
|
||||
def test_constructor(self):
|
||||
"""
|
||||
Test the Constructor from the PptViewController
|
||||
"""
|
||||
# GIVEN: No presentation controller
|
||||
controller = None
|
||||
|
||||
# WHEN: The presentation controller object is created
|
||||
controller = PptviewController(plugin=self.mock_plugin)
|
||||
|
||||
# THEN: The name of the presentation controller should be correct
|
||||
assert 'Powerpoint Viewer' == controller.name, 'The name of the presentation controller should be correct'
|
||||
|
||||
def test_check_available(self):
|
||||
"""
|
||||
Test check_available / check_installed
|
||||
"""
|
||||
# GIVEN: A mocked dll loader and a controller
|
||||
with patch('ctypes.cdll.LoadLibrary') as mocked_load_library:
|
||||
mocked_process = MagicMock()
|
||||
mocked_process.CheckInstalled.return_value = True
|
||||
mocked_load_library.return_value = mocked_process
|
||||
controller = PptviewController(plugin=self.mock_plugin)
|
||||
|
||||
# WHEN: check_available is called
|
||||
available = controller.check_available()
|
||||
|
||||
# THEN: On windows it should return True, on other platforms False
|
||||
if is_win():
|
||||
assert available is True, 'check_available should return True on windows.'
|
||||
else:
|
||||
assert available is False, 'check_available should return False when not on windows.'
|
||||
|
||||
|
||||
class TestPptviewDocument(TestCase):
|
||||
"""
|
||||
Test the PptviewDocument Class
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up the patches and mocks need for all tests.
|
||||
"""
|
||||
self.pptview_document_create_thumbnails_patcher = patch(
|
||||
'openlp.plugins.presentations.lib.pptviewcontroller.PptviewDocument.create_thumbnails')
|
||||
self.pptview_document_stop_presentation_patcher = patch(
|
||||
'openlp.plugins.presentations.lib.pptviewcontroller.PptviewDocument.stop_presentation')
|
||||
self.presentation_document_get_temp_folder_patcher = patch(
|
||||
'openlp.plugins.presentations.lib.pptviewcontroller.PresentationDocument.get_temp_folder')
|
||||
self.presentation_document_setup_patcher = patch(
|
||||
'openlp.plugins.presentations.lib.pptviewcontroller.PresentationDocument._setup')
|
||||
self.screen_list_patcher = patch('openlp.plugins.presentations.lib.pptviewcontroller.ScreenList')
|
||||
self.rect_patcher = MagicMock()
|
||||
self.mock_pptview_document_create_thumbnails = self.pptview_document_create_thumbnails_patcher.start()
|
||||
self.mock_pptview_document_stop_presentation = self.pptview_document_stop_presentation_patcher.start()
|
||||
self.mock_presentation_document_get_temp_folder = self.presentation_document_get_temp_folder_patcher.start()
|
||||
self.mock_presentation_document_setup = self.presentation_document_setup_patcher.start()
|
||||
self.mock_rect = self.rect_patcher.start()
|
||||
self.mock_screen_list = self.screen_list_patcher.start()
|
||||
self.mock_controller = MagicMock()
|
||||
self.mock_presentation = MagicMock()
|
||||
self.temp_folder = mkdtemp()
|
||||
self.mock_presentation_document_get_temp_folder.return_value = self.temp_folder
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Stop the patches
|
||||
"""
|
||||
self.pptview_document_create_thumbnails_patcher.stop()
|
||||
self.pptview_document_stop_presentation_patcher.stop()
|
||||
self.presentation_document_get_temp_folder_patcher.stop()
|
||||
self.presentation_document_setup_patcher.stop()
|
||||
self.rect_patcher.stop()
|
||||
self.screen_list_patcher.stop()
|
||||
shutil.rmtree(self.temp_folder)
|
||||
|
||||
@skipIf(not is_win(), 'Not Windows')
|
||||
def test_load_presentation_succesful(self):
|
||||
"""
|
||||
Test the PptviewDocument.load_presentation() method when the PPT is successfully opened
|
||||
"""
|
||||
# GIVEN: A reset mocked_os
|
||||
self.mock_controller.process.OpenPPT.return_value = 0
|
||||
instance = PptviewDocument(self.mock_controller, self.mock_presentation)
|
||||
instance.file_path = 'test\path.ppt'
|
||||
|
||||
# WHEN: The temporary directory exists and OpenPPT returns successfully (not -1)
|
||||
result = instance.load_presentation()
|
||||
|
||||
# THEN: PptviewDocument.load_presentation should return True
|
||||
assert result is True
|
||||
|
||||
@skipIf(not is_win(), 'Not Windows')
|
||||
def test_load_presentation_un_succesful(self):
|
||||
"""
|
||||
Test the PptviewDocument.load_presentation() method when the temporary directory does not exist and the PPT is
|
||||
not successfully opened
|
||||
"""
|
||||
# GIVEN: A reset mock_os_isdir
|
||||
self.mock_controller.process.OpenPPT.return_value = -1
|
||||
instance = PptviewDocument(self.mock_controller, self.mock_presentation)
|
||||
instance.file_path = 'test\path.ppt'
|
||||
|
||||
# WHEN: The temporary directory does not exist and OpenPPT returns unsuccessfully (-1)
|
||||
with patch.object(instance, 'get_temp_folder') as mocked_get_folder:
|
||||
mocked_get_folder.return_value = MagicMock(spec=Path)
|
||||
result = instance.load_presentation()
|
||||
|
||||
# THEN: The temp folder should be created and PptviewDocument.load_presentation should return False
|
||||
assert result is False
|
||||
|
||||
def test_create_titles_and_notes(self):
|
||||
"""
|
||||
Test PowerpointController.create_titles_and_notes
|
||||
"""
|
||||
# GIVEN: mocked PresentationController.save_titles_and_notes and a pptx file
|
||||
doc = PptviewDocument(self.mock_controller, self.mock_presentation)
|
||||
doc.file_path = RESOURCE_PATH / 'presentations' / 'test.pptx'
|
||||
doc.save_titles_and_notes = MagicMock()
|
||||
|
||||
# WHEN reading the titles and notes
|
||||
doc.create_titles_and_notes()
|
||||
|
||||
# THEN save_titles_and_notes should have been called once with empty arrays
|
||||
doc.save_titles_and_notes.assert_called_once_with(['Test 1\n', '\n', 'Test 2\n', 'Test 4\n', 'Test 3\n'],
|
||||
['Notes for slide 1', 'Inserted', 'Notes for slide 2',
|
||||
'Notes \nfor slide 4', 'Notes for slide 3'])
|
||||
|
||||
def test_create_titles_and_notes_nonexistent_file(self):
|
||||
"""
|
||||
Test PowerpointController.create_titles_and_notes with nonexistent file
|
||||
"""
|
||||
# GIVEN: mocked PresentationController.save_titles_and_notes and an nonexistent file
|
||||
with patch('builtins.open') as mocked_open, \
|
||||
patch.object(Path, 'exists') as mocked_path_exists, \
|
||||
patch('openlp.plugins.presentations.lib.presentationcontroller.create_paths') as \
|
||||
mocked_dir_exists:
|
||||
mocked_path_exists.return_value = False
|
||||
mocked_dir_exists.return_value = False
|
||||
doc = PptviewDocument(self.mock_controller, self.mock_presentation)
|
||||
doc.file_path = Path('Idontexist.pptx')
|
||||
doc.save_titles_and_notes = MagicMock()
|
||||
|
||||
# WHEN: Reading the titles and notes
|
||||
doc.create_titles_and_notes()
|
||||
|
||||
# THEN: File existens should have been checked, and not have been opened.
|
||||
doc.save_titles_and_notes.assert_called_once_with(None, None)
|
||||
mocked_path_exists.assert_called_with()
|
||||
assert mocked_open.call_count == 0, 'There should be no calls to open a file.'
|
||||
|
||||
def test_create_titles_and_notes_invalid_file(self):
|
||||
"""
|
||||
Test PowerpointController.create_titles_and_notes with invalid file
|
||||
"""
|
||||
# GIVEN: mocked PresentationController.save_titles_and_notes and an invalid file
|
||||
with patch('builtins.open') as mocked_open, \
|
||||
patch('openlp.plugins.presentations.lib.pptviewcontroller.zipfile.is_zipfile') as mocked_is_zf:
|
||||
mocked_is_zf.return_value = False
|
||||
mocked_open.filesize = 10
|
||||
doc = PptviewDocument(self.mock_controller, self.mock_presentation)
|
||||
doc.file_path = RESOURCE_PATH / 'presentations' / 'test.ppt'
|
||||
doc.save_titles_and_notes = MagicMock()
|
||||
|
||||
# WHEN: reading the titles and notes
|
||||
doc.create_titles_and_notes()
|
||||
|
||||
# THEN:
|
||||
doc.save_titles_and_notes.assert_called_once_with(None, None)
|
||||
assert mocked_is_zf.call_count == 1, 'is_zipfile should have been called once'
|
Loading…
Reference in New Issue
Block a user