← Back to team overview

openlp-core team mailing list archive

Re: [Merge] lp:~raoul-snyman/openlp/powerpointmac into lp:openlp

 

Review: Needs Fixing

See Inline
Unable to test!

Diff comments:

> === modified file 'openlp/plugins/presentations/lib/messagelistener.py'
> --- openlp/plugins/presentations/lib/messagelistener.py	2014-03-08 21:23:47 +0000
> +++ openlp/plugins/presentations/lib/messagelistener.py	2014-07-06 20:44:24 +0000
> @@ -98,7 +98,7 @@
>              return True
>          if not self.doc.is_loaded():
>              if not self.doc.load_presentation():
> -                log.warn('Failed to activate %s' % self.doc.filepath)
> +                log.warn('Failed to activate %s' % self.doc.file_path)
>                  return False
>          if self.is_live:
>              self.doc.start_presentation()
> @@ -109,7 +109,7 @@
>          if self.doc.is_active():
>              return True
>          else:
> -            log.warn('Failed to activate %s' % self.doc.filepath)
> +            log.warn('Failed to activate %s' % self.doc.file_path)
>              return False
>  
>      def slide(self, slide):
> 
> === modified file 'openlp/plugins/presentations/lib/powerpointcontroller.py'
> --- openlp/plugins/presentations/lib/powerpointcontroller.py	2014-04-12 20:19:22 +0000
> +++ openlp/plugins/presentations/lib/powerpointcontroller.py	2014-07-06 20:44:24 +0000
> @@ -27,8 +27,9 @@
>  # Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
>  ###############################################################################
>  """
> -This modul is for controlling powerpiont. PPT API documentation:
> -`http://msdn.microsoft.com/en-us/library/aa269321(office.10).aspx`_
> +This module is for controlling Microsoft PowerPoint on Windows.
> +
> +See the PPT API documentation: `http://msdn.microsoft.com/en-us/library/aa269321(office.10).aspx`_
>  """
>  import os
>  import logging
> 
> === added file 'openlp/plugins/presentations/lib/powerpointmaccontroller.py'
> --- openlp/plugins/presentations/lib/powerpointmaccontroller.py	1970-01-01 00:00:00 +0000
> +++ openlp/plugins/presentations/lib/powerpointmaccontroller.py	2014-07-06 20:44:24 +0000
> @@ -0,0 +1,412 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection                                      #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2012 Raoul Snyman                                        #
> +# Portions copyright (c) 2008-2012 Tim Bentley, Gerald Britton, Jonathan      #
> +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
> +# Meinert Jordan, Armin Köhler, Eric Ludin, Edwin Lunando, Brian T. Meyer,    #
> +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #
> +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith,             #
> +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock,              #
> +# Frode Woldsund, Martin Zibricky                                             #
> +# --------------------------------------------------------------------------- #
> +# 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 os
> +import sys
> +import logging
> +import shutil
> +
> +if sys.platform == 'darwin':
> +    from appscript import ApplicationNotFoundError, CantLaunchApplicationError, CommandError, app, k
> +
> +from openlp.core.common import Settings
> +from openlp.core.lib import ScreenList
> +from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument
> +
> +
> +log = logging.getLogger(__name__)
> +
> +
> +class PowerPointMacController(PresentationController):
> +    """
> +    Class to control interactions with PowerPoint 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('PowerpointController loaded')
> +
> +    def __init__(self, plugin):
> +        """
> +        Initialise the class
> +        """
> +        log.debug('Initialising')
> +        super(PowerPointMacController, self).__init__(plugin, 'PowerPoint for Mac', PowerPointMacDocument)
> +        self.supports = ['ppt', 'pps', 'pptx', 'ppsx']
> +        self.process = None
> +
> +    def check_available(self):
> +        """
> +        PowerPoint is able to run on this machine
> +        """
> +        log.debug('check_available')
> +        result = False
> +        if sys.platform == 'darwin':
> +            try:
> +                self.process = app(id='com.microsoft.powerpoint')
> +                result = True
> +            except ApplicationNotFoundError:
> +                pass
> +            finally:
> +                self.kill()
> +        return result
> +
> +    # Check if OpenLP is running on Mac OS, and then declare these methods
> +    # Not sure why we do this (surely there's a better way)
> +    if sys.platform == 'darwin':
> +
> +        def start_process(self):
> +            """
> +            Loads PowerPoint process
> +            """
> +            log.debug('start_process')
> +            if not self.process:
> +                self.process = app(id='com.microsoft.powerpoint')
> +                if not self.process.isrunning():
> +                    self.process.relaunchmode = 'limited'  # 'always'
> +                    self.process.launch()
> +                    self.apply_app_settings()
> +
> +        def kill(self):
> +            """
> +            Called at system exit to clean up any running presentations
> +            """
> +            log.debug('Kill Powerpoint')
> +            while self.docs and self.process.isrunning():
> +                self.docs[0].close_presentation()
> +            if self.process is None or not self.process.isrunning():
> +                return
> +            try:
> +                total = self.process.presentations()
> +                if self.process and total != k.missing_value and len(total) > 0:
> +                    return
> +                self.process.quit(saving=k.ask)
> +            except CantLaunchApplicationError:
> +                log.debug('Kill Keynote failed')
> +            self.process = None
> +
> +        def apply_app_settings(self):
> +            """
> +            Apply settings for PowerPoint
> +            14\Options\Options\Save graphics screen heigth = 240
> +            14\Options\Options\Save graphics screen width = 320
> +            14\Options\Options\Save only current slide graphics = 0
> +
> +            """
> +            # TODO: add settings for different versions of MS PPT
> +            pass
> +
> +
> +class PowerPointMacDocument(PresentationDocument):
> +    """
> +    Class which holds information and controls a single presentation
> +    """
> +
> +    def __init__(self, controller, presentation):
> +        """
> +        Constructor, store information about the file and initialise
> +        """
> +        log.debug('Init Presentation Powerpoint')
> +        super(PowerPointMacDocument, self).__init__(controller, presentation)
> +        self.presentation = None
> +
> +    def load_presentation(self):
> +        """
> +        Called when a presentation is added to the SlideController.
> +        Opens the PowerPoint file using the process created earlier.
> +        """
> +        log.debug('load_presentation')
> +        if not self.controller.process or not self.controller.process.isrunning():
> +            self.controller.start_process()
> +        try:
> +            self.controller.process.open(self.file_path)
> +        except CommandError:
> +            log.debug('PPT open failed')
> +            return False
> +        presentations = self.controller.process.presentations()
> +        for presentation in presentations:
> +            full_name = presentation.full_name()
> +            full_name = full_name.replace('Macintosh HD', '')
> +            full_name = full_name.replace(':', '/')
> +            if self.file_path == full_name:
> +                self.presentation = presentation
> +                self.create_thumbnails()
> +                return True
> +        return False
> +
> +    def create_thumbnails(self):
> +        """
> +        Create the thumbnail images for the current presentation.
> +        """
> +        log.debug('create_thumbnails')
> +        if self.check_thumbnails():
> +            return
> +        thumbnail_folder = self.get_thumbnail_folder()
> +        temp_dir = self.get_temp_folder()
> +        self.presentation.save(in_=temp_dir, as_=k.save_as_PNG)
> +        slide_no = 0
> +        self.move_thumbnails(thumbnail_folder, slide_no, temp_dir)
> +
> +    def move_thumbnails(self, thumbnail_folder, slide_no, temp_dir):
> +        """
> +        Move the thumbnail images from subdirectories to right path.
> +
> +        :param thumbnail_folder:
> +        :param slide_no:
> +        :param temp_dir:
> +        """
> +        if not os.path.isdir(temp_dir):
> +            return
> +        for filename in os.listdir(temp_dir):
> +            full_filename = os.path.join(temp_dir, filename)
> +            if os.path.isdir(full_filename):
> +                self.move_thumbnails(thumbnail_folder, slide_no, full_filename)
> +            elif os.path.isfile(full_filename) and filename.endswith('.png') and not filename == 'icon.png':
> +                slide_no += 1
> +                new_name = os.path.join(thumbnail_folder, self.controller.thumbnail_prefix + str(slide_no) + '.png')
> +                if full_filename == new_name \
> +                        or full_filename.startswith(os.path.join(thumbnail_folder, self.controller.thumbnail_prefix)):
> +                    continue
> +                try:
> +                    os.renames(full_filename, new_name)
> +                except:
> +                    open(new_name, 'w').write(open(full_filename, 'r').read())
> +                    os.unlink(full_filename)
> +        if temp_dir != thumbnail_folder:
> +            try:
> +                shutil.rmtree(temp_dir)
> +            except:
> +                pass
> +
> +    def close_presentation(self):
> +        """
> +        Close presentation and clean up objects. This is triggered by a new
> +        object being added to SlideController or OpenLP being shut down.
> +        """
> +        log.debug('close_presentation')
> +        if self.presentation:
> +            try:
> +                self.presentation.close()
> +            except CommandError:
> +                log.debug('Could not close the presentation')
> +        self.presentation = None
> +        self.controller.remove_doc(self)
> +
> +    def is_loaded(self):
> +        """
> +        Returns ``True`` if a presentation is loaded.
> +        """
> +        log.debug('is_loaded')
> +        try:
> +            if not self.controller.process.isrunning():
> +                return False
> +            if len(self.controller.process.document_windows()) == 0:
> +                return False
> +            presentations = self.controller.process.presentations()
> +            if len(presentations) == 0:
> +                return False
> +        except:
> +            return False
> +        for presentation in presentations:
> +            full_name = presentation.full_name()
> +            full_name = full_name.replace('Macintosh HD', '')
> +            full_name = full_name.replace(':', '/')
> +            if self.file_path == full_name:
> +                return True
> +        return False
> +
> +    def is_active(self):
> +        """
> +        Returns ``True`` if a presentation is currently active.
> +        """
> +        log.debug('is_active')
> +        if not self.is_loaded():
> +            return False
> +        try:
> +            slide_show_window = self.presentation.slide_show_window
> +            if slide_show_window is None:
> +                return False
> +            slide_state = self.presentation.slide_show_window.slideshow_view.slide_state()
> +            if slide_state is None or slide_state == k.missing_value:
> +                return False
> +        except:
> +            return False
> +        return True
> +
> +    def unblank_screen(self):
> +        """
> +        Unblanks (restores) the presentation.
> +        """
> +        log.debug('unblank_screen')
> +        if self.is_blank():
> +            self.presentation.slide_show_window.slideshow_view.slide_state.set(k.slide_show_state_running)
> +            self.presentation.slide_show_window.slideshow_view.go_to_next_slide()
> +            self.presentation.slide_show_window.slideshow_view.go_to_previous_slide()
> +
> +    def blank_screen(self):
> +        """
> +        Blanks the screen.
> +        """
> +        log.debug('blank_screen')
> +        self.presentation.slide_show_window.slideshow_view.slide_state.set(k.slide_show_state_black_screen)
> +
> +    def is_blank(self):
> +        """
> +        Returns ``True`` if screen is blank.
> +        """
> +        log.debug('is_blank')
> +        if self.is_active() and self.presentation.slide_show_window.slideshow_view.slide_state() \
> +                is k.slide_show_state_black_screen:
> +            return True
> +        else:
> +            return False
> +
> +    def stop_presentation(self):
> +        """
> +        Stops the current presentation and hides the output.
> +        """
> +        log.debug('stop_presentation')
> +        self.presentation.slide_show_window.slideshow_view.exit_slide_show()
> +
> +    def start_presentation(self):
> +        """
> +        Starts a presentation from the beginning.
> +        """
> +        log.debug('start_presentation')
> +        rect = ScreenList().current['size']
> +        ppt_settings = self.presentation.slide_show_settings
> +        openlp_settings = Settings("openlp.org", "OpenLP")
> +        show_presenter_view = openlp_settings.value(self.controller.plugin.settings_section + '/show presenter view')
> +        override_position = openlp_settings.value('core/override position')
> +        if show_presenter_view:
> +            ppt_settings.show_type.set(k.slide_show_type_presenter)
> +        elif override_position:
> +            ppt_settings.show_type.set(k.slide_show_type_window)
> +        else:
> +            ppt_settings.show_type.set(k.slide_show_type_kiosk)
> +        ppt_window = ppt_settings.run_slide_show()
> +        # TODO: set slideshow state = running
> +        #if ppt_window.slideshow_view.slide_state() != appscript.k.slide_show_state_running:
> +        #   ppt_window.slideshow_view.slide_state.set(appscript.k.slide_show_state_running)
> +        if not ppt_window or show_presenter_view:
> +            return
> +        if override_position:
> +            top = float(rect.y())
> +            height = float(rect.height())
> +            left_position = float(rect.x())
> +            width = float(rect.width())
> +            ppt_window.top.set(top)
> +            ppt_window.height.set(height)
> +            ppt_window.left_position.set(left_position)
> +            ppt_window.width.set(width)
> +        else:
> +            # TODO: set output monitor for fullscreen mode
> +            pass
> +
> +    def get_slide_number(self):
> +        """
> +        Returns the current slide number.
> +        """
> +        log.debug('get_slide_number')
> +        return self.presentation.slide_show_window.slideshow_view.current_show_position()
> +
> +    def get_slide_count(self):
> +        """
> +        Returns total number of slides.
> +        """
> +        log.debug('get_slide_count')
> +        return len(self.presentation.slides())
> +
> +    def goto_slide(self, slideno):
> +        """
> +        Moves to a specific slide in the presentation.
> +
> +        :param slideno:
> +        """
> +        log.debug('goto_slide')
> +        # FIXME: this works. but needs to fix this code
> +        while self.get_slide_number() != slideno:
> +            if self.get_slide_number() < slideno:
> +                self.presentation.slide_show_window.slideshow_view.go_to_next_slide()
> +            else:
> +                self.presentation.slide_show_window.slideshow_view.go_to_previous_slide()
> +        self.controller.process.activate()
> +
> +    def next_step(self):
> +        """
> +        Triggers the next effect of slide on the running presentation.
> +        """
> +        log.debug('next_step')
> +        # TODO: check if slideshow stopped then restart it from current position
> +        if self.is_active():
> +            self.presentation.slide_show_window.slideshow_view.go_to_next_slide()
> +            self.controller.process.activate()
> +            if self.get_slide_number() > self.get_slide_count():
> +                self.previous_step()
> +
> +    def previous_step(self):
> +        """
> +        Triggers the previous slide on the running presentation.
> +        """
> +        log.debug('previous_step')
> +        # TODO: check if slideshow stopped then restart it from current position
> +        if self.is_active():
> +            self.presentation.slide_show_window.slideshow_view.go_to_previous_slide()
> +            self.controller.process.activate()
> +
> +    def get_slide_text(self, slide_no):
> +        """
> +        Returns the text on the slide.
> +
> +        :param slide_no: The slide the text is required for, starting at 1.
> +        """
> +        return _get_text_from_shapes(self.presentation.slides[slide_no - 1].shapes)
> +
> +    def get_slide_notes(self, slide_no):
> +        """
> +        Returns the text on the slide.
> +
> +        :param slide_no: The slide the notes are required for, starting at 1.
> +        """
> +        return _get_text_from_shapes(
> +            self.presentation.slides[slide_no].notes_page.shapes())
> +
> +
> +def _get_text_from_shapes(shapes):
> +    """
> +    Returns any text extracted from the shapes on a presentation slide.
> +
> +    ``shapes``
> +        A set of shapes to search for text.
> +    """
> +    text = ''
> +    for idx in range(len(shapes)):
> +        shape = shapes[idx + 1]
> +        if shape.has_text_frame():
> +            text += shape.text_frame.text_range.content() + '\n'
> +    return text
> 
> === modified file 'openlp/plugins/presentations/lib/presentationtab.py'
> --- openlp/plugins/presentations/lib/presentationtab.py	2014-04-12 20:19:22 +0000
> +++ openlp/plugins/presentations/lib/presentationtab.py	2014-07-06 20:44:24 +0000
> @@ -27,6 +27,8 @@
>  # Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
>  ###############################################################################
>  
> +import sys
> +
>  from PyQt4 import QtGui
>  
>  from openlp.core.common import Settings, UiStrings, translate
> @@ -39,6 +41,7 @@
>      """
>      PresentationsTab is the Presentations settings tab in the settings dialog.
>      """
> +
>      def __init__(self, parent, title, visible_title, controllers, icon_path):
>          """
>          Constructor
> @@ -74,6 +77,11 @@
>          self.override_app_check_box = QtGui.QCheckBox(self.advanced_group_box)
>          self.override_app_check_box.setObjectName('override_app_check_box')
>          self.advanced_layout.addWidget(self.override_app_check_box)
> +        self.show_presenter_view_check_box = QtGui.QCheckBox(self.advanced_group_box)
> +        self.show_presenter_view_check_box.setObjectName(u'show_presenter_view_check_box')
> +        self.advanced_layout.addWidget(self.show_presenter_view_check_box)
> +        if not sys.platform.startswith('darwin'):
> +            self.show_presenter_view_check_box.setVisible(False)
>          self.left_layout.addWidget(self.advanced_group_box)
>          # Pdf options
>          self.pdf_group_box = QtGui.QGroupBox(self.left_column)
> @@ -117,6 +125,8 @@
>          self.pdf_group_box.setTitle(translate('PresentationPlugin.PresentationTab', 'PDF options'))
>          self.override_app_check_box.setText(
>              translate('PresentationPlugin.PresentationTab', 'Allow presentation application to be overridden'))
> +        self.show_presenter_view_check_box.setText(translate('PresentationPlugin.PresentationTab',
> +                                                             'Show Presenter View'))
>          self.pdf_program_check_box.setText(
>              translate('PresentationPlugin.PresentationTab', 'Use given full path for mudraw or ghostscript binary:'))
>  
> @@ -135,6 +145,7 @@
>              checkbox = self.presenter_check_boxes[controller.name]
>              checkbox.setChecked(Settings().value(self.settings_section + '/' + controller.name))
>          self.override_app_check_box.setChecked(Settings().value(self.settings_section + '/override app'))
> +        self.show_presenter_view_check_box.setChecked(Settings().value(self.settings_section + '/show presenter view'))
>          # load pdf-program settings
>          enable_pdf_program = Settings().value(self.settings_section + '/enable_pdf_program')
>          self.pdf_program_check_box.setChecked(enable_pdf_program)
> @@ -168,6 +179,10 @@
>          if Settings().value(setting_key) != self.override_app_check_box.checkState():
>              Settings().setValue(setting_key, self.override_app_check_box.checkState())
>              changed = True
> +        setting_key = self.settings_section + '/show presenter view'
> +        if Settings().value(setting_key) != self.show_presenter_view_check_box.checkState():
> +            Settings().setValue(setting_key, self.show_presenter_view_check_box.isChecked())
> +            changed = True
>          # Save pdf-settings
>          pdf_program = self.pdf_program_path.text()
>          enable_pdf_program = self.pdf_program_check_box.checkState()
> 
> === modified file 'openlp/plugins/presentations/presentationplugin.py'
> --- openlp/plugins/presentations/presentationplugin.py	2014-04-12 20:19:22 +0000
> +++ openlp/plugins/presentations/presentationplugin.py	2014-07-06 20:44:24 +0000
> @@ -31,6 +31,7 @@
>  formats.
>  """
>  import os
> +import sys
>  import logging
>  
>  from PyQt4 import QtCore
> @@ -42,14 +43,19 @@
>  
>  log = logging.getLogger(__name__)
>  
> -
>  __default_settings__ = {'presentations/override app': QtCore.Qt.Unchecked,
>                          'presentations/enable_pdf_program': QtCore.Qt.Unchecked,
>                          'presentations/pdf_program': '',
>                          'presentations/Impress': QtCore.Qt.Checked,
>                          'presentations/Powerpoint': QtCore.Qt.Checked,
>                          'presentations/Powerpoint Viewer': QtCore.Qt.Checked,
> +                        'presentations/PowerPoint for Mac': QtCore.Qt.Checked,
> +                        'presentations/Keynote': QtCore.Qt.Checked,
>                          'presentations/Pdf': QtCore.Qt.Checked,
> +                        'presentations/show presenter view': QtCore.Qt.Checked,
> +                        'PresentationModeUseSecondary': '',
> +                        'PresentationModeEnableFeedbackDisplay': False,
> +                        'PresentationModePlayWellWithOthers': False,
>                          'presentations/presentations files': []
>                          }
>  
> @@ -127,6 +133,10 @@
>          controller_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), 'presentations', 'lib')
>          for filename in os.listdir(controller_dir):
>              if filename.endswith('controller.py') and not filename == 'presentationcontroller.py':
> +                if not sys.platform.startswith('darwin') and filename == 'powerpointmaccontroller.py':
> +                    continue
> +                if not sys.platform.startswith('win') and filename == 'powerpointcontroller.py':
> +                    continue
>                  path = os.path.join(controller_dir, filename)
>                  if os.path.isfile(path):
>                      module_name = 'openlp.plugins.presentations.lib.' + os.path.splitext(filename)[0]
> @@ -146,10 +156,10 @@
>          Return information about this plugin.
>          """
>          about_text = translate('PresentationPlugin', '<strong>Presentation '
> -                               'Plugin</strong><br />The presentation plugin provides the '
> -                               'ability to show presentations using a number of different '
> -                               'programs. The choice of available presentation programs is '
> -                               'available to the user in a drop down box.')
> +                                                     'Plugin</strong><br />The presentation plugin provides the '
> +                                                     'ability to show presentations using a number of different '
> +                                                     'programs. The choice of available presentation programs is '
> +                                                     'available to the user in a drop down box.')
>          return about_text
>  
>      def set_plugin_text_strings(self):
> 
> === modified file 'scripts/check_dependencies.py'
> --- scripts/check_dependencies.py	2014-03-31 17:57:15 +0000
> +++ scripts/check_dependencies.py	2014-07-06 20:44:24 +0000
> @@ -47,6 +47,7 @@
>      nose = None
>  
>  IS_WIN = sys.platform.startswith('win')
> +IS_OSX = sys.platform.startswith('darwin')
>  
>  
>  VERS = {
> @@ -57,6 +58,8 @@
>      # pyenchant 1.6 required on Windows
>      'enchant': '1.6' if IS_WIN else '1.3'
>  }
> +if IS_OSX:
> +    VERS.update({'appscript': '1.0.0'})
>  
>  # pywin32
>  WIN32_MODULES = [
> @@ -67,6 +70,11 @@
>      'icu',
>  ]
>  
> +# OSX
> +OSX_MODULES = [
> +    'appscript',
> +    'mactypes', 
> +]
>  MODULES = [
>      'PyQt4',
>      'PyQt4.QtCore',
> @@ -178,6 +186,12 @@
>          check_vers(enchant.__version__, VERS['enchant'], 'enchant')
>      except ImportError:
>          print_vers_fail(VERS['enchant'], 'enchant')
> +    if IS_OSX:
> +        try:
> +            import appscript
> +            check_vers(appscript.__version__, VERS['appscript'], 'appscript')
> +        except ImportError:
> +            print_vers_fail(VERS['appscript'], 'appscript')
>  
>  
>  def print_enchant_backends_and_languages():
> @@ -229,6 +243,10 @@
>          print('Checking for Windows specific modules...')
>          for m in WIN32_MODULES:
>              check_module(m)
> +    if IS_OSX:
> +        print('Checking for OS X specific modules...')
> +        for m in OSX_MODULES:
> +            check_module(m)
>      verify_versions()
>      print_qt_image_formats()
>      print_enchant_backends_and_languages()
> 
> === modified file 'tests/functional/__init__.py'
> --- tests/functional/__init__.py	2014-03-14 22:08:44 +0000
> +++ tests/functional/__init__.py	2014-07-06 20:44:24 +0000
> @@ -42,9 +42,9 @@
>  from PyQt4 import QtGui
>  
>  if sys.version_info[1] >= 3:
> -    from unittest.mock import MagicMock, patch, mock_open, call
> +    from unittest.mock import MagicMock, PropertyMock, patch, mock_open, call
>  else:
> -    from mock import MagicMock, patch, mock_open, call
> +    from mock import MagicMock, PropertyMock, patch, mock_open, call
>  
>  # Only one QApplication can be created. Use QtGui.QApplication.instance() when you need to "create" a  QApplication.
>  application = QtGui.QApplication([])
> 
> === added file 'tests/functional/openlp_plugins/presentations/test_powerpointmaccontroller.py'
> --- tests/functional/openlp_plugins/presentations/test_powerpointmaccontroller.py	1970-01-01 00:00:00 +0000
> +++ tests/functional/openlp_plugins/presentations/test_powerpointmaccontroller.py	2014-07-06 20:44:24 +0000
> @@ -0,0 +1,232 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection                                      #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2014 Raoul Snyman                                        #
> +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan      #
> +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
> +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
> +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #
> +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith,             #
> +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock,              #
> +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann                         #
> +# --------------------------------------------------------------------------- #
> +# 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 PowerpointController class of the Presentations plugin.
> +"""
> +import sys
> +from unittest import TestCase, SkipTest
> +
> +if sys.platform != 'darwin':
> +    raise SkipTest('Not OS X, skipping PowerPoint for Mac tests')
> +
> +from appscript import ApplicationNotFoundError
> +
> +from openlp.plugins.presentations.lib.powerpointmaccontroller import PowerPointMacController, PowerPointMacDocument
> +from tests.functional import MagicMock, PropertyMock, patch
> +
> +# TODO: write tests for classes
> +# - PowerpointController
> +# - PowerpointDocument
> +
> +
> +class TestPowerpointController(TestCase):
> +    """
> +    Test the PowerpointController class
> +    """
> +    def setUp(self):
> +        """
> +        Set up the PowerpointController
> +        """
> +        presentation_controller = MagicMock()
> +        presentation_controller.settings_section = 'presentations'
> +        self.powerpoint_controller = PowerPointMacController(presentation_controller)
> +
> +    def check_available_true_test(self):
> +        """
> +        Test the check_available() method calls the correct underlying methods and returns True
> +        """
> +        # GIVEN: Various appscript methods mocked out and set not to raise exceptions
> +        with patch('openlp.plugins.presentations.lib.powerpointmaccontroller.app') as mocked_app, \
> +                patch('openlp.plugins.presentations.lib.powerpointmaccontroller.PowerPointMacController.kill') as mocked_kill:
> +            mocked_application = MagicMock()
> +            mocked_app.return_value = mocked_application
> +
> +            # WHEN: The check_available() method is called
> +            result = self.powerpoint_controller.check_available()
> +
> +            # THEN: The controller should be available
> +            self.assertTrue(result, 'The result of check_available() should be True')
> +            mocked_app.assert_called_with(id='com.microsoft.powerpoint')
> +            mocked_kill.assert_called_with()
> +            self.assertIs(mocked_application, self.powerpoint_controller.process, 'The process attribute should be the '
> +                                                                                  'mocked out return value of app()')
> +
> +    def check_available_false_test(self):
> +        """
> +        Test when an underlying function call in check_available() raises an exception, then the result should be False
> +        """
> +        # GIVEN: Various appscript methods mocked out and set to raise an exception
> +        with patch('openlp.plugins.presentations.lib.powerpointmaccontroller.app') as mocked_app:
> +            mocked_app.side_effect = ApplicationNotFoundError(name='OpenLP')
> +
> +            # WHEN: The check_available() method is called
> +            result = self.powerpoint_controller.check_available()
> +
> +            # THEN: The controller should be available
> +            self.assertFalse(result, 'The result of check_available() should be True')
> +            mocked_app.assert_called_with(id='com.microsoft.powerpoint')
> +
> +    def start_process_not_running_test(self):
> +        """
> +        Test that when start_process() is called and PowerPoint is not running, it is started
> +        """
> +        # GIVEN: A None process attribute and a mocked out app() method
> +        with patch('openlp.plugins.presentations.lib.powerpointmaccontroller.app') as mocked_app:
> +            mocked_process = MagicMock()
> +            mocked_process.isrunning.return_value = False
> +            #mocked_process.launch.return_value = None
> +            mocked_app.return_value = mocked_process
> +            self.powerpoint_controller.apply_app_settings = MagicMock()
> +
> +            # WHEN: The start_process() method is called
> +            self.powerpoint_controller.start_process()
> +
> +            # THEN: The process should not be None
> +            mocked_app.assert_called_with(id='com.microsoft.powerpoint')
> +            mocked_process.isrunning.assert_called_with()
> +            self.assertIsNotNone(self.powerpoint_controller.process, 'The PowerPoint process should have been created')
> +            mocked_process.launch.assert_called_with()
> +            self.powerpoint_controller.apply_app_settings.assert_called_with()
> +
> +    def start_process_is_running_test(self):
> +        """
> +        Test that when start_process() is called and PowerPoint is running then nothing else happens
> +        """
> +        # GIVEN: A None process attribute and a mocked out app() method
> +        with patch('openlp.plugins.presentations.lib.powerpointmaccontroller.app') as mocked_app:
> +            mocked_process = MagicMock()
> +            mocked_process.isrunning.return_value = True
> +            #mocked_process.launch.return_value = None
> +            mocked_app.return_value = mocked_process
> +            self.powerpoint_controller.apply_app_settings = MagicMock()
> +
> +            # WHEN: The start_process() method is called
> +            self.powerpoint_controller.start_process()
> +
> +            # THEN: The process should not be None
> +            mocked_app.assert_called_with(id='com.microsoft.powerpoint')
> +            mocked_process.isrunning.assert_called_with()
> +            self.assertIsNotNone(self.powerpoint_controller.process, 'The PowerPoint process should have been created')
> +            self.assertEqual(0, mocked_process.launch.call_count, 'The launch method should not hae been called')
> +            self.assertEqual(0, self.powerpoint_controller.apply_app_settings.call_count,
> +                             'The apply_app_settings method should not have been called')
> +
> +    def load_presentation_test(self):
> +        """
> +        Test loading a document in PPT
> +        """
> +        pass
> +
> +    def close_presentation_test(self):
> +        """
> +        Test closing a document in PPT
> +        """
> +        pass
> +
> +    def kill_with_no_more_docs_test(self):
> +        """
> +        Test running the kill() method when the first document is successfully closed
> +        """
> +        # GIVEN: A PowerPoint process and a document
> +        mocked_process = MagicMock()
> +        mocked_process.isrunning.side_effect = [True, False, False]
> +        mocked_doc = MagicMock()
> +        self.powerpoint_controller.process = mocked_process
> +        self.powerpoint_controller.docs = [mocked_doc]
> +
> +        # WHEN: kill() is called
> +        self.powerpoint_controller.kill()
> +
> +        # THEN: close_presentation() should only be called once, and nothing else in the method should have been called
> +        mocked_doc.close_presentation.assert_called_once_with()
> +        self.assertEqual(0, mocked_process.presentations.call_count,
> +                         'The presentations() method should not have been called')
> +
> +    def kill_runs_on_docs_more_than_once_test(self):
> +        """
> +        Test running the kill() method with multiple documents to close
> +        """
> +        # GIVEN: A PowerPoint process and a document
> +        mocked_process = MagicMock()
> +        mocked_process.isrunning.side_effect = [True, True, True, False, False]
> +        mocked_doc = MagicMock()
> +        self.powerpoint_controller.process = mocked_process
> +        self.powerpoint_controller.docs = [mocked_doc]
> +
> +        # WHEN: kill() is called
> +        self.powerpoint_controller.kill()
> +
> +        # THEN: close_presentation() should only be called once, and nothing else in the method should have been called
> +        self.assertEqual(3, mocked_doc.close_presentation.call_count,
> +                         'The presentations() method should not have been called')
> +        self.assertEqual(0, mocked_process.presentations.call_count,
> +                         'The presentations() method should not have been called')
> +
> +    def kill_with_process_missing_value_gt0_test(self):
> +        """
> +        Test running the kill() method with total != k.missing_value returns correctly
> +        """
> +        # GIVEN: A PowerPoint process and a mocked out "k"
> +        with patch('openlp.plugins.presentations.lib.powerpointmaccontroller.k') as mocked_k:
> +            mocked_k.missing_value = [None]
> +            mocked_process = MagicMock()
> +            mocked_process.isrunning.side_effect = [False, True]
> +            mocked_process.presentations.return_value = [MagicMock(), MagicMock()]
> +            mocked_doc = MagicMock()
> +            self.powerpoint_controller.process = mocked_process
> +            self.powerpoint_controller.docs = [mocked_doc]
> +
> +            # WHEN: kill() is called
> +            self.powerpoint_controller.kill()
> +
> +            # THEN: process.quit() should not have been called
> +            self.assertEqual(0, mocked_process.quit.call_count, 'The quit() method should not have been called')
> +
> +    def kill_calls_quit_test(self):
> +        """
> +        Test running the kill() method calls quit
> +        """
> +        # GIVEN: A PowerPoint process and a mocked out "k"
> +        with patch('openlp.plugins.presentations.lib.powerpointmaccontroller.k') as mocked_k:
> +            mocked_k.missing_value = []
> +            mocked_k.ask = 1
> +            mocked_process = MagicMock()
> +            mocked_process.isrunning.side_effect = [False, True]
> +            mocked_process.presentations.return_value = []
> +            mocked_doc = MagicMock()
> +            self.powerpoint_controller.process = mocked_process
> +            self.powerpoint_controller.docs = [mocked_doc]
> +
> +            # WHEN: kill() is called
> +            self.powerpoint_controller.kill()
> +
> +            # THEN: process.quit() should have been called and eventually process is set to None
> +            mocked_process.quit.assert_called_with(saving=mocked_k.ask)
> +            self.assertIsNone(self.powerpoint_controller.process, 'process should be None')
> \ No newline at end of file

Hummm

> 
> === added directory 'tests/interfaces/openlp_plugins/presentations'
> === added file 'tests/interfaces/openlp_plugins/presentations/__init__.py'
> --- tests/interfaces/openlp_plugins/presentations/__init__.py	1970-01-01 00:00:00 +0000
> +++ tests/interfaces/openlp_plugins/presentations/__init__.py	2014-07-06 20:44:24 +0000
> @@ -0,0 +1,1 @@
> +__author__ = 'raoul'

Need correct Licence.

> 
> === added file 'tests/interfaces/openlp_plugins/presentations/test_powerpointmaccontroller.py'
> --- tests/interfaces/openlp_plugins/presentations/test_powerpointmaccontroller.py	1970-01-01 00:00:00 +0000
> +++ tests/interfaces/openlp_plugins/presentations/test_powerpointmaccontroller.py	2014-07-06 20:44:24 +0000
> @@ -0,0 +1,125 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection                                      #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2014 Raoul Snyman                                        #
> +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan      #
> +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
> +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
> +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #
> +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith,             #
> +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock,              #
> +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann                         #
> +# --------------------------------------------------------------------------- #
> +# 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 PowerpointController class of the Presentations plugin.
> +"""
> +
> +from unittest import TestCase, skipIf
> +
> +from tests.functional import MagicMock
> +import os
> +from openlp.plugins.presentations.lib.powerpointmaccontroller import PowerPointMacController, PowerPointMacDocument
> +from tests.utils.constants import TEST_RESOURCES_PATH
> +
> +# TODO: write tests for classes
> +# - PowerpointController
> +# - PowerpointDocument
> +
> +
> +class TestPowerpointController(TestCase):
> +    """
> +    Test the PowerpointController class
> +    """
> +    def setUp(self):
> +        """
> +        Set up the PowerpointController
> +        """
> +        presentation_controller = MagicMock()
> +        presentation_controller.settings_section = 'presentations'
> +        self.powerpoint_controller = PowerPointMacController(presentation_controller)
> +        self.file_name = os.path.join(TEST_RESOURCES_PATH, 'presentations', 'test.pptx')
> +        self.doc = PowerPointMacDocument(self.powerpoint_controller, self.file_name)
> +        self.doc.presentation_deleted()
> +
> +    def tearDown(self):
> +        self.doc.controller.kill()
> +
> +    def verify_installation_test(self):
> +        """
> +        Test that PowerPoint for Mac is installed, and sets the controller to available
> +        """
> +        # GIVEN: A boolean value set to false (to confirm the test works)
> +        is_installed = False
> +
> +        # WHEN: The check_available() method is called
> +        is_installed = self.powerpoint_controller.check_available()
> +
> +        # THEN: The controller should be available
> +        self.assertTrue(is_installed, 'The result of check_available() should be True')
> +
> +    def start_process_test(self):
> +        """
> +        Test the start_process() of PPT
> +        """
> +        # GIVEN: A boolean value set to true
> +        # WHEN: We "convert" it to a bool
> +        self.powerpoint_controller.start_process()
> +        # THEN: The process should not be None
> +        assert self.powerpoint_controller.process is not None, 'The result of start_process() should be True'
> +
> +    def load_presentation_test(self):
> +        """
> +        Test loading a document in PPT
> +        """
> +        # GIVEN: the filename
> +        # WHEN: loading the filename
> +        self.doc.load_presentation()
> +        result = self.doc.is_loaded()
> +        # THEN: result should be true
> +        assert result is True, 'The PPT should load document'
> +        assert len(self.doc.controller.docs) != 0, 'The powerpoint_controller.docs should have one document.'
> +
> +    def close_presentation_test(self):
> +        """
> +        Test closing a document in PPT
> +        """
> +        # GIVEN: loading the filename
> +        self.doc.load_presentation()
> +        # WHEN: closing the filename
> +        self.doc.close_presentation()
> +        result = self.doc.is_loaded()
> +        # THEN: result should be true
> +        assert result is False, 'The PPT should close document'
> +        assert len(self.doc.controller.docs) == 0, 'The controller should have 0 documents.'
> +
> +    def kill_test(self):
> +        """
> +        Test running the kill() method with an PowerpointController
> +        """
> +        # GIVEN: A PowerpointController instance and a list of documents
> +        ppt_document = MagicMock()
> +        self.powerpoint_controller.start_process()
> +        self.powerpoint_controller.docs.append(ppt_document)
> +        # WHEN: we run kill()
> +        self.powerpoint_controller.kill()
> +        # THEN: all docs should be deleted from controller
> +        assert len(self.powerpoint_controller.docs) == 0, 'The powerpoint_controller should close all documents.'
> +        assert self.powerpoint_controller.process in None, \
> +            'The powerpoint_controller should shout down PPT application.'
> 
> === added file 'tests/resources/presentations/test.pptx'
> Binary files tests/resources/presentations/test.pptx	1970-01-01 00:00:00 +0000 and tests/resources/presentations/test.pptx	2014-07-06 20:44:24 +0000 differ


-- 
https://code.launchpad.net/~raoul-snyman/openlp/powerpointmac/+merge/225762
Your team OpenLP Core is subscribed to branch lp:openlp.


References