diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py
index b12438679..062a8440f 100644
--- a/openlp/core/ui/slidecontroller.py
+++ b/openlp/core/ui/slidecontroller.py
@@ -374,7 +374,8 @@ class SlideController(DisplayController, RegistryProperties):
triggers=self._slide_shortcut_activated) for s in shortcuts])
self.shortcut_timer.timeout.connect(self._slide_shortcut_activated)
# Signals
- self.preview_widget.itemSelectionChanged.connect(self.on_slide_selected)
+ self.preview_widget.clicked.connect(self.on_slide_selected)
+ self.preview_widget.verticalHeader().sectionClicked.connect(self.on_slide_selected)
if self.is_live:
# Need to use event as called across threads and UI is updated
QtCore.QObject.connect(self, QtCore.SIGNAL('slidecontroller_toggle_display'), self.toggle_display)
diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py
index 661f8f906..54c570c9a 100644
--- a/openlp/plugins/presentations/lib/mediaitem.py
+++ b/openlp/plugins/presentations/lib/mediaitem.py
@@ -38,7 +38,7 @@ from openlp.core.lib import MediaManagerItem, ItemCapabilities, ServiceItemConte
from openlp.core.lib.ui import critical_error_message_box, create_horizontal_adjusting_combo_box
from openlp.core.utils import get_locale_key
from openlp.plugins.presentations.lib import MessageListener
-
+from openlp.plugins.presentations.lib.pdfcontroller import PDF_CONTROLLER_FILETYPES
log = logging.getLogger(__name__)
@@ -260,11 +260,11 @@ class PresentationMediaItem(MediaManagerItem):
filename = presentation_file
if filename is None:
filename = items[0].data(QtCore.Qt.UserRole)
- file_type = os.path.splitext(filename)[1][1:]
+ file_type = os.path.splitext(filename.lower())[1][1:]
if not self.display_type_combo_box.currentText():
return False
service_item.add_capability(ItemCapabilities.CanEditTitle)
- if (file_type == 'pdf' or file_type == 'xps') and context != ServiceItemContext.Service:
+ if file_type in PDF_CONTROLLER_FILETYPES and context != ServiceItemContext.Service:
service_item.add_capability(ItemCapabilities.CanMaintain)
service_item.add_capability(ItemCapabilities.CanPreview)
service_item.add_capability(ItemCapabilities.CanLoop)
diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py
index ac115228a..fb37ef403 100644
--- a/openlp/plugins/presentations/lib/messagelistener.py
+++ b/openlp/plugins/presentations/lib/messagelistener.py
@@ -29,12 +29,14 @@
import logging
import copy
+import os
from PyQt4 import QtCore
from openlp.core.common import Registry
from openlp.core.ui import HideMode
from openlp.core.lib import ServiceItemContext, ServiceItem
+from openlp.plugins.presentations.lib.pdfcontroller import PDF_CONTROLLER_FILETYPES
log = logging.getLogger(__name__)
@@ -320,10 +322,11 @@ class MessageListener(object):
file = item.get_frame_path()
self.handler = item.processor
# When starting presentation from the servicemanager we convert
- # PDF/XPS-serviceitems into image-serviceitems. When started from the mediamanager
+ # PDF/XPS/OXPS-serviceitems into image-serviceitems. When started from the mediamanager
# the conversion has already been done at this point.
- if file.endswith('.pdf') or file.endswith('.xps'):
- log.debug('Converting from pdf/xps to images for serviceitem with file %s', file)
+ file_type = os.path.splitext(file.lower())[1][1:]
+ if file_type in PDF_CONTROLLER_FILETYPES:
+ log.debug('Converting from pdf/xps/oxps to images for serviceitem with file %s', file)
# Create a copy of the original item, and then clear the original item so it can be filled with images
item_cpy = copy.copy(item)
item.__init__(None)
@@ -338,7 +341,7 @@ class MessageListener(object):
item.image_border = item_cpy.image_border
item.main = item_cpy.main
item.theme_data = item_cpy.theme_data
- # When presenting PDF or XPS, we are using the image presentation code,
+ # When presenting PDF/XPS/OXPS, we are using the image presentation code,
# so handler & processor is set to None, and we skip adding the handler.
self.handler = None
if self.handler == self.media_item.automatic:
@@ -349,7 +352,7 @@ class MessageListener(object):
controller = self.live_handler
else:
controller = self.preview_handler
- # When presenting PDF or XPS, we are using the image presentation code,
+ # When presenting PDF/XPS/OXPS, we are using the image presentation code,
# so handler & processor is set to None, and we skip adding the handler.
if self.handler is None:
self.controller = controller
diff --git a/openlp/plugins/presentations/lib/pdfcontroller.py b/openlp/plugins/presentations/lib/pdfcontroller.py
index 0283fefd4..e1d0dc8f0 100644
--- a/openlp/plugins/presentations/lib/pdfcontroller.py
+++ b/openlp/plugins/presentations/lib/pdfcontroller.py
@@ -34,12 +34,17 @@ import re
from subprocess import check_output, CalledProcessError, STDOUT
from openlp.core.utils import AppLocation
-from openlp.core.common import Settings, is_win
+from openlp.core.common import Settings, is_win, trace_error_handler
from openlp.core.lib import ScreenList
from .presentationcontroller import PresentationController, PresentationDocument
+if is_win():
+ from subprocess import STARTUPINFO, STARTF_USESHOWWINDOW
+
log = logging.getLogger(__name__)
+PDF_CONTROLLER_FILETYPES = ['pdf', 'xps', 'oxps']
+
class PdfController(PresentationController):
"""
@@ -74,11 +79,19 @@ class PdfController(PresentationController):
runlog = ''
log.debug('testing program_path: %s', program_path)
try:
- runlog = check_output([program_path, '--help'], stderr=STDOUT)
+ # Setup startupinfo options for check_output to avoid console popping up on windows
+ if is_win():
+ startupinfo = STARTUPINFO()
+ startupinfo.dwFlags |= STARTF_USESHOWWINDOW
+ else:
+ startupinfo = None
+ runlog = check_output([program_path, '--help'], stderr=STDOUT, startupinfo=startupinfo)
except CalledProcessError as e:
runlog = e.output
except Exception:
+ trace_error_handler(log)
runlog = ''
+ log.debug('check_output returned: %s' % runlog)
# Analyse the output to see it the program is mudraw, ghostscript or neither
for line in runlog.splitlines():
decoded_line = line.decode()
@@ -148,7 +161,7 @@ class PdfController(PresentationController):
if os.path.isfile(os.path.join(application_path, 'mudraw')):
self.mudrawbin = os.path.join(application_path, 'mudraw')
if self.mudrawbin:
- self.also_supports = ['xps']
+ self.also_supports = ['xps', 'oxps']
return True
elif self.gsbin:
return True
@@ -182,6 +195,12 @@ class PdfDocument(PresentationDocument):
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
def gs_get_resolution(self, size):
"""
@@ -199,7 +218,8 @@ class PdfDocument(PresentationDocument):
runlog = []
try:
runlog = check_output([self.controller.gsbin, '-dNOPAUSE', '-dNODISPLAY', '-dBATCH',
- '-sFile=' + self.file_path, gs_resolution_script])
+ '-sFile=' + self.file_path, gs_resolution_script],
+ startupinfo=self.startupinfo)
except CalledProcessError as e:
log.debug(' '.join(e.cmd))
log.debug(e.output)
@@ -248,13 +268,14 @@ class PdfDocument(PresentationDocument):
os.makedirs(self.get_temp_folder())
if self.controller.mudrawbin:
runlog = check_output([self.controller.mudrawbin, '-w', str(size.right()), '-h', str(size.bottom()),
- '-o', os.path.join(self.get_temp_folder(), 'mainslide%03d.png'), self.file_path])
+ '-o', os.path.join(self.get_temp_folder(), 'mainslide%03d.png'), self.file_path],
+ startupinfo=self.startupinfo)
elif self.controller.gsbin:
resolution = self.gs_get_resolution(size)
runlog = check_output([self.controller.gsbin, '-dSAFER', '-dNOPAUSE', '-dBATCH', '-sDEVICE=png16m',
'-r' + str(resolution), '-dTextAlphaBits=4', '-dGraphicsAlphaBits=4',
'-sOutputFile=' + os.path.join(self.get_temp_folder(), 'mainslide%03d.png'),
- self.file_path])
+ self.file_path], startupinfo=self.startupinfo)
created_files = sorted(os.listdir(self.get_temp_folder()))
for fn in created_files:
if os.path.isfile(os.path.join(self.get_temp_folder(), fn)):
diff --git a/openlp/plugins/songs/lib/openlyricsxml.py b/openlp/plugins/songs/lib/openlyricsxml.py
index 9458d6180..cab51093c 100644
--- a/openlp/plugins/songs/lib/openlyricsxml.py
+++ b/openlp/plugins/songs/lib/openlyricsxml.py
@@ -239,6 +239,7 @@ class OpenLyrics(object):
def __init__(self, manager):
self.manager = manager
+ FormattingTags.load_tags()
def song_to_xml(self, song):
"""
@@ -582,18 +583,20 @@ class OpenLyrics(object):
# Some tags have only start html e.g. {br}
'end html': tag.close.text if hasattr(tag, 'close') else '',
'protected': False,
+ # Add 'temporary' key in case the formatting tag should not be saved otherwise it is supposed that
+ # formatting tag is permanent.
+ 'temporary': temporary
}
- # Add 'temporary' key in case the formatting tag should not be saved otherwise it is supposed that
- # formatting tag is permanent.
- if temporary:
- openlp_tag['temporary'] = temporary
found_tags.append(openlp_tag)
existing_tag_ids = [tag['start tag'] for tag in FormattingTags.get_html_tags()]
new_tags = [tag for tag in found_tags if tag['start tag'] not in existing_tag_ids]
# Do not save an empty list.
if new_tags:
FormattingTags.add_html_tags(new_tags)
- FormattingTags.save_html_tags()
+ if not temporary:
+ custom_tags = [tag for tag in FormattingTags.get_html_tags()
+ if not tag['protected'] and not tag['temporary']]
+ FormattingTags.save_html_tags(custom_tags)
def _process_lines_mixed_content(self, element, newlines=True):
"""
diff --git a/tests/functional/openlp_plugins/presentations/test_mediaitem.py b/tests/functional/openlp_plugins/presentations/test_mediaitem.py
index 2210c7d1f..b826a4cae 100644
--- a/tests/functional/openlp_plugins/presentations/test_mediaitem.py
+++ b/tests/functional/openlp_plugins/presentations/test_mediaitem.py
@@ -71,7 +71,7 @@ class TestMediaItem(TestCase, TestMixin):
pdf_controller = MagicMock()
pdf_controller.enabled.return_value = True
pdf_controller.supports = ['pdf']
- pdf_controller.also_supports = ['xps']
+ pdf_controller.also_supports = ['xps', 'oxps']
# Mock the controllers.
self.media_item.controllers = {
'Impress': impress_controller,
@@ -90,3 +90,4 @@ class TestMediaItem(TestCase, TestMixin):
self.assertIn('*.ppt', self.media_item.on_new_file_masks, 'The file mask should contain the ppt extension')
self.assertIn('*.pdf', self.media_item.on_new_file_masks, 'The file mask should contain the pdf extension')
self.assertIn('*.xps', self.media_item.on_new_file_masks, 'The file mask should contain the xps extension')
+ self.assertIn('*.oxps', self.media_item.on_new_file_masks, 'The file mask should contain the oxps extension')
diff --git a/tests/functional/openlp_plugins/remotes/test_router.py b/tests/functional/openlp_plugins/remotes/test_router.py
index c0190ecda..a88a8822f 100644
--- a/tests/functional/openlp_plugins/remotes/test_router.py
+++ b/tests/functional/openlp_plugins/remotes/test_router.py
@@ -132,6 +132,7 @@ class TestRouter(TestCase, TestMixin):
Test the main poll logic
"""
# GIVEN: a defined router with two slides
+ Registry.create()
Registry().register('live_controller', MagicMock)
router = HttpRouter()
router.send_response = MagicMock()
diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py
index f441084e7..3cb2c3bbd 100644
--- a/tests/functional/openlp_plugins/songs/test_ewimport.py
+++ b/tests/functional/openlp_plugins/songs/test_ewimport.py
@@ -35,6 +35,7 @@ from unittest import TestCase
from tests.functional import MagicMock, patch
+from openlp.core.common import Registry
from openlp.plugins.songs.lib.importers.easyworship import EasyWorshipSongImport, FieldDescEntry, FieldType
TEST_PATH = os.path.abspath(
@@ -153,6 +154,11 @@ class TestEasyWorshipSongImport(TestCase):
"""
Test the functions in the :mod:`ewimport` module.
"""
+ def setUp(self):
+ """
+ Create the registry
+ """
+ Registry.create()
def create_field_desc_entry_test(self):
"""
diff --git a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py
index 25db3e9e4..d7ba07beb 100644
--- a/tests/functional/openlp_plugins/songs/test_openlyricsimport.py
+++ b/tests/functional/openlp_plugins/songs/test_openlyricsimport.py
@@ -31,11 +31,18 @@ This module contains tests for the OpenLyrics song importer.
"""
import os
+import json
from unittest import TestCase
+from lxml import etree, objectify
from tests.functional import MagicMock, patch
+from tests.helpers.testmixin import TestMixin
from openlp.plugins.songs.lib.importers.openlyrics import OpenLyricsImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
+from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics
+from openlp.core.common import Registry, Settings
+from openlp.core.lib import FormattingTags
+
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__),
'..', '..', '..', 'resources', 'openlyricssongs'))
@@ -59,11 +66,33 @@ SONG_TEST_DATA = {
}
}
+start_tags = [{"protected": False, "desc": "z", "start tag": "{z}", "end html": "", "temporary": False,
+ "end tag": "{/z}", "start html": "strong>"}]
+result_tags = [{"temporary": False, "protected": False, "desc": "z", "start tag": "{z}", "start html": "strong>",
+ "end html": "", "end tag": "{/z}"},
+ {"temporary": False, "end tag": "{/c}", "desc": "c", "start tag": "{c}",
+ "start html": "", "end html": "",
+ "protected": False}]
-class TestOpenLyricsImport(TestCase):
+
+class TestOpenLyricsImport(TestCase, TestMixin):
"""
Test the functions in the :mod:`openlyricsimport` module.
"""
+ def setUp(self):
+ """
+ Create the registry
+ """
+ self.get_application()
+ Registry.create()
+ self.build_settings()
+
+ def tearDown(self):
+ """
+ Cleanup
+ """
+ self.destroy_settings()
+
def create_importer_test(self):
"""
Test creating an instance of the OpenLyrics file importer
@@ -97,3 +126,24 @@ class TestOpenLyricsImport(TestCase):
# THEN: The xml_to_song() method should have been called
self.assertTrue(importer.open_lyrics.xml_to_song.called)
+
+ def process_formatting_tags_test(self):
+ """
+ Test that _process_formatting_tags works
+ """
+ # GIVEN: A OpenLyric XML with formatting tags and a mocked out manager
+ mocked_manager = MagicMock()
+ Settings().setValue('formattingTags/html_tags', json.dumps(start_tags))
+ ol = OpenLyrics(mocked_manager)
+ parser = etree.XMLParser(remove_blank_text=True)
+ parsed_file = etree.parse(open(os.path.join(TEST_PATH, 'duchu-tags.xml'), 'rb'), parser)
+ xml = etree.tostring(parsed_file).decode()
+ song_xml = objectify.fromstring(xml)
+
+ # WHEN: processing the formatting tags
+ ol._process_formatting_tags(song_xml, False)
+
+ # THEN: New tags should have been saved
+ self.assertListEqual(json.loads(json.dumps(result_tags)),
+ json.loads(str(Settings().value('formattingTags/html_tags'))),
+ 'The formatting tags should contain both the old and the new')
diff --git a/tests/functional/openlp_plugins/songs/test_opensongimport.py b/tests/functional/openlp_plugins/songs/test_opensongimport.py
index 07b275f98..09d9eb61f 100644
--- a/tests/functional/openlp_plugins/songs/test_opensongimport.py
+++ b/tests/functional/openlp_plugins/songs/test_opensongimport.py
@@ -35,6 +35,7 @@ from unittest import TestCase
from tests.helpers.songfileimport import SongImportTestHelper
from openlp.plugins.songs.lib.importers.opensong import OpenSongImport
+from openlp.core.common import Registry
from tests.functional import patch, MagicMock
TEST_PATH = os.path.abspath(
@@ -64,6 +65,12 @@ class TestOpenSongImport(TestCase):
"""
Test the functions in the :mod:`opensongimport` module.
"""
+ def setUp(self):
+ """
+ Create the registry
+ """
+ Registry.create()
+
def create_importer_test(self):
"""
Test creating an instance of the OpenSong file importer
diff --git a/tests/functional/openlp_plugins/songs/test_powerpraiseimport.py b/tests/functional/openlp_plugins/songs/test_powerpraiseimport.py
index e6a2a5194..eac51f6da 100644
--- a/tests/functional/openlp_plugins/songs/test_powerpraiseimport.py
+++ b/tests/functional/openlp_plugins/songs/test_powerpraiseimport.py
@@ -34,6 +34,7 @@ ProPresenter song files into the current installation database.
import os
from tests.helpers.songfileimport import SongImportTestHelper
+from openlp.core.common import Registry
TEST_PATH = os.path.abspath(
os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'powerpraisesongs'))
diff --git a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py
index 3d872ae65..493d8fd2a 100644
--- a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py
+++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py
@@ -36,6 +36,7 @@ from unittest import TestCase
from tests.functional import MagicMock, patch
from openlp.plugins.songs.lib.importers.songbeamer import SongBeamerImport
from openlp.plugins.songs.lib import VerseType
+from openlp.core.common import Registry
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__),
'..', '..', '..', 'resources', 'songbeamersongs'))
@@ -59,6 +60,12 @@ class TestSongBeamerImport(TestCase):
"""
Test the functions in the :mod:`songbeamerimport` module.
"""
+ def setUp(self):
+ """
+ Create the registry
+ """
+ Registry.create()
+
def create_importer_test(self):
"""
Test creating an instance of the SongBeamer file importer
diff --git a/tests/functional/openlp_plugins/songs/test_zionworximport.py b/tests/functional/openlp_plugins/songs/test_zionworximport.py
index faedc7005..2d2bae4d0 100644
--- a/tests/functional/openlp_plugins/songs/test_zionworximport.py
+++ b/tests/functional/openlp_plugins/songs/test_zionworximport.py
@@ -35,12 +35,19 @@ from unittest import TestCase
from tests.functional import MagicMock, patch
from openlp.plugins.songs.lib.importers.zionworx import ZionWorxImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
+from openlp.core.common import Registry
class TestZionWorxImport(TestCase):
"""
Test the functions in the :mod:`zionworximport` module.
"""
+ def setUp(self):
+ """
+ Create the registry
+ """
+ Registry.create()
+
def create_importer_test(self):
"""
Test creating an instance of the ZionWorx file importer
diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py
index 01bfafdd8..7a911dea8 100644
--- a/tests/helpers/songfileimport.py
+++ b/tests/helpers/songfileimport.py
@@ -34,6 +34,8 @@ import json
import logging
from unittest import TestCase
+from openlp.plugins.songs.lib.importers.opensong import OpenSongImport
+from openlp.core.common import Registry
from tests.functional import patch, MagicMock, call
log = logging.getLogger(__name__)
@@ -53,6 +55,7 @@ class SongImportTestHelper(TestCase):
"""
Patch and set up the mocks required.
"""
+ Registry.create()
self.add_copyright_patcher = patch('openlp.plugins.songs.lib.importers.%s.%s.add_copyright' %
(self.importer_module_name, self.importer_class_name))
self.add_verse_patcher = patch('openlp.plugins.songs.lib.importers.%s.%s.add_verse' %
diff --git a/tests/resources/openlyricssongs/duchu-tags.xml b/tests/resources/openlyricssongs/duchu-tags.xml
new file mode 100644
index 000000000..e082ce5fc
--- /dev/null
+++ b/tests/resources/openlyricssongs/duchu-tags.xml
@@ -0,0 +1,27 @@
+
+
+
+
+ Duchu svätý volám príď <akordy>
+
+
+ Author Unknown
+
+
+
+
+
+ <span class="chord" style="display:none"><strong>
+ </strong></span>
+
+
+
+
+
+ [D]Duchu svätý volám príď, [Ami]oheň mojej duši daj,
[G]Oheň môjmu telu daj, [D]rozpáľ ma.
+
+
+ Všemoh[Ami]úci [G]Boh tu s nami [D]je,
neko[Ami]nečne [G]milostivý [D]je,
Uka[Ami]zuje [G]dobrotivú [D]tvár voči [Ami]t[G]ým,
ktorí milovať ho [D]chcú.
+
+
+
\ No newline at end of file