diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py index 1c1186feb..947a9ce6c 100644 --- a/openlp/core/common/settings.py +++ b/openlp/core/common/settings.py @@ -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 diff --git a/openlp/core/ui/aboutdialog.py b/openlp/core/ui/aboutdialog.py index d60f0ee0c..b162d0586 100644 --- a/openlp/core/ui/aboutdialog.py +++ b/openlp/core/ui/aboutdialog.py @@ -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' diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py deleted file mode 100644 index 18fad273a..000000000 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ /dev/null @@ -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) diff --git a/openlp/plugins/presentations/lib/pptviewlib/README.TXT b/openlp/plugins/presentations/lib/pptviewlib/README.TXT deleted file mode 100644 index 85bbf7954..000000000 --- a/openlp/plugins/presentations/lib/pptviewlib/README.TXT +++ /dev/null @@ -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. - diff --git a/openlp/plugins/presentations/lib/pptviewlib/ppttest.py b/openlp/plugins/presentations/lib/pptviewlib/ppttest.py deleted file mode 100644 index 008e66164..000000000 --- a/openlp/plugins/presentations/lib/pptviewlib/ppttest.py +++ /dev/null @@ -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()) diff --git a/openlp/plugins/presentations/lib/pptviewlib/pptviewlib.cpp b/openlp/plugins/presentations/lib/pptviewlib/pptviewlib.cpp deleted file mode 100644 index 8a7da02a2..000000000 --- a/openlp/plugins/presentations/lib/pptviewlib/pptviewlib.cpp +++ /dev/null @@ -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 -#include -#include -#include -#include -#include -#include -#include -#include -#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. -// ".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; -} diff --git a/openlp/plugins/presentations/lib/pptviewlib/pptviewlib.h b/openlp/plugins/presentations/lib/pptviewlib/pptviewlib.h deleted file mode 100644 index 6411ae828..000000000 --- a/openlp/plugins/presentations/lib/pptviewlib/pptviewlib.h +++ /dev/null @@ -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; -}; diff --git a/openlp/plugins/presentations/lib/pptviewlib/pptviewlib.vcproj b/openlp/plugins/presentations/lib/pptviewlib/pptviewlib.vcproj deleted file mode 100644 index d2843e510..000000000 --- a/openlp/plugins/presentations/lib/pptviewlib/pptviewlib.vcproj +++ /dev/null @@ -1,202 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/openlp/plugins/presentations/lib/pptviewlib/test.ppt b/openlp/plugins/presentations/lib/pptviewlib/test.ppt deleted file mode 100644 index 1d90168b1..000000000 Binary files a/openlp/plugins/presentations/lib/pptviewlib/test.ppt and /dev/null differ diff --git a/openlp/plugins/presentations/lib/pptviewlib/test.pptx b/openlp/plugins/presentations/lib/pptviewlib/test.pptx deleted file mode 100644 index c8beab172..000000000 Binary files a/openlp/plugins/presentations/lib/pptviewlib/test.pptx and /dev/null differ diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index b0fd8d7cf..fb0cbee94 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -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') diff --git a/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py b/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py deleted file mode 100644 index 5e6a7abdd..000000000 --- a/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py +++ /dev/null @@ -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'