Esempio n. 1
0
 def setUp(self):
     """
     Create the UI
     """
     Registry.create()
     self.setup_application()
     self.main_window = QtWidgets.QMainWindow()
     Registry().register('main_window', self.main_window)
     media_item = MagicMock()
     manager = MagicMock()
     self.form = EditCustomForm(media_item, self.main_window, manager)
Esempio n. 2
0
 def setup_item(self):
     """
     Do some additional setup.
     """
     self.edit_custom_form = EditCustomForm(self, self.main_window, self.plugin.db_manager)
     self.single_service_item = False
     self.quick_preview_allowed = True
     self.has_search = True
     # Holds information about whether the edit is remotely triggered and
     # which Custom is required.
     self.remote_custom = -1
Esempio n. 3
0
 def setUp(self):
     """
     Create the UI
     """
     Registry.create()
     self.setup_application()
     self.main_window = QtWidgets.QMainWindow()
     Registry().register('main_window', self.main_window)
     media_item = MagicMock()
     manager = MagicMock()
     self.form = EditCustomForm(media_item, self.main_window, manager)
Esempio n. 4
0
 def __init__(self, parent, plugin):
     self.icon_path = 'custom/custom'
     super(CustomMediaItem, self).__init__(parent, plugin)
     self.edit_custom_form = EditCustomForm(self, self.main_window, self.plugin.manager)
     self.single_service_item = False
     self.quick_preview_allowed = True
     self.has_search = True
     # Holds information about whether the edit is remotely triggered and
     # which Custom is required.
     self.remote_custom = -1
     self.manager = plugin.manager
Esempio n. 5
0
class TestEditCustomForm(TestCase, TestMixin):
    """
    Test the EditCustomForm.
    """
    def setUp(self):
        """
        Create the UI
        """
        Registry.create()
        self.setup_application()
        self.main_window = QtWidgets.QMainWindow()
        Registry().register('main_window', self.main_window)
        media_item = MagicMock()
        manager = MagicMock()
        self.form = EditCustomForm(media_item, self.main_window, manager)

    def tearDown(self):
        """
        Delete all the C++ objects at the end so that we don't have a segfault
        """
        del self.form
        del self.main_window

    def test_load_themes(self):
        """
        Test the load_themes() method.
        """
        # GIVEN: A theme list.
        theme_list = ['First Theme', 'Second Theme']

        # WHEN: Show the dialog and add pass a theme list.
        self.form.load_themes(theme_list)

        # THEN: There should be three items in the combo box.
        assert self.form.theme_combo_box.count() == 3, 'There should be three items (themes) in the combo box.'

    def test_load_custom(self):
        """
        Test the load_custom() method.
        """
        # WHEN: Create a new custom item.
        self.form.load_custom(0)

        # THEN: The line edits should not contain any text.
        self.assertEqual(self.form.title_edit.text(), '', 'The title edit should be empty')
        self.assertEqual(self.form.credit_edit.text(), '', 'The credit edit should be empty')

    def test_on_add_button_clicked(self):
        """
        Test the on_add_button_clicked_test method / add_button button.
        """
        # GIVEN: A mocked QDialog.exec() method
        with patch('PyQt5.QtWidgets.QDialog.exec') as mocked_exec:
            # WHEN: Add a new slide.
            QtTest.QTest.mouseClick(self.form.add_button, QtCore.Qt.LeftButton)

            # THEN: One slide should be added.
            assert self.form.slide_list_view.count() == 1, 'There should be one slide added.'

    def test_validate_not_valid_part1(self):
        """
        Test the _validate() method.
        """
        # GIVEN: Mocked methods.
        with patch('openlp.plugins.custom.forms.editcustomform.critical_error_message_box') as \
                mocked_critical_error_message_box:
            self.form.title_edit.displayText = MagicMock(return_value='')
            mocked_setFocus = MagicMock()
            self.form.title_edit.setFocus = mocked_setFocus

            # WHEN: Call the method.
            result = self.form._validate()

            # THEN: The validate method should have returned False.
            assert not result, 'The _validate() method should have retured False'
            mocked_setFocus.assert_called_with()
            mocked_critical_error_message_box.assert_called_with(message='You need to type in a title.')

    def test_validate_not_valid_part2(self):
        """
        Test the _validate() method.
        """
        # GIVEN: Mocked methods.
        with patch('openlp.plugins.custom.forms.editcustomform.critical_error_message_box') as \
                mocked_critical_error_message_box:
            self.form.title_edit.displayText = MagicMock(return_value='something')
            self.form.slide_list_view.count = MagicMock(return_value=0)

            # WHEN: Call the method.
            result = self.form._validate()

            # THEN: The validate method should have returned False.
            assert not result, 'The _validate() method should have retured False'
            mocked_critical_error_message_box.assert_called_with(message='You need to add at least one slide.')

    def test_update_slide_list(self):
        """
        Test the update_slide_list() method
        """
        # GIVEN: Mocked slide_list_view with a slide with 3 lines
        self.form.slide_list_view = MagicMock()
        self.form.slide_list_view.count.return_value = 1
        self.form.slide_list_view.currentRow.return_value = 0
        self.form.slide_list_view.item.return_value = MagicMock(return_value='1st Slide\n2nd Slide\n3rd Slide')

        # WHEN: updating the slide by splitting the lines into slides
        self.form.update_slide_list(['1st Slide', '2nd Slide', '3rd Slide'])

        # THEN: The slides should be created in correct order
        self.form.slide_list_view.addItems.assert_called_with(['1st Slide', '2nd Slide', '3rd Slide'])
