diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index 833aa5d10..9f200fea9 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -233,6 +233,15 @@ def qmd5_hash(salt, data): return decode(hash_value.data(), 'ascii') +def clean_button_text(button_text): + """ + Clean the & and other characters out of button text + + :param button_text: The text to clean + """ + return button_text.replace('&', '').replace('< ', '').replace(' >', '') + + from .openlpmixin import OpenLPMixin from .registry import Registry from .registrymixin import RegistryMixin diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 5893e8c38..466021af4 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -563,8 +563,11 @@ s self.add_to_service(replace=self.remote_triggered) else: items = self.list_view.selectedIndexes() + drop_position = self.service_manager.get_drop_position() for item in items: - self.add_to_service(item) + self.add_to_service(item, position=drop_position) + if drop_position != -1: + drop_position += 1 def add_to_service_remote(self, message): """ @@ -574,18 +577,19 @@ s """ self.add_to_service(message[0], remote=message[1]) - def add_to_service(self, item=None, replace=None, remote=False): + def add_to_service(self, item=None, replace=None, remote=False, position=-1): """ Add this item to the current service. :param item: Item to be processed :param replace: Replace the existing item :param remote: Triggered from remote + :param position: Position to place item """ service_item = self.build_service_item(item, True, remote=remote, context=ServiceItemContext.Service) if service_item: service_item.from_plugin = False - self.service_manager.add_service_item(service_item, replace=replace) + self.service_manager.add_service_item(service_item, replace=replace, position=position) def on_add_edit_click(self): """ diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index 1887eb53a..02d9e65f7 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -41,7 +41,8 @@ from configparser import ConfigParser from PyQt4 import QtCore, QtGui -from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, check_directory_exists, translate +from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, check_directory_exists, \ + translate, clean_button_text from openlp.core.lib import PluginStatus, build_icon from openlp.core.utils import get_web_page from .firsttimewizard import UiFirstTimeWizard, FirstTimePage @@ -395,20 +396,20 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties): if self.has_run_wizard: self.progress_label.setText(translate('OpenLP.FirstTimeWizard', 'Download complete. Click the %s button to return to OpenLP.') % - self.buttonText(QtGui.QWizard.FinishButton)) + clean_button_text(self.buttonText(QtGui.QWizard.FinishButton))) else: self.progress_label.setText(translate('OpenLP.FirstTimeWizard', 'Download complete. Click the %s button to start OpenLP.') % - self.buttonText(QtGui.QWizard.FinishButton)) + clean_button_text(self.buttonText(QtGui.QWizard.FinishButton))) else: if self.has_run_wizard: self.progress_label.setText(translate('OpenLP.FirstTimeWizard', 'Click the %s button to return to OpenLP.') % - self.buttonText(QtGui.QWizard.FinishButton)) + clean_button_text(self.buttonText(QtGui.QWizard.FinishButton))) else: self.progress_label.setText(translate('OpenLP.FirstTimeWizard', 'Click the %s button to start OpenLP.') % - self.buttonText(QtGui.QWizard.FinishButton)) + clean_button_text(self.buttonText(QtGui.QWizard.FinishButton))) self.finish_button.setVisible(True) self.finish_button.setEnabled(True) self.cancel_button.setVisible(False) diff --git a/openlp/core/ui/firsttimewizard.py b/openlp/core/ui/firsttimewizard.py index a3c8dd7b2..4e83a3e74 100644 --- a/openlp/core/ui/firsttimewizard.py +++ b/openlp/core/ui/firsttimewizard.py @@ -31,7 +31,7 @@ The UI widgets for the first time wizard. """ from PyQt4 import QtCore, QtGui -from openlp.core.common import translate, is_macosx +from openlp.core.common import translate, is_macosx, clean_button_text from openlp.core.lib import build_icon from openlp.core.lib.ui import add_welcome_page @@ -220,7 +220,7 @@ class UiFirstTimeWizard(object): first_time_wizard.information_label.setText( translate('OpenLP.FirstTimeWizard', 'This wizard will help you to configure OpenLP for initial use. ' 'Click the %s button below to start.') % - first_time_wizard.buttonText(QtGui.QWizard.NextButton)) + clean_button_text(first_time_wizard.buttonText(QtGui.QWizard.NextButton))) self.plugin_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Activate required Plugins')) self.plugin_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select the Plugins you wish to use. ')) self.songs_check_box.setText(translate('OpenLP.FirstTimeWizard', 'Songs')) @@ -245,7 +245,7 @@ class UiFirstTimeWizard(object): self.cancel_wizard_text = translate('OpenLP.FirstTimeWizard', '\n\nTo cancel the First Time Wizard completely (and not start OpenLP), ' 'click the %s button now.') % \ - first_time_wizard.buttonText(QtGui.QWizard.CancelButton) + clean_button_text(first_time_wizard.buttonText(QtGui.QWizard.CancelButton)) self.songs_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Songs')) self.songs_page.setSubTitle(translate('OpenLP.FirstTimeWizard', 'Select and download public domain songs.')) self.bibles_page.setTitle(translate('OpenLP.FirstTimeWizard', 'Sample Bibles')) diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index de0ff57ba..04afeb034 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -168,6 +168,8 @@ class VlcPlayer(MediaPlayer): 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() diff --git a/openlp/core/ui/projector/__init__.py b/openlp/core/ui/projector/__init__.py new file mode 100644 index 000000000..9b51f110b --- /dev/null +++ b/openlp/core/ui/projector/__init__.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# This program is free software; you can redistribute it and/or modify it # +# under the terms of the GNU General Public License as published by the Free # +# Software Foundation; version 2 of the License. # +# # +# This program is distributed in the hope that it will be useful, but WITHOUT # +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # +# more details. # +# # +# You should have received a copy of the GNU General Public License along # +# with this program; if not, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +The Projector driver module. +""" \ No newline at end of file diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index d0ac1560e..48d4de34a 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -324,7 +324,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage self.inactive = build_icon(':/media/auto-start_inactive.png') self.service_items = [] self.suffixes = [] - self.drop_position = 0 + self.drop_position = -1 self.service_id = 0 # is a new service and has not been saved self._modified = False @@ -1377,7 +1377,8 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage self.live_controller.replace_service_manager_item(new_item) self.set_modified() - def add_service_item(self, item, rebuild=False, expand=None, replace=False, repaint=True, selected=False): + def add_service_item(self, item, rebuild=False, expand=None, replace=False, repaint=True, selected=False, + position=-1): """ Add a Service item to the list @@ -1387,11 +1388,13 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage :param replace: Is the service item a replacement (Default False) :param repaint: Do we need to repaint the service item list (Default True) :param selected: Has the item been selected (Default False) + :param position: The position where the item is dropped (Default -1) """ # if not passed set to config value if expand is None: expand = Settings().value('advanced/expand service item') item.from_service = True + self.drop_position = position if replace: s_item, child = self.find_service_item() item.merge(self.service_items[s_item]['service_item']) @@ -1401,7 +1404,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage else: item.render() # nothing selected for dnd - if self.drop_position == 0: + if self.drop_position == -1: if isinstance(item, list): for ind_item in item: self.service_items.append({'service_item': ind_item, @@ -1421,7 +1424,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage # if rebuilding list make sure live is fixed. if rebuild: self.live_controller.replace_service_manager_item(item) - self.drop_position = 0 + self.drop_position = -1 self.set_modified() def make_preview(self): @@ -1604,7 +1607,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage item.setSelected(True) replace = True else: - self.drop_position = self._get_parent_item_data(item) + self.drop_position = self._get_parent_item_data(item) - 1 Registry().execute('%s_add_service_item' % plugin, replace) def update_theme_list(self, theme_list): @@ -1666,3 +1669,9 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage """ setting_dialog = PrintServiceForm() setting_dialog.exec_() + + def get_drop_position(self): + """ + Getter for drop_position. Used in: MediaManagerItem + """ + return self.drop_position diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index b07c7fd2b..03890a028 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -563,12 +563,12 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager, R else: abort_import = False for name in theme_zip.namelist(): - name = name.replace('/', os.path.sep) - split_name = name.split(os.path.sep) + out_name = name.replace('/', os.path.sep) + split_name = out_name.split(os.path.sep) if split_name[-1] == '' or len(split_name) == 1: # is directory or preview file continue - full_name = os.path.join(directory, name) + full_name = os.path.join(directory, out_name) check_directory_exists(os.path.dirname(full_name)) if os.path.splitext(name)[1].lower() == '.xml': file_xml = str(theme_zip.read(name), 'utf-8') diff --git a/openlp/plugins/media/forms/mediaclipselectorform.py b/openlp/plugins/media/forms/mediaclipselectorform.py index d63e8a8bb..86727d336 100644 --- a/openlp/plugins/media/forms/mediaclipselectorform.py +++ b/openlp/plugins/media/forms/mediaclipselectorform.py @@ -219,7 +219,7 @@ class MediaClipSelectorForm(QtGui.QDialog, Ui_MediaClipSelector, RegistryPropert 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 os.name == 'nt': + if is_win(): # If the given path is in the format "D:\" or "D:", prefix it with "/" to make VLC happy pattern = re.compile('^\w:\\\\*$') if pattern.match(path): diff --git a/tests/functional/openlp_core_common/test_common.py b/tests/functional/openlp_core_common/test_common.py index 0474bd404..bd506cb64 100644 --- a/tests/functional/openlp_core_common/test_common.py +++ b/tests/functional/openlp_core_common/test_common.py @@ -33,7 +33,7 @@ Functional tests to test the AppLocation class and related methods. from unittest import TestCase from openlp.core.common import check_directory_exists, de_hump, trace_error_handler, translate, is_win, is_macosx, \ - is_linux + is_linux, clean_button_text from tests.functional import MagicMock, patch @@ -188,3 +188,17 @@ class TestCommonFunctions(TestCase): 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') + + def clean_button_text_test(self): + """ + Test the clean_button_text() function. + """ + # GIVEN: Button text + input_text = '&Next >' + expected_text = 'Next' + + # WHEN: The button caption is sent through the clean_button_text function + actual_text = clean_button_text(input_text) + + # THEN: The text should have been cleaned + self.assertEqual(expected_text, actual_text, 'The text should be clean') diff --git a/tests/functional/openlp_core_ui/test_thememanager.py b/tests/functional/openlp_core_ui/test_thememanager.py index d8f2116f9..287ab6219 100644 --- a/tests/functional/openlp_core_ui/test_thememanager.py +++ b/tests/functional/openlp_core_ui/test_thememanager.py @@ -31,9 +31,11 @@ Package to test the openlp.core.ui.thememanager package. """ import zipfile import os +import shutil from unittest import TestCase from tests.interfaces import MagicMock +from tempfile import mkdtemp from openlp.core.ui import ThemeManager from openlp.core.common import Registry @@ -135,3 +137,25 @@ class TestThemeManager(TestCase): # THEN: The mocked_copyfile should not have been called self.assertTrue(mocked_copyfile.called, 'shutil.copyfile should be called') + + def unzip_theme_test(self): + """ + Test that unzipping of themes works + """ + # GIVEN: A theme file, a output folder and some mocked out internal functions + with patch('openlp.core.ui.thememanager.critical_error_message_box') \ + as mocked_critical_error_message_box: + theme_manager = ThemeManager(None) + theme_manager._create_theme_from_xml = MagicMock() + theme_manager.generate_and_save_image = MagicMock() + theme_manager.path = '' + folder = mkdtemp() + theme_file = os.path.join(TEST_RESOURCES_PATH, 'themes', 'Moss_on_tree.otz') + + # WHEN: We try to unzip it + theme_manager.unzip_theme(theme_file, folder) + + # THEN: Files should be unpacked + self.assertTrue(os.path.exists(os.path.join(folder, 'Moss on tree', 'Moss on tree.xml'))) + self.assertEqual(mocked_critical_error_message_box.call_count, 0, 'No errors should have happened') + shutil.rmtree(folder) diff --git a/tests/resources/themes/Moss_on_tree.otz b/tests/resources/themes/Moss_on_tree.otz new file mode 100755 index 000000000..14d8c829e Binary files /dev/null and b/tests/resources/themes/Moss_on_tree.otz differ