diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py index 6b1aa425a..56fbe7d3a 100644 --- a/openlp/core/common/settings.py +++ b/openlp/core/common/settings.py @@ -186,6 +186,7 @@ class Settings(QtCore.QSettings): 'advanced/x11 bypass wm': X11_BYPASS_DEFAULT, 'advanced/search as type': True, 'advanced/ui_theme_name': UiThemes.Automatic, + 'advanced/delete service item confirmation': False, 'alerts/font face': QtGui.QFont().family(), 'alerts/font size': 40, 'alerts/db type': 'sqlite', diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 4d2eb7f81..1f7582ee8 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -359,6 +359,18 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi self.service_item_edit_form = ServiceItemEditForm() self.start_time_form = StartTimeForm() + def _delete_confirmation_dialog(self): + msg_box = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Question, + translate('OpenLP.ServiceManager', 'Delete item from service'), + translate('OpenLP.ServiceManager', 'Are you sure you want to delete ' + 'this item from the service?'), + QtWidgets.QMessageBox.StandardButtons( + QtWidgets.QMessageBox.Close | QtWidgets.QMessageBox.Cancel), self) + del_button = msg_box.button(QtWidgets.QMessageBox.Close) + del_button.setText(translate('OpenLP.ServiceManager', '&Delete item')) + msg_box.setDefaultButton(QtWidgets.QMessageBox.Close) + return msg_box.exec() + def add_media_suffixes(self): """ Add the suffixes supported by :mod:`openlp.core.ui.media.vlcplayer` @@ -1276,7 +1288,8 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi Remove the current ServiceItem from the list. """ item = self.find_service_item()[0] - if item != -1: + if item != -1 and (not self.settings.value('advanced/delete service item confirmation') or + self._delete_confirmation_dialog() == QtWidgets.QMessageBox.Close): self.service_items.remove(self.service_items[item]) self.repaint_service_list(item - 1, -1) self.set_modified() diff --git a/openlp/core/ui/servicetab.py b/openlp/core/ui/servicetab.py index 2fb9bdd76..52355b4fa 100644 --- a/openlp/core/ui/servicetab.py +++ b/openlp/core/ui/servicetab.py @@ -133,6 +133,15 @@ class ServiceTab(SettingsTab): self.next_item_radio_button.setObjectName('next_item_radio_button') self.slide_layout.addWidget(self.next_item_radio_button) self.right_layout.addWidget(self.slide_group_box) + # Service Editing + self.service_editing_groupbox = QtWidgets.QGroupBox(self.left_column) + self.service_editing_groupbox.setObjectName('service_editing_groupbox') + self.service_editing_layout = QtWidgets.QFormLayout(self.service_editing_groupbox) + self.service_editing_layout.setObjectName('service_editing_layout') + self.service_editing_check_box = QtWidgets.QCheckBox(self.service_editing_groupbox) + self.service_editing_check_box.setObjectName('service_editing_check_box') + self.service_editing_layout.addRow(self.service_editing_check_box) + self.right_layout.addWidget(self.service_editing_groupbox) # Push everything in both columns to the top self.left_layout.addStretch() self.right_layout.addStretch() @@ -189,6 +198,10 @@ class ServiceTab(SettingsTab): self.end_slide_radio_button.setText(translate('OpenLP.ServiceTab', '&Remain on Slide')) self.wrap_slide_radio_button.setText(translate('OpenLP.ServiceTab', '&Wrap around')) self.next_item_radio_button.setText(translate('OpenLP.ServiceTab', '&Move to next/previous service item')) + # Service Editing + self.service_editing_groupbox.setTitle(translate('OpenLP.ServiceTab', 'Service Editing')) + self.service_editing_check_box.setText(translate('OpenLP.ServiceTab', 'Show confirmation box when deleting ' + 'item from service')) def load(self): """ @@ -216,6 +229,8 @@ class ServiceTab(SettingsTab): self.wrap_slide_radio_button.setChecked(True) else: self.next_item_radio_button.setChecked(True) + # Service Editing + self.service_editing_check_box.setChecked(self.settings.value('advanced/delete service item confirmation')) def save(self): """ @@ -242,6 +257,8 @@ class ServiceTab(SettingsTab): # Service Item Wrapping self.settings.setValue('advanced/slide limits', self.slide_limits) self.settings_form.register_post_process('slidecontroller_update_slide_limits') + # Service Editing + self.settings.setValue('advanced/delete service item confirmation', self.service_editing_check_box.isChecked()) self.post_set_up() def post_set_up(self): diff --git a/tests/openlp_core/ui/test_servicemanager.py b/tests/openlp_core/ui/test_servicemanager.py index cd36edee2..6398d0227 100644 --- a/tests/openlp_core/ui/test_servicemanager.py +++ b/tests/openlp_core/ui/test_servicemanager.py @@ -25,6 +25,7 @@ from pathlib import Path from unittest import TestCase from unittest.mock import MagicMock, patch +import pytest from PyQt5 import QtCore, QtGui, QtWidgets from openlp.core.common import ThemeLevel @@ -1097,6 +1098,105 @@ def test_on_new_service_clicked_unmodified_blank_service(MockQMessageBox, regist service_manager.new_file.assert_called_once_with() +def _create_mock_action(parent, name, **kwargs): + """ + Create a fake action with some "real" attributes + """ + action = QtWidgets.QAction(parent) + action.setObjectName(name) + if kwargs.get('triggers'): + action.triggered.connect(kwargs.pop('triggers')) + return action + + +def _add_service_item(s_manager): + "adds a mocked service item to the passed service manager" + mocked_plugin = MagicMock() + mocked_plugin.name = 'songs' + service_item = ServiceItem(mocked_plugin) + service_item.add_icon() + slide = "Test slide" + service_item.add_from_text(slide) + service_item.title = "Test item" + s_manager.add_service_item(service_item, rebuild=True, selected=True) + + +@pytest.fixture() +def service_manager(registry, settings): + """Setup a service manager with a service item and a few mocked registry entries""" + # Mocked registry entries + Registry().register('main_window', MagicMock()) + Registry().register('live_controller', MagicMock()) + Registry().register('renderer', MagicMock()) + + # Service manager + service_manager = ServiceManager() + add_toolbar_action_patcher = patch('openlp.core.ui.servicemanager.OpenLPToolbar.add_toolbar_action') + mocked_add_toolbar_action = add_toolbar_action_patcher.start() + mocked_add_toolbar_action.side_effect = \ + lambda name, **kwargs: _create_mock_action(service_manager.toolbar, name, **kwargs) + service_manager.setup_ui(service_manager) + + yield service_manager + del service_manager + + add_toolbar_action_patcher.stop() + + +def test_on_delete_from_service_confirmation_disabled(settings, service_manager): + """ + Test that when the on_delete_from_service function is called and + confirmation for deleting items is disabled, the currently selected + item is removed. + """ + # GIVEN delete item confirmation is disabled and a mock service item + settings.setValue('advanced/delete service item confirmation', False) + _add_service_item(service_manager) + + # WHEN the on_delete_from_service function is called + service_manager.on_delete_from_service() + + # THEN the service_items list should be empty + assert len(service_manager.service_items) == 0 + + +def test_on_delete_from_service_confirmation_enabled_choose_delete(settings, service_manager): + """ + Test that when the on_delete_from_service function is called and + confirmation for deleting items is enabled, and the user confirms + deletion, the currently selected item is removed from the service. + """ + # GIVEN delete item confirmation is enabled and a mock service item + settings.setValue('advanced/delete service item confirmation', True) + _add_service_item(service_manager) + + # WHEN the on_delete_from_service function is called and the user chooses to delete + service_manager._delete_confirmation_dialog = MagicMock(return_value=QtWidgets.QMessageBox.Close) + service_manager.on_delete_from_service() + + # THEN the service_items list should be empty + assert len(service_manager.service_items) == 0 + + +def test_on_delete_from_service_confirmation_enabled_choose_cancel(settings, service_manager): + """ + Test that when the on_delete_from_service function is called and + confirmation for deleting items is enabled, and the user confirms + deletion, the service remains unchanged. + """ + # GIVEN delete item confirmation is enabled a mock service item + settings.setValue('advanced/delete service item confirmation', True) + _add_service_item(service_manager) + service_items_copy = service_manager.service_items.copy() + + # WHEN the on_delete_from_service function is called and the user cancels + service_manager._delete_confirmation_dialog = MagicMock(return_value=QtWidgets.QMessageBox.Cancel) + service_manager.on_delete_from_service() + + # THEN the service_items list should be unchanged + assert service_manager.service_items == service_items_copy + + def test_on_load_service_clicked(registry): """Test that a service is loaded when you click the button""" # GIVEN: A service manager