Esempio n. 6
0
class TestEditCustomForm(TestCase, TestMixin):
    """
    Test the EditCustomForm.
    """
    def setUp(self):
        """
        Create the UI
        """
        Registry.create()
        self.setup_application()
        self.main_window = QtWidgets.QMainWindow()
        Registry().register('main_window', self.main_window)
        media_item = MagicMock()
        manager = MagicMock()
        self.form = EditCustomForm(media_item, self.main_window, manager)

    def tearDown(self):
        """
        Delete all the C++ objects at the end so that we don't have a segfault
        """
        del self.form
        del self.main_window

    def test_load_themes(self):
        """
        Test the load_themes() method.
        """
        # GIVEN: A theme list.
        theme_list = ['First Theme', 'Second Theme']

        # WHEN: Show the dialog and add pass a theme list.
        self.form.load_themes(theme_list)

        # THEN: There should be three items in the combo box.
        assert self.form.theme_combo_box.count(
        ) == 3, 'There should be three items (themes) in the combo box.'

    def test_load_custom(self):
        """
        Test the load_custom() method.
        """
        # WHEN: Create a new custom item.
        self.form.load_custom(0)

        # THEN: The line edits should not contain any text.
        assert self.form.title_edit.text(
        ) == '', 'The title edit should be empty'
        assert self.form.credit_edit.text(
        ) == '', 'The credit edit should be empty'

    def test_on_add_button_clicked(self):
        """
        Test the on_add_button_clicked_test method / add_button button.
        """
        # GIVEN: A mocked QDialog.exec() method
        with patch('PyQt5.QtWidgets.QDialog.exec') as mocked_exec:
            # WHEN: Add a new slide.
            QtTest.QTest.mouseClick(self.form.add_button, QtCore.Qt.LeftButton)

            # THEN: One slide should be added.
            assert self.form.slide_list_view.count(
            ) == 1, 'There should be one slide added.'

    def test_validate_not_valid_part1(self):
        """
        Test the _validate() method.
        """
        # GIVEN: Mocked methods.
        with patch('openlp.plugins.custom.forms.editcustomform.critical_error_message_box') as \
                mocked_critical_error_message_box:
            self.form.title_edit.displayText = MagicMock(return_value='')
            mocked_setFocus = MagicMock()
            self.form.title_edit.setFocus = mocked_setFocus

            # WHEN: Call the method.
            result = self.form._validate()

            # THEN: The validate method should have returned False.
            assert not result, 'The _validate() method should have retured False'
            mocked_setFocus.assert_called_with()
            mocked_critical_error_message_box.assert_called_with(
                message='You need to type in a title.')

    def test_validate_not_valid_part2(self):
        """
        Test the _validate() method.
        """
        # GIVEN: Mocked methods.
        with patch('openlp.plugins.custom.forms.editcustomform.critical_error_message_box') as \
                mocked_critical_error_message_box:
            self.form.title_edit.displayText = MagicMock(
                return_value='something')
            self.form.slide_list_view.count = MagicMock(return_value=0)

            # WHEN: Call the method.
            result = self.form._validate()

            # THEN: The validate method should have returned False.
            assert not result, 'The _validate() method should have retured False'
            mocked_critical_error_message_box.assert_called_with(
                message='You need to add at least one slide.')

    def test_update_slide_list(self):
        """
        Test the update_slide_list() method
        """
        # GIVEN: Mocked slide_list_view with a slide with 3 lines
        self.form.slide_list_view = MagicMock()
        self.form.slide_list_view.count.return_value = 1
        self.form.slide_list_view.currentRow.return_value = 0
        self.form.slide_list_view.item.return_value = MagicMock(
            return_value='1st Slide\n2nd Slide\n3rd Slide')

        # WHEN: updating the slide by splitting the lines into slides
        self.form.update_slide_list(['1st Slide', '2nd Slide', '3rd Slide'])

        # THEN: The slides should be created in correct order
        self.form.slide_list_view.addItems.assert_called_with(
            ['1st Slide', '2nd Slide', '3rd Slide'])
