Example #1
0
def get_vlc():
    """
    In order to make this module more testable, we have to wrap the VLC import inside a method. We do this so that we
    can mock out the VLC module entirely.

    :return: The "vlc" module, or None
    """
    if 'openlp.core.ui.media.vendor.vlc' in sys.modules:
        # If VLC has already been imported, no need to do all the stuff below again
        return sys.modules['openlp.core.ui.media.vendor.vlc']
    is_vlc_available = False
    try:
        if is_macosx():
            # Newer versions of VLC on OS X need this. See https://forum.videolan.org/viewtopic.php?t=124521
            os.environ['VLC_PLUGIN_PATH'] = '/Applications/VLC.app/Contents/MacOS/plugins'
        # On Windows when frozen in PyInstaller, we need to blank SetDllDirectoryW to allow loading of the VLC dll.
        # This is due to limitations (by desgin) in PyInstaller. SetDllDirectoryW original value is restored once
        # VLC has been imported.
        if is_win():
            buffer_size = 1024
            dll_directory = ctypes.create_unicode_buffer(buffer_size)
            new_buffer_size = ctypes.windll.kernel32.GetDllDirectoryW(buffer_size, dll_directory)
            dll_directory = ''.join(dll_directory[:new_buffer_size]).replace('\0', '')
            log.debug('Original DllDirectory: %s' % dll_directory)
            ctypes.windll.kernel32.SetDllDirectoryW(None)
        from openlp.core.ui.media.vendor import vlc
        if is_win():
            ctypes.windll.kernel32.SetDllDirectoryW(dll_directory)
        is_vlc_available = bool(vlc.get_default_instance())
    except (ImportError, NameError, NotImplementedError):
        pass
    except OSError as e:
        if is_win():
            if not isinstance(e, WindowsError) and e.winerror != 126:
                raise
        else:
            pass
    if is_vlc_available:
        try:
            VERSION = vlc.libvlc_get_version().decode('UTF-8')
        except:
            VERSION = '0.0.0'
        # LooseVersion does not work when a string contains letter and digits (e. g. 2.0.5 Twoflower).
        # http://bugs.python.org/issue14894
        if LooseVersion(VERSION.split()[0]) < LooseVersion('1.1.0'):
            is_vlc_available = False
            log.debug('VLC could not be loaded, because the vlc version is too old: %s' % VERSION)
    if is_vlc_available:
        return vlc
    else:
        return None
Example #2
0
    def test_pylint(self):
        """
        Test for pylint errors
        """
        # Test if this file is specified in the arguments, if not skip the test.
        in_argv = False
        for arg in sys.argv:
            if arg.endswith('test_pylint.py') or arg.endswith('test_pylint'):
                in_argv = True
                break
        if not in_argv:
            raise SkipTest('test_pylint.py not specified in arguments - skipping tests using pylint.')

        # GIVEN: Some checks to disable and enable, and the pylint script
        disabled_checks = 'import-error,no-member'
        enabled_checks = 'missing-format-argument-key,unused-format-string-argument,bad-format-string'
        if is_win() or 'arch' in platform.dist()[0].lower():
            pylint_script = 'pylint'
        else:
            pylint_script = 'pylint3'

        # WHEN: Running pylint
        (pylint_stdout, pylint_stderr) = \
            lint.py_run('openlp --errors-only --disable={disabled} --enable={enabled} '
                        '--reports=no --output-format=parseable'.format(disabled=disabled_checks,
                                                                        enabled=enabled_checks),
                        return_std=True, script=pylint_script)
        stdout = pylint_stdout.read()
        stderr = pylint_stderr.read()
        filtered_stdout = self._filter_tolerated_errors(stdout)
        print(filtered_stdout)
        print(stderr)

        # THEN: The output should be empty
        self.assertTrue(filtered_stdout == '', 'PyLint should find no errors')
Example #3
0
 def start_ooo(self):
     """
     Start OpenOffice.org process
     TODO: The presentation/Impress plugin may already have it running
     """
     if is_win():
         self.start_ooo_process()
         self.desktop = self.ooo_manager.createInstance('com.sun.star.frame.Desktop')
     else:
         context = uno.getComponentContext()
         resolver = context.ServiceManager.createInstanceWithContext('com.sun.star.bridge.UnoUrlResolver', context)
         uno_instance = None
         loop = 0
         while uno_instance is None and loop < 5:
             try:
                 uno_instance = get_uno_instance(resolver)
             except NoConnectException:
                 time.sleep(0.1)
                 log.exception("Failed to resolve uno connection")
                 self.start_ooo_process()
                 loop += 1
             else:
                 manager = uno_instance.ServiceManager
                 self.desktop = manager.createInstanceWithContext("com.sun.star.frame.Desktop", uno_instance)
                 return
         raise Exception('Unable to start LibreOffice')
Example #4
0
    def hide_display(self, mode=HideMode.Screen):
        """
        Hide the display by making all layers transparent Store the images so they can be replaced when required

        :param mode: How the screen is to be hidden
        """
        self.log_debug('hide_display mode = {mode:d}'.format(mode=mode))
        if self.screens.display_count == 1:
            # Only make visible if setting enabled.
            if not Settings().value('core/display on monitor'):
                return
        if mode == HideMode.Screen:
            self.frame.evaluateJavaScript('show_blank("desktop");')
            self.setVisible(False)
        elif mode == HideMode.Blank or self.initial_fame:
            self.frame.evaluateJavaScript('show_blank("black");')
        else:
            self.frame.evaluateJavaScript('show_blank("theme");')
        if mode != HideMode.Screen:
            if self.isHidden():
                self.setVisible(True)
                self.web_view.setVisible(True)
            # Workaround for bug #1531319, should not be needed with PyQt 5.6.
            if is_win():
                self.shake_web_view()
        self.hide_mode = mode
Example #5
0
    def alert(self, text, location):
        """
        Display an alert.

        :param text: The text to be displayed.
        :param location: Where on the screen is the text to be displayed
        """
        # First we convert <>& marks to html variants, then apply
        # formattingtags, finally we double all backslashes for JavaScript.
        text_prepared = expand_tags(html.escape(text)).replace('\\', '\\\\').replace('\"', '\\\"')
        if self.height() != self.screen['size'].height() or not self.isVisible():
            shrink = True
            js = 'show_alert("{text}", "{top}")'.format(text=text_prepared, top='top')
        else:
            shrink = False
            js = 'show_alert("{text}", "")'.format(text=text_prepared)
        height = self.frame.evaluateJavaScript(js)
        if shrink:
            if text:
                alert_height = int(height)
                self.resize(self.width(), alert_height)
                self.setVisible(True)
                if location == AlertLocation.Middle:
                    self.move(self.screen['size'].left(), (self.screen['size'].height() - alert_height) // 2)
                elif location == AlertLocation.Bottom:
                    self.move(self.screen['size'].left(), self.screen['size'].height() - alert_height)
            else:
                self.setVisible(False)
                self.setGeometry(self.screen['size'])
        # Workaround for bug #1531319, should not be needed with PyQt 5.6.
        if is_win():
            self.shake_web_view()
Example #6
0
 def setup(self, display):
     """
     Set up the media player
     """
     vlc = get_vlc()
     display.vlc_widget = QtGui.QFrame(display)
     display.vlc_widget.setFrameStyle(QtGui.QFrame.NoFrame)
     # creating a basic vlc instance
     command_line_options = '--no-video-title-show'
     if not display.has_audio:
         command_line_options += ' --no-audio --no-video-title-show'
     if Settings().value('advanced/hide mouse') and display.controller.is_live:
         command_line_options += ' --mouse-hide-timeout=0'
     display.vlc_instance = vlc.Instance(command_line_options)
     # creating an empty vlc media player
     display.vlc_media_player = display.vlc_instance.media_player_new()
     display.vlc_widget.resize(display.size())
     display.vlc_widget.raise_()
     display.vlc_widget.hide()
     # The media player has to be 'connected' to the QFrame.
     # (otherwise a video would be displayed in it's own window)
     # This is platform specific!
     # You have to give the id of the QFrame (or similar object)
     # to vlc, different platforms have different functions for this.
     win_id = int(display.vlc_widget.winId())
     if is_win():
         display.vlc_media_player.set_hwnd(win_id)
     elif is_macosx():
         # We have to use 'set_nsobject' since Qt4 on OSX uses Cocoa
         # framework and not the old Carbon.
         display.vlc_media_player.set_nsobject(win_id)
     else:
         # for Linux/*BSD using the X Server
         display.vlc_media_player.set_xwindow(win_id)
     self.has_own_widget = True
 def kill(self):
     """
     Called at system exit to clean up any running presentations.
     """
     log.debug('Kill OpenOffice')
     while self.docs:
         self.docs[0].close_presentation()
     desktop = None
     try:
         if not is_win():
             desktop = self.get_uno_desktop()
         else:
             desktop = self.get_com_desktop()
     except:
         log.warning('Failed to find an OpenOffice desktop to terminate')
     if not desktop:
         return
     docs = desktop.getComponents()
     cnt = 0
     if docs.hasElements():
         list_elements = docs.createEnumeration()
         while list_elements.hasMoreElements():
             doc = list_elements.nextElement()
             if doc.getImplementationName() != 'com.sun.star.comp.framework.BackingComp':
                 cnt += 1
     if cnt > 0:
         log.debug('OpenOffice not terminated as docs are still open')
     else:
         try:
             desktop.terminate()
             log.debug('OpenOffice killed')
         except:
             log.warning('Failed to terminate OpenOffice')
Example #8
0
    def load(self, display):
        """
        Load a video into VLC

        :param display: The display where the media is
        :return:
        """
        vlc = get_vlc()
        log.debug('load vid in Vlc Controller')
        controller = display.controller
        volume = controller.media_info.volume
        file_path = str(controller.media_info.file_info.absoluteFilePath())
        path = os.path.normcase(file_path)
        # create the media
        if controller.media_info.media_type == MediaType.CD:
            if is_win():
                path = '/' + path
            display.vlc_media = display.vlc_instance.media_new_location('cdda://' + path)
            display.vlc_media_player.set_media(display.vlc_media)
            display.vlc_media_player.play()
            # Wait for media to start playing. In this case VLC actually returns an error.
            self.media_state_wait(display, vlc.State.Playing)
            # If subitems exists, this is a CD
            audio_cd_tracks = display.vlc_media.subitems()
            if not audio_cd_tracks or audio_cd_tracks.count() < 1:
                return False
            display.vlc_media = audio_cd_tracks.item_at_index(controller.media_info.title_track)
        else:
            display.vlc_media = display.vlc_instance.media_new_path(path)
        # put the media in the media player
        display.vlc_media_player.set_media(display.vlc_media)
        # parse the metadata of the file
        display.vlc_media.parse()
        self.volume(display, volume)
        return True
Example #9
0
    def dropEvent(self, event):
        """
        Receive drop event, check if it is a file or internal object and process it if it is.

        :param event: Handle of the event pint passed
        """
        # If we are on Windows, OpenLP window will not be set on top. For example, user can drag images to Library and
        # the folder stays on top of the group creation box. This piece of code fixes this issue.
        if is_win():
            self.setWindowState(self.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)
            self.setWindowState(QtCore.Qt.WindowNoState)
        if event.mimeData().hasUrls():
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
            files = []
            for url in event.mimeData().urls():
                local_file = url.toLocalFile()
                if os.path.isfile(local_file):
                    files.append(local_file)
                elif os.path.isdir(local_file):
                    listing = os.listdir(local_file)
                    for file_name in listing:
                        files.append(os.path.join(local_file, file_name))
            Registry().execute('%s_dnd' % self.mime_data_text, {'files': files, 'target': self.itemAt(event.pos())})
        elif self.allow_internal_dnd:
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
            Registry().execute('%s_dnd_internal' % self.mime_data_text, self.itemAt(event.pos()))
        else:
            event.ignore()
Example #10
0
 def load_presentation(self):
     """
     Called when a presentation is added to the SlideController. It builds the environment, starts communcations with
     the background OpenOffice task started earlier. If OpenOffice is not present is is started. Once the environment
     is available the presentation is loaded and started.
     """
     log.debug('Load Presentation OpenOffice')
     if is_win():
         desktop = self.controller.get_com_desktop()
         if desktop is None:
             self.controller.start_process()
             desktop = self.controller.get_com_desktop()
         url = 'file:///' + self.file_path.replace('\\', '/').replace(':', '|').replace(' ', '%20')
     else:
         desktop = self.controller.get_uno_desktop()
         url = uno.systemPathToFileUrl(self.file_path)
     if desktop is None:
         return False
     self.desktop = desktop
     properties = []
     properties.append(self.create_property('Hidden', True))
     properties = tuple(properties)
     try:
         self.document = desktop.loadComponentFromURL(url, '_blank', 0, properties)
     except:
         log.warning('Failed to load presentation %s' % url)
         return False
     self.presentation = self.document.getPresentation()
     self.presentation.Display = ScreenList().current['number'] + 1
     self.control = None
     self.create_thumbnails()
     self.create_titles_and_notes()
     return True
Example #11
0
def setUp():
    if not is_win():
        # Wine creates a sharing violation during tests. Ignore.
        try:
            os.remove(tmpfile)
        except:
            pass
Example #12
0
 def create_thumbnails(self):
     """
     Create thumbnail images for presentation.
     """
     log.debug('create thumbnails OpenOffice')
     if self.check_thumbnails():
         return
     if is_win():
         thumb_dir_url = 'file:///' + self.get_temp_folder().replace('\\', '/') \
             .replace(':', '|').replace(' ', '%20')
     else:
         thumb_dir_url = uno.systemPathToFileUrl(self.get_temp_folder())
     properties = []
     properties.append(self.create_property('FilterName', 'impress_png_Export'))
     properties = tuple(properties)
     doc = self.document
     pages = doc.getDrawPages()
     if not pages:
         return
     if not os.path.isdir(self.get_temp_folder()):
         os.makedirs(self.get_temp_folder())
     for index in range(pages.getCount()):
         page = pages.getByIndex(index)
         doc.getCurrentController().setCurrentPage(page)
         url_path = '%s/%s.png' % (thumb_dir_url, str(index + 1))
         path = os.path.join(self.get_temp_folder(), str(index + 1) + '.png')
         try:
             doc.storeToURL(url_path, properties)
             self.convert_thumbnail(path, index + 1)
             delete_file(path)
         except ErrorCodeIOException as exception:
             log.exception('ERROR! ErrorCodeIOException %d' % exception.ErrCode)
         except:
             log.exception('%s - Unable to store openoffice preview' % path)
 def setup_vlc(self):
     """
     Setup VLC instance and mediaplayer
     """
     self.vlc_instance = vlc.Instance()
     # creating an empty vlc media player
     self.vlc_media_player = self.vlc_instance.media_player_new()
     # The media player has to be 'connected' to the QFrame.
     # (otherwise a video would be displayed in it's own window)
     # This is platform specific!
     # You have to give the id of the QFrame (or similar object)
     # to vlc, different platforms have different functions for this.
     win_id = int(self.preview_frame.winId())
     if is_win():
         self.vlc_media_player.set_hwnd(win_id)
     elif is_macosx():
         # We have to use 'set_nsobject' since Qt4 on OSX uses Cocoa
         # framework and not the old Carbon.
         self.vlc_media_player.set_nsobject(win_id)
     else:
         # for Linux using the X Server
         self.vlc_media_player.set_xwindow(win_id)
     self.vlc_media = None
     # Setup timer every 100 ms to update position
     self.timer = QtCore.QTimer(self)
     self.timer.timeout.connect(self.update_position)
     self.timer.start(100)
     self.find_optical_devices()
     self.audio_cd = False
     self.audio_cd_tracks = None
Example #14
0
 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()
Example #15
0
 def check_available(self):
     """
     Impress is able to run on this machine.
     """
     log.debug('check_available')
     if is_win():
         return self.get_com_servicemanager() is not None
     else:
         return uno_available
Example #16
0
 def preview(self):
     """
     Generates a preview of the image displayed.
     """
     was_visible = self.isVisible()
     self.application.process_events()
     # We must have a service item to preview.
     if self.is_live and hasattr(self, 'service_item'):
         # Wait for the fade to finish before geting the preview.
         # Important otherwise preview will have incorrect text if at all!
         if self.service_item.theme_data and self.service_item.theme_data.display_slide_transition:
             # Workaround for bug #1531319, should not be needed with PyQt 5.6.
             if is_win():
                 fade_shake_timer = QtCore.QTimer(self)
                 fade_shake_timer.setInterval(25)
                 fade_shake_timer.timeout.connect(self.shake_web_view)
                 fade_shake_timer.start()
             while not self.frame.evaluateJavaScript('show_text_completed()'):
                 self.application.process_events()
             # Workaround for bug #1531319, should not be needed with PyQt 5.6.
             if is_win():
                 fade_shake_timer.stop()
     # Wait for the webview to update before getting the preview.
     # Important otherwise first preview will miss the background !
     while not self.web_loaded:
         self.application.process_events()
     # if was hidden keep it hidden
     if self.is_live:
         if self.hide_mode:
             self.hide_display(self.hide_mode)
         # Only continue if the visibility wasn't changed during method call.
         elif was_visible == self.isVisible():
             # Single screen active
             if self.screens.display_count == 1:
                 # Only make visible if setting enabled.
                 if Settings().value('core/display on monitor'):
                     self.setVisible(True)
             else:
                 self.setVisible(True)
     # Workaround for bug #1531319, should not be needed with PyQt 5.6.
     if is_win():
         self.shake_web_view()
     return self.grab()
Example #17
0
    def check_installed(self):
        """
        Check the viewer is installed.

        :return: True if program to open PDF-files was found, otherwise False.
        """
        log.debug('check_installed Pdf')
        self.mudrawbin = ''
        self.mutoolbin = ''
        self.gsbin = ''
        self.also_supports = []
        # Use the user defined program if given
        if Settings().value('presentations/enable_pdf_program'):
            pdf_program = Settings().value('presentations/pdf_program')
            program_type = self.process_check_binary(pdf_program)
            if program_type == 'gs':
                self.gsbin = pdf_program
            elif program_type == 'mudraw':
                self.mudrawbin = pdf_program
            elif program_type == 'mutool':
                self.mutoolbin = pdf_program
        else:
            # Fallback to autodetection
            application_path = AppLocation.get_directory(AppLocation.AppDir)
            if is_win():
                # for windows we only accept mudraw.exe or mutool.exe in the base folder
                application_path = AppLocation.get_directory(AppLocation.AppDir)
                if os.path.isfile(os.path.join(application_path, 'mudraw.exe')):
                    self.mudrawbin = os.path.join(application_path, 'mudraw.exe')
                elif os.path.isfile(os.path.join(application_path, 'mutool.exe')):
                    self.mutoolbin = os.path.join(application_path, 'mutool.exe')
            else:
                DEVNULL = open(os.devnull, 'wb')
                # First try to find mudraw
                self.mudrawbin = which('mudraw')
                # if mudraw isn't installed, try mutool
                if not self.mudrawbin:
                    self.mutoolbin = which('mutool')
                    # Check we got a working mutool
                    if not self.mutoolbin or self.process_check_binary(self.mutoolbin) != 'mutool':
                        self.gsbin = which('gs')
                # Last option: check if mudraw or mutool is placed in OpenLP base folder
                if not self.mudrawbin and not self.mutoolbin and not self.gsbin:
                    application_path = AppLocation.get_directory(AppLocation.AppDir)
                    if os.path.isfile(os.path.join(application_path, 'mudraw')):
                        self.mudrawbin = os.path.join(application_path, 'mudraw')
                    elif os.path.isfile(os.path.join(application_path, 'mutool')):
                        self.mutoolbin = os.path.join(application_path, 'mutool')
        if self.mudrawbin or self.mutoolbin:
            self.also_supports = ['xps', 'oxps']
            return True
        elif self.gsbin:
            return True
        else:
            return False
Example #18
0
 def application(self):
     """
     Adds the openlp to the class dynamically.
     Windows needs to access the application in a dynamic manner.
     """
     if is_win():
         return Registry().get('application')
     else:
         if not hasattr(self, '_application') or not self._application:
             self._application = Registry().get('application')
         return self._application
Example #19
0
 def create_property(self, name, value):
     """
     Create an OOo style property object which are passed into some Uno methods.
     """
     log.debug('create property OpenOffice')
     if is_win():
         property_object = self.controller.manager.Bridge_GetStruct('com.sun.star.beans.PropertyValue')
     else:
         property_object = PropertyValue()
     property_object.Name = name
     property_object.Value = value
     return property_object
Example #20
0
    def test_is_linux(self):
        """
        Test the is_linux() function
        """
        # GIVEN: Mocked out objects
        with patch('openlp.core.common.os') as mocked_os, patch('openlp.core.common.sys') as mocked_sys:

            # WHEN: The mocked os.name and sys.platform are set to 'posix' and 'linux3' repectivly
            mocked_os.name = 'posix'
            mocked_sys.platform = 'linux3'

            # THEN: The three platform functions should perform properly
            self.assertTrue(is_linux(), 'is_linux() should return True')
            self.assertFalse(is_win(), 'is_win() should return False')
            self.assertFalse(is_macosx(), 'is_macosx() should return False')
Example #21
0
 def start_process(self):
     """
     Loads a running version of OpenOffice in the background. It is not displayed to the user but is available to the
     UNO interface when required.
     """
     log.debug('start process Openoffice')
     if is_win():
         self.manager = self.get_com_servicemanager()
         self.manager._FlagAsMethod('Bridge_GetStruct')
         self.manager._FlagAsMethod('Bridge_GetValueObject')
     else:
         # -headless
         cmd = get_uno_command()
         self.process = QtCore.QProcess()
         self.process.startDetached(cmd)
Example #22
0
 def start_ooo_process(self):
     """
     Start the OO Process
     """
     try:
         if is_win():
             self.ooo_manager = Dispatch('com.sun.star.ServiceManager')
             self.ooo_manager._FlagAsMethod('Bridge_GetStruct')
             self.ooo_manager._FlagAsMethod('Bridge_GetValueObject')
         else:
             cmd = get_uno_command()
             process = QtCore.QProcess()
             process.startDetached(cmd)
         self.process_started = True
     except:
         log.exception("start_ooo_process failed")
    def verify_loading_document(self):
        """
        Test loading a document in PowerPoint
        """
        if is_win() and self.real_controller.check_available():
            # GIVEN: A PowerpointDocument and a presentation
            doc = PowerpointDocument(self.real_controller, self.file_name)

            # WHEN: loading the filename
            doc.load_presentation()
            result = doc.is_loaded()

            # THEN: result should be true
            self.assertEqual(result, True, 'The result should be True')
        else:
            self.skipTest('Powerpoint not available, skipping test.')
Example #24
0
 def __init__(self, controller, presentation):
     """
     Constructor, store information about the file and initialise.
     """
     log.debug('Init Presentation Pdf')
     PresentationDocument.__init__(self, controller, presentation)
     self.presentation = None
     self.blanked = False
     self.hidden = False
     self.image_files = []
     self.num_pages = -1
     # Setup startupinfo options for check_output to avoid console popping up on windows
     if is_win():
         self.startupinfo = STARTUPINFO()
         self.startupinfo.dwFlags |= STARTF_USESHOWWINDOW
     else:
         self.startupinfo = None
Example #25
0
    def get_translator(language):
        """
        Set up a translator to use in this instance of OpenLP

        :param language: The language to load into the translator
        """
        if LanguageManager.auto_language:
            language = QtCore.QLocale.system().name()
        lang_path = AppLocation.get_directory(AppLocation.LanguageDir)
        app_translator = QtCore.QTranslator()
        app_translator.load(language, lang_path)
        # A translator for buttons and other default strings provided by Qt.
        if not is_win() and not is_macosx():
            lang_path = QtCore.QLibraryInfo.location(QtCore.QLibraryInfo.TranslationsPath)
        default_translator = QtCore.QTranslator()
        default_translator.load("qt_%s" % language, lang_path)
        return app_translator, default_translator
    def load_presentation_succesfull_test(self):
        """
        Test the PptviewDocument.load_presentation() method when the PPT is successfully opened
        """
        # GIVEN: A reset mocked_os
        self.mock_os_isdir.reset()

        # WHEN: The temporary directory exists and OpenPPT returns successfully (not -1)
        self.mock_os_isdir.return_value = True
        self.mock_controller.process.OpenPPT.return_value = 0
        instance = PptviewDocument(self.mock_controller, self.mock_presentation)
        instance.file_path = 'test\path.ppt'

        if is_win():
            result = instance.load_presentation()

            # THEN: PptviewDocument.load_presentation should return True
            self.assertTrue(result)
Example #27
0
def get_vlc():
    """
    In order to make this module more testable, we have to wrap the VLC import inside a method. We do this so that we
    can mock out the VLC module entirely.

    :return: The "vlc" module, or None
    """
    if 'openlp.core.ui.media.vendor.vlc' in sys.modules:
        # If VLC has already been imported, no need to do all the stuff below again
        return sys.modules['openlp.core.ui.media.vendor.vlc']

    is_vlc_available = False
    try:
        if is_macosx():
            # Newer versions of VLC on OS X need this. See https://forum.videolan.org/viewtopic.php?t=124521
            os.environ['VLC_PLUGIN_PATH'] = '/Applications/VLC.app/Contents/MacOS/plugins'
        from openlp.core.ui.media.vendor import vlc

        is_vlc_available = bool(vlc.get_default_instance())
    except (ImportError, NameError, NotImplementedError):
        pass
    except OSError as e:
        if is_win():
            if not isinstance(e, WindowsError) and e.winerror != 126:
                raise
        elif is_macosx():
            pass
        else:
            raise
    if is_vlc_available:
        try:
            VERSION = vlc.libvlc_get_version().decode('UTF-8')
        except:
            VERSION = '0.0.0'
        # LooseVersion does not work when a string contains letter and digits (e. g. 2.0.5 Twoflower).
        # http://bugs.python.org/issue14894
        if LooseVersion(VERSION.split()[0]) < LooseVersion('1.1.0'):
            is_vlc_available = False
            log.debug('VLC could not be loaded, because the vlc version is too old: %s' % VERSION)
    if is_vlc_available:
        return vlc
    else:
        return None
    def check_available_test(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():
                self.assertTrue(available, 'check_available should return True on windows.')
            else:
                self.assertFalse(available, 'check_available should return False when not on windows.')
Example #29
0
    def delete_theme(self, theme):
        """
        Delete a theme.

        :param theme: The theme to delete.
        """
        self.theme_list.remove(theme)
        thumb = '{name}.png'.format(name=theme)
        delete_file(os.path.join(self.path, thumb))
        delete_file(os.path.join(self.thumb_path, thumb))
        try:
            # Windows is always unicode, so no need to encode filenames
            if is_win():
                shutil.rmtree(os.path.join(self.path, theme))
            else:
                encoding = get_filesystem_encoding()
                shutil.rmtree(os.path.join(self.path, theme).encode(encoding))
        except OSError as os_error:
            shutil.Error = os_error
            self.log_exception('Error deleting theme {name}'.format(name=theme))
    def load_presentation_un_succesfull_test(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_os_isdir.reset()

        # WHEN: The temporary directory does not exist and OpenPPT returns unsuccessfully (-1)
        with patch('openlp.plugins.presentations.lib.pptviewcontroller.os.makedirs') as mock_makedirs:
            self.mock_os_isdir.return_value = False
            self.mock_controller.process.OpenPPT.return_value = -1
            instance = PptviewDocument(self.mock_controller, self.mock_presentation)
            instance.file_path = 'test\path.ppt'
            if is_win():
                result = instance.load_presentation()

                # THEN: The temp folder should be created and PptviewDocument.load_presentation should return False
                mock_makedirs.assert_called_once_with(self.temp_folder)
                self.assertFalse(result)
Example #31
0
File: app.py Project: ipic/projecao
def main():
    """
    The main function which parses command line options and then runs
    """
    args = parse_options()
    qt_args = ['--disable-web-security']
    # qt_args = []
    if args and args.loglevel.lower() in ['d', 'debug']:
        log.setLevel(logging.DEBUG)
    elif args and args.loglevel.lower() in ['w', 'warning']:
        log.setLevel(logging.WARNING)
    else:
        log.setLevel(logging.INFO)
    # Throw the rest of the arguments at Qt, just in case.
    qt_args.extend(args.rargs)
    # Bug #1018855: Set the WM_CLASS property in X11
    if not is_win() and not is_macosx():
        qt_args.append('OpenLP')
    # Initialise the resources
    qInitResources()
    # Now create and actually run the application.
    application = QtWidgets.QApplication(qt_args)
    application.setOrganizationName('OpenLP')
    application.setOrganizationDomain('openlp.org')
    application.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True)
    application.setAttribute(QtCore.Qt.AA_DontCreateNativeWidgetSiblings, True)
    if args.portable:
        application.setApplicationName('OpenLPPortable')
        Settings.setDefaultFormat(Settings.IniFormat)
        # Get location OpenLPPortable.ini
        if args.portablepath:
            if os.path.isabs(args.portablepath):
                portable_path = Path(args.portablepath)
            else:
                portable_path = AppLocation.get_directory(
                    AppLocation.AppDir) / '..' / args.portablepath
        else:
            portable_path = AppLocation.get_directory(
                AppLocation.AppDir) / '..' / '..'
        portable_path = portable_path.resolve()
        data_path = portable_path / 'Data'
        set_up_logging(portable_path / 'Other')
        log.info('Running portable')
        portable_settings_path = data_path / 'OpenLP.ini'
        # Make this our settings file
        log.info('INI file: {name}'.format(name=portable_settings_path))
        Settings.set_filename(portable_settings_path)
        portable_settings = Settings()
        # Set our data path
        log.info('Data path: {name}'.format(name=data_path))
        # Point to our data path
        portable_settings.setValue('advanced/data path', data_path)
        portable_settings.setValue('advanced/is portable', True)
        portable_settings.sync()
    else:
        application.setApplicationName('OpenLP')
        set_up_logging(AppLocation.get_directory(AppLocation.CacheDir))
    # Set the libvlc environment variable if we're frozen
    if getattr(sys, 'frozen', False) and is_win():
        # Path to libvlc and the plugins
        os.environ['PYTHON_VLC_LIB_PATH'] = str(
            AppLocation.get_directory(AppLocation.AppDir) / 'vlc' /
            'libvlc.dll')
        os.environ['PYTHON_VLC_MODULE_PATH'] = str(
            AppLocation.get_directory(AppLocation.AppDir) / 'vlc')
        os.environ['PATH'] += ';' + str(
            AppLocation.get_directory(AppLocation.AppDir) / 'vlc')
        log.debug('VLC Path: {}'.format(os.environ['PYTHON_VLC_LIB_PATH']))
    app = OpenLP()
    # Initialise the Registry
    Registry.create()
    Registry().register('application-qt', application)
    Registry().register('application', app)
    Registry().set_flag('no_web_server', args.no_web_server)
    # Upgrade settings.
    settings = Settings()
    Registry().register('settings', settings)
    application.setApplicationVersion(get_version()['version'])
    # Check if an instance of OpenLP is already running. Quit if there is a running instance and the user only wants one
    server = Server()
    if server.is_another_instance_running():
        app.is_already_running()
        server.post_to_server(qt_args)
        sys.exit()
    else:
        server.start_server()
        app.server = server
    # If the custom data path is missing and the user wants to restore the data path, quit OpenLP.
    if app.is_data_path_missing():
        server.close_server()
        sys.exit()
    if settings.can_upgrade():
        now = datetime.now()
        # Only back up if OpenLP has previously run.
        if settings.value('core/has run wizard'):
            back_up_path = AppLocation.get_data_path() / (
                now.strftime('%Y-%m-%d %H-%M') + '.conf')
            log.info(
                'Settings about to be upgraded. Existing settings are being backed up to {back_up_path}'
                .format(back_up_path=back_up_path))
            QtWidgets.QMessageBox.information(
                None, translate('OpenLP', 'Settings Upgrade'),
                translate(
                    'OpenLP',
                    'Your settings are about to be upgraded. A backup will be created at '
                    '{back_up_path}').format(back_up_path=back_up_path))
            try:
                settings.export(back_up_path)
            except OSError:
                QtWidgets.QMessageBox.warning(
                    None, translate('OpenLP', 'Settings Upgrade'),
                    translate(
                        'OpenLP',
                        'Settings back up failed.\n\nContinuing to upgrade.'))
        settings.upgrade_settings()
    # First time checks in settings
    if not settings.value('core/has run wizard'):
        if not FirstTimeLanguageForm().exec():
            # if cancel then stop processing
            server.close_server()
            sys.exit()
    # i18n Set Language
    language = LanguageManager.get_language()
    translators = LanguageManager.get_translators(language)
    for translator in translators:
        if not translator.isEmpty():
            app.installTranslator(translator)
    if not translators:
        log.debug('Could not find translators.')
    if args and not args.no_error_form:
        sys.excepthook = app.hook_exception
    sys.exit(app.run(qt_args))
Example #32
0
class PowerpointController(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(PowerpointController, self).__init__(plugin, 'Powerpoint', PowerpointDocument)
        self.supports = ['ppt', 'pps', 'pptx', 'ppsx', 'pptm']
        self.process = None

    def check_available(self):
        """
        PowerPoint is able to run on this machine.
        """
        log.debug('check_available')
        if is_win():
            try:
                winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, 'PowerPoint.Application').Close()
                try:
                    # Try to detect if the version is 12 (2007) or above, and if so add 'odp' as a support filetype
                    version_key = winreg.OpenKey(winreg.HKEY_CLASSES_ROOT, 'PowerPoint.Application\\CurVer')
                    tmp1, app_version_string, tmp2 = winreg.EnumValue(version_key, 0)
                    version_key.Close()
                    app_version = int(app_version_string[-2:])
                    if app_version >= 12:
                        self.also_supports = ['odp']
                except (OSError, ValueError):
                    log.warning('Detection of powerpoint version using registry failed.')
                return True
            except OSError:
                pass
        return False

    if is_win():
        def start_process(self):
            """
            Loads PowerPoint process.
            """
            log.debug('start_process')
            if not self.process:
                self.process = Dispatch('PowerPoint.Application')

        def kill(self):
            """
            Called at system exit to clean up any running presentations.
            """
            log.debug('Kill powerpoint')
            while self.docs:
                self.docs[0].close_presentation()
            if self.process is None:
                return
            try:
                if self.process.Presentations.Count > 0:
                    return
                self.process.Quit()
            except (AttributeError, pywintypes.com_error) as e:
                log.exception('Exception caught while killing powerpoint process')
                log.exception(e)
                trace_error_handler(log)
            self.process = None
Example #33
0
    def on_load_disc_button_clicked(self, clicked):
        """
        Load the media when the load-button has been clicked

        :param clicked: Given from signal, not used.
        """
        log.debug('on_load_disc_button_clicked')
        vlc = get_vlc()
        self.disable_all()
        self.application.set_busy_cursor()
        path = self.media_path_combobox.currentText()
        # Check if given path is non-empty and exists before starting VLC
        if not path:
            log.debug('no given path')
            critical_error_message_box(message=translate(
                'MediaPlugin.MediaClipSelectorForm', 'No path was given'))
            self.toggle_disable_load_media(False)
            self.application.set_normal_cursor()
            return
        if not os.path.exists(path):
            log.debug('Given path does not exists')
            critical_error_message_box(
                message=translate('MediaPlugin.MediaClipSelectorForm',
                                  'Given path does not exists'))
            self.toggle_disable_load_media(False)
            self.application.set_normal_cursor()
            return
        # VLC behaves a bit differently on windows and linux when loading, which creates problems when trying to
        # detect if we're dealing with a DVD or CD, so we use different loading approaches depending on the OS.
        if is_win():
            # If the given path is in the format "D:\" or "D:", prefix it with "/" to make VLC happy
            pattern = re.compile(r'^\w:\\\\*$')
            if pattern.match(path):
                path = '/' + path
            self.vlc_media = self.vlc_instance.media_new_location('dvd://' +
                                                                  path)
        else:
            self.vlc_media = self.vlc_instance.media_new_path(path)
        if not self.vlc_media:
            log.debug('vlc media player is none')
            critical_error_message_box(message=translate(
                'MediaPlugin.MediaClipSelectorForm',
                'An error happened during initialization of VLC player'))
            self.toggle_disable_load_media(False)
            self.application.set_normal_cursor()
            return
        # put the media in the media player
        self.vlc_media_player.set_media(self.vlc_media)
        self.vlc_media_player.audio_set_mute(True)
        # start playback to get vlc to parse the media
        if self.vlc_media_player.play() < 0:
            log.debug('vlc play returned error')
            critical_error_message_box(
                message=translate('MediaPlugin.MediaClipSelectorForm',
                                  'VLC player failed playing the media'))
            self.toggle_disable_load_media(False)
            self.application.set_normal_cursor()
            self.vlc_media_player.audio_set_mute(False)
            return
        self.vlc_media_player.audio_set_mute(True)
        if not self.media_state_wait(vlc.State.Playing):
            # Tests if this is an audio CD
            if not self.detect_audio_cd(path):
                critical_error_message_box(
                    message=translate('MediaPlugin.MediaClipSelectorForm',
                                      'VLC player failed playing the media'))
                self.toggle_disable_load_media(False)
                self.application.set_normal_cursor()
                self.vlc_media_player.audio_set_mute(False)
                return
        # pause
        self.vlc_media_player.set_time(0)
        self.vlc_media_player.set_pause(1)
        self.media_state_wait(vlc.State.Paused)
        self.toggle_disable_load_media(False)
        self.application.set_normal_cursor()
        self.vlc_media_player.audio_set_mute(False)
        if not self.audio_cd:
            # Temporarily disable signals
            self.blockSignals(True)
            # Get titles, insert in combobox
            titles = self.vlc_media_player.video_get_title_description()
            self.titles_combo_box.clear()
            for title in titles:
                self.titles_combo_box.addItem(title[1].decode(), title[0])
            # Re-enable signals
            self.blockSignals(False)
            # Main title is usually title #1
            if len(titles) > 1:
                self.titles_combo_box.setCurrentIndex(1)
            # Enable audio track combobox if anything is in it
            if len(titles) > 0:
                self.titles_combo_box.setDisabled(False)
        log.debug('load_disc_button end - '
                  'vlc_media_player state: {state}'.format(
                      state=self.vlc_media_player.get_state()))
Example #34
0
 def find_optical_devices(self):
     """
     Attempt to autodetect optical devices on the computer, and add them to the media-dropdown
     :return:
     """
     # Clear list first
     self.media_path_combobox.clear()
     if is_win():
         # use win api to find optical drives
         fso = Dispatch('scripting.filesystemobject')
         for drive in fso.Drives:
             log.debug('Drive {drive} has type {types:d}'.format(
                 drive=drive.DriveLetter, types=drive.DriveType))
             # if type is 4, it is a cd-rom drive
             if drive.DriveType == 4:
                 self.media_path_combobox.addItem(
                     '{drive}:\\'.format(drive=drive.DriveLetter))
     elif is_linux():
         # Get disc devices from dbus and find the ones that are optical
         bus = dbus.SystemBus()
         try:
             udev_manager_obj = bus.get_object('org.freedesktop.UDisks',
                                               '/org/freedesktop/UDisks')
             udev_manager = dbus.Interface(udev_manager_obj,
                                           'org.freedesktop.UDisks')
             for dev in udev_manager.EnumerateDevices():
                 device_obj = bus.get_object("org.freedesktop.UDisks", dev)
                 device_props = dbus.Interface(device_obj,
                                               dbus.PROPERTIES_IFACE)
                 if device_props.Get('org.freedesktop.UDisks.Device',
                                     'DeviceIsDrive'):
                     drive_props = device_props.Get(
                         'org.freedesktop.UDisks.Device',
                         'DriveMediaCompatibility')
                     if any('optical' in prop for prop in drive_props):
                         self.media_path_combobox.addItem(
                             device_props.Get(
                                 'org.freedesktop.UDisks.Device',
                                 'DeviceFile'))
             return
         except dbus.exceptions.DBusException:
             log.debug('could not use udisks, will try udisks2')
         udev_manager_obj = bus.get_object('org.freedesktop.UDisks2',
                                           '/org/freedesktop/UDisks2')
         udev_manager = dbus.Interface(
             udev_manager_obj, 'org.freedesktop.DBus.ObjectManager')
         for k, v in udev_manager.GetManagedObjects().items():
             drive_info = v.get('org.freedesktop.UDisks2.Drive', {})
             drive_props = drive_info.get('MediaCompatibility')
             if drive_props and any('optical' in prop
                                    for prop in drive_props):
                 for device in udev_manager.GetManagedObjects().values():
                     if dbus.String(
                             'org.freedesktop.UDisks2.Block') in device:
                         if device[dbus.String(
                                 'org.freedesktop.UDisks2.Block')][
                                     dbus.String('Drive')] == k:
                             block_file = ''
                             for c in device[dbus.String(
                                     'org.freedesktop.UDisks2.Block')][
                                         dbus.String('PreferredDevice')]:
                                 if chr(c) != '\x00':
                                     block_file += chr(c)
                             self.media_path_combobox.addItem(block_file)
     elif is_macosx():
         # Look for DVD folders in devices to find optical devices
         volumes = os.listdir('/Volumes')
         for volume in volumes:
             if volume.startswith('.'):
                 continue
             dirs = os.listdir('/Volumes/' + volume)
             # Detect DVD
             if 'VIDEO_TS' in dirs:
                 self.media_path_combobox.addItem('/Volumes/' + volume)
             # Detect audio cd
             files = [f for f in dirs if os.path.isfile(f)]
             for file in files:
                 if file.endswith('aiff'):
                     self.media_path_combobox.addItem('/Volumes/' + volume)
                     break
Example #35
0
    def check_installed(self):
        """
        Check the viewer is installed.

        :return: True if program to open PDF-files was found, otherwise False.
        """
        log.debug('check_installed Pdf')
        self.mudrawbin = ''
        self.mutoolbin = ''
        self.gsbin = ''
        self.also_supports = []
        # Use the user defined program if given
        if Settings().value('presentations/enable_pdf_program'):
            pdf_program = Settings().value('presentations/pdf_program')
            program_type = self.process_check_binary(pdf_program)
            if program_type == 'gs':
                self.gsbin = pdf_program
            elif program_type == 'mudraw':
                self.mudrawbin = pdf_program
            elif program_type == 'mutool':
                self.mutoolbin = pdf_program
        else:
            # Fallback to autodetection
            application_path = AppLocation.get_directory(AppLocation.AppDir)
            if is_win():
                # for windows we only accept mudraw.exe or mutool.exe in the base folder
                application_path = AppLocation.get_directory(
                    AppLocation.AppDir)
                if os.path.isfile(os.path.join(application_path,
                                               'mudraw.exe')):
                    self.mudrawbin = os.path.join(application_path,
                                                  'mudraw.exe')
                elif os.path.isfile(
                        os.path.join(application_path, 'mutool.exe')):
                    self.mutoolbin = os.path.join(application_path,
                                                  'mutool.exe')
            else:
                DEVNULL = open(os.devnull, 'wb')
                # First try to find mudraw
                self.mudrawbin = which('mudraw')
                # if mudraw isn't installed, try mutool
                if not self.mudrawbin:
                    self.mutoolbin = which('mutool')
                    # Check we got a working mutool
                    if not self.mutoolbin or self.process_check_binary(
                            self.mutoolbin) != 'mutool':
                        self.gsbin = which('gs')
                # Last option: check if mudraw or mutool is placed in OpenLP base folder
                if not self.mudrawbin and not self.mutoolbin and not self.gsbin:
                    application_path = AppLocation.get_directory(
                        AppLocation.AppDir)
                    if os.path.isfile(os.path.join(application_path,
                                                   'mudraw')):
                        self.mudrawbin = os.path.join(application_path,
                                                      'mudraw')
                    elif os.path.isfile(
                            os.path.join(application_path, 'mutool')):
                        self.mutoolbin = os.path.join(application_path,
                                                      'mutool')
        if self.mudrawbin or self.mutoolbin:
            self.also_supports = ['xps', 'oxps']
            return True
        elif self.gsbin:
            return True
        else:
            return False
Example #36
0
class Settings(QtCore.QSettings):
    """
    Class to wrap QSettings.

    * Exposes all the methods of QSettings.
    * Adds functionality for OpenLP Portable. If the ``defaultFormat`` is set to
      ``IniFormat``, and the path to the Ini file is set using ``set_filename``,
      then the Settings constructor (without any arguments) will create a Settings
      object for accessing settings stored in that Ini file.

    ``__default_settings__``
        This dict contains all core settings with their default values.

    ``__obsolete_settings__``
        Each entry is structured in the following way::

            ('general/enable slide loop', 'advanced/slide limits', [(SlideLimits.Wrap, True), (SlideLimits.End, False)])

        The first entry is the *old key*; it will be removed.

        The second entry is the *new key*; we will add it to the config. If this is just an empty string, we just remove
        the old key. The last entry is a list containing two-pair tuples. If the list is empty, no conversion is made.
        If the first value is callable i.e. a function, the function will be called with the old setting's value.
        Otherwise each pair describes how to convert the old setting's value::

            (SlideLimits.Wrap, True)

        This means, that if the value of ``general/enable slide loop`` is equal (``==``) ``True`` then we set
        ``advanced/slide limits`` to ``SlideLimits.Wrap``. **NOTE**, this means that the rules have to cover all cases!
        So, if the type of the old value is bool, then there must be two rules.
    """
    __default_settings__ = {
        'advanced/add page break':
        False,
        'advanced/alternate rows':
        not is_win(),
        'advanced/autoscrolling': {
            'dist': 1,
            'pos': 0
        },
        'advanced/current media plugin':
        -1,
        'advanced/data path':
        '',
        # 7 stands for now, 0 to 6 is Monday to Sunday.
        'advanced/default service day':
        7,
        'advanced/default service enabled':
        True,
        'advanced/default service hour':
        11,
        'advanced/default service minute':
        0,
        'advanced/default service name':
        UiStrings().DefaultServiceName,
        'advanced/display size':
        0,
        'advanced/double click live':
        False,
        'advanced/enable exit confirmation':
        True,
        'advanced/expand service item':
        False,
        'advanced/hide mouse':
        True,
        'advanced/is portable':
        False,
        'advanced/max recent files':
        20,
        'advanced/print file meta data':
        False,
        'advanced/print notes':
        False,
        'advanced/print slide text':
        False,
        'advanced/recent file count':
        4,
        'advanced/save current plugin':
        False,
        'advanced/slide limits':
        SlideLimits.End,
        'advanced/slide max height':
        -4,
        'advanced/single click preview':
        False,
        'advanced/single click service preview':
        False,
        'advanced/x11 bypass wm':
        X11_BYPASS_DEFAULT,
        'advanced/search as type':
        True,
        'crashreport/last directory':
        '',
        'formattingTags/html_tags':
        '',
        'core/audio repeat list':
        False,
        'core/auto open':
        False,
        'core/auto preview':
        False,
        'core/audio start paused':
        True,
        'core/auto unblank':
        False,
        'core/click live slide to unblank':
        False,
        'core/blank warning':
        False,
        'core/ccli number':
        '',
        'core/has run wizard':
        False,
        'core/language':
        '[en]',
        'core/last version test':
        '',
        'core/loop delay':
        5,
        'core/recent files': [],
        'core/save prompt':
        False,
        'core/screen blank':
        False,
        'core/show splash':
        True,
        'core/logo background color':
        '#ffffff',
        'core/logo file':
        ':/graphics/openlp-splash-screen.png',
        'core/logo hide on startup':
        False,
        'core/songselect password':
        '',
        'core/songselect username':
        '',
        'core/update check':
        True,
        'core/view mode':
        'default',
        # The other display settings (display position and dimensions) are defined in the ScreenList class due to a
        # circular dependency.
        'core/display on monitor':
        True,
        'core/override position':
        False,
        'core/application version':
        '0.0',
        'images/background color':
        '#000000',
        'media/players':
        'system,webkit',
        'media/override player':
        QtCore.Qt.Unchecked,
        'players/background color':
        '#000000',
        'servicemanager/last directory':
        '',
        'servicemanager/last file':
        '',
        'servicemanager/service theme':
        '',
        'SettingsImport/file_date_created':
        datetime.datetime.now().strftime("%Y-%m-%d %H:%M"),
        'SettingsImport/Make_Changes':
        'At_Own_RISK',
        'SettingsImport/type':
        'OpenLP_settings_export',
        'SettingsImport/version':
        '',
        'themes/global theme':
        '',
        'themes/last directory':
        '',
        'themes/last directory export':
        '',
        'themes/last directory import':
        '',
        'themes/theme level':
        ThemeLevel.Song,
        'themes/wrap footer':
        False,
        'user interface/live panel':
        True,
        'user interface/live splitter geometry':
        QtCore.QByteArray(),
        'user interface/lock panel':
        True,
        'user interface/main window geometry':
        QtCore.QByteArray(),
        'user interface/main window position':
        QtCore.QPoint(0, 0),
        'user interface/main window splitter geometry':
        QtCore.QByteArray(),
        'user interface/main window state':
        QtCore.QByteArray(),
        'user interface/preview panel':
        True,
        'user interface/preview splitter geometry':
        QtCore.QByteArray(),
        'user interface/is preset layout':
        False,
        'projector/show after wizard':
        False,
        'projector/db type':
        'sqlite',
        'projector/db username':
        '',
        'projector/db password':
        '',
        'projector/db hostname':
        '',
        'projector/db database':
        '',
        'projector/enable':
        True,
        'projector/connect on start':
        False,
        'projector/last directory import':
        '',
        'projector/last directory export':
        '',
        'projector/poll time':
        20,  # PJLink  timeout is 30 seconds
        'projector/socket timeout':
        5,  # 5 second socket timeout
        'projector/source dialog type':
        0  # Source select dialog box type
    }
    __file_path__ = ''
    __obsolete_settings__ = [
        # Changed during 2.2.x development.
        # ('advanced/stylesheet fix', '', []),
        # ('general/recent files', 'core/recent files', [(recent_files_conv, None)]),
        ('songs/search as type', 'advanced/search as type', []),
        ('media/players', 'media/players_temp',
         [(media_players_conv, None)]),  # Convert phonon to system
        ('media/players_temp', 'media/players',
         []),  # Move temp setting from above to correct setting
        ('advanced/default color', 'core/logo background color',
         []),  # Default image renamed + moved to general > 2.4.
        ('advanced/default image', '/core/logo file', []
         )  # Default image renamed + moved to general after 2.4.
    ]

    @staticmethod
    def extend_default_settings(default_values):
        """
        Static method to merge the given ``default_values`` with the ``Settings.__default_settings__``.

        :param default_values: A dict with setting keys and their default values.
        """
        Settings.__default_settings__.update(default_values)

    @staticmethod
    def set_filename(ini_file):
        """
        Sets the complete path to an Ini file to be used by Settings objects.

        Does not affect existing Settings objects.
        """
        Settings.__file_path__ = ini_file

    @staticmethod
    def set_up_default_values():
        """
        This static method is called on start up. It is used to perform any operation on the __default_settings__ dict.
        """
        # Make sure the string is translated (when building the dict the string is not translated because the translate
        # function was not set up as this stage).
        Settings.__default_settings__[
            'advanced/default service name'] = UiStrings().DefaultServiceName

    def __init__(self, *args):
        """
        Constructor which checks if this should be a native settings object, or an INI file.
        """
        if not args and Settings.__file_path__ and Settings.defaultFormat(
        ) == Settings.IniFormat:
            QtCore.QSettings.__init__(self, Settings.__file_path__,
                                      Settings.IniFormat)
        else:
            QtCore.QSettings.__init__(self, *args)
        # Add shortcuts here so QKeySequence has a QApplication instance to use.
        Settings.__default_settings__.update({
            'shortcuts/aboutItem':
            [QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_F1)],
            'shortcuts/addToService': [],
            'shortcuts/audioPauseItem': [],
            'shortcuts/displayTagItem': [],
            'shortcuts/blankScreen':
            [QtGui.QKeySequence(QtCore.Qt.Key_Period)],
            'shortcuts/collapse': [QtGui.QKeySequence(QtCore.Qt.Key_Minus)],
            'shortcuts/desktopScreen': [QtGui.QKeySequence(QtCore.Qt.Key_D)],
            'shortcuts/delete':
            [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
            'shortcuts/down': [QtGui.QKeySequence(QtCore.Qt.Key_Down)],
            'shortcuts/editSong': [],
            'shortcuts/escapeItem': [QtGui.QKeySequence(QtCore.Qt.Key_Escape)],
            'shortcuts/expand': [QtGui.QKeySequence(QtCore.Qt.Key_Plus)],
            'shortcuts/exportThemeItem': [],
            'shortcuts/fileNewItem':
            [QtGui.QKeySequence(QtGui.QKeySequence.New)],
            'shortcuts/fileSaveAsItem':
            [QtGui.QKeySequence(QtGui.QKeySequence.SaveAs)],
            'shortcuts/fileExitItem':
            [QtGui.QKeySequence(QtGui.QKeySequence.Quit)],
            'shortcuts/fileSaveItem':
            [QtGui.QKeySequence(QtGui.QKeySequence.Save)],
            'shortcuts/fileOpenItem':
            [QtGui.QKeySequence(QtGui.QKeySequence.Open)],
            'shortcuts/goLive': [],
            'shortcuts/importThemeItem': [],
            'shortcuts/importBibleItem': [],
            'shortcuts/listViewBiblesDeleteItem':
            [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
            'shortcuts/listViewBiblesPreviewItem': [
                QtGui.QKeySequence(QtCore.Qt.Key_Return),
                QtGui.QKeySequence(QtCore.Qt.Key_Enter)
            ],
            'shortcuts/listViewBiblesLiveItem': [
                QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
                QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)
            ],
            'shortcuts/listViewBiblesServiceItem': [
                QtGui.QKeySequence(QtCore.Qt.Key_Plus),
                QtGui.QKeySequence(QtCore.Qt.Key_Equal)
            ],
            'shortcuts/listViewCustomDeleteItem':
            [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
            'shortcuts/listViewCustomPreviewItem': [
                QtGui.QKeySequence(QtCore.Qt.Key_Return),
                QtGui.QKeySequence(QtCore.Qt.Key_Enter)
            ],
            'shortcuts/listViewCustomLiveItem': [
                QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
                QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)
            ],
            'shortcuts/listViewCustomServiceItem': [
                QtGui.QKeySequence(QtCore.Qt.Key_Plus),
                QtGui.QKeySequence(QtCore.Qt.Key_Equal)
            ],
            'shortcuts/listViewImagesDeleteItem':
            [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
            'shortcuts/listViewImagesPreviewItem': [
                QtGui.QKeySequence(QtCore.Qt.Key_Return),
                QtGui.QKeySequence(QtCore.Qt.Key_Enter)
            ],
            'shortcuts/listViewImagesLiveItem': [
                QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
                QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)
            ],
            'shortcuts/listViewImagesServiceItem': [
                QtGui.QKeySequence(QtCore.Qt.Key_Plus),
                QtGui.QKeySequence(QtCore.Qt.Key_Equal)
            ],
            'shortcuts/listViewMediaDeleteItem':
            [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
            'shortcuts/listViewMediaPreviewItem': [
                QtGui.QKeySequence(QtCore.Qt.Key_Return),
                QtGui.QKeySequence(QtCore.Qt.Key_Enter)
            ],
            'shortcuts/listViewMediaLiveItem': [
                QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
                QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)
            ],
            'shortcuts/listViewMediaServiceItem': [
                QtGui.QKeySequence(QtCore.Qt.Key_Plus),
                QtGui.QKeySequence(QtCore.Qt.Key_Equal)
            ],
            'shortcuts/listViewPresentationsDeleteItem': [
                QtGui.QKeySequence(QtGui.QKeySequence.Delete)
            ],
            'shortcuts/listViewPresentationsPreviewItem': [
                QtGui.QKeySequence(QtCore.Qt.Key_Return),
                QtGui.QKeySequence(QtCore.Qt.Key_Enter)
            ],
            'shortcuts/listViewPresentationsLiveItem': [
                QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
                QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)
            ],
            'shortcuts/listViewPresentationsServiceItem': [
                QtGui.QKeySequence(QtCore.Qt.Key_Plus),
                QtGui.QKeySequence(QtCore.Qt.Key_Equal)
            ],
            'shortcuts/listViewSongsDeleteItem': [
                QtGui.QKeySequence(QtGui.QKeySequence.Delete)
            ],
            'shortcuts/listViewSongsPreviewItem': [
                QtGui.QKeySequence(QtCore.Qt.Key_Return),
                QtGui.QKeySequence(QtCore.Qt.Key_Enter)
            ],
            'shortcuts/listViewSongsLiveItem': [
                QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
                QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)
            ],
            'shortcuts/listViewSongsServiceItem': [
                QtGui.QKeySequence(QtCore.Qt.Key_Plus),
                QtGui.QKeySequence(QtCore.Qt.Key_Equal)
            ],
            'shortcuts/lockPanel': [],
            'shortcuts/modeDefaultItem': [],
            'shortcuts/modeLiveItem': [],
            'shortcuts/make_live': [
                QtGui.QKeySequence(QtCore.Qt.Key_Return),
                QtGui.QKeySequence(QtCore.Qt.Key_Enter)
            ],
            'shortcuts/moveUp': [QtGui.QKeySequence(QtCore.Qt.Key_PageUp)],
            'shortcuts/moveTop': [QtGui.QKeySequence(QtCore.Qt.Key_Home)],
            'shortcuts/modeSetupItem': [],
            'shortcuts/moveBottom': [QtGui.QKeySequence(QtCore.Qt.Key_End)],
            'shortcuts/moveDown': [QtGui.QKeySequence(QtCore.Qt.Key_PageDown)],
            'shortcuts/nextTrackItem': [],
            'shortcuts/nextItem_live': [
                QtGui.QKeySequence(QtCore.Qt.Key_Down),
                QtGui.QKeySequence(QtCore.Qt.Key_PageDown)
            ],
            'shortcuts/nextItem_preview': [
                QtGui.QKeySequence(QtCore.Qt.Key_Down),
                QtGui.QKeySequence(QtCore.Qt.Key_PageDown)
            ],
            'shortcuts/nextService': [QtGui.QKeySequence(QtCore.Qt.Key_Right)],
            'shortcuts/newService': [],
            'shortcuts/offlineHelpItem': [
                QtGui.QKeySequence(QtGui.QKeySequence.HelpContents)
            ],
            'shortcuts/onlineHelpItem': [
                QtGui.QKeySequence(QtGui.QKeySequence.HelpContents)
            ],
            'shortcuts/openService': [],
            'shortcuts/saveService': [],
            'shortcuts/previousItem_live': [
                QtGui.QKeySequence(QtCore.Qt.Key_Up),
                QtGui.QKeySequence(QtCore.Qt.Key_PageUp)
            ],
            'shortcuts/playbackPause': [],
            'shortcuts/playbackPlay': [],
            'shortcuts/playbackStop': [],
            'shortcuts/playSlidesLoop': [],
            'shortcuts/playSlidesOnce': [],
            'shortcuts/previousService': [
                QtGui.QKeySequence(QtCore.Qt.Key_Left)
            ],
            'shortcuts/previousItem_preview': [
                QtGui.QKeySequence(QtCore.Qt.Key_Up),
                QtGui.QKeySequence(QtCore.Qt.Key_PageUp)
            ],
            'shortcuts/printServiceItem': [
                QtGui.QKeySequence(QtGui.QKeySequence.Print)
            ],
            'shortcuts/songExportItem': [],
            'shortcuts/songUsageStatus': [
                QtGui.QKeySequence(QtCore.Qt.Key_F4)
            ],
            'shortcuts/searchShortcut': [
                QtGui.QKeySequence(QtGui.QKeySequence.Find)
            ],
            'shortcuts/settingsShortcutsItem': [],
            'shortcuts/settingsImportItem': [],
            'shortcuts/settingsPluginListItem': [
                QtGui.QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_F7)
            ],
            'shortcuts/songUsageDelete': [],
            'shortcuts/settingsConfigureItem': [
                QtGui.QKeySequence(QtGui.QKeySequence.Preferences)
            ],
            'shortcuts/shortcutAction_B': [
                QtGui.QKeySequence(QtCore.Qt.Key_B)
            ],
            'shortcuts/shortcutAction_C': [
                QtGui.QKeySequence(QtCore.Qt.Key_C)
            ],
            'shortcuts/shortcutAction_E': [
                QtGui.QKeySequence(QtCore.Qt.Key_E)
            ],
            'shortcuts/shortcutAction_I': [
                QtGui.QKeySequence(QtCore.Qt.Key_I)
            ],
            'shortcuts/shortcutAction_O': [
                QtGui.QKeySequence(QtCore.Qt.Key_O)
            ],
            'shortcuts/shortcutAction_P': [
                QtGui.QKeySequence(QtCore.Qt.Key_P)
            ],
            'shortcuts/shortcutAction_V': [
                QtGui.QKeySequence(QtCore.Qt.Key_V)
            ],
            'shortcuts/shortcutAction_0': [
                QtGui.QKeySequence(QtCore.Qt.Key_0)
            ],
            'shortcuts/shortcutAction_1': [
                QtGui.QKeySequence(QtCore.Qt.Key_1)
            ],
            'shortcuts/shortcutAction_2': [
                QtGui.QKeySequence(QtCore.Qt.Key_2)
            ],
            'shortcuts/shortcutAction_3': [
                QtGui.QKeySequence(QtCore.Qt.Key_3)
            ],
            'shortcuts/shortcutAction_4': [
                QtGui.QKeySequence(QtCore.Qt.Key_4)
            ],
            'shortcuts/shortcutAction_5': [
                QtGui.QKeySequence(QtCore.Qt.Key_5)
            ],
            'shortcuts/shortcutAction_6': [
                QtGui.QKeySequence(QtCore.Qt.Key_6)
            ],
            'shortcuts/shortcutAction_7': [
                QtGui.QKeySequence(QtCore.Qt.Key_7)
            ],
            'shortcuts/shortcutAction_8': [
                QtGui.QKeySequence(QtCore.Qt.Key_8)
            ],
            'shortcuts/shortcutAction_9': [
                QtGui.QKeySequence(QtCore.Qt.Key_9)
            ],
            'shortcuts/settingsExportItem': [],
            'shortcuts/songUsageReport': [],
            'shortcuts/songImportItem': [],
            'shortcuts/themeScreen': [QtGui.QKeySequence(QtCore.Qt.Key_T)],
            'shortcuts/toolsReindexItem': [],
            'shortcuts/toolsFindDuplicates': [],
            'shortcuts/toolsAlertItem': [QtGui.QKeySequence(QtCore.Qt.Key_F7)],
            'shortcuts/toolsFirstTimeWizard': [],
            'shortcuts/toolsOpenDataFolder': [],
            'shortcuts/toolsAddToolItem': [],
            'shortcuts/updateThemeImages': [],
            'shortcuts/up': [QtGui.QKeySequence(QtCore.Qt.Key_Up)],
            'shortcuts/viewProjectorManagerItem': [
                QtGui.QKeySequence(QtCore.Qt.Key_F6)
            ],
            'shortcuts/viewThemeManagerItem': [
                QtGui.QKeySequence(QtCore.Qt.Key_F10)
            ],
            'shortcuts/viewMediaManagerItem': [
                QtGui.QKeySequence(QtCore.Qt.Key_F8)
            ],
            'shortcuts/viewPreviewPanel': [
                QtGui.QKeySequence(QtCore.Qt.Key_F11)
            ],
            'shortcuts/viewLivePanel': [QtGui.QKeySequence(QtCore.Qt.Key_F12)],
            'shortcuts/viewServiceManagerItem': [
                QtGui.QKeySequence(QtCore.Qt.Key_F9)
            ],
            'shortcuts/webSiteItem': []
        })

    def get_default_value(self, key):
        """
        Get the default value of the given key
        """
        if self.group():
            key = self.group() + '/' + key
        return Settings.__default_settings__[key]

    def remove_obsolete_settings(self):
        """
        This method is only called to clean up the config. It removes old settings and it renames settings. See
        ``__obsolete_settings__`` for more details.
        """
        for old_key, new_key, rules in Settings.__obsolete_settings__:
            # Once removed we don't have to do this again.
            if self.contains(old_key):
                if new_key:
                    # Get the value of the old_key.
                    old_value = super(Settings, self).value(old_key)
                    # When we want to convert the value, we have to figure out the default value (because we cannot get
                    # the default value from the central settings dict.
                    if rules:
                        default_value = rules[0][1]
                        old_value = self._convert_value(
                            old_value, default_value)
                    # Iterate over our rules and check what the old_value should be "converted" to.
                    for new, old in rules:
                        # If the value matches with the condition (rule), then use the provided value. This is used to
                        # convert values. E. g. an old value 1 results in True, and 0 in False.
                        if callable(new):
                            old_value = new(old_value)
                        elif old == old_value:
                            old_value = new
                            break
                    self.setValue(new_key, old_value)
                self.remove(old_key)

    def value(self, key):
        """
        Returns the value for the given ``key``. The returned ``value`` is of the same type as the default value in the
        *Settings.__default_settings__* dict.

        :param key: The key to return the value from.
        """
        # if group() is not empty the group has not been specified together with the key.
        if self.group():
            default_value = Settings.__default_settings__[self.group() + '/' +
                                                          key]
        else:
            default_value = Settings.__default_settings__[key]
        setting = super(Settings, self).value(key, default_value)
        return self._convert_value(setting, default_value)

    def _convert_value(self, setting, default_value):
        """
        This converts the given ``setting`` to the type of the given ``default_value``.

        :param setting: The setting to convert. This could be ``true`` for example.Settings()
        :param default_value: Indication the type the setting should be converted to. For example ``True``
        (type is boolean), meaning that we convert the string ``true`` to a python boolean.

        **Note**, this method only converts a few types and might need to be extended if a certain type is missing!
        """
        # Handle 'None' type (empty value) properly.
        if setting is None:
            # An empty string saved to the settings results in a None type being returned.
            # Convert it to empty unicode string.
            if isinstance(default_value, str):
                return ''
            # An empty list saved to the settings results in a None type being returned.
            else:
                return []
        # Convert the setting to the correct type.
        if isinstance(default_value, bool):
            if isinstance(setting, bool):
                return setting
            # Sometimes setting is string instead of a boolean.
            return setting == 'true'
        if isinstance(default_value, int):
            return int(setting)
        return setting

    def get_files_from_config(self, plugin):
        """
        This removes the settings needed for old way we saved files (e. g. the image paths for the image plugin). A list
        of file paths are returned.

         **Note**: Only a list of paths is returned; this does not convert anything!

         :param plugin: The Plugin object.The caller has to convert/save the list himself; o
        """
        files_list = []
        # We need QSettings instead of Settings here to bypass our central settings dict.
        # Do NOT do this anywhere else!
        settings = QtCore.QSettings(self.fileName(), Settings.IniFormat)
        settings.beginGroup(plugin.settings_section)
        if settings.contains('{name} count'.format(name=plugin.name)):
            # Get the count.
            list_count = int(
                settings.value('{name} count'.format(name=plugin.name), 0))
            if list_count:
                for counter in range(list_count):
                    # The keys were named e. g.: "image 0"
                    item = settings.value(
                        '{name} {counter:d}'.format(name=plugin.name,
                                                    counter=counter), '')
                    if item:
                        files_list.append(item)
                    settings.remove('{name} {counter:d}'.format(
                        name=plugin.name, counter=counter))
            settings.remove('{name} count'.format(name=plugin.name))
        settings.endGroup()
        return files_list
