openlp/openlp/plugins/presentations/lib/maclocontroller.py

267 lines
9.1 KiB
Python

# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2019 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
import logging
from subprocess import Popen
from Pyro4 import Proxy
from openlp.core.common import delete_file, is_macosx
from openlp.core.common.applocation import AppLocation
from openlp.core.common.path import Path
from openlp.core.common.registry import Registry
from openlp.core.display.screens import ScreenList
from openlp.plugins.presentations.lib.serializers import register_classes
from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument
LIBREOFFICE_PATH = Path('/Applications/LibreOffice.app')
LIBREOFFICE_PYTHON = LIBREOFFICE_PATH / 'Contents' / 'Resources' / 'python'
if is_macosx() and LIBREOFFICE_PATH.exists():
macuno_available = True
else:
macuno_available = False
log = logging.getLogger(__name__)
register_classes()
class MacLOController(PresentationController):
"""
Class to control interactions with MacLO presentations on Mac OS X via Pyro4. It starts the Pyro4 nameserver,
starts the LibreOfficeServer, and then controls MacLO via Pyro4.
"""
log.info('MacLOController loaded')
def __init__(self, plugin):
"""
Initialise the class
"""
log.debug('Initialising')
super(MacLOController, self).__init__(plugin, 'maclo', MacLODocument, 'Impress on macOS')
self.supports = ['odp']
self.also_supports = ['ppt', 'pps', 'pptx', 'ppsx', 'pptm']
self.server_process = None
self._client = None
self._start_server()
def _start_server(self):
"""
Start a LibreOfficeServer
"""
libreoffice_python = Path('/Applications/LibreOffice.app/Contents/Resources/python')
libreoffice_server = AppLocation.get_directory(AppLocation.PluginsDir).joinpath('presentations', 'lib',
'libreofficeserver.py')
if libreoffice_python.exists():
self.server_process = Popen([str(libreoffice_python), str(libreoffice_server)])
@property
def client(self):
"""
Set up a Pyro4 client so that we can talk to the LibreOfficeServer
"""
if not self._client:
self._client = Proxy('PYRO:openlp.libreofficeserver@localhost:4310')
if not self._client._pyroConnection:
self._client._pyroReconnect()
return self._client
def check_available(self):
"""
MacLO is able to run on this machine.
"""
log.debug('check_available')
return macuno_available
def start_process(self):
"""
Loads a running version of LibreOffice in the background. It is not displayed to the user but is available to
the UNO interface when required.
"""
log.debug('Started automatically by the Pyro server')
self.client.start_process()
def kill(self):
"""
Called at system exit to clean up any running presentations.
"""
log.debug('Kill LibreOffice')
self.client.shutdown()
self.server_process.kill()
class MacLODocument(PresentationDocument):
"""
Class which holds information and controls a single presentation.
"""
def __init__(self, controller, presentation):
"""
Constructor, store information about the file and initialise.
"""
log.debug('Init Presentation LibreOffice')
super(MacLODocument, self).__init__(controller, presentation)
self.client = controller.client
def load_presentation(self):
"""
Tell the LibreOfficeServer to start the presentation.
"""
log.debug('Load Presentation LibreOffice')
if not self.client.load_presentation(str(self.file_path), ScreenList().current.number + 1):
return False
self.create_thumbnails()
self.create_titles_and_notes()
return True
def create_thumbnails(self):
"""
Create thumbnail images for presentation.
"""
log.debug('create thumbnails LibreOffice')
if self.check_thumbnails():
return
temp_thumbnails = self.client.extract_thumbnails(str(self.get_temp_folder()))
for index, temp_thumb in enumerate(temp_thumbnails):
temp_thumb = Path(temp_thumb)
self.convert_thumbnail(temp_thumb, index + 1)
delete_file(temp_thumb)
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 = self.client.get_titles_and_notes()
self.save_titles_and_notes(titles, notes)
def close_presentation(self):
"""
Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being
shutdown.
"""
log.debug('close Presentation LibreOffice')
self.client.close_presentation()
self.controller.remove_doc(self)
def is_loaded(self):
"""
Returns true if a presentation is loaded.
"""
log.debug('is loaded LibreOffice')
return self.client.is_loaded()
def is_active(self):
"""
Returns true if a presentation is active and running.
"""
log.debug('is active LibreOffice')
return self.client.is_active()
def unblank_screen(self):
"""
Unblanks the screen.
"""
log.debug('unblank screen LibreOffice')
return self.client.unblank_screen()
def blank_screen(self):
"""
Blanks the screen.
"""
log.debug('blank screen LibreOffice')
self.client.blank_screen()
def is_blank(self):
"""
Returns true if screen is blank.
"""
log.debug('is blank LibreOffice')
return self.client.is_blank()
def stop_presentation(self):
"""
Stop the presentation, remove from screen.
"""
log.debug('stop presentation LibreOffice')
self.client.stop_presentation()
def start_presentation(self):
"""
Start the presentation from the beginning.
"""
log.debug('start presentation LibreOffice')
self.client.start_presentation()
# Make sure impress doesn't steal focus, unless we're on a single screen setup
if len(ScreenList()) > 1:
Registry().get('main_window').activateWindow()
def get_slide_number(self):
"""
Return the current slide number on the screen, from 1.
"""
return self.client.get_slide_number()
def get_slide_count(self):
"""
Return the total number of slides.
"""
return self.client.get_slide_count()
def goto_slide(self, slide_no):
"""
Go to a specific slide (from 1).
:param slide_no: The slide the text is required for, starting at 1
"""
self.client.goto_slide(slide_no)
def next_step(self):
"""
Triggers the next effect of slide on the running presentation.
"""
self.client.next_step()
def previous_step(self):
"""
Triggers the previous slide on the running presentation.
"""
self.client.previous_step()
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 self.client.get_slide_text(slide_no)
def get_slide_notes(self, slide_no):
"""
Returns the text in the slide notes.
:param slide_no: The slide the notes are required for, starting at 1
"""
return self.client.get_slide_notes(slide_no)