Esempio n. 7
0
class CustomMediaItem(MediaManagerItem):
    """
    This is the custom media manager item for Custom Slides.
    """
    log.info('Custom Media Item loaded')

    def __init__(self, parent, plugin):
        self.icon_path = 'custom/custom'
        super(CustomMediaItem, self).__init__(parent, plugin)

    def setup_item(self):
        """
        Do some additional setup.
        """
        self.edit_custom_form = EditCustomForm(self, self.main_window, self.plugin.db_manager)
        self.single_service_item = False
        self.quick_preview_allowed = True
        self.has_search = True
        # Holds information about whether the edit is remotely triggered and
        # which Custom is required.
        self.remote_custom = -1

    def add_end_header_bar(self):
        """
        Add the Custom End Head bar and register events and functions
        """
        self.toolbar.addSeparator()
        self.add_search_to_toolbar()
        # Signals and slots
        QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL('cleared()'), self.on_clear_text_button_click)
        QtCore.QObject.connect(self.search_text_edit, QtCore.SIGNAL('searchTypeChanged(int)'),
                               self.on_search_text_button_clicked)
        Registry().register_function('custom_load_list', self.load_list)
        Registry().register_function('custom_preview', self.on_preview_click)
        Registry().register_function('custom_create_from_service', self.create_from_service_item)

    def config_update(self):
        """
        Config has been updated so reload values
        """
        log.debug('Config loaded')
        self.add_custom_from_service = Settings().value(self.settings_section + '/add custom from service')

    def retranslateUi(self):
        """

        """
        self.search_text_label.setText('%s:' % UiStrings().Search)
        self.search_text_button.setText(UiStrings().Search)

    def initialise(self):
        """
        Initialise the UI so it can provide Searches
        """
        self.search_text_edit.set_search_types(
            [(CustomSearch.Titles, ':/songs/song_search_title.png', translate('SongsPlugin.MediaItem', 'Titles'),
              translate('SongsPlugin.MediaItem', 'Search Titles...')),
             (CustomSearch.Themes, ':/slides/slide_theme.png', UiStrings().Themes, UiStrings().SearchThemes)])
        self.search_text_edit.set_current_search_type(Settings().value('%s/last search type' % self.settings_section))
        self.load_list(self.plugin.db_manager.get_all_objects(CustomSlide, order_by_ref=CustomSlide.title))
        self.config_update()

    def load_list(self, custom_slides, target_group=None):
        # Sort out what custom we want to select after loading the list.
        """

        :param custom_slides:
        :param target_group:
        """
        self.save_auto_select_id()
        self.list_view.clear()
        custom_slides.sort()
        for custom_slide in custom_slides:
            custom_name = QtGui.QListWidgetItem(custom_slide.title)
            custom_name.setData(QtCore.Qt.UserRole, custom_slide.id)
            self.list_view.addItem(custom_name)
            # Auto-select the custom.
            if custom_slide.id == self.auto_select_id:
                self.list_view.setCurrentItem(custom_name)
        self.auto_select_id = -1
        # Called to redisplay the custom list screen edith from a search
        # or from the exit of the Custom edit dialog. If remote editing is
        # active trigger it and clean up so it will not update again.
        self.check_search_result()

    def on_new_click(self):
        """
        Handle the New item event
        """
        self.edit_custom_form.load_custom(0)
        self.edit_custom_form.exec_()
        self.on_clear_text_button_click()
        self.on_selection_change()

    def on_remote_edit(self, custom_id, preview=False):
        """
        Called by ServiceManager or SlideController by event passing the custom Id in the payload along with an
        indicator to say which type of display is required.

        :param custom_id: The id of the item to be edited
        :param preview: Do we need to update the Preview after the edit. (Default False)
        """
        custom_id = int(custom_id)
        valid = self.plugin.db_manager.get_object(CustomSlide, custom_id)
        if valid:
            self.edit_custom_form.load_custom(custom_id, preview)
            if self.edit_custom_form.exec_() == QtGui.QDialog.Accepted:
                self.remote_triggered = True
                self.remote_custom = custom_id
                self.auto_select_id = -1
                self.on_search_text_button_clicked()
                item = self.build_service_item(remote=True)
                self.remote_triggered = None
                self.remote_custom = 1
                if item:
                    if preview:
                        # A custom slide can only be edited if it comes from plugin,
                        # so we set it again for the new item.
                        item.from_plugin = True
                    return item
        return None

    def on_edit_click(self):
        """
        Edit a custom item
        """
        if check_item_selected(self.list_view, UiStrings().SelectEdit):
            item = self.list_view.currentItem()
            item_id = item.data(QtCore.Qt.UserRole)
            self.edit_custom_form.load_custom(item_id, False)
            self.edit_custom_form.exec_()
            self.auto_select_id = -1
            self.on_search_text_button_clicked()

    def on_delete_click(self):
        """
        Remove a custom item from the list and database
        """
        if check_item_selected(self.list_view, UiStrings().SelectDelete):
            items = self.list_view.selectedIndexes()
            if QtGui.QMessageBox.question(self, UiStrings().ConfirmDelete,
                                          translate('CustomPlugin.MediaItem',
                                                    'Are you sure you want to delete the %n selected custom slide(s)?',
                                                    '', QtCore.QCoreApplication.CodecForTr, len(items)),
                                          QtGui.QMessageBox.StandardButtons(
                                              QtGui.QMessageBox.Yes | QtGui.QMessageBox.No),
                                          QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No:
                return
            row_list = [item.row() for item in self.list_view.selectedIndexes()]
            row_list.sort(reverse=True)
            id_list = [(item.data(QtCore.Qt.UserRole)) for item in self.list_view.selectedIndexes()]
            for id in id_list:
                self.plugin.db_manager.delete_object(CustomSlide, id)
            self.on_search_text_button_clicked()

    def on_focus(self):
        """
        Set the focus
        """
        self.search_text_edit.setFocus()

    def generate_slide_data(self, service_item, item=None, xml_version=False,
                            remote=False, context=ServiceItemContext.Service):
        """
        Generate the slide data. Needs to be implemented by the plugin.
        :param service_item: To be updated
        :param item: The custom database item to be used
        :param xml_version: No used
        :param remote: Is this triggered by the Preview Controller or Service Manager.
        :param context: Why is this item required to be build (Default Service).
        """
        item_id = self._get_id_of_item_to_generate(item, self.remote_custom)
        service_item.add_capability(ItemCapabilities.CanEdit)
        service_item.add_capability(ItemCapabilities.CanPreview)
        service_item.add_capability(ItemCapabilities.CanLoop)
        service_item.add_capability(ItemCapabilities.CanSoftBreak)
        service_item.add_capability(ItemCapabilities.OnLoadUpdate)
        custom_slide = self.plugin.db_manager.get_object(CustomSlide, item_id)
        title = custom_slide.title
        credit = custom_slide.credits
        service_item.edit_id = item_id
        theme = custom_slide.theme_name
        if theme:
            service_item.theme = theme
        custom_xml = CustomXMLParser(custom_slide.text)
        verse_list = custom_xml.get_verses()
        raw_slides = [verse[1] for verse in verse_list]
        service_item.title = title
        for slide in raw_slides:
            service_item.add_from_text(slide)
        if Settings().value(self.settings_section + '/display footer') or credit:
            service_item.raw_footer.append(' '.join([title, credit]))
        else:
            service_item.raw_footer.append('')
        return True

    def on_search_text_button_clicked(self):
        """
        Search the plugin database
        """
        # Save the current search type to the configuration.
        Settings().setValue('%s/last search type' % self.settings_section, self.search_text_edit.current_search_type())
        # Reload the list considering the new search type.
        search_type = self.search_text_edit.current_search_type()
        search_keywords = '%' + self.whitespace.sub(' ', self.search_text_edit.displayText()) + '%'
        if search_type == CustomSearch.Titles:
            log.debug('Titles Search')
            search_results = self.plugin.db_manager.get_all_objects(CustomSlide,
                                                                    CustomSlide.title.like(search_keywords),
                                                                    order_by_ref=CustomSlide.title)
            self.load_list(search_results)
        elif search_type == CustomSearch.Themes:
            log.debug('Theme Search')
            search_results = self.plugin.db_manager.get_all_objects(CustomSlide,
                                                                    CustomSlide.theme_name.like(search_keywords),
                                                                    order_by_ref=CustomSlide.title)
            self.load_list(search_results)
        self.check_search_result()

    def on_search_text_edit_changed(self, text):
        """
        If search as type enabled invoke the search on each key press. If the Title is being searched do not start until
        2 characters have been entered.

        :param text: The search text
        """
        search_length = 2
        if len(text) > search_length:
            self.on_search_text_button_clicked()
        elif not text:
            self.on_clear_text_button_click()

    def service_load(self, item):
        """
        Triggered by a custom item being loaded by the service manager.

        :param item: The service Item from the service to load found in the database.
        """
        log.debug('service_load')
        if self.plugin.status != PluginStatus.Active:
            return
        if item.theme:
            custom = self.plugin.db_manager.get_object_filtered(CustomSlide, and_(CustomSlide.title == item.title,
                                                                CustomSlide.theme_name == item.theme,
                                                                CustomSlide.credits ==
                                                                item.raw_footer[0][len(item.title) + 1:]))
        else:
            custom = self.plugin.db_manager.get_object_filtered(CustomSlide, and_(CustomSlide.title == item.title,
                                                                CustomSlide.credits ==
                                                                item.raw_footer[0][len(item.title) + 1:]))
        if custom:
            item.edit_id = custom.id
            return item
        else:
            if self.add_custom_from_service:
                self.create_from_service_item(item)

    def create_from_service_item(self, item):
        """
        Create a custom slide from a text service item

        :param item:  the service item to be converted to a Custom item
        """
        custom = CustomSlide()
        custom.title = item.title
        if item.theme:
            custom.theme_name = item.theme
        else:
            custom.theme_name = ''
        footer = ' '.join(item.raw_footer)
        if footer:
            if footer.startswith(item.title):
                custom.credits = footer[len(item.title) + 1:]
            else:
                custom.credits = footer
        else:
            custom.credits = ''
        custom_xml = CustomXMLBuilder()
        for (idx, slide) in enumerate(item._raw_frames):
            custom_xml.add_verse_to_lyrics('custom', str(idx + 1), slide['raw_slide'])
        custom.text = str(custom_xml.extract_xml(), 'utf-8')
        self.plugin.db_manager.save_object(custom)
        self.on_search_text_button_clicked()

    def on_clear_text_button_click(self):
        """
        Clear the search text.
        """
        self.search_text_edit.clear()
        self.on_search_text_button_clicked()

    def search(self, string, show_error):
        """
        Search the database for a given item.

        :param string: The search string
        :param show_error: The error string to be show.
        """
        search = '%' + string.lower() + '%'
        search_results = self.plugin.db_manager.get_all_objects(CustomSlide,
                                                                or_(func.lower(CustomSlide.title).like(search),
                                                                    func.lower(CustomSlide.text).like(search)),
                                                                order_by_ref=CustomSlide.title)
        return [[custom.id, custom.title] for custom in search_results]