Example #37
0
# with this program; if not, write to the Free Software Foundation, Inc., 59  #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA                          #
###############################################################################
"""
This module is for controlling powerpoint. PPT API documentation:
`http://msdn.microsoft.com/en-us/library/aa269321(office.10).aspx`_
2010: https://msdn.microsoft.com/en-us/library/office/ff743835%28v=office.14%29.aspx
2013: https://msdn.microsoft.com/en-us/library/office/ff743835.aspx
"""
import os
import logging
import time

from openlp.core.common import is_win, Settings

if is_win():
    from win32com.client import Dispatch
    import win32con
    import winreg
    import win32ui
    import win32gui
    import pywintypes


from openlp.core.lib import ScreenList
from openlp.core.lib.ui import UiStrings, critical_error_message_box, translate
from openlp.core.common import trace_error_handler, Registry
from .presentationcontroller import PresentationController, PresentationDocument

log = logging.getLogger(__name__)
Example #38
0
class PowerpointDocument(PresentationDocument):
    """
    Class which holds information and controls a single presentation.
    """

    def __init__(self, controller, presentation):
        """
        Constructor, store information about the file and initialise.

        :param controller:
        :param presentation:
        """
        log.debug('Init Presentation Powerpoint')
        super(PowerpointDocument, self).__init__(controller, presentation)
        self.presentation = None
        self.index_map = {}
        self.slide_count = 0
        self.blank_slide = 1
        self.blank_click = None
        self.presentation_hwnd = 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')
        try:
            if not self.controller.process:
                self.controller.start_process()
            self.controller.process.Presentations.Open(os.path.normpath(self.file_path), False, False, False)
            self.presentation = self.controller.process.Presentations(self.controller.process.Presentations.Count)
            self.create_thumbnails()
            self.create_titles_and_notes()
            # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup
            if len(ScreenList().screen_list) > 1:
                Registry().get('main_window').activateWindow()
            return True
        except (AttributeError, pywintypes.com_error) as e:
            log.exception('Exception caught while loading Powerpoint presentation')
            log.exception(e)
            trace_error_handler(log)
            return False

    def create_thumbnails(self):
        """
        Create the thumbnail images for the current presentation.

        Note an alternative and quicker method would be do::

            self.presentation.Slides[n].Copy()
            thumbnail = QApplication.clipboard.image()

        However, for the moment, we want a physical file since it makes life easier elsewhere.
        """
        log.debug('create_thumbnails')
        if self.check_thumbnails():
            return
        key = 1
        for num in range(self.presentation.Slides.Count):
            if not self.presentation.Slides(num + 1).SlideShowTransition.Hidden:
                self.index_map[key] = num + 1
                self.presentation.Slides(num + 1).Export(
                    os.path.join(self.get_thumbnail_folder(), 'slide%d.png' % (key)), 'png', 320, 240)
                key += 1
        self.slide_count = key - 1

    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('ClosePresentation')
        if self.presentation:
            try:
                self.presentation.Close()
            except (AttributeError, pywintypes.com_error) as e:
                log.exception('Caught exception while closing powerpoint presentation')
                log.exception(e)
                trace_error_handler(log)
        self.presentation = None
        self.controller.remove_doc(self)
        # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup
        if len(ScreenList().screen_list) > 1:
            Registry().get('main_window').activateWindow()

    def is_loaded(self):
        """
        Returns ``True`` if a presentation is loaded.
        """
        log.debug('is_loaded')
        try:
            if self.controller.process.Presentations.Count == 0:
                return False
        except (AttributeError, pywintypes.com_error) as e:
            log.exception('Caught exception while in is_loaded')
            log.exception(e)
            trace_error_handler(log)
            return False
        return True

    def is_active(self):
        """
        Returns ``True`` if a presentation is currently active.
        """
        log.debug('is_active')
        if not self.is_loaded():
            return False
        try:
            if self.presentation.SlideShowWindow is None:
                return False
            if self.presentation.SlideShowWindow.View is None:
                return False
        except (AttributeError, pywintypes.com_error) as e:
            log.exception('Caught exception while in is_active')
            log.exception(e)
            trace_error_handler(log)
            return False
        return True

    def unblank_screen(self):
        """
        Unblanks (restores) the presentation.
        """
        log.debug('unblank_screen')
        try:
            self.presentation.SlideShowWindow.Activate()
            self.presentation.SlideShowWindow.View.State = 1
            # Unblanking is broken in PowerPoint 2010 (14.0), need to redisplay
            if 15.0 > float(self.presentation.Application.Version) >= 14.0:
                self.presentation.SlideShowWindow.View.GotoSlide(self.index_map[self.blank_slide], False)
                if self.blank_click:
                    self.presentation.SlideShowWindow.View.GotoClick(self.blank_click)
        except (AttributeError, pywintypes.com_error) as e:
            log.exception('Caught exception while in unblank_screen')
            log.exception(e)
            trace_error_handler(log)
            self.show_error_msg()
        # Stop powerpoint from flashing in the taskbar
        if self.presentation_hwnd:
            win32gui.FlashWindowEx(self.presentation_hwnd, win32con.FLASHW_STOP, 0, 0)
        # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup
        if len(ScreenList().screen_list) > 1:
            Registry().get('main_window').activateWindow()

    def blank_screen(self):
        """
        Blanks the screen.
        """
        log.debug('blank_screen')
        try:
            # Unblanking is broken in PowerPoint 2010 (14.0), need to save info for later
            if 15.0 > float(self.presentation.Application.Version) >= 14.0:
                self.blank_slide = self.get_slide_number()
                self.blank_click = self.presentation.SlideShowWindow.View.GetClickIndex()
            # ppSlideShowBlackScreen = 3
            self.presentation.SlideShowWindow.View.State = 3
        except (AttributeError, pywintypes.com_error) as e:
            log.exception('Caught exception while in blank_screen')
            log.exception(e)
            trace_error_handler(log)
            self.show_error_msg()

    def is_blank(self):
        """
        Returns ``True`` if screen is blank.
        """
        log.debug('is_blank')
        if self.is_active():
            try:
                # ppSlideShowBlackScreen = 3
                return self.presentation.SlideShowWindow.View.State == 3
            except (AttributeError, pywintypes.com_error) as e:
                log.exception('Caught exception while in is_blank')
                log.exception(e)
                trace_error_handler(log)
                self.show_error_msg()
        else:
            return False

    def stop_presentation(self):
        """
        Stops the current presentation and hides the output. Used when blanking to desktop.
        """
        log.debug('stop_presentation')
        try:
            self.presentation.SlideShowWindow.View.Exit()
        except (AttributeError, pywintypes.com_error) as e:
            log.exception('Caught exception while in stop_presentation')
            log.exception(e)
            trace_error_handler(log)
            self.show_error_msg()

    if is_win():
        def start_presentation(self):
            """
            Starts a presentation from the beginning.
            """
            log.debug('start_presentation')
            # SlideShowWindow measures its size/position by points, not pixels
            # https://technet.microsoft.com/en-us/library/dn528846.aspx
            try:
                dpi = win32ui.GetActiveWindow().GetDC().GetDeviceCaps(88)
            except win32ui.error:
                try:
                    dpi = win32ui.GetForegroundWindow().GetDC().GetDeviceCaps(88)
                except win32ui.error:
                    dpi = 96
            size = ScreenList().current['size']
            ppt_window = None
            try:
                ppt_window = self.presentation.SlideShowSettings.Run()
            except (AttributeError, pywintypes.com_error) as e:
                log.exception('Caught exception while in start_presentation')
                log.exception(e)
                trace_error_handler(log)
                self.show_error_msg()
            if ppt_window and not Settings().value('presentations/powerpoint control window'):
                try:
                    ppt_window.Top = size.y() * 72 / dpi
                    ppt_window.Height = size.height() * 72 / dpi
                    ppt_window.Left = size.x() * 72 / dpi
                    ppt_window.Width = size.width() * 72 / dpi
                except AttributeError as e:
                    log.exception('AttributeError while in start_presentation')
                    log.exception(e)
            # Find the presentation window and save the handle for later
            self.presentation_hwnd = None
            if ppt_window:
                log.debug('main display size:  y=%d, height=%d, x=%d, width=%d'
                          % (size.y(), size.height(), size.x(), size.width()))
                win32gui.EnumWindows(self._window_enum_callback, size)
            # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup
            if len(ScreenList().screen_list) > 1:
                Registry().get('main_window').activateWindow()

    def _window_enum_callback(self, hwnd, size):
        """
        Method for callback from win32gui.EnumWindows.
        Used to find the powerpoint presentation window and stop it flashing in the taskbar.
        """
        # Get the size of the current window and if it matches the size of our main display we assume
        # it is the powerpoint presentation window.
        (left, top, right, bottom) = win32gui.GetWindowRect(hwnd)
        window_title = win32gui.GetWindowText(hwnd)
        log.debug('window size:  left=%d, top=%d, right=%d, width=%d' % (left, top, right, bottom))
        log.debug('compare size:  %d and %d, %d and %d, %d and %d, %d and %d'
                  % (size.y(), top, size.height(), (bottom - top), size.x(), left, size.width(), (right - left)))
        log.debug('window title: %s' % window_title)
        filename_root, filename_ext = os.path.splitext(os.path.basename(self.file_path))
        if size.y() == top and size.height() == (bottom - top) and size.x() == left and \
                size.width() == (right - left) and filename_root in window_title:
            log.debug('Found a match and will save the handle')
            self.presentation_hwnd = hwnd
            # Stop powerpoint from flashing in the taskbar
            win32gui.FlashWindowEx(self.presentation_hwnd, win32con.FLASHW_STOP, 0, 0)

    def get_slide_number(self):
        """
        Returns the current slide number.
        """
        log.debug('get_slide_number')
        ret = 0
        try:
            # We need 2 approaches to getting the current slide number, because
            # SlideShowWindow.View.Slide.SlideIndex wont work on the end-slide where Slide isn't available, and
            # SlideShowWindow.View.CurrentShowPosition returns 0 when called when a transistion is executing (in 2013)
            # So we use SlideShowWindow.View.Slide.SlideIndex unless the state is done (ppSlideShowDone = 5)
            if self.presentation.SlideShowWindow.View.State != 5:
                ret = self.presentation.SlideShowWindow.View.Slide.SlideNumber
                # Do reverse lookup in the index_map to find the slide number to return
                ret = next((key for key, slidenum in self.index_map.items() if slidenum == ret), None)
            else:
                ret = self.presentation.SlideShowWindow.View.CurrentShowPosition
        except (AttributeError, pywintypes.com_error) as e:
            log.exception('Caught exception while in get_slide_number')
            log.exception(e)
            trace_error_handler(log)
            self.show_error_msg()
        return ret

    def get_slide_count(self):
        """
        Returns total number of slides.
        """
        log.debug('get_slide_count')
        return self.slide_count

    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
        """
        log.debug('goto_slide')
        try:
            if Settings().value('presentations/powerpoint slide click advance') \
                    and self.get_slide_number() == slide_no:
                click_index = self.presentation.SlideShowWindow.View.GetClickIndex()
                click_count = self.presentation.SlideShowWindow.View.GetClickCount()
                log.debug('We are already on this slide - go to next effect if any left, idx: %d, count: %d'
                          % (click_index, click_count))
                if click_index < click_count:
                    self.next_step()
            else:
                self.presentation.SlideShowWindow.View.GotoSlide(self.index_map[slide_no])
        except (AttributeError, pywintypes.com_error) as e:
            log.exception('Caught exception while in goto_slide')
            log.exception(e)
            trace_error_handler(log)
            self.show_error_msg()

    def next_step(self):
        """
        Triggers the next effect of slide on the running presentation.
        """
        log.debug('next_step')
        try:
            self.presentation.SlideShowWindow.Activate()
            self.presentation.SlideShowWindow.View.Next()
        except (AttributeError, pywintypes.com_error) as e:
            log.exception('Caught exception while in next_step')
            log.exception(e)
            trace_error_handler(log)
            self.show_error_msg()
            return
        if self.get_slide_number() > self.get_slide_count():
            log.debug('past end, stepping back to previous')
            self.previous_step()
        # Stop powerpoint from flashing in the taskbar
        if self.presentation_hwnd:
            win32gui.FlashWindowEx(self.presentation_hwnd, win32con.FLASHW_STOP, 0, 0)
        # Make sure powerpoint doesn't steal focus, unless we're on a single screen setup
        if len(ScreenList().screen_list) > 1:
            Registry().get('main_window').activateWindow()

    def previous_step(self):
        """
        Triggers the previous slide on the running presentation.
        """
        log.debug('previous_step')
        try:
            self.presentation.SlideShowWindow.View.Previous()
        except (AttributeError, pywintypes.com_error) as e:
            log.exception('Caught exception while in previous_step')
            log.exception(e)
            trace_error_handler(log)
            self.show_error_msg()

    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(self.index_map[slide_no]).Shapes)

    def get_slide_notes(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(self.index_map[slide_no]).NotesPage.Shapes)

    def create_titles_and_notes(self):
        """
        Writes the list of titles (one per slide)
        to 'titles.txt'
        and the notes to 'slideNotes[x].txt'
        in the thumbnails directory
        """
        titles = []
        notes = []
        for num in range(self.get_slide_count()):
            slide = self.presentation.Slides(self.index_map[num + 1])
            try:
                text = slide.Shapes.Title.TextFrame.TextRange.Text
            except Exception as e:
                log.exception(e)
                text = ''
            titles.append(text.replace('\n', ' ').replace('\x0b', ' ') + '\n')
            note = _get_text_from_shapes(slide.NotesPage.Shapes)
            if len(note) == 0:
                note = ' '
            notes.append(note)
        self.save_titles_and_notes(titles, notes)

    def show_error_msg(self):
        """
        Stop presentation and display an error message.
        """
        try:
            self.presentation.SlideShowWindow.View.Exit()
        except (AttributeError, pywintypes.com_error) as e:
            log.exception('Failed to exit Powerpoint presentation after error')
            log.exception(e)
        critical_error_message_box(UiStrings().Error, translate('PresentationPlugin.PowerpointDocument',
                                                                'An error occurred in the Powerpoint integration '
                                                                'and the presentation will be stopped. '
                                                                'Restart the presentation if you wish to present it.'))
Example #39
0
class Settings(QtCore.QSettings):
    """
    Class to wrap QSettings.

    * Exposes all the methods of QSettings.
    * Adds functionality for OpenLP Portable. If the ``defaultFormat`` is set to ``IniFormat``, and the path to the Ini
      file is set using ``set_filename``, then the Settings constructor (without any arguments) will create a Settings
      object for accessing settings stored in that Ini file.

    ``__default_settings__``
        This dict contains all core settings with their default values.

    ``__obsolete_settings__``
        Each entry is structured in the following way::

            ('general/enable slide loop', 'advanced/slide limits', [(SlideLimits.Wrap, True), (SlideLimits.End, False)])

        The first entry is the *old key*; if it is different from the *new key* it will be removed.

        The second entry is the *new key*; we will add it to the config. If this is just an empty string, we just remove
        the old key. The last entry is a list containing two-pair tuples. If the list is empty, no conversion is made.
        If the first value is callable i.e. a function, the function will be called with the old setting's value.
        Otherwise each pair describes how to convert the old setting's value::

            (SlideLimits.Wrap, True)

        This means, that if the value of ``general/enable slide loop`` is equal (``==``) ``True`` then we set
        ``advanced/slide limits`` to ``SlideLimits.Wrap``. **NOTE**, this means that the rules have to cover all cases!
        So, if the type of the old value is bool, then there must be two rules.
    """
    __default_settings__ = {
        'settings/version': 0,
        'advanced/add page break': False,
        'advanced/alternate rows': not is_win(),
        'advanced/autoscrolling': {'dist': 1, 'pos': 0},
        'advanced/current media plugin': -1,
        'advanced/data path': None,
        # 7 stands for now, 0 to 6 is Monday to Sunday.
        'advanced/default service day': 7,
        'advanced/default service enabled': True,
        'advanced/default service hour': 11,
        'advanced/default service minute': 0,
        'advanced/default service name': 'Service %Y-%m-%d %H-%M',
        'advanced/display size': 0,
        'advanced/double click live': False,
        'advanced/enable exit confirmation': True,
        'advanced/expand service item': False,
        'advanced/hide mouse': True,
        'advanced/ignore aspect ratio': False,
        'advanced/is portable': False,
        'advanced/max recent files': 20,
        'advanced/print file meta data': False,
        'advanced/print notes': False,
        'advanced/print slide text': False,
        'advanced/proxy mode': ProxyMode.SYSTEM_PROXY,
        'advanced/proxy http': '',
        'advanced/proxy https': '',
        'advanced/proxy username': '',
        'advanced/proxy password': '',
        'advanced/recent file count': 4,
        'advanced/save current plugin': False,
        'advanced/slide limits': SlideLimits.End,
        'advanced/slide max height': -4,
        'advanced/single click preview': False,
        'advanced/single click service preview': False,
        'advanced/x11 bypass wm': X11_BYPASS_DEFAULT,
        'advanced/search as type': True,
        'advanced/use_dark_style': False,
        'api/twelve hour': True,
        'api/port': 4316,
        'api/websocket port': 4317,
        'api/user id': 'openlp',
        'api/password': '******',
        'api/authentication enabled': False,
        'api/ip address': '0.0.0.0',
        'api/thumbnails': True,
        'crashreport/last directory': None,
        'formattingTags/html_tags': '',
        'core/audio repeat list': False,
        'core/auto open': False,
        'core/auto preview': False,
        'core/audio start paused': True,
        'core/auto unblank': False,
        'core/click live slide to unblank': False,
        'core/blank warning': False,
        'core/ccli number': '',
        'core/has run wizard': False,
        'core/language': '[en]',
        'core/last version test': '',
        'core/loop delay': 5,
        'core/recent files': [],
        'core/save prompt': False,
        'core/screen blank': False,
        'core/show splash': True,
        'core/logo background color': '#ffffff',
        'core/logo file': Path(':/graphics/openlp-splash-screen.png'),
        'core/logo hide on startup': False,
        'core/songselect password': '',
        'core/songselect username': '',
        'core/update check': True,
        'core/view mode': 'default',
        # The other display settings (display position and dimensions) are defined in the ScreenList class due to a
        # circular dependency.
        'core/display on monitor': True,
        'core/override position': False,
        'core/application version': '0.0',
        'images/background color': '#000000',
        'media/players': 'system,webkit',
        'media/override player': QtCore.Qt.Unchecked,
        'remotes/download version': '0.0',
        'players/background color': '#000000',
        'servicemanager/last directory': None,
        'servicemanager/last file': None,
        'servicemanager/service theme': None,
        'SettingsImport/file_date_created': datetime.datetime.now().strftime("%Y-%m-%d %H:%M"),
        'SettingsImport/Make_Changes': 'At_Own_RISK',
        'SettingsImport/type': 'OpenLP_settings_export',
        'SettingsImport/version': '',
        'themes/global theme': '',
        'themes/last directory': None,
        'themes/last directory export': None,
        'themes/last directory import': None,
        'themes/theme level': ThemeLevel.Song,
        'themes/wrap footer': False,
        'user interface/live panel': True,
        'user interface/live splitter geometry': QtCore.QByteArray(),
        'user interface/lock panel': True,
        'user interface/main window geometry': QtCore.QByteArray(),
        'user interface/main window position': QtCore.QPoint(0, 0),
        'user interface/main window splitter geometry': QtCore.QByteArray(),
        'user interface/main window state': QtCore.QByteArray(),
        'user interface/preview panel': True,
        'user interface/preview splitter geometry': QtCore.QByteArray(),
        'user interface/is preset layout': False,
        'projector/show after wizard': False,
        'projector/db type': 'sqlite',
        'projector/db username': '',
        'projector/db password': '',
        'projector/db hostname': '',
        'projector/db database': '',
        'projector/enable': True,
        'projector/connect on start': False,
        'projector/connect when LKUP received': True,  # PJLink v2: Projector sends LKUP command after it powers up
        'projector/last directory import': None,
        'projector/last directory export': None,
        'projector/poll time': 20,  # PJLink  timeout is 30 seconds
        'projector/socket timeout': 5,  # 5 second socket timeout
        'projector/source dialog type': 0  # Source select dialog box type
    }
    __file_path__ = ''
    # Settings upgrades prior to 3.0
    __setting_upgrade_1__ = [
        ('songs/search as type', 'advanced/search as type', []),
        ('media/players', 'media/players_temp', [(media_players_conv, None)]),  # Convert phonon to system
        ('media/players_temp', 'media/players', []),  # Move temp setting from above to correct setting
    ]
    # Settings upgrades for 3.0 (aka 2.6)
    __setting_upgrade_2__ = [
        ('advanced/default color', 'core/logo background color', []),  # Default image renamed + moved to general > 2.4.
        ('advanced/default image', 'core/logo file', []),  # Default image renamed + moved to general after 2.4.
        ('remotes/https enabled', '', []),
        ('remotes/https port', '', []),
        ('remotes/twelve hour', 'api/twelve hour', []),
        ('remotes/port', 'api/port', []),
        ('remotes/websocket port', 'api/websocket port', []),
        ('remotes/user id', 'api/user id', []),
        ('remotes/password', 'api/password', []),
        ('remotes/authentication enabled', 'api/authentication enabled', []),
        ('remotes/ip address', 'api/ip address', []),
        ('remotes/thumbnails', 'api/thumbnails', []),
        ('shortcuts/escapeItem', 'shortcuts/desktopScreenEnable', []),  # Escape item was removed in 2.6.
        ('shortcuts/offlineHelpItem', 'shortcuts/userManualItem', []),  # Online and Offline help were combined in 2.6.
        ('shortcuts/onlineHelpItem', 'shortcuts/userManualItem', []),  # Online and Offline help were combined in 2.6.
        ('bibles/advanced bible', '', []),  # Common bible search widgets combined in 2.6
        ('bibles/quick bible', 'bibles/primary bible', []),  # Common bible search widgets combined in 2.6
        # Last search type was renamed to last used search type in 2.6 since Bible search value type changed in 2.6.
        ('songs/last search type', 'songs/last used search type', []),
        ('bibles/last search type', '', []),
        ('custom/last search type', 'custom/last used search type', []),
        # The following changes are being made for the conversion to using Path objects made in 2.6 development
        ('advanced/data path', 'advanced/data path', [(lambda p: Path(p) if p is not None else None, None)]),
        ('crashreport/last directory', 'crashreport/last directory', [(str_to_path, None)]),
        ('servicemanager/last directory', 'servicemanager/last directory', [(str_to_path, None)]),
        ('servicemanager/last file', 'servicemanager/last file', [(str_to_path, None)]),
        ('themes/last directory', 'themes/last directory', [(str_to_path, None)]),
        ('themes/last directory export', 'themes/last directory export', [(str_to_path, None)]),
        ('themes/last directory import', 'themes/last directory import', [(str_to_path, None)]),
        ('projector/last directory import', 'projector/last directory import', [(str_to_path, None)]),
        ('projector/last directory export', 'projector/last directory export', [(str_to_path, None)]),
        ('bibles/last directory import', 'bibles/last directory import', [(str_to_path, None)]),
        ('presentations/pdf_program', 'presentations/pdf_program', [(str_to_path, None)]),
        ('songs/last directory import', 'songs/last directory import', [(str_to_path, None)]),
        ('songs/last directory export', 'songs/last directory export', [(str_to_path, None)]),
        ('songusage/last directory export', 'songusage/last directory export', [(str_to_path, None)]),
        ('core/recent files', 'core/recent files', [(files_to_paths, None)]),
        ('media/media files', 'media/media files', [(files_to_paths, None)]),
        ('presentations/presentations files', 'presentations/presentations files', [(files_to_paths, None)]),
        ('core/logo file', 'core/logo file', [(str_to_path, None)]),
        ('presentations/last directory', 'presentations/last directory', [(str_to_path, None)]),
        ('images/last directory', 'images/last directory', [(str_to_path, None)]),
        ('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', []),
        ('presentations / Powerpoint Viewer', '', [])
    ]

    @staticmethod
    def extend_default_settings(default_values):
        """
        Static method to merge the given ``default_values`` with the ``Settings.__default_settings__``.

        :param default_values: A dict with setting keys and their default values.
        """
        Settings.__default_settings__.update(default_values)

    @staticmethod
    def set_filename(ini_path):
        """
        Sets the complete path to an Ini file to be used by Settings objects.

        Does not affect existing Settings objects.

        :param openlp.core.common.path.Path ini_path: ini file path
        :rtype: None
        """
        Settings.__file_path__ = str(ini_path)

    @staticmethod
    def set_up_default_values():
        """
        This static method is called on start up. It is used to perform any operation on the __default_settings__ dict.
        """
        # Make sure the string is translated (when building the dict the string is not translated because the translate
        # function was not set up as this stage).
        from openlp.core.common.i18n import UiStrings
        Settings.__default_settings__['advanced/default service name'] = UiStrings().DefaultServiceName

    def __init__(self, *args):
        """
        Constructor which checks if this should be a native settings object, or an INI file.
        """
        if not args and Settings.__file_path__ and Settings.defaultFormat() == Settings.IniFormat:
            QtCore.QSettings.__init__(self, Settings.__file_path__, Settings.IniFormat)
        else:
            QtCore.QSettings.__init__(self, *args)
        # Add shortcuts here so QKeySequence has a QApplication instance to use.
        Settings.__default_settings__.update({
            'shortcuts/aboutItem': [QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_F1)],
            'shortcuts/addToService': [],
            'shortcuts/audioPauseItem': [],
            'shortcuts/displayTagItem': [],
            'shortcuts/blankScreen': [QtGui.QKeySequence(QtCore.Qt.Key_Period)],
            'shortcuts/collapse': [QtGui.QKeySequence(QtCore.Qt.Key_Minus)],
            'shortcuts/desktopScreen': [QtGui.QKeySequence(QtCore.Qt.Key_D)],
            'shortcuts/desktopScreenEnable': [QtGui.QKeySequence(QtCore.Qt.Key_Escape)],
            'shortcuts/delete': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
            'shortcuts/down': [QtGui.QKeySequence(QtCore.Qt.Key_Down)],
            'shortcuts/editSong': [],
            'shortcuts/expand': [QtGui.QKeySequence(QtCore.Qt.Key_Plus)],
            'shortcuts/exportThemeItem': [],
            'shortcuts/fileNewItem': [QtGui.QKeySequence(QtGui.QKeySequence.New)],
            'shortcuts/fileSaveAsItem': [QtGui.QKeySequence(QtGui.QKeySequence.SaveAs)],
            'shortcuts/fileExitItem': [QtGui.QKeySequence(QtGui.QKeySequence.Quit)],
            'shortcuts/fileSaveItem': [QtGui.QKeySequence(QtGui.QKeySequence.Save)],
            'shortcuts/fileOpenItem': [QtGui.QKeySequence(QtGui.QKeySequence.Open)],
            'shortcuts/goLive': [],
            'shortcuts/userManualItem': [QtGui.QKeySequence(QtGui.QKeySequence.HelpContents)],
            'shortcuts/importThemeItem': [],
            'shortcuts/importBibleItem': [],
            'shortcuts/listViewBiblesDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
            'shortcuts/listViewBiblesPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return),
                                                    QtGui.QKeySequence(QtCore.Qt.Key_Enter)],
            'shortcuts/listViewBiblesLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
                                                 QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)],
            'shortcuts/listViewBiblesServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus),
                                                    QtGui.QKeySequence(QtCore.Qt.Key_Equal)],
            'shortcuts/listViewCustomDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
            'shortcuts/listViewCustomPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return),
                                                    QtGui.QKeySequence(QtCore.Qt.Key_Enter)],
            'shortcuts/listViewCustomLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
                                                 QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)],
            'shortcuts/listViewCustomServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus),
                                                    QtGui.QKeySequence(QtCore.Qt.Key_Equal)],
            'shortcuts/listViewImagesDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
            'shortcuts/listViewImagesPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return),
                                                    QtGui.QKeySequence(QtCore.Qt.Key_Enter)],
            'shortcuts/listViewImagesLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
                                                 QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)],
            'shortcuts/listViewImagesServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus),
                                                    QtGui.QKeySequence(QtCore.Qt.Key_Equal)],
            'shortcuts/listViewMediaDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
            'shortcuts/listViewMediaPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return),
                                                   QtGui.QKeySequence(QtCore.Qt.Key_Enter)],
            'shortcuts/listViewMediaLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
                                                QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)],
            'shortcuts/listViewMediaServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus),
                                                   QtGui.QKeySequence(QtCore.Qt.Key_Equal)],
            'shortcuts/listViewPresentationsDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
            'shortcuts/listViewPresentationsPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return),
                                                           QtGui.QKeySequence(QtCore.Qt.Key_Enter)],
            'shortcuts/listViewPresentationsLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
                                                        QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)],
            'shortcuts/listViewPresentationsServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus),
                                                           QtGui.QKeySequence(QtCore.Qt.Key_Equal)],
            'shortcuts/listViewSongsDeleteItem': [QtGui.QKeySequence(QtGui.QKeySequence.Delete)],
            'shortcuts/listViewSongsPreviewItem': [QtGui.QKeySequence(QtCore.Qt.Key_Return),
                                                   QtGui.QKeySequence(QtCore.Qt.Key_Enter)],
            'shortcuts/listViewSongsLiveItem': [QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Return),
                                                QtGui.QKeySequence(QtCore.Qt.SHIFT + QtCore.Qt.Key_Enter)],
            'shortcuts/listViewSongsServiceItem': [QtGui.QKeySequence(QtCore.Qt.Key_Plus),
                                                   QtGui.QKeySequence(QtCore.Qt.Key_Equal)],
            'shortcuts/lockPanel': [],
            'shortcuts/modeDefaultItem': [],
            'shortcuts/modeLiveItem': [],
            'shortcuts/make_live': [QtGui.QKeySequence(QtCore.Qt.Key_Return), QtGui.QKeySequence(QtCore.Qt.Key_Enter)],
            'shortcuts/moveUp': [QtGui.QKeySequence(QtCore.Qt.Key_PageUp)],
            'shortcuts/moveTop': [QtGui.QKeySequence(QtCore.Qt.Key_Home)],
            'shortcuts/modeSetupItem': [],
            'shortcuts/moveBottom': [QtGui.QKeySequence(QtCore.Qt.Key_End)],
            'shortcuts/moveDown': [QtGui.QKeySequence(QtCore.Qt.Key_PageDown)],
            'shortcuts/nextTrackItem': [],
            'shortcuts/nextItem_live': [QtGui.QKeySequence(QtCore.Qt.Key_Down),
                                        QtGui.QKeySequence(QtCore.Qt.Key_PageDown)],
            'shortcuts/nextItem_preview': [QtGui.QKeySequence(QtCore.Qt.Key_Down),
                                           QtGui.QKeySequence(QtCore.Qt.Key_PageDown)],
            'shortcuts/nextService': [QtGui.QKeySequence(QtCore.Qt.Key_Right)],
            'shortcuts/newService': [],
            'shortcuts/openService': [],
            'shortcuts/saveService': [],
            'shortcuts/previousItem_live': [QtGui.QKeySequence(QtCore.Qt.Key_Up),
                                            QtGui.QKeySequence(QtCore.Qt.Key_PageUp)],
            'shortcuts/playbackPause': [],
            'shortcuts/playbackPlay': [],
            'shortcuts/playbackStop': [],
            'shortcuts/playSlidesLoop': [],
            'shortcuts/playSlidesOnce': [],
            'shortcuts/previousService': [QtGui.QKeySequence(QtCore.Qt.Key_Left)],
            'shortcuts/previousItem_preview': [QtGui.QKeySequence(QtCore.Qt.Key_Up),
                                               QtGui.QKeySequence(QtCore.Qt.Key_PageUp)],
            'shortcuts/printServiceItem': [QtGui.QKeySequence(QtGui.QKeySequence.Print)],
            'shortcuts/songExportItem': [],
            'shortcuts/songUsageStatus': [QtGui.QKeySequence(QtCore.Qt.Key_F4)],
            'shortcuts/searchShortcut': [QtGui.QKeySequence(QtGui.QKeySequence.Find)],
            'shortcuts/settingsShortcutsItem': [],
            'shortcuts/settingsImportItem': [],
            'shortcuts/settingsPluginListItem': [QtGui.QKeySequence(QtCore.Qt.ALT + QtCore.Qt.Key_F7)],
            'shortcuts/songUsageDelete': [],
            'shortcuts/settingsConfigureItem': [QtGui.QKeySequence(QtGui.QKeySequence.Preferences)],
            'shortcuts/shortcutAction_B': [QtGui.QKeySequence(QtCore.Qt.Key_B)],
            'shortcuts/shortcutAction_C': [QtGui.QKeySequence(QtCore.Qt.Key_C)],
            'shortcuts/shortcutAction_E': [QtGui.QKeySequence(QtCore.Qt.Key_E)],
            'shortcuts/shortcutAction_I': [QtGui.QKeySequence(QtCore.Qt.Key_I)],
            'shortcuts/shortcutAction_O': [QtGui.QKeySequence(QtCore.Qt.Key_O)],
            'shortcuts/shortcutAction_P': [QtGui.QKeySequence(QtCore.Qt.Key_P)],
            'shortcuts/shortcutAction_V': [QtGui.QKeySequence(QtCore.Qt.Key_V)],
            'shortcuts/shortcutAction_0': [QtGui.QKeySequence(QtCore.Qt.Key_0)],
            'shortcuts/shortcutAction_1': [QtGui.QKeySequence(QtCore.Qt.Key_1)],
            'shortcuts/shortcutAction_2': [QtGui.QKeySequence(QtCore.Qt.Key_2)],
            'shortcuts/shortcutAction_3': [QtGui.QKeySequence(QtCore.Qt.Key_3)],
            'shortcuts/shortcutAction_4': [QtGui.QKeySequence(QtCore.Qt.Key_4)],
            'shortcuts/shortcutAction_5': [QtGui.QKeySequence(QtCore.Qt.Key_5)],
            'shortcuts/shortcutAction_6': [QtGui.QKeySequence(QtCore.Qt.Key_6)],
            'shortcuts/shortcutAction_7': [QtGui.QKeySequence(QtCore.Qt.Key_7)],
            'shortcuts/shortcutAction_8': [QtGui.QKeySequence(QtCore.Qt.Key_8)],
            'shortcuts/shortcutAction_9': [QtGui.QKeySequence(QtCore.Qt.Key_9)],
            'shortcuts/settingsExportItem': [],
            'shortcuts/songUsageReport': [],
            'shortcuts/songImportItem': [],
            'shortcuts/themeScreen': [QtGui.QKeySequence(QtCore.Qt.Key_T)],
            'shortcuts/toolsReindexItem': [],
            'shortcuts/toolsFindDuplicates': [],
            'shortcuts/toolsSongListReport': [],
            'shortcuts/toolsAlertItem': [QtGui.QKeySequence(QtCore.Qt.Key_F7)],
            'shortcuts/toolsFirstTimeWizard': [],
            'shortcuts/toolsOpenDataFolder': [],
            'shortcuts/toolsAddToolItem': [],
            'shortcuts/updateThemeImages': [],
            'shortcuts/up': [QtGui.QKeySequence(QtCore.Qt.Key_Up)],
            'shortcuts/viewProjectorManagerItem': [QtGui.QKeySequence(QtCore.Qt.Key_F6)],
            'shortcuts/viewThemeManagerItem': [QtGui.QKeySequence(QtCore.Qt.Key_F10)],
            'shortcuts/viewMediaManagerItem': [QtGui.QKeySequence(QtCore.Qt.Key_F8)],
            'shortcuts/viewPreviewPanel': [QtGui.QKeySequence(QtCore.Qt.Key_F11)],
            'shortcuts/viewLivePanel': [QtGui.QKeySequence(QtCore.Qt.Key_F12)],
            'shortcuts/viewServiceManagerItem': [QtGui.QKeySequence(QtCore.Qt.Key_F9)],
            'shortcuts/webSiteItem': []
        })

    def get_default_value(self, key):
        """
        Get the default value of the given key
        """
        if self.group():
            key = self.group() + '/' + key
        return Settings.__default_settings__[key]

    def can_upgrade(self):
        """
        Can / should the settings be upgraded

        :rtype: bool
        """
        return __version__ != self.value('settings/version')

    def upgrade_settings(self):
        """
        This method is only called to clean up the config. It removes old settings and it renames settings. See
        ``__obsolete_settings__`` for more details.
        """
        current_version = self.value('settings/version')
        for version in range(current_version, __version__):
            version += 1
            upgrade_list = getattr(self, '__setting_upgrade_{version}__'.format(version=version))
            for old_keys, new_key, rules in upgrade_list:
                # Once removed we don't have to do this again. - Can be removed once fully switched to the versioning
                # system.
                if not isinstance(old_keys, (tuple, list)):
                    old_keys = [old_keys]
                if any([not self.contains(old_key) for old_key in old_keys]):
                    log.warning('One of {} does not exist, skipping upgrade'.format(old_keys))
                    continue
                if new_key:
                    # Get the value of the old_key.
                    old_values = [super(Settings, self).value(old_key) for old_key in old_keys]
                    # When we want to convert the value, we have to figure out the default value (because we cannot get
                    # the default value from the central settings dict.
                    if rules:
                        default_values = rules[0][1]
                        if not isinstance(default_values, (list, tuple)):
                            default_values = [default_values]
                        old_values = [self._convert_value(old_value, default_value)
                                      for old_value, default_value in zip(old_values, default_values)]
                    # Iterate over our rules and check what the old_value should be "converted" to.
                    new_value = None
                    for new_rule, old_rule in rules:
                        # If the value matches with the condition (rule), then use the provided value. This is used to
                        # convert values. E. g. an old value 1 results in True, and 0 in False.
                        if callable(new_rule):
                            new_value = new_rule(*old_values)
                        elif old_rule in old_values:
                            new_value = new_rule
                            break
                    self.setValue(new_key, new_value)
                [self.remove(old_key) for old_key in old_keys if old_key != new_key]
            self.setValue('settings/version', version)

    def value(self, key):
        """
        Returns the value for the given ``key``. The returned ``value`` is of the same type as the default value in the
        *Settings.__default_settings__* dict.

        :param str key: The key to return the value from.
        :return: The value stored by the setting.
        """
        # if group() is not empty the group has not been specified together with the key.
        if self.group():
            default_value = Settings.__default_settings__[self.group() + '/' + key]
        else:
            default_value = Settings.__default_settings__[key]
        setting = super(Settings, self).value(key, default_value)
        return self._convert_value(setting, default_value)

    def setValue(self, key, value):
        """
        Reimplement the setValue method to handle Path objects.

        :param str key: The key of the setting to save
        :param value: The value to save
        :rtype: None
        """
        if isinstance(value, Path) or (isinstance(value, list) and value and isinstance(value[0], Path)):
            value = json.dumps(value, cls=OpenLPJsonEncoder)
        super().setValue(key, value)

    def _convert_value(self, setting, default_value):
        """
        This converts the given ``setting`` to the type of the given ``default_value``.

        :param setting: The setting to convert. This could be ``true`` for example.Settings()
        :param default_value: Indication the type the setting should be converted to. For example ``True``
        (type is boolean), meaning that we convert the string ``true`` to a python boolean.

        **Note**, this method only converts a few types and might need to be extended if a certain type is missing!
        """
        # Handle 'None' type (empty value) properly.
        if setting is None:
            # An empty string saved to the settings results in a None type being returned.
            # Convert it to empty unicode string.
            if isinstance(default_value, str):
                return ''
            # An empty list saved to the settings results in a None type being returned.
            elif isinstance(default_value, list):
                return []
        elif isinstance(setting, str):
            if '__Path__' in setting:
                return json.loads(setting, cls=OpenLPJsonDecoder)
        # Convert the setting to the correct type.
        if isinstance(default_value, bool):
            if isinstance(setting, bool):
                return setting
            # Sometimes setting is string instead of a boolean.
            return setting == 'true'
        if isinstance(default_value, int):
            return int(setting)
        return setting

    def export(self, dest_path):
        """
        Export the settings to file.

        :param openlp.core.common.path.Path dest_path: The file path to create the export file.
        :return: Success
        :rtype: bool
        """
        temp_path = Path(gettempdir(), 'openlp', 'exportConf.tmp')
        # Delete old files if found.
        if temp_path.exists():
            temp_path.unlink()
        if dest_path.exists():
            dest_path.unlink()
        self.remove('SettingsImport')
        # Get the settings.
        keys = self.allKeys()
        export_settings = QtCore.QSettings(str(temp_path), Settings.IniFormat)
        # Add a header section.
        # This is to insure it's our conf file for import.
        now = datetime.datetime.now()
        # Write INI format using QSettings.
        # Write our header.
        export_settings.beginGroup('SettingsImport')
        export_settings.setValue('Make_Changes', 'At_Own_RISK')
        export_settings.setValue('type', 'OpenLP_settings_export')
        export_settings.setValue('file_date_created', now.strftime("%Y-%m-%d %H:%M"))
        export_settings.endGroup()
        # Write all the sections and keys.
        for section_key in keys:
            # FIXME: We are conflicting with the standard "General" section.
            if 'eneral' in section_key:
                section_key = section_key.lower()
            key_value = super().value(section_key)
            if key_value is not None:
                export_settings.setValue(section_key, key_value)
        export_settings.sync()
        # Temp CONF file has been written.  Blanks in keys are now '%20'.
        # Read the  temp file and output the user's CONF file with blanks to
        # make it more readable.
        try:
            with dest_path.open('w') as export_conf_file, temp_path.open('r') as temp_conf:
                for file_record in temp_conf:
                    # Get rid of any invalid entries.
                    if file_record.find('@Invalid()') == -1:
                        file_record = file_record.replace('%20', ' ')
                        export_conf_file.write(file_record)
        finally:
            temp_path.unlink()
Example #40
0
def main(args=None):
    """
    The main function which parses command line options and then runs

    :param args: Some args
    """
    args = parse_options(args)
    qt_args = []
    if args and args.loglevel.lower() in ['d', 'debug']:
        log.setLevel(logging.DEBUG)
    elif args and args.loglevel.lower() in ['w', 'warning']:
        log.setLevel(logging.WARNING)
    else:
        log.setLevel(logging.INFO)
    if args and args.style:
        qt_args.extend(['-style', args.style])
    # Throw the rest of the arguments at Qt, just in case.
    qt_args.extend(args.rargs)
    # Bug #1018855: Set the WM_CLASS property in X11
    if not is_win() and not is_macosx():
        qt_args.append('OpenLP')
    # Initialise the resources
    qInitResources()
    # Now create and actually run the application.
    application = OpenLP(qt_args)
    application.setOrganizationName('OpenLP')
    application.setOrganizationDomain('openlp.org')
    application.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True)
    if args and args.portable:
        application.setApplicationName('OpenLPPortable')
        Settings.setDefaultFormat(Settings.IniFormat)
        # Get location OpenLPPortable.ini
        application_path = AppLocation.get_directory(AppLocation.AppDir)
        set_up_logging(
            os.path.abspath(os.path.join(application_path, '..', '..',
                                         'Other')))
        log.info('Running portable')
        portable_settings_file = os.path.abspath(
            os.path.join(application_path, '..', '..', 'Data', 'OpenLP.ini'))
        # Make this our settings file
        log.info('INI file: {name}'.format(name=portable_settings_file))
        Settings.set_filename(portable_settings_file)
        portable_settings = Settings()
        # Set our data path
        data_path = os.path.abspath(
            os.path.join(
                application_path,
                '..',
                '..',
                'Data',
            ))
        log.info('Data path: {name}'.format(name=data_path))
        # Point to our data path
        portable_settings.setValue('advanced/data path', data_path)
        portable_settings.setValue('advanced/is portable', True)
        portable_settings.sync()
    else:
        application.setApplicationName('OpenLP')
        set_up_logging(AppLocation.get_directory(AppLocation.CacheDir))
    Registry.create()
    Registry().register('application', application)
    application.setApplicationVersion(get_application_version()['version'])
    # Instance check
    if application.is_already_running():
        sys.exit()
    # Remove/convert obsolete settings.
    Settings().remove_obsolete_settings()
    # First time checks in settings
    if not Settings().value('core/has run wizard'):
        if not FirstTimeLanguageForm().exec():
            # if cancel then stop processing
            sys.exit()
    # i18n Set Language
    language = LanguageManager.get_language()
    application_translator, default_translator = LanguageManager.get_translator(
        language)
    if not application_translator.isEmpty():
        application.installTranslator(application_translator)
    if not default_translator.isEmpty():
        application.installTranslator(default_translator)
    else:
        log.debug('Could not find default_translator.')
    if args and not args.no_error_form:
        sys.excepthook = application.hook_exception
    sys.exit(application.run(qt_args))
Example #41
0
    def run(self, args):
        """
        Run the OpenLP application.

        :param args: Some Args
        """
        self.is_event_loop_active = False
        # On Windows, the args passed into the constructor are ignored. Not very handy, so set the ones we want to use.
        # On Linux and FreeBSD, in order to set the WM_CLASS property for X11, we pass "OpenLP" in as a command line
        # argument. This interferes with files being passed in as command line arguments, so we remove it from the list.
        if 'OpenLP' in args:
            args.remove('OpenLP')
        self.args.extend(args)
        # Decide how many screens we have and their size
        screens = ScreenList.create(self.desktop())
        # First time checks in settings
        has_run_wizard = Settings().value('core/has run wizard')
        if not has_run_wizard:
            ftw = FirstTimeForm()
            ftw.initialize(screens)
            if ftw.exec() == QtWidgets.QDialog.Accepted:
                Settings().setValue('core/has run wizard', True)
            elif ftw.was_cancelled:
                QtCore.QCoreApplication.exit()
                sys.exit()
        # Correct stylesheet bugs
        application_stylesheet = ''
        if not Settings().value('advanced/alternate rows'):
            base_color = self.palette().color(QtGui.QPalette.Active,
                                              QtGui.QPalette.Base)
            alternate_rows_repair_stylesheet = \
                'QTableWidget, QListWidget, QTreeWidget {alternate-background-color: ' + base_color.name() + ';}\n'
            application_stylesheet += alternate_rows_repair_stylesheet
        if is_win():
            application_stylesheet += WIN_REPAIR_STYLESHEET
        if application_stylesheet:
            self.setStyleSheet(application_stylesheet)
        show_splash = Settings().value('core/show splash')
        if show_splash:
            self.splash = SplashScreen()
            self.splash.show()
        # make sure Qt really display the splash screen
        self.processEvents()
        # Check if OpenLP has been upgrade and if a backup of data should be created
        self.backup_on_upgrade(has_run_wizard)
        # start the main app window
        self.main_window = MainWindow()
        Registry().execute('bootstrap_initialise')
        Registry().execute('bootstrap_post_set_up')
        Registry().initialise = False
        self.main_window.show()
        if show_splash:
            # now kill the splashscreen
            self.splash.finish(self.main_window)
            log.debug('Splashscreen closed')
        # make sure Qt really display the splash screen
        self.processEvents()
        self.main_window.repaint()
        self.processEvents()
        if not has_run_wizard:
            self.main_window.first_time()
        # update_check = Settings().value('core/update check')
        # if update_check:
        #     version = VersionThread(self.main_window)
        #     version.start()
        self.main_window.is_display_blank()
        self.main_window.app_startup()
        return self.